signup: no resend path for the setup-link email — a failed send strands the account #116
Etiketter
Inga etiketter
area/backups
area/ci
area/control-panel
area/identity
area/infra
area/observability
area/payments
area/security
area/storage
area/web
blocked
needs-info
needs-triage
ready-for-implementation
type
bug
type
chore
type
docs
type
epic
type
feature
type
task
wontfix
Ingen milstolpe
Inget projekt
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Inget förfallodatum satt.
Beroenden
Inga beroenden satta
Referens
bitborg/bitborg-web#116
Läser in…
Hänvisa till i nytt ärende
Ingen beskrivning angiven.
Ta bort grenen "%!s()"
Borttagning av en gren är permanent. Även om den borttagna grenen kan fortsätta existera en kort tid innan den faktiskt tas bort, kan det INTE ångras i de flesta fall. Vill du fortsätta?
When the setup-link email fails to send, the account is already provisioned in Kanidm but the person
has no way to set a credential. There is no resend path — recovery requires SSH access and the
runbook.
src/pages/api/signup.ts:188names the gap in a comment:The best-effort design is right and should stay: a mail hiccup must not fail a sign-up that already
succeeded. But the consequence is that a failed send leaves a user stranded with no self-service
route out, and no operator route either short of a shell.
This is not hypothetical
It happened on 2026-07-31. A wrong-account Sweego API key made every send fail with HTTP 422
(bitborg-infra#273).
bitborg-test-1was provisioned with no deliverable link, and recovering itmeant:
then handing the printed URL over out of band. For a test account that is merely awkward. For a real
sign-up it means an account that looks created, a user who cannot log in, and no way for them to
report it except email — which is the thing that is broken.
What exists today
The mechanism is already there and used at sign-up: a Kanidm credential update-intent
(
src/lib/kanidm.ts—GET /v1/person/<username>/_credential/_update_intent/<ttl>), turned into<publicBase>/ui/reset?token=…byresetLinkUrl. TTL is 24 h (RESET_TTL_SECONDS). So a resend isre-minting an intent and re-sending the same email — no new integration.
Design considerations
The obvious shape — an unauthenticated "resend my setup link" form taking an email address — is an
abuse and enumeration risk: it mints credential-reset intents for arbitrary accounts and reveals
which addresses exist. Worth thinking about before building:
src/lib/captcha.ts, ADR 0029), andrespond identically whether or not the address matches an account.
enumeration surface entirely and still removes the SSH requirement. Smaller scope, less user-facing
value.
sendEmailfails outright, tell the user theiraccount exists but the email could not be sent, with a support contact. Cheapest, and it converts
an invisible failure into a visible one even without a resend button.
The third option composes with either of the first two and is worth doing regardless — it is the same
"best-effort masks systematic failure" gap recorded as gap 3 in #115.
Done when
A person whose setup-link email failed can obtain a working link without an operator opening an SSH
session, and the path cannot be used to enumerate accounts or mint unbounded reset intents.