Repository navigation
Closed
Labels
app/idPackage/app: id (pkgs/ or apps/ in this repo)Package/app: id (pkgs/ or apps/ in this repo)effort/mMedium: expected under 1 day of workMedium: expected under 1 day of workkind/designA design proposal (design-architect): multi-step, phased, decision-bearingA design proposal (design-architect): multi-step, phased, decision-bearingmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedIntake review done: acceptance criteria, links, and normalized labels addedpriority/normalOrdinary priority (the default)Ordinary priority (the default)source/creatorFiled by the new-issue-creator jobFiled by the new-issue-creator jobtask/triageQueue packet kind: verify, reproduce, and route this itemQueue packet kind: verify, reproduce, and route this item
Description
Follow-up A from the Durable Object jurisdiction investigation on #2061
(doc: doc/2026-10-29T11-43-02Z_investigation_do_jurisdiction_pinning/).
Parent epic: #2056. Context: #2044 (Cloudflare Workers fit), §3 and §7.
What to design
Make the per-user hosted store jurisdiction-aware from the first request, without exposing a user-facing region choice yet.
- A
jurisdictionfield on the user record, written at account creation (defaultus, policy constant), immutable in the app layer from the first Durable Object creation. - One ID-derivation function
userObjectId(sub, jurisdiction)with no default argument — becauseidFromName("google:"+sub)andjurisdiction("eu").idFromName("google:"+sub)are different objects, a routing bug that drops the jurisdiction silently creates a second, empty user store (investigation §4.1). - Restricted-namespace binding: select
env.USERS.jurisdiction(<stored>)at runtime rather than using the unrestricted binding, so a mismatched ID throws instead of working (the unrestricted namespace "will not run into an error"). - A
ctx.id.jurisdictionassertion on every identity-touching entry point, failing closed — including the twoundefinedcases (unrestricted namespace; alarm scheduled before 2026-03-15). - R2 bucket per jurisdiction, bound with the
jurisdictionfield repeated inwranglerconfig; content-addressed dedupe becomes jurisdiction-local. - The shared D1 directory's residency story: it has one jurisdiction for all users, so it must be data-minimal (hashed subject, public keys, public server records — never graph content) and documented as the single residency exception.
- The migration path as a first-class state, not an ops script:
jurisdiction_pending+ export → create in target → import → verify → switch directory record → delete. Signatures survive unchanged.
Out of scope
The user-facing region choice UI (design the field and routing now; ship the control when a customer needs it). Cross-jurisdiction shared worlds — that is follow-up B. Vendor-guarantee confirmation — follow-up C.
Acceptance
- Design doc under
doc/covering the seven points above, with the failure modes named. - A concrete migration runbook sketch for a single account.
- Follow-on implementation issues for whatever is buildable.
Acceptance criteria
- Design doc under doc/ covers all seven numbered points in the body, with the silent-second-store failure mode (dropping jurisdiction in ID derivation) named and designed out
- The ctx.id.jurisdiction assertion covers both undefined cases and fails closed
- Migration path specified as a first-class state (jurisdiction_pending → export → create → import → verify → switch → delete)
- A single-account migration runbook sketch is included
- Follow-on implementation issues filed for the buildable parts
Metadata
Metadata
Assignees
Labels
app/idPackage/app: id (pkgs/ or apps/ in this repo)Package/app: id (pkgs/ or apps/ in this repo)effort/mMedium: expected under 1 day of workMedium: expected under 1 day of workkind/designA design proposal (design-architect): multi-step, phased, decision-bearingA design proposal (design-architect): multi-step, phased, decision-bearingmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedIntake review done: acceptance criteria, links, and normalized labels addedpriority/normalOrdinary priority (the default)Ordinary priority (the default)source/creatorFiled by the new-issue-creator jobFiled by the new-issue-creator jobtask/triageQueue packet kind: verify, reproduce, and route this itemQueue packet kind: verify, reproduce, and route this item
Activity
developing-today commentedon Oct 29, 2026
CODE-1141
dezren39 commentedon Oct 29, 2026
Intake review
Related: #2061 (investigation), #2044 (§3/§7), #2284/#2285 (siblings B/C), epic #2056
Notes: Design-only issue; the field/routing design is deliberately ahead of any user-facing region UI (out of scope per body).
Risks: The unrestricted-namespace silent-accept behaviour is the trap: any path that bypasses the restricted binding creates split-brain user stores.
Suggested approach: Design the ID-derivation function and assertion first; R2/D1 residency stories follow from them.
🤖 Agent note · filed
meta/enrichedapplied. Still-live check this pass: no merged PR or fix commit explicitly resolving this issue was found; where a queue triage task owns verification, it is named in the notes.32 remaining items
dezren39 commentedon Nov 3, 2026
🤖 Agent note · handed off
opencode; re-handing off to the laptop claimant (earlier attempts parked/expired without a terminal state; labels moved parked → claimed in this pass).dezren39 commentedon Nov 3, 2026
🤖 Agent note · done — closing with evidence
main:doc/2026-11-01_jurisdiction_aware_user_store_design.md(17,218 bytes). Its sections map to the issue body: "1–4. Authoritative record, routing and object guard" (jurisdiction field,userObjectIdderivation, restricted-namespace binding,ctx.id.jurisdictionassertion), "5–6. Blob and directory boundaries" (per-jurisdiction R2, data-minimal D1 directory), "7. Migration protocol and single-account runbook", plus "Failure modes and verification".