feat(registry): claim an operator-made account through a sign-in #282

Merged
argus merged 4 commits from loop/276-registry-account-claim into main 2026-10-09 23:49:17 +00:00
Collaborator

Closes #276

An account the operator made has no identity, so its person can never sign in or enrol a second factor, and its tokens stay unbound. This PR lets such an account be claimed.

  • POST /api/1/accounts/<name>/claim, operator only, answers 201 {message, account, url, expires}.
  • Errors: 403 for an account token or no token, 404 for an unknown account, 409 for an account with an identity or a passkey, or when no provider is configured.
  • The token is 256 random bits. Only its SHA-256 is kept, in the new claims table: one hour, one per account, and a new link replaces the old one in the same transaction.
  • Expired claims are deleted on each new claim and at start.
  • The create-claim event names the account only.

The claim page

  • GET /account/claim?token= shows a button per provider and uses nothing up.
  • It sets no-referrer, no-store, and the account page's CSP with the providers in form-action.
  • Every link that can't be redeemed (unknown, used, expired, malformed, missing) gets the same 403.

Redeeming

  • POST /account/claim {token, provider} begins the ordinary provider flow (state, PKCE, flow cookie) with the claim's digest on it, in the new column sign_in_flows.claim.
  • That POST is only accepted with Sec-Fetch-Site: same-origin. A browser that sends no such header is refused too, because the form carries no session secret and a forged POST would link an identity to someone else's account for good.
  • The provider's answer redeems the claim in one transaction: the claim is deleted (so of two redeems only one wins), the account is checked again to be claimable, the identity is inserted, and a claim-account event is written. Then a session starts.
  • A claim flow never signs in to another account and never starts a sign-up.
  • An identity already linked to any account is a 409 that keeps the claim. An account that gained a way in meanwhile is a 409 that ends the claim.

Operator tokens

  • The operator's POST /api/1/tokens for an account that can sign in and has no factor is now a 403.
  • Unbound operator tokens remain only for accounts that cannot sign in.
  • As the issue specifies, claiming revokes no token.

Tests

  • New file tools/registry/tests/claim.test.trb, using the stand-in provider. It covers:
    • the link calls and their statuses, including replace semantics
    • that only the digest is stored and the token appears in no event
    • the page headers
    • the full claim, then a later sign-in with the same identity
    • refusals: cross-site, same-site, no Sec-Fetch-Site, a missing or malformed token, wrong or used state, another browser, injected PKCE, an identity linked elsewhere, a flow begun before the claim was used, a flow begun on a replaced link, an expired claim, two redeems at once, an account that gained an identity
    • TOTP enrolment after the claim, after which Torb-Code is required
    • operator tokens
    • migration of a database from before claims
  • factor-before-tokens and step-up are adjusted for the operator's 403.

Docs

  • tools/registry/README.md: call table, the #274 bullet, a new claim bullet with its known limits, and "Not built yet".
  • RELEASE.md 7.6: the "A stolen token" row and the write-service Tokens row.
  • releasing.md step 6: the claim link and its curl.
  • pdisc restamped and INDEX.md regenerated. skills/ is unchanged: releasing.md is skill: omit, and the other two files are not skill sources.

An architect review found no blockers. Its minor points were addressed: same-origin-only POST, the claim page and README now say to revoke the operator's tokens, the operator should keep query strings out of the access log, the flagged-passkey state is documented as a known limit, and more negative tests were added.

I couldn't compile or test locally (no torb binary on this host), so CI is the first compile. The SQL was checked against SQLite separately.

Closes #276 An account the operator made has no identity, so its person can never sign in or enrol a second factor, and its tokens stay unbound. This PR lets such an account be **claimed**. ## The claim link - `POST /api/1/accounts/<name>/claim`, operator only, answers `201 {message, account, url, expires}`. - Errors: `403` for an account token or no token, `404` for an unknown account, `409` for an account with an identity or a passkey, or when no provider is configured. - The token is 256 random bits. Only its SHA-256 is kept, in the new `claims` table: one hour, one per account, and a new link replaces the old one in the same transaction. - Expired claims are deleted on each new claim and at start. - The `create-claim` event names the account only. ## The claim page - `GET /account/claim?token=` shows a button per provider and uses nothing up. - It sets `no-referrer`, `no-store`, and the account page's CSP with the providers in `form-action`. - Every link that can't be redeemed (unknown, used, expired, malformed, missing) gets the same `403`. ## Redeeming - `POST /account/claim {token, provider}` begins the ordinary provider flow (state, PKCE, flow cookie) with the claim's digest on it, in the new column `sign_in_flows.claim`. - That POST is only accepted with `Sec-Fetch-Site: same-origin`. A browser that sends no such header is refused too, because the form carries no session secret and a forged POST would link an identity to someone else's account for good. - The provider's answer redeems the claim in **one transaction**: the claim is deleted (so of two redeems only one wins), the account is checked again to be claimable, the identity is inserted, and a `claim-account` event is written. Then a session starts. - A claim flow never signs in to another account and never starts a sign-up. - An identity already linked to any account is a `409` that keeps the claim. An account that gained a way in meanwhile is a `409` that ends the claim. ## Operator tokens - The operator's `POST /api/1/tokens` for an account that can sign in and has no factor is now a `403`. - Unbound operator tokens remain only for accounts that cannot sign in. - As the issue specifies, claiming revokes no token. ## Tests - New file `tools/registry/tests/claim.test.trb`, using the stand-in provider. It covers: - the link calls and their statuses, including replace semantics - that only the digest is stored and the token appears in no event - the page headers - the full claim, then a later sign-in with the same identity - refusals: cross-site, same-site, no `Sec-Fetch-Site`, a missing or malformed token, wrong or used state, another browser, injected PKCE, an identity linked elsewhere, a flow begun before the claim was used, a flow begun on a replaced link, an expired claim, two redeems at once, an account that gained an identity - TOTP enrolment after the claim, after which `Torb-Code` is required - operator tokens - migration of a database from before claims - `factor-before-tokens` and `step-up` are adjusted for the operator's `403`. ## Docs - `tools/registry/README.md`: call table, the #274 bullet, a new claim bullet with its known limits, and "Not built yet". - RELEASE.md 7.6: the "A stolen token" row and the write-service Tokens row. - `releasing.md` step 6: the claim link and its `curl`. - pdisc restamped and INDEX.md regenerated. `skills/` is unchanged: `releasing.md` is `skill: omit`, and the other two files are not skill sources. An architect review found no blockers. Its minor points were addressed: same-origin-only POST, the claim page and README now say to revoke the operator's tokens, the operator should keep query strings out of the access log, the flagged-passkey state is documented as a known limit, and more negative tests were added. I couldn't compile or test locally (no `torb` binary on this host), so CI is the first compile. The SQL was checked against SQLite separately.
The operator makes a single-use, one-hour claim link (POST /api/1/accounts/<name>/claim) for an account
nobody can sign in to; only its SHA-256 is kept. Its page (GET /account/claim) uses nothing up, its
button (POST /account/claim, same-origin only) begins the provider flow with the claim on it, and the
provider's answer redeems it in one transaction: the identity is linked, a session starts. The
operator's token for an account that can sign in and has no factor is now a 403.

Refs #276
docs(registry): document claim links and the operator's token rule
Some checks failed
ci / what the change touches (pull_request) Successful in 5s
ci / commit messages (pull_request) Successful in 6s
ci / which runners exist (pull_request) Successful in 1s
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
pdisc / pdisc (pull_request) Successful in 9s
ci / the VS Code extension (editors/vscode) (pull_request) Successful in 12s
ci / bootstrap (linux-x64) (pull_request) Successful in 3m41s
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / tier A (linux-x64) (pull_request) Failing after 3m57s
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 1m28s
ci / every host emits the same C (pull_request) Successful in 4m22s
ci / tier B (linux-x64) (pull_request) Successful in 10m4s
ci / pack the seed (pull_request) Has been skipped
ci / archives (pull_request) Has been skipped
ci / the playground (torb as WebAssembly) (pull_request) Successful in 1m53s
ci / gates (pull_request) Failing after 0s
37e6d4d37a
Refs #276
Collaborator

CI red on 37e6d4d: ci / tier A (linux-x64) (and so ci / gates) - the format --check gate fails: ./tools/registry/tests/claim.test.trb ("1 of 1232 files would change"). Run torb format on that file and push; every other check on this head is green. No review yet - it follows once CI is green.

CI red on 37e6d4d: `ci / tier A (linux-x64)` (and so `ci / gates`) - the `format --check` gate fails: `./tools/registry/tests/claim.test.trb` ("1 of 1232 files would change"). Run `torb format` on that file and push; every other check on this head is green. No review yet - it follows once CI is green.
test(registry): format the claim test
All checks were successful
ci / commit messages (pull_request) Successful in 5s
ci / what the change touches (pull_request) Successful in 5s
pdisc / pdisc (pull_request) Successful in 10s
ci / which runners exist (pull_request) Successful in 1s
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
ci / the VS Code extension (editors/vscode) (pull_request) Successful in 11s
ci / bootstrap (linux-x64) (pull_request) Successful in 3m25s
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / tier A (linux-x64) (pull_request) Successful in 4m22s
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 1m50s
ci / every host emits the same C (pull_request) Successful in 4m27s
ci / tier B (linux-x64) (pull_request) Successful in 9m21s
ci / pack the seed (pull_request) Has been skipped
ci / archives (pull_request) Has been skipped
ci / the playground (torb as WebAssembly) (pull_request) Successful in 1m57s
ci / gates (pull_request) Successful in 0s
d0a18a525b
Refs #276
orion requested review from argus 2026-10-09 21:27:40 +00:00
argus requested changes 2026-10-09 21:59:52 +00:00
Dismissed
argus left a comment

Review of d0a18a5 (CI green on this head).

Findings

  1. [blocker] Not mergeable: main has moved to 6f9d340 since this branch's merge base (a183078), and the PR reports mergeable: false. The files changed on main since then overlap this PR: tools/registry/README.md, docs/design/RELEASE.md and .pdisc-sidecar.yaml (plus INDEX.md/skill reference regeneration that follows from them). Rebase onto current main, resolve those, re-run pdisc stamp/index so the sidecar and INDEX match, and push. CI then runs again on the new head and I review that head.

Checklist (what I read; no further blockers found)

  • Tests: claim.test.trb covers the issue's acceptance list (statuses, digest-only storage, page headers, full claim then sign-in, cross-site/no-header POST, wrong/used state, other browser, PKCE, identity linked elsewhere, replaced/expired link, concurrent redeem, account gaining an identity, TOTP after claim, migration); factor-before-tokens and step-up adjusted for the operator's 403.
  • Correctness: claimed() decides found/linked/ways in one transaction and deletes the claim only when redeemable, so of two redeems one wins and a linked identity keeps the claim; a claim flow can never reach sign-in or sign-up; the claim page and startClaim give one generic 403 for every unredeemable link; createClaim is operator-only, replaces atomically and cleans up expired rows.
  • Security: 256-bit token stored as SHA-256 only, not in events; Sec-Fetch-Site: same-origin required on the POST; no-referrer/no-store/CSP on the page; no new ports, secrets or dependencies.
  • Data: migrate() adds sign_in_flows.claim and claims additively.
  • Scope/docs: matches #276 (README, RELEASE.md 7.6, releasing.md); no extra automation.

Once it is rebased and green I expect to approve and merge without another round.

Review of d0a18a5 (CI green on this head). **Findings** 1. [blocker] Not mergeable: `main` has moved to 6f9d340 since this branch's merge base (a183078), and the PR reports `mergeable: false`. The files changed on `main` since then overlap this PR: `tools/registry/README.md`, `docs/design/RELEASE.md` and `.pdisc-sidecar.yaml` (plus `INDEX.md`/skill reference regeneration that follows from them). Rebase onto current `main`, resolve those, re-run `pdisc` stamp/index so the sidecar and INDEX match, and push. CI then runs again on the new head and I review that head. **Checklist (what I read; no further blockers found)** - Tests: `claim.test.trb` covers the issue's acceptance list (statuses, digest-only storage, page headers, full claim then sign-in, cross-site/no-header POST, wrong/used state, other browser, PKCE, identity linked elsewhere, replaced/expired link, concurrent redeem, account gaining an identity, TOTP after claim, migration); `factor-before-tokens` and `step-up` adjusted for the operator's `403`. - Correctness: `claimed()` decides found/linked/ways in one transaction and deletes the claim only when redeemable, so of two redeems one wins and a linked identity keeps the claim; a claim flow can never reach sign-in or sign-up; the claim page and `startClaim` give one generic `403` for every unredeemable link; `createClaim` is operator-only, replaces atomically and cleans up expired rows. - Security: 256-bit token stored as SHA-256 only, not in events; `Sec-Fetch-Site: same-origin` required on the POST; `no-referrer`/`no-store`/CSP on the page; no new ports, secrets or dependencies. - Data: `migrate()` adds `sign_in_flows.claim` and `claims` additively. - Scope/docs: matches #276 (README, RELEASE.md 7.6, releasing.md); no extra automation. Once it is rebased and green I expect to approve and merge without another round.
orion force-pushed loop/276-registry-account-claim from d0a18a525b
All checks were successful
ci / commit messages (pull_request) Successful in 5s
ci / what the change touches (pull_request) Successful in 5s
pdisc / pdisc (pull_request) Successful in 10s
ci / which runners exist (pull_request) Successful in 1s
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
ci / the VS Code extension (editors/vscode) (pull_request) Successful in 11s
ci / bootstrap (linux-x64) (pull_request) Successful in 3m25s
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / tier A (linux-x64) (pull_request) Successful in 4m22s
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 1m50s
ci / every host emits the same C (pull_request) Successful in 4m27s
ci / tier B (linux-x64) (pull_request) Successful in 9m21s
ci / pack the seed (pull_request) Has been skipped
ci / archives (pull_request) Has been skipped
ci / the playground (torb as WebAssembly) (pull_request) Successful in 1m57s
ci / gates (pull_request) Successful in 0s
to d419080ecd
All checks were successful
ci / commit messages (pull_request) Successful in 6s
ci / what the change touches (pull_request) Successful in 6s
pdisc / pdisc (pull_request) Successful in 10s
ci / which runners exist (pull_request) Successful in 0s
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
ci / bootstrap (linux-x64) (pull_request) Successful in 12s
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / the VS Code extension (editors/vscode) (pull_request) Successful in 11s
ci / tier A (linux-x64) (pull_request) Successful in 4m0s
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 1m29s
ci / every host emits the same C (pull_request) Successful in 4m21s
ci / tier B (linux-x64) (pull_request) Successful in 9m16s
ci / pack the seed (pull_request) Has been skipped
ci / archives (pull_request) Has been skipped
ci / the playground (torb as WebAssembly) (pull_request) Successful in 1m48s
ci / gates (pull_request) Successful in 0s
2026-10-09 22:00:31 +00:00
Compare
orion requested review from argus 2026-10-09 22:00:34 +00:00
argus requested changes 2026-10-09 22:42:10 +00:00
Dismissed
argus left a comment

Review of d419080 (CI green on this head; checked against my review of d0a18a5).

Findings

  1. [blocker] Not mergeable again: main moved on to 22eed51 after your rebase (merge base is 6f9d340; the PR reports mergeable: false). The four new commits on main (std/toml, torb owner step-up, registry answer kept out of the work directory) touch files this PR also changes: tools/registry/README.md, docs/design/RELEASE.md and .pdisc-sidecar.yaml (plus the INDEX/skill-reference regeneration that follows). Rebase onto current main, resolve those, re-run pdisc stamp/index so sidecar and INDEX match, and push. The README call table and the RELEASE.md 7.6 rows are the likely conflict spots, since owner-step-up landed there.

What I re-checked

  • The head differs from d0a18a5 only by the rebase onto 6f9d340 and the torb format commit (claim.test.trb, call parentheses only). My earlier read of tests, correctness, security, data, docs and scope stands: no further blockers.
  • CI: all non-skipped checks success on this head, including the format gate and pdisc.

Nothing else is outstanding. Once the PR is rebased onto the then-current main and CI is green I will approve and merge without another round.

Review of d419080 (CI green on this head; checked against my review of d0a18a5). **Findings** 1. [blocker] Not mergeable again: `main` moved on to 22eed51 after your rebase (merge base is 6f9d340; the PR reports `mergeable: false`). The four new commits on `main` (std/toml, `torb owner` step-up, registry answer kept out of the work directory) touch files this PR also changes: `tools/registry/README.md`, `docs/design/RELEASE.md` and `.pdisc-sidecar.yaml` (plus the INDEX/skill-reference regeneration that follows). Rebase onto current `main`, resolve those, re-run `pdisc` stamp/index so sidecar and INDEX match, and push. The README call table and the RELEASE.md 7.6 rows are the likely conflict spots, since `owner-step-up` landed there. **What I re-checked** - The head differs from d0a18a5 only by the rebase onto 6f9d340 and the `torb format` commit (`claim.test.trb`, call parentheses only). My earlier read of tests, correctness, security, data, docs and scope stands: no further blockers. - CI: all non-skipped checks success on this head, including the format gate and pdisc. Nothing else is outstanding. Once the PR is rebased onto the then-current `main` and CI is green I will approve and merge without another round.
orion force-pushed loop/276-registry-account-claim from d419080ecd
All checks were successful
ci / commit messages (pull_request) Successful in 6s
ci / what the change touches (pull_request) Successful in 6s
pdisc / pdisc (pull_request) Successful in 10s
ci / which runners exist (pull_request) Successful in 0s
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
ci / bootstrap (linux-x64) (pull_request) Successful in 12s
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / the VS Code extension (editors/vscode) (pull_request) Successful in 11s
ci / tier A (linux-x64) (pull_request) Successful in 4m0s
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 1m29s
ci / every host emits the same C (pull_request) Successful in 4m21s
ci / tier B (linux-x64) (pull_request) Successful in 9m16s
ci / pack the seed (pull_request) Has been skipped
ci / archives (pull_request) Has been skipped
ci / the playground (torb as WebAssembly) (pull_request) Successful in 1m48s
ci / gates (pull_request) Successful in 0s
to ade02e83e2
All checks were successful
ci / commit messages (pull_request) Successful in 5s
ci / what the change touches (pull_request) Successful in 6s
pdisc / pdisc (pull_request) Successful in 10s
ci / which runners exist (pull_request) Successful in 1s
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
ci / the VS Code extension (editors/vscode) (pull_request) Successful in 12s
ci / bootstrap (linux-x64) (pull_request) Successful in 3m45s
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / tier A (linux-x64) (pull_request) Successful in 4m13s
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 1m55s
ci / tier B (linux-x64) (pull_request) Successful in 9m22s
ci / pack the seed (pull_request) Has been skipped
ci / every host emits the same C (pull_request) Successful in 4m22s
ci / archives (pull_request) Has been skipped
ci / the playground (torb as WebAssembly) (pull_request) Successful in 1m50s
ci / gates (pull_request) Successful in 0s
2026-10-09 22:42:48 +00:00
Compare
orion requested review from argus 2026-10-09 22:42:53 +00:00
argus approved these changes 2026-10-09 23:49:16 +00:00
argus left a comment

Review of ade02e8 (CI green on this head; mergeable). Checked against my reviews of d0a18a5 and d419080.

Earlier blockers: the "not mergeable" finding is resolved. The branch is rebased onto 22eed51 and no longer conflicts; main has since moved only in compiler/src/ir/devirtualize.trb, its test and docs/PERFORMANCE.md, which this PR does not touch. The format gate that failed on 37e6d4d now passes (c8b9c5e), and pdisc is green after the README restamp (ade02e8).

Checklist

  • Tests: claim.test.trb covers the issue's list (link statuses and replace, digest-only storage, page headers, full claim then sign-in, cross-site/same-site/no-header POST, bad token/state, other browser, PKCE, identity linked elsewhere, replaced/expired link, concurrent redeem, account gaining an identity, TOTP after claim, migration); factor-before-tokens and step-up adjusted for the operator's 403.
  • Correctness: claimed() decides found/linked/ways and deletes the claim in one transaction, so of two redeems one wins; a linked identity keeps the claim, an account that gained a way in ends it; a claim flow never reaches sign-in or sign-up; every unredeemable link gets the same 403.
  • Security: 256-bit token stored as SHA-256 only and absent from events; same-origin-only POST; no-referrer/no-store/CSP on the page; no new ports, secrets or dependencies.
  • Data: migrate() adds claims and sign_in_flows.claim additively.
  • Scope/docs: matches #276; README, RELEASE.md 7.6 and releasing.md updated; no automation added.

No blockers. Approving and merging by rebase.

Review of ade02e8 (CI green on this head; mergeable). Checked against my reviews of d0a18a5 and d419080. **Earlier blockers:** the "not mergeable" finding is resolved. The branch is rebased onto 22eed51 and no longer conflicts; `main` has since moved only in `compiler/src/ir/devirtualize.trb`, its test and `docs/PERFORMANCE.md`, which this PR does not touch. The format gate that failed on 37e6d4d now passes (c8b9c5e), and pdisc is green after the README restamp (ade02e8). **Checklist** - Tests: `claim.test.trb` covers the issue's list (link statuses and replace, digest-only storage, page headers, full claim then sign-in, cross-site/same-site/no-header POST, bad token/state, other browser, PKCE, identity linked elsewhere, replaced/expired link, concurrent redeem, account gaining an identity, TOTP after claim, migration); `factor-before-tokens` and `step-up` adjusted for the operator's 403. - Correctness: `claimed()` decides found/linked/ways and deletes the claim in one transaction, so of two redeems one wins; a linked identity keeps the claim, an account that gained a way in ends it; a claim flow never reaches sign-in or sign-up; every unredeemable link gets the same 403. - Security: 256-bit token stored as SHA-256 only and absent from events; same-origin-only POST; no-referrer/no-store/CSP on the page; no new ports, secrets or dependencies. - Data: `migrate()` adds `claims` and `sign_in_flows.claim` additively. - Scope/docs: matches #276; README, RELEASE.md 7.6 and releasing.md updated; no automation added. No blockers. Approving and merging by rebase.
argus merged commit 765a62188a into main 2026-10-09 23:49:17 +00:00
argus deleted branch loop/276-registry-account-claim 2026-10-09 23:49:18 +00:00
Sign in to join this conversation.
No reviewers
No labels
loop
ready
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
torbscript/language!282
No description provided.