You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Ship a deliberately narrow TypeScript Cloudflare Worker that accepts automatic Protocol v1 uploads and durably stores their ciphertext in R2.
The Worker is an untrusted, bounded ingress—not an enrollment, device-authentication, analysis, or management service. Its only successful mutation is creating an immutable R2 object and returning the matching receipt.
Requirements
Scope and trust boundary
Expose one Protocol v1 upload POST route.
Do not expose list, download, delete, admin, dashboard, configuration, participant, or management APIs.
Do not use D1, Queues, KV, Durable Objects, a private decryption key, or any decrypt/parse path.
Treat ingress as public and unauthenticated. A deploy-time allowlist may contain accepted configuration digests and researcher signer fingerprints, but these cleartext claims are not participant or device authentication.
Fix the accepted protocol version, media type, header set, configuration allowlist, and R2 binding at deployment. Do not add remote configuration.
Give researchers read access directly through least-privilege, S3-compatible R2 credentials outside the public Worker.
Request validation and streaming
Reject every method, path, media type, transfer shape, or required header not defined by Protocol v1.
Enforce a 32 MiB body hard limit before and while reading. Reject missing, conflicting, malformed, truncated, overlong, or trailing input.
Stream the request ReadableStream directly into R2. Do not buffer the complete body in Worker memory.
Supply the expected SHA-256 to R2 so the durable object write itself validates content integrity.
Use bundle_id as the immutable R2 object key under a fixed deployment namespace.
Store bounded R2 custom metadata for ciphertext SHA-256, byte count, claimed range, claimed event count, configuration digest, producer platform/version, and receive time. Mark all routing metadata as untrusted claims.
Idempotency and receipts
Use a create-only conditional R2 write so an existing bundle_id can never be overwritten.
After a successful durable new write, return 201 Created with a canonical receipt containing the exact stored identity, digest, size, range, event count, and configuration digest.
If the same ID already exists and its object checksum and metadata are identical, return 200 OK with the identical receipt.
If the same ID exists with any different digest, size, or metadata, return 409 Conflict; never replace either object.
Handle concurrent identical or conflicting requests safely. Rely on R2 conditional writes and strong consistency, not a non-atomic read-then-overwrite sequence.
Return no 2xx until R2 confirms durable persistence. A lost response followed by an exact replay must be safe.
Abuse and operations
Provide a Wrangler deployment template with explicit bindings, secrets/configuration, routes, compatibility date, and environment separation.
Configure or document Cloudflare WAF/rate limiting, a fixed request bound, deployment isolation, R2 lifecycle/retention policy, storage-budget alerts, and emergency ingress disablement.
Log only bounded operational reason codes, response status, coarse timing, and non-sensitive aggregate sizes. Do not log participant UUIDs, configuration artifacts, raw headers, URLs containing secrets, or body bytes.
Expose no claim of participant authenticity, device authenticity, enrollment, completeness, or decrypted validity in receipts or documentation.
Acceptance criteria
A valid upload is streamed to R2 and returns 201 only after the object is readable with the expected checksum and metadata.
Response loss followed by exact replay returns the same receipt with 200 and creates no second object.
Concurrent exact duplicates yield one creation and safe replay responses; a same-ID content or metadata conflict yields 409 without overwrite.
Wrong method/path/media type, malformed headers, digest mismatch, truncation, extra bytes, and bodies above 32 MiB fail without a durable object.
Simulated R2 failure never returns 2xx.
Worker-memory tests demonstrate that body handling does not buffer a full 32 MiB request.
Deployment and log tests prove that the Worker has no R2 list/download/admin surface, no decrypt key, and no sensitive logging.
Operational documentation explicitly describes ingress and all stored routing metadata as unauthenticated/untrusted.
Summary
Ship a deliberately narrow TypeScript Cloudflare Worker that accepts automatic Protocol v1 uploads and durably stores their ciphertext in R2.
The Worker is an untrusted, bounded ingress—not an enrollment, device-authentication, analysis, or management service. Its only successful mutation is creating an immutable R2 object and returning the matching receipt.
Requirements
Scope and trust boundary
POSTroute.Request validation and streaming
bundle_id,Content-Length, SHA-256Content-Digest, exact claimed sequence range, event count, configuration digest, and producer metadata defined by Finalize a language-neutral Particeps Protocol v1 and conformance corpus #8/Implement a durable replay-safe Android upload outbox #9.ReadableStreamdirectly into R2. Do not buffer the complete body in Worker memory.bundle_idas the immutable R2 object key under a fixed deployment namespace.Idempotency and receipts
bundle_idcan never be overwritten.201 Createdwith a canonical receipt containing the exact stored identity, digest, size, range, event count, and configuration digest.200 OKwith the identical receipt.409 Conflict; never replace either object.Abuse and operations
Acceptance criteria
201only after the object is readable with the expected checksum and metadata.200and creates no second object.409without overwrite.Documentation
Add the upload/receipt contract, Worker threat model, Wrangler deployment guide, R2 naming/metadata specification, WAF/rate-limit/lifecycle runbook, retention/cost guidance, researcher S3 read-access guide, and incident procedures.
References: R2 Workers API, R2 consistency, and Workers limits.
Dependencies
Blocked by #8 (final Protocol v1) and #9 (durable exact-replay Android outbox).