Object Storage Cutovers Need URL-Compatibility Evidence, Not S3-Compatible Labels Alone
S3-compatible object storage migrations can look straightforward when both source and target expose familiar APIs, but application readiness depends on more than compatibility labels. This workspace-grounded synthesis studies an nk304 MinIO-to-Cloudflare R2 migration through the migration plan, WBS, API storage URL helper, worker HLS storage path, runtime file-flow E2E script, package guidance, targeted tests, AlexandrAI graph search, and official Cloudflare and AWS documentation. The inspected checkout has strong ingredients: a plan that requires public URL compatibility, upload freeze, final delta sync, R2-based validation, monitoring, and rollback; code that separates public and server endpoint preferences; and 15 passing targeted storage URL/statistics tests. It also has unresolved evidence: 28 open WBS tasks, a stale minimum test command path, residual local object-store assumptions in the E2E script, many remaining source-backend references, and no observed live bucket, copy, public URL, monitoring, or rollback rehearsal records. The contribution is a URL-Compatibility Evidence Chain for object storage cutover readiness.
Introduction
Object storage migrations are often summarized as moving from one S3-compatible backend to another. That phrase is useful but incomplete. The visible user promise is not only that PUT, GET, multipart, and signatures exist. The stronger promise is that existing public URLs keep working, upload and download hosts are intentionally separated, multipart lifecycle semantics remain intact, HLS media still works, data copy and delta sync are checked, and rollback can restore the old path. This paper studies that boundary in an nk304 MinIO to Cloudflare R2 migration plan [[cite:localPlan,cfR2S3,cfR2Api]].
The local plan is thoughtful. It fixes an all-at-once switch, all environments together, public URL compatibility, short upload freeze, final delta sync, automated R2 tests, smoke tests, monitoring, and rollback [[cite:localPlan]]. The WBS is explicit that each item needs evidence such as logs, screenshots, or command-output summaries [[cite:localWbs]]. Yet the inspected state is not migration complete. The WBS has 28 open checkbox tasks and none marked complete; the targeted storage URL and stats contract tests pass, but data migration, route cutover, operational monitoring, and rollback rehearsal were not observed [[cite:localWbsCounts,localTestsRun]].
The contribution is a URL-Compatibility Evidence Chain for object storage cutovers. It separates nine stages: storage contract, endpoint split, public URL compatibility, multipart lifecycle, HLS media path, data copy integrity, cutover freeze, monitoring, and rollback. The chain is narrower than a general cloud migration checklist. It is grounded in the exact surfaces this repository exposes: API storage URL helpers, worker HLS storage, file-flow E2E script, generated contracts, WBS tasks, and Cloudflare/AWS documentation [[cite:localStorageUrls,localWorkerStorage,localWorkerHls,localE2E,cfR2Presign,awsMultipart]].
The central conclusion is proportional. The checkout has credible ingredients for an R2 cutover: S3-style code, endpoint separation, targeted tests, and a strong WBS. It does not yet support a migration-complete claim. A public status should therefore report partial readiness: plan defined, code path partially covered, targeted tests passing, test command path stale, E2E local-store assumptions still present, WBS open, and no live bucket/cutover/rollback evidence observed.
Graph And External Context
AlexandrAI graph search did not find a direct R2 migration paper. Searches for R2 migration and storage cutover returned no direct match; broader object storage and S3 compatibility searches returned archive, provenance, and access-control items rather than an object-storage cutover study [[cite:graphR2Search,graphObjectMigration]]. That means this paper can add a distinct workspace-grounded contribution while still borrowing the archive's recurring principle: claims should stop at the weakest verified stage.
Cloudflare documents R2 as usable through S3-compatible APIs and separately documents presigned URLs, S3 API coverage, limits, public buckets/custom domains, and Sippy migration [[cite:cfR2S3,cfR2Presign,cfR2Api,cfR2Limits,cfR2Public,cfR2Sippy]]. Those documents support the feasibility of an R2 migration, but they do not prove a particular application migration. A real app may rely on endpoint shape, hostnames in generated URLs, CORS, lifecycle cleanup, HLS segment access, or local scripts that assume a source backend.
Amazon S3 documentation supplies the baseline semantics for presigned URLs and multipart upload. A presigned URL is time-limited and inherits the permissions of the signer; multipart upload is a lifecycle that must be initiated, have parts uploaded, and then be completed or aborted [[cite:awsPresigned,awsMultipart]]. The nk304 plan recognizes both concepts by shortening presign expiry during cutover and testing single upload, multipart upload, HLS, admin conversion, deletion and rollback [[cite:localPlan,localWbs]].
Workspace Evidence
The migration plan says the code is already S3-compatible enough that the main work is endpoint and operational procedure, not a new storage driver [[cite:localPlan]]. Code inspection supports that as a partial claim. The API storage helper builds S3-style signatures, separates public endpoint and server endpoint preference, exposes download URL, signed download URL, multipart part URL, multipart create, complete and abort helpers, and rejects unsafe endpoint configuration [[cite:localStorageUrls]]. The worker storage helper and HLS task use S3-style server-side URL generation for source and output operations [[cite:localWorkerStorage,localWorkerHls]].
The local test result is also positive but narrow. Running the current storage URL and stats contract tests produced 15 passes and one deprecation warning [[cite:localTestsRun]]. The command in the plan, however, points to old test locations; the current tests live under platform and contracts subdirectories [[cite:localPlan,localTestPaths]]. This is a classic migration-runbook problem: the tests may exist and pass, while the documented command a future operator follows is stale.
The WBS denominator is clear. It has 28 checkbox tasks and all are open in the inspected tree [[cite:localWbsCounts]]. The tasks cover bucket preparation, credential injection path, public route design, CORS/lifecycle, runbook, endpoint and public URL changes, stats cleanup, fixed-string cleanup, local/deploy script cleanup, E2E script update, docs, bulk copy, integrity sampling, final delta rehearsal, cutover steps, smoke tests, monitoring, public URL checks, operational report, rollback command rehearsal, and rollback trigger definitions [[cite:localWbs]].
Figure 1 should not be read as a defect chart. The 93 MinIO-related references include plans, tests, scripts, generated contracts, locales and code; some are expected before migration, and some may intentionally remain for rollback or compatibility [[cite:localRefCounts]]. The useful claim is that cleanup and compatibility are broad enough to require a ledger. A simple string replacement would be unsafe because public URL compatibility, HLS access, generated contracts, and rollback may need some legacy terminology or routing to remain visible.
URL-Compatibility Evidence Chain
The chain begins with storage contract evidence: the application can generate and validate S3-style operations under its own configuration. The targeted tests support part of this stage [[cite:localTestsRun]]. It continues with endpoint split evidence: server-side write/read operations should use the object API endpoint, while public download URLs should use the public object endpoint or compatible public domain [[cite:localStorageUrls,localWorkerStorage,localWorkerHls]]. The local code has this distinction through public and server endpoint preferences, which is exactly the right architectural seam for an R2 cutover.
The next stage is public URL compatibility. The plan requires existing public URLs to keep working and identifies custom-domain/public-bucket setup as a design concern [[cite:localPlan,cfR2Public]]. This is not proved by S3 API compatibility. A presigned PUT may work while an old public HLS manifest URL breaks. Conversely, a public download URL may work while server-side upload signatures use the wrong host. The ledger must record both directions.
Multipart lifecycle is a separate stage. The local API helper has multipart create, part signing, complete and abort operations [[cite:localStorageUrls]]. The plan requires multipart upload success as a validation scenario [[cite:localPlan,localWbs]]. AWS's multipart overview is relevant because a failed cutover can strand incomplete uploads or produce incomplete-object states if completion/abort behavior differs from assumption [[cite:awsMultipart]]. The migration should therefore record multipart init, part upload, complete and abort evidence under R2.
HLS media compatibility is also distinct. The worker HLS task downloads the source object, derives key material, produces a manifest and segments, uploads outputs, and updates file state [[cite:localWorkerHls]]. The E2E script checks HLS worker readiness, HLS key authorization, manifest reachability, and segment reachability [[cite:localE2E]]. The plan correctly includes HLS as a smoke-test scenario [[cite:localPlan]]. That scenario must be R2-based before the migration can claim parity.
Cutover Evidence
The plan's cutover sequence is strong because it recognizes presigned URL lifetime. It reduces presign expiry before deployment, enters a maintenance window, freezes uploads, waits for the old short-lived URLs to expire, runs final delta copy, switches configuration, restarts services, and performs smoke tests [[cite:localPlan]]. That sequence is consistent with the nature of presigned URLs as time-limited requests using signer permissions [[cite:awsPresigned]].
The missing evidence is execution. No live copy report, object count, byte total, sample HEAD/GET result, old-link check, final delta log, config switch record, or monitoring window was observed. The WBS says those records should exist, but every checkbox is still open [[cite:localWbs,localWbsCounts]]. The paper therefore treats the current state as a planned cutover with partial code/test support, not a completed cutover.
The E2E script is the most important repair target because it already follows the right user journey: presigned upload, completion, worker HLS conversion, HLS status, HLS key authorization, manifest fetch, segment fetch, and admin conversion [[cite:localE2E]]. But the inspected script still contains local object-store manifest/segment checks that the migration plan explicitly says should be replaced by R2 endpoint-based validation [[cite:localPlan,localE2E]]. A cutover-ready E2E should make the storage backend, public endpoint, object endpoint, bucket, and expected public host explicit test inputs without publishing credentials or local ports.
The reference count result reinforces the point. There are more MinIO-related references than R2-related references in the selected migration surface [[cite:localRefCounts]]. Some will remain valid as source-side or rollback language. Others may be stale UI copy, tests, comments, generated contracts, or local-only assumptions. A readiness report should classify each residual reference as source-system, compatibility alias, rollback requirement, stale test fixture, user-visible copy, or generated artifact, rather than deleting strings blindly.
Rollback Evidence
Rollback is not a paragraph; it is a tested reverse path. The local plan defines rollback triggers such as upload failures, HLS failures, and admin download/convert failures, then requires upload freeze, reverse delta copy, configuration and route reversal, service restart, smoke tests, and service resume [[cite:localPlan]]. The WBS asks for rollback command rehearsal and quantitative trigger definitions [[cite:localWbs]]. In the inspected state those tasks are open, so rollback is a planned control, not a proven control.
Rollback evidence should mirror cutover evidence. If forward cutover has object count, byte total, exception list, public URL sample, HLS sample, and multipart sample, rollback should have the same artifacts for the reverse direction. Otherwise a team may discover during an incident that copied objects, public routes, or presigned URL assumptions are asymmetric. The chain therefore requires reverse delta proof before a migration is called reversible.
The practical status label should be `rollback designed, not rehearsed`. That label is stronger than silence because it tells the operator what remains. It also prevents a common management misunderstanding: the presence of a rollback section in a plan does not mean rollback can be executed under pressure. Evidence of rehearsal, owner, command, expected duration, fail conditions, and smoke-test result are the missing pieces.
Implementation Checklist
A minimal public readiness report for this migration should have a row for every stage in Table 1. Each row should contain status, evidence id, command or source, redacted output summary, residual risk, and next action. For example, `targeted storage tests` can point to the 15-test pass; `R2 E2E` should remain not observed; `WBS completion` should state 0 of 28 tasks complete; `public URL compatibility` should remain planned until old and new public paths are sampled [[cite:localTestsRun,localWbsCounts,localPlan]].
The next concrete repair is to update the minimum test command in the plan to current test locations. The tests exist and pass, but a runbook with stale paths is an operational risk [[cite:localTestPaths,localTestsRun]]. A future operator should be able to run the copied command without path archaeology. This is a low-cost, high-value fix because it turns the plan's test requirement into reproducible evidence.
The second repair is to make R2-specific E2E inputs explicit and remove local object-store assumptions from the migration E2E path. The existing E2E journey is valuable and should be retained, but its manifest and segment checks need to target the configured public endpoint rather than a hardcoded local store [[cite:localE2E,localPlan]]. This does not require publishing real endpoints. The report can use placeholders and summarized HTTP status outcomes.
The third repair is to classify residual MinIO references. Each reference should be tagged as source-system, compatibility alias, rollback text, stale test fixture, stale user-visible copy, generated artifact, or harmless historical plan text. Only then should cleanup proceed. That protects rollback and compatibility while still driving stale strings out of runtime and UI surfaces [[cite:localRefCounts,localAgents]].
Compatibility Ledger
The evidence chain becomes operational when each compatibility claim has an owner, an observable check, and a failure interpretation. For storage contract evidence, the observable is not simply that S3 APIs are available. It is that the application's own signing helper can create upload, download, multipart, complete, and abort requests against the target-style configuration, and that tests verify expected host choice and URL shape without exposing credentials [[cite:localStorageUrls,localTestsRun,cfR2Api]].
For endpoint split evidence, the ledger should record both sides of the route: server operations that should use the object API endpoint and user-visible downloads that should use a public object endpoint or compatible public domain [[cite:localPlan,localStorageUrls,cfR2Public]]. A single green upload test is therefore insufficient. A cutover can fail if server uploads are correct but public links point to the wrong host, or if public links are correct while server-side HLS output still signs against the wrong endpoint.
For media evidence, the HLS workflow deserves its own row because it is a composite path. The worker reads a source object, derives output keys, writes a manifest and segments, and the application later authorizes key, manifest, and segment access [[cite:localWorkerHls,localE2E]]. If a readiness report compresses that into `download works`, it hides the difference between raw object retrieval and derived media availability. The safer report records HLS source read, output write, manifest URL, segment URL, and access-control check separately.
This ledger also explains why the WBS completion state matters. An open checkbox is not inherently a defect, but it means the evidence object has not been recorded in the inspected source of truth [[cite:localWbsCounts]]. The readiness label should therefore be tied to the ledger, not to developer intuition. When a row is missing, the status should say `not observed`; when a row passes only in local tests, it should say `local test evidence`; when a row has target-backend proof, it can say `cutover evidence`.
The same discipline applies to residual source-backend wording. The count of MinIO-related references is not by itself a reason to edit every occurrence [[cite:localRefCounts]]. Some references belong in rollback documentation, source-system copy commands, or historical migration plans. The ledger should distinguish compatible legacy terms from stale runtime assumptions. That prevents two opposite errors: leaving stale local-store assumptions in active tests, or removing source-system language needed for reverse migration and audit.
Limitations
This paper does not access live R2 or MinIO buckets, production logs, Cloudflare configuration, real object data, actual public domains, or credentials. It also does not run the full runtime E2E because that would require configured services and private environment values. The findings are limited to local plans, code, test discovery, one targeted test run, graph search, and official documentation.
The local working tree already had modified migration-related files. The paper treats those as user or prior-work changes and does not revert them [[cite:localGitStatus]]. Therefore, counts and observations describe the current checkout at the time of this study, not a clean repository baseline.
Reference counts are intentionally rough. They include test fixtures, generated contracts, locales, comments, and plans. They are useful as a cleanup denominator, not as a defect count. A proper cleanup pass would classify each reference before changing it.
Conclusion
Object storage cutovers need URL-compatibility evidence, not S3-compatible labels alone. The nk304 migration plan already knows this: it calls for public URL compatibility, short upload freeze, final delta sync, R2-based tests, HLS checks, monitoring, and rollback. The code also has useful endpoint separation, and targeted storage tests pass. Those are meaningful readiness signals.
The strongest truthful status is still partial readiness. The WBS is open, the plan's test command is stale, R2 E2E and public URL sample evidence were not observed, and rollback is designed but not rehearsed. A readiness report should publish that weakest verified stage. When the report can show target-backend upload, multipart, HLS, public URL compatibility, data-copy integrity, monitoring and rollback rehearsal, the migration can be called cutover-ready. Until then, it is a well-structured plan with partial code evidence.