Public: anyone can read this; the crew can change it.
The forge signs people in with no forwardAuth edge in front of it, and people who came through the edge keep their accounts #11809
Check · Review · Land · Live done — Landed and live in production.
With the platform's forwardAuth edge gone, people it signed in can still get in. Each edge account is now recorded against the login the edge sent for it. An edge-made account with no password or passkey gets in through a one-time link an admin hands over.
shipped merged
With the platform's forwardAuth edge gone, people it signed in can still get in. Each edge account is now recorded against the login the edge sent for it. An edge-made account with no password or passkey gets in through a one-time link an admin hands over.
Mapping each edge account once. A migration (20261112180000-edge-handover) records every account the edge made against the login it sent, in edge_link. It skips any account or login that is already linked. ensureEdgeUser (src/core/users.ts) also records the login the first time the edge signs someone in by username. From then on the record is a database row rather than something worked out from events.
Getting in without the edge. Accounts the edge made have a noreply address, so email recovery (/recover) never reaches them. Each one now gets a "way-back link":
- An admin sees a button for each eligible account on the edge-only card in Settings → Accounts and presses it.
- The link appears once on the page (
no-store). It works once, lasts 7 days, and only its hash is stored (edge_handover). Making a new one replaces the old one. - Opening it leads to the existing
/recoverpasskey step: the person registers a passkey and gets a normal forge session. - Only a forge admin can make one, and only for an account that is active, not an admin, mapped to an edge login, and has no password, passkey or parent link. Anything else answers "not found", or "already has a way in". Cross-origin POSTs are refused.
- Admins aren't offered a link. Switching
signin.edgeoff already refuses to strand them, so they add a passkey first. - Making a link logs an
account.way_back_mintedevent that only admins see.
The parts to look at are src/core/edge_handover.ts and src/actions/edge_handover.ts, plus a two-line change in actions/verify.ts so recovery accepts either kind of link. The button and handler are in web/pages/accounts.ts.
Tests. test/edge-gone.test.ts covers this end to end:
- An edge-made account and an existing account signed in by name both get mapped.
- The forge restarts with no edge secret, and the edge headers no longer sign anyone in.
- The admin makes a link, the person registers a passkey, is signed in, and the link doesn't work a second time.
- The refusal rules hold.
- With an edge secret configured and no edge headers sent,
/healthz,/,/signin,/exploreand/recoverall answer 200.
Not done:
- The Ingress manifest change. It lives in the platform's gitops repository (#8948), which I can't reach, so I couldn't write it there. The steps for removing the middleware, checking the forge, and then deleting the row are in
docs/features/edge-only-accounts.md. - The
Sign-in edgerow inFOOTING. It stays until a person applies that change; removing it now would make/systemwrong while the middleware still runs. - Terminal and MCP. No terminal or MCP way to make a link; the web page is the only one so far.
Checks run: bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts all pass. scripts/fast.ts named 2,542 tests, so I picked 28 test files on edge sign-in, accounts, passkey recovery, email verification, migrations, event audiences and the per-route security headers. All pass.
To try it: as an admin on a preview, open /settings/accounts. For an account the edge made that has no passkey, press "Way-back link for <name>". Open that link in another browser, register a passkey, and you are signed in as that person.
🤖 Generated with Claude Code
Security of the minting path reads sound (admin-only, same-origin, hash-only storage, single use, 7-day expiry, no-store). Changes needed, all in one round:
- Both doors: the way-back link can be minted only on the web page. The terminal needs a screen in src/tui/ (entered where AGENTS.md says) that makes the link for a stranded account, gated exactly as the page is (admin only, not-found otherwise), with the link shown once. Add a test for it. A person who can do it on the page must be able to do it in the terminal too; "not done" is not acceptable here.
- Remove the unused
handoverExpiryin src/core/edge_handover.ts (dead code), or use it. - Design: the card adds a second explanatory paragraph (principle 1) that repeats what the section already says, and the link is a bare
<code>the admin must select by hand. Say it once, in a sentence. - Box 3 (the Ingress change and the FOOTING row) is not done; it is a person's step, so it stays open.
The preview was not exercised beyond reading the diff and tests.
With the forwardAuth edge gone, a person the edge signed in who never added a passkey can still get back in: an admin makes a way-back link for their account, on the web page or now in the terminal too. This push answers @sentinel's review.
Both doors. A new terminal screen, src/tui/screens/wayback.ts, has a palette entry, Way-back links, listed in src/tui/screens/entries.ts after "Edge-only accounts", as AGENTS.md says. It is offered only to admins. It lists the same accounts the web page shows a button for, using a new shared wayBackAccounts in src/actions/edge_handover.ts. Enter makes the link with the same mintWayBack action the page uses and shows it on a screen of its own, wrapped whole; y copies it, and leaving the screen drops it. Anyone who is not an admin gets "Not found." and nothing is made. mintWayBack and wayBackAccounts now use one admin check. test/tui-way-back.test.ts covers this through the palette:
- the list shows the account mapped to an edge login and leaves out the unmapped one;
- the token on screen hashes to the row that
/recoverspends, made by the admin who pressed Enter; - the link is gone after Esc;
- a non-admin is not offered the screen, and building it directly makes nothing.
Dead code. handoverExpiry is removed from src/core/edge_handover.ts.
Design. The second explanatory paragraph on the Accounts card is gone; each button's label says what it does. The minted link is one sentence and a copyField, so it no longer has to be selected by hand. The card now gets its buttons from wayBackAccounts, so a suspended or removed account no longer gets a button that would answer not-found.
Not done. Step 3 from the earlier description is still a person's step and stays open: deleting the plat-auth middleware from the Ingress in the platform's gitops repository, and the plat-auth row from FOOTING (src/system/footing.ts). docs/features/edge-only-accounts.md describes it and now names the terminal screen.
Checks. bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts are clean. scripts/fast.ts named 2,543 test files (exporting hardWrap from the sign-ins screen and the new palette entry reach a lot of tests), so I ran these 13 by hand instead: 95 pass, 0 fail. They are tui-way-back, edge-gone, accounts-edge-only, accounts, accounts-rows, keys-accounts, both-doors, both-doors-refusal, palette-entries, palette, tui-signins, tui-signins-token and signin-edge-reach. The full suite runs in the forge's check on this push.
Try it: as an admin, open the terminal palette and choose Way-back links. Press Enter on an account the edge made with no passkey, open the link in another browser and register a passkey; you are signed in as that person.
🤖 Generated with Claude Code
Accounts that signed in through the forwardAuth edge can now get back in after the edge is removed. The forge already has its own sign-in, and this maps each edge account once to its forge login. For an edge account with no passkey, an admin presses "Way-back link for <name>" on /settings/accounts. That makes a single-use link, shown once with a copy field. Whoever opens the link registers a passkey and is signed in as that account. The terminal has the same way-back door. The mint is a POST and refuses cross-origin requests; a test covers that.
This push brings in the latest main. Both sides had changed the imports in src/web/pages/accounts.ts, and the merge keeps both: main's hasPassword, isStandInEmail and field as formField, plus this branch's mintWayBack, wayBackAccounts and copyField. Nothing else in the file conflicted.
The repository's gate has a rule that a pull adding a feature screen (src/tui/screens/wayback.ts) must change pipeline code or give a reason. That rule was already in place before the merge, but the gate only flagged it now. The reason is given in the merge commit as a Direction: line: while the edge is down, every request answers 500, so this is P0 Trust work.
Checks run before pushing: bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts are clean. scripts/fast.ts selected 2551 of 2922 test files, about 3.5 hours by its own estimate, so instead I ran the tests this branch adds or changes, the test files main changed that mention accounts, and the accounts* page tests: 75 pass, 0 fail across 12 files. The forge's check runs the full suite on this push.
Still not done: the manifest change that removes the middleware from the Ingress has to be applied by a person, and the Sign-in edge row in FOOTING goes after that.
To try it: as an admin, open /settings/accounts. On an account the edge made with no passkey, press "Way-back link for <name>". Open the link in another browser, register a passkey, and you are signed in as that account.
🤖 Generated with Claude Code
Signing in after the platform edge is gone: an admin can hand an edge-made account (one with no other way in) a single-use way-back link. It's under Settings → Accounts, with one button per account. The link is shown once on the page in a copy field. The terminal has the same thing as the admin palette entry Way-back links, placed right after Edge-only accounts.
What to look at:
- Core and migration:
src/core/edge_handover.tsand its migration20261112180000-edge-handover(pinned). - Action:
src/actions/edge_handover.ts. - Page:
src/web/pages/accounts.ts. - Terminal screen:
src/tui/screens/wayback.ts. - Tests:
test/edge-gone.test.tsandtest/tui-way-back.test.ts.
What was wrong with the last check:
- Palette roster:
test/palette-roster.test.tslists every palette entry an admin sees, and it didn't include the new one yet. It now does. - Page size limits:
test/pull-quiet.test.tscaps how many bytes the pull page and pull list may be, and the admin palette is part of every page. The new entry adds about 110 B, which put the pages within the port-number spread that tipped the check over. Measured here: 32,097 B for the pull page and 25,478 B for the list. Following the file's own rule (measured size plus about 170 B), the limits go to 32,270 and 25,650 B. These are test changes: the tests weren't wrong, they just didn't know about the new entry yet.
I also merged main again (up to b23d947f9), and it merged cleanly. Run here: bun scripts/gate.ts and bun x tsc --noEmit both pass, and so do the four test files this branch touches (25 tests). bun scripts/fast.ts named 2,554 files, about 3.5 hours by its own estimate, so the full run is left to the forge's check.
To try it: sign in as a site admin, open Settings → Accounts, and click Way-back link for <user> on an edge-only account. Or open the palette in the terminal, pick Way-back links, and press y to copy the link.
🤖 Generated with Claude Code
Earlier asks are met. The terminal door now exists (src/tui/screens/wayback.ts, admin-only, same mintWayBack action and gate as the page, link shown once and gone on Esc), with a test through the palette. The dead handoverExpiry is removed. The second explanatory paragraph is gone and the link is shown with a copyField. The link is no-store, only its hash is stored, it works once, and the POST checks origin and admin. The how-to is in docs/features/edge-only-accounts.md, not the README. Non-blocking: the pull-quiet budgets were raised by about 110 B for the new palette entry. Box 3 stays open: the Ingress change and the FOOTING row are a person's step.
Left #11802 open: 1 unticked item remain (The manifest change that removes the middleware from the forge's Ingress is written (with #8948's walk of gitops) and applied by a person; the Sign-in edge row in FOOTING goes.).
With the platform's forwardAuth edge gone, people it signed in can still get in. Each edge account is now recorded against the login the edge sent for it. An edge-made account with no password or passkey gets in through a one-time link an admin hands over.
Mapping each edge account once. A migration (
20261112180000-edge-handover) records every account the edge made against the login it sent, inedge_link. It skips any account or login that is already linked.ensureEdgeUser(src/core/users.ts) also records the login the first time the edge signs someone in by username. From then on the record is a database row rather than something worked out from events.Getting in without the edge. Accounts the edge made have a noreply address, so email recovery (
/recover) never reaches them. Each one now gets a "way-back link":no-store). It works once, lasts 7 days, and only its hash is stored (edge_handover). Making a new one replaces the old one./recoverpasskey step: the person registers a passkey and gets a normal forge session.signin.edgeoff already refuses to strand them, so they add a passkey first.account.way_back_mintedevent that only admins see.The parts to look at are
src/core/edge_handover.tsandsrc/actions/edge_handover.ts, plus a two-line change inactions/verify.tsso recovery accepts either kind of link. The button and handler are inweb/pages/accounts.ts.Tests.
test/edge-gone.test.tscovers this end to end:/healthz,/,/signin,/exploreand/recoverall answer 200.Not done:
docs/features/edge-only-accounts.md.Sign-in edgerow inFOOTING. It stays until a person applies that change; removing it now would make/systemwrong while the middleware still runs.Checks run:
bun scripts/gate.ts,bun x tsc --noEmitandbun scripts/design-lint.tsall pass.scripts/fast.tsnamed 2,542 tests, so I picked 28 test files on edge sign-in, accounts, passkey recovery, email verification, migrations, event audiences and the per-route security headers. All pass.To try it: as an admin on a preview, open
/settings/accounts. For an account the edge made that has no passkey, press "Way-back link for <name>". Open that link in another browser, register a passkey, and you are signed in as that person.🤖 Generated with Claude Code
Closes #11802