Skip to main content

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 mandate when_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 0.015/GB−month,ClassA0.015/GB-month, Class A 4.50/million and Class B 0.36/million.InfrequentAccesscosts0.36/million. Infrequent Access costs 0.01/GB-month, Class A 9/million,ClassB9/million, Class B 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 upstream NewDatasetFileUpload.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.

Conditional multipart completion requires live verification

Checked 2026-09-10: the R2 S3 compatibility matrix lists CompleteMultipartUpload, but does not explicitly enumerate its destination conditions. The extensions page documents destination conditions for CopyObject, not multipart completion. Successful ordinary multipart completion does not establish create-only behavior. The opt-in driver suite now initiates a multipart upload, creates a different object at that same test key, and requires completion to return a precondition failure while retaining the original ETag and bytes. This adds one tiny final part and one tiny object to each selected driver’s billable test scope. Cleanup uses only its recorded test key and upload ID. Offline fixtures validate the harness; they do not establish actual R2 behavior. Do not activate managed multipart uploads until the real development-bucket case passes. A failed case requires a reviewed design change, never removal of the create-only condition.