From 28c77b7648b2a44a18be64ec44344d91cccfd742 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Thu, 15 Oct 2026 14:25:00 +0200 Subject: feat(auth): the recovery key never reaches the hub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ticking "also e-mail it to me" at registration sent the recovery key R in the register body, and the hub appended it to the verification e-mail: the hub saw R in clear on its way out, and the resend door — no captcha — let anyone who knew a pending username and address have the hub mail a "recovery key" of their choosing. The box is gone. The recovery screen offers "E-mail it to myself", a mailto: link that opens the person's own mail application with the key filled in; the desktop shell hands mailto: to the system (http, https and mailto only) and the Android shell to a mail application (ACTION_SENDTO). The CLI drops --email-recovery-key and prints the key, as before. The hub mails the code alone, and refuses a register that still carries a recovery_key rather than dropping it, so an older client offering the option cannot tell someone their key was sent when it was not. A mailbox holding R remains, with the reset code that reaches the same mailbox, enough to recover the account's identities; the link says so, and it is the person's choice. Checked in Chrome and Firefox (headless, the real page against a stubbed hub) and by the CLI's end-to-end registration against a local hub. The Android change was not built here, and the desktop link was not clicked in the application. Co-Authored-By: Claude Opus 5.5 --- docs/MESHBAY_DESIGN.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md index 1a1f9fc..ae7f8fc 100644 --- a/docs/MESHBAY_DESIGN.md +++ b/docs/MESHBAY_DESIGN.md @@ -598,10 +598,15 @@ browser writes an identity bundle to a node and holds the recovery key, a **seco copy** is written beside it sealed under `recovery_key` (`bundle_enc_recovery`, additive on the wire), in the same format and bound to the same account and node. The desktop application writes one when the recovery key is entered on the -Profile page, for an account with browser access. `R` is a pass-through: shown once -on screen at registration and mailed with the verification code only if the person -ticks the box for it (unticked by default), never written to any database, never -logged. +Profile page, for an account with browser access. **`R` never reaches the hub.** +It is shown once on screen at registration, and the person may mail it to +themselves from their own mail application (a `mailto:` link, which the desktop +and Android shells hand to the system), so it never passes through the hub's +mail, is never written to any database and is never logged. The hub refuses a +registration that carries one: an older client that still offers to have it +e-mailed is told so rather than left to say it was sent. A mailbox holding `R` +is, with the reset code that reaches the same mailbox, enough to recover the +account's identities — that is the person's choice to make, and the link says so. The reset endpoints are built to leak nothing. `POST /v1/users/password/reset-request` requires **the username and the email on file as a pair**, checked against a blind -- cgit v1.2.3