R2 migration: documentation checks and discrepancies
Checked 2026-09-08. Phase 0 only. These are proposed implementation requirements, not deployed behavior. Official documentation takes precedence over assumptions. The R2 documentation index was fetched first.API compatibility
The S3 compatibility matrix supports GetObject with Range, HeadObject, ListObjects/ListObjectsV2, PutObject, CopyObject, DeleteObject/DeleteObjects, and multipart create/upload/complete/abort/list operations. Feature-level restrictions still matter. No S3 bucket versioning, object tagging, ACL policy API, replication API, Inventory, or S3 notification configuration equivalence is available. Keep revision identity, ownership, retention and tags in application records; use Cloudflare configuration APIs for infrastructure.Upload protocol and checksum defaults
R2 presigned URLs accept GET, HEAD, PUT and DELETE. HTML form POST upload is unsupported. They expire between one second and seven days and work on the S3 API endpoint, not a custom domain. Existing Avala POST forms must remain supported for old clients and in-flight S3 uploads while a new protocol-aware response is introduced. A hostname rewrite invalidates signing. Presigned URLs. The current Cloudflare boto3 example and JavaScript v3 example do not mandatewhen_required. The release notes report CRC64NVME support, including full-object multipart checksums, since 2025-07-03. Therefore, a blanket claim that R2 rejects all modern checksums is outdated. The matrix still distinguishes checksum algorithms and full/composite types. Documentation alone does not establish that every installed SDK streaming body works.
Retain the requested R2-only request_checksum_calculation="when_required" and response_checksum_validation="when_required", or JS requestChecksumCalculation: "WHEN_REQUIRED" and responseChecksumValidation: "WHEN_REQUIRED", as conservative compatibility settings. Verify installed versions and real R2 behavior before promotion, including unknown-length streams and multipart uploads. Keep application SHA-256 validation; disabling automatic optional SDK checksums is not a substitute for integrity verification. AWS integrity settings.
Multipart non-final parts must be equally sized, at least 5 MiB, with at most 10,000 parts. Propose a stored 64 MiB part size for ordinary uploads, increased before initialization when total size requires it; never change it mid-upload. Incomplete uploads expire after seven days by default, so the requested one-day abort requires configuration. Upload objects. Apply object/part ceilings from R2 limits; do not proxy multi-GB uploads through Worker request bodies.
Migration correctness
Sippy is not an exactly-once transfer guarantee: concurrent cache misses can fetch a source multiple times, and objects above 199 MiB may need multiple requests to finish copying. HEAD does not copy. Source overwrites do not refresh an existing R2 copy. Deleting only the R2 object can resurrect the S3 source on a later GET. Only the documented custom metadata is migrated. Sippy. Propose Sippy only for immutable, collision-free source mappings with application tombstones checked before access. Validate Range and metadata behavior in a cost-bounded canary. Multi-source consolidation and renamed tenant keys need explicit mapping; do not assume a destination bucket can infer which original source bucket owns a key. Super Slurper defaults to overwrite; select skip-existing for coexistence with new writes. It supports source subpaths and skips objects above 1 TB. Independently reconcile skipped/failed objects and handle larger objects separately. It does not delete the source. Super Slurper. Prefix batches and catch-up verification remain necessary for live sources. Migration strategies. R2 NFC-normalizes keys by default, so two distinct S3 byte strings can collide. Inventory normalized-key collisions before copying; use opaque destination keys with a lossless original-key map, or obtain a documented byte-oriented configuration. Never overwrite one source with another. Unicode interoperability.Serving, residency and economics
An R2 public custom domain exposes bucket objects. Private assets require an authenticated Worker/proxy and a private binding, or short-lived S3 API presigns. Do not attach an unguarded public custom domain to managed private buckets.r2.dev is for development. Public buckets.
Location hints are placement preferences, not residency guarantees. Use EU jurisdiction plus the EU endpoint for all EU private classes, including derived and tmp. The existing managed-upload bucket is documented in us-east-1, so enam is a candidate; wnam remains a candidate for west-coast pipeline workloads. Actual fleet placement and contracts must decide. Data location.
R2 encrypts at rest and in transit. SSE-C is available, but is not equivalent to AWS KMS key custody or its audit/recovery controls. Determine contractual requirements before replacing the Sessions KMS bucket. EU storage does not by itself make processing on US EC2 EU-resident. Data security.
R2 Standard costs 4.50/million and Class B 0.01/GB-month, Class A 0.90/million and retrieval $0.01/GB, with a 30-day minimum. Egress is free. Pricing. IA also has a 64 KiB minimum billable object size. Leave derived on Standard until observed access frequency supports a transition. Storage classes.
R2 events use Cloudflare Queues. Keep explicit upload completion as the primary enqueue path where available, with idempotent recovery for event-driven producers. Event notifications.
Domain update: the user selected data.avala.ai as the preferred serving domain. It is a naming preference, not a DNS change or permission to expose private buckets. The same custom-domain authentication and public-release separation requirements apply.
Generated integer protocol enum limitation
The upstreamNewDatasetFileUpload.upload_protocol_version schema correctly uses
type: integer, enum: [2], and nullable legacy omission. The pinned
swagger_dart_code_generator 4.1.1 generates an int? property even though it
also emits an unused protocol enum. In its
lib/src/code_generators/swagger_models_generator.dart, getParameterTypeName
lines 425 to 440 returns int for integer schemas before checking enums; the
enum branch applies only to strings. Keep the integer wire contract. Changing
the schema to string would describe the wrong contract, and a global generator
patch would affect unrelated APIs. Server regressions reject versions 1 and 3;
existing generated wire tests cover integer 2 and null. A future pinned generator
upgrade can adopt integer enums after whole-client compatibility verification.