Live PDS provisioning: creating a mailbox with a new Atmosphere account never creates the account
openObserved (mx99 staging, 2026-10-08)
Adding [email protected] through the UI with “create a new Atmosphere account” (mode new, handle test.at.test.kilimanjaro.io), not tied to an existing ATProto identity, created the Stalwart mailbox but no ATProto account: ZDS for the domain (did:web:test-kilimanjaro-io.pds1.eu.sovrn.at) lists one repo, the domain’s postmaster (did:plc:6ynxrsfjhhm62o72cgptdvmo), and nothing for test. POST /domains/{id}/users/new took 21 ms and logged no error. Tying mail accounts to ATProto accounts is the core of the product, so this blocks beta.
Cause
The live provisioner was never built:
- router.go:182-194: every cfg.PDSMode (“fake”, “manual”, “live”, default) installs pdsprovisioner.Fake{} or Manual{}. The “live” case says “Real PDS wiring lands in Issue 4; fall back to the pending stub until then”. No bug was ever filed for that Issue 4; 31b625f (pending accounts: admin nudge + user activation/password) depends on it.
- Fake/Manual.Provision returns DID “did:plc:pending:
Wanted
- A live pdsprovisioner.Provisioner (selected by pds mode “live”, the default on cells) that, for ModeNew, creates
- Decide the initial state with 31b625f: create the account with a random password and status pending until the user activates and sets their own, or active immediately; either way the DID is real from the start.
- Idempotent and safe against partial failure: a mailbox without an account, or an account without a mailbox, must be retried or rolled back (the code already provisions only after validation, “so a rejected form never leaves an orphan”).
- The service record (at.sovrn.mail.service, written on first login by authbroker) and the handle (needs
*.at.<domain>DNS, bug 6844d4f) then make the account usable from Bluesky and the sovrn login. - Migration for existing pending accounts (placeholder DIDs): provision them when live mode turns on, or list them for the admin.
- Tests: unit for the provisioner against a fake ZDS; extend checks.cell (or a new VM test with a ZDS instance) to create a user and assert a real did:plc and a repo on the instance.
Related
31b625f (pending accounts, activation/password), 6844d4f (crawl + *.at.
2 Comments
Reuse existing identities (user, 2026-10-08)
Testing creates postmaster.at. and .at. repeatedly. The provisioner must find and reuse an identity that already exists instead of minting another DID for the same handle, the UI must show it, and every partial failure must be recoverable.
What exists today
*.at.<domain>DNS (6844d4f), so today an existing DID is invisible to it; and “taken” just blocks, it never offers to reuse.Wanted behaviour
Failure handling (must be designed and tested step by step)
Write an intent before any external side effect: a provisioning row (domain, handle, email, idempotency key, state = intent) committed in sovrn.db first; every later step records its result on that row; a reconciler (startup + periodic) drives intents to done or rolled back. Cases: 1. PLC/ZDS succeeded, sovrnd crashed or failed before persisting the DID: the intent row exists; the reconciler resolves the handle on the instance, finds the account, records the DID, continues. Never create again. 2. createAccount timed out or errored ambiguously (the PLC op may or may not have been published): retry only after looking the handle up on the instance; if it exists, it’s ours (intent proves it): adopt. 3. DID persisted, Stalwart mailbox creation failed: retry the mailbox; don’t delete a live identity. If the admin abandons, deactivate (not delete) the account and record it, so the handle can be reused later. 4. Mailbox created, account row write failed: the reconciler finds both via the intent and links them. 5. Double submit / two admins / concurrent saga runs for the same handle: idempotency key + unique (domain, handle) in sovrn.db; the second attempt joins the first intent. 6. plc.directory or the ZDS instance unavailable: fail before creating the mailbox (or leave the intent pending and retry), never leave a mailbox pointing at a placeholder DID silently. 7. Handle claimed by a DID whose PDS is gone (instance lost without backups): adopt via PLC rotation key if ours; otherwise report. 8. Domain removed: what happens to its postmaster and user identities (deactivate, keep rotation authority) so re-adding the domain reuses them. 9. Placeholder DIDs (did:plc:pending:*) from before live mode: the reconciler treats each as an intent.
Tests
Fault injection at every step boundary (crash after PLC/ZDS, after DID persist, after mailbox) with a fake ZDS + fake PLC, asserting exactly one DID per handle and a consistent end state after reconcile; UI tests for the “exists, use it?” path; a VM test that re-adds a removed domain and gets the same postmaster DID.
Scope note (2026-10-08): the domain-saga half of the identity-reuse comment above (postmaster.at. reuse across instances/cells, crash after PLC in the saga, domain removal and re-add) moved to 4e9d7ba. This issue keeps user accounts: the live provisioner, user-handle reuse in the add-user form, and the intent/reconciler design for account creation.