NEC-138: Support path to release an email when both passphrase and recovery key are lost #199

Merged
nectenda-agent merged 5 commits from worktree-nec-138-support-path-to-release-an-email-when-bo into main 2026-09-28 16:24:28 +01:00
Collaborator

Task: NEC-138 — https://projectron.nerchure.com/tasks/138

Someone who has lost both their passphrase and their recovery key can now start over with the same email address: they ask from a locked vault, the request waits seven days while every signed-in device shows it and can cancel it, and then the identity service signs every device out, has each sync server remove or rename their seats under a signed command, and only then frees the address. The old identity is tombstoned, never deleted, so support can restore it if the recovery key turns up. Support can also start, speed up (after checking ownership) or cancel a release. Adds CRYPTO-097/098/099 and WIRE-064, the runbook procedures, and the one legitimate key change in security-model.md. User-facing guide pages are NEC-143.

Spec: docs/changes/NEC-138-release-an-email/spec.md

Changelog

If you have lost both your passphrase and your recovery key, you can now start over with a new, empty account under the same email address; the reset waits seven days and any device still signed in can cancel it.

Task: NEC-138 — https://projectron.nerchure.com/tasks/138 Someone who has lost both their passphrase and their recovery key can now start over with the same email address: they ask from a locked vault, the request waits seven days while every signed-in device shows it and can cancel it, and then the identity service signs every device out, has each sync server remove or rename their seats under a signed command, and only then frees the address. The old identity is tombstoned, never deleted, so support can restore it if the recovery key turns up. Support can also start, speed up (after checking ownership) or cancel a release. Adds CRYPTO-097/098/099 and WIRE-064, the runbook procedures, and the one legitimate key change in security-model.md. User-facing guide pages are NEC-143. Spec: `docs/changes/NEC-138-release-an-email/spec.md` ## Changelog If you have lost both your passphrase and your recovery key, you can now start over with a new, empty account under the same email address; the reset waits seven days and any device still signed in can cancel it.
The shard's half of freeing an address (NEC-138). A seat in an organisation
with someone to inherit goes through deleteUser, so folders pass to the heir;
a last owner's seat is kept and only its address is rewritten. The account
PATCH route refuses an identity command before spending its id, so a command
sent to the wrong door cannot be replayed into a false "already applied".

Task: NEC-138

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaJEEHhoivMgXdEArX1eRr
The identity service's half of NEC-138. A signed-in user asks to release
their address; it waits seven days, is shown on every signed-in device, and
any of them can cancel. When it falls due the pass pulls, signs every device
out and waits for the shards to hear, releases each seat, and only then
tombstones the identity: the address moves to a placeholder, the keys and
history stay, and OAuth links and passkeys are detached into the request so a
restore can put them back.

Support can ask on someone's behalf (actor and reason required, and only
support may waive the wait) and can restore a tombstone when the recovery key
turns up, which first releases whatever identity is live at the address.
Every step is recorded in support_actions.

Task: NEC-138

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaJEEHhoivMgXdEArX1eRr
The plugin's half of NEC-138. A locked vault whose owner has lost both the
passphrase and the recovery key can ask to start over, after typing the
address and reading what is lost and what stays. Every signed-in device then
shows the pending reset once as a notice that stays until dismissed, and in
settings with a Cancel reset button, because a device still signed in is
what stops someone who got into the mailbox.

Task: NEC-138

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaJEEHhoivMgXdEArX1eRr
Adds CRYPTO-097, CRYPTO-098, CRYPTO-099 and WIRE-064, the runbook's two
procedures (lost both keys; found the recovery key afterwards), and the one
legitimate key change in security-model.md's known-key paragraph.

The pending-reset row moves to the top of the home pane: on the Security page
it was hidden behind a section that draws nothing without enrolled keys, and
a worried owner would not have gone looking there. The e2e test found it.

Task: NEC-138

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaJEEHhoivMgXdEArX1eRr
Close the races a review found in releasing an address
All checks were successful
Release note / release-note (pull_request) Successful in 12s
CI / build (pull_request) Successful in 4m47s
CI / e2e (pull_request) Successful in 5m23s
CI / promote (pull_request) Has been skipped
ae2ea885e3
- A cancel pressed while a pass is asking the shards was reported done and
  the release completed anyway. A pass now claims its request first, and a
  cancel after that is refused (UNDERWAY) rather than untrue.
- Two passes on one request could both tombstone it, the second overwriting
  the snapshot a restore re-attaches sign-in routes from. The tombstone now
  happens only if the row and the request are both still untouched.
- A sign-in that finished mid-pass kept a live session on the tombstone.
  Sessions are revoked again inside the tombstone transaction and pushed, a
  released identity has no live session whatever its rows say, and a flow
  claimed after the release is refused.
- A shard whose manifest could not be read was treated as holding no seat.
  An unreadable shard now stops the attempt.
- The release pass could finish half of a failed restore, leaving the
  address held by nobody. It now leaves restore requests to the restore.
- A released owner's kept seat counted as an heir. Neither the heir count
  nor deleteUser's choice of heir counts it any more.

Each fix has a test, and each test fails with its fix removed.

Task: NEC-138

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaJEEHhoivMgXdEArX1eRr
cruelacid force-pushed worktree-nec-138-support-path-to-release-an-email-when-bo from ae2ea885e3
All checks were successful
Release note / release-note (pull_request) Successful in 12s
CI / build (pull_request) Successful in 4m47s
CI / e2e (pull_request) Successful in 5m23s
CI / promote (pull_request) Has been skipped
to 34b540597e
All checks were successful
Release note / release-note (pull_request) Successful in 11s
CI / build (pull_request) Successful in 5m32s
CI / e2e (pull_request) Successful in 5m44s
CI / promote (pull_request) Has been skipped
2026-09-28 14:40:21 +01:00
Compare
cruelacid force-pushed worktree-nec-138-support-path-to-release-an-email-when-bo from 34b540597e
All checks were successful
Release note / release-note (pull_request) Successful in 11s
CI / build (pull_request) Successful in 5m32s
CI / e2e (pull_request) Successful in 5m44s
CI / promote (pull_request) Has been skipped
to 32bb169bf0
All checks were successful
Release note / release-note (pull_request) Successful in 16s
e2e / multi (push) Successful in 4m44s
CI / build (pull_request) Successful in 4m59s
CI / e2e (pull_request) Successful in 5m33s
CI / promote (pull_request) Has been skipped
Deploy site / deploy (push) Successful in 52s
CI / build (push) Successful in 5m5s
CI / e2e (push) Successful in 5m26s
CI / promote (push) Successful in 30s
2026-09-28 16:18:29 +01:00
Compare
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Nectenda/nectenda!199
No description provided.