Public: anyone can read this; the crew can change it.

No account depends on the platform's sign-in: every edge-made account gets a passkey, then the edge can be switched off #9021

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

An admin can now see which accounts would be locked out if `signin.edge` went off. The first checklist item is done. The other two need the owner present, so I did not touch them.

shipped production is serving it deploy e209d31

opened this

An admin can now see which accounts would be locked out if signin.edge went off. The first checklist item is done. The other two need the owner present, so I did not touch them.

What changed

  • edgeOnlyAccounts(db) in src/core/accounts.ts lists active people with no password, no passkey and no parent-forge link. They count as edge accounts if the edge made them or they are linked to an edge login. Admins come first, then oldest.
  • /settings/accounts shows a card at the top for admins, listing those people with a nudge to add a passkey at Settings → Passkeys. The card disappears when the list is empty.
  • docs/OPERATING.md describes the card, the owner-present switch-off on a preview, and the rollback in Settings → Capacity.
  • test/accounts-edge-only.test.ts checks that edge-made and edge-linked accounts are listed, that accounts with a password or no edge origin are not, that the page shows the card, and that the list empties once they have a way in.

Already in place: the refusal for an admin who has neither a password nor a passkey is in src/actions/capacity.ts (hasOwnCredential). Rollback needed no code either.

Not done

  • The card is on the Accounts page only. I added no MCP or API list for it, and no way to nudge a person from there.
  • I did not set signin.edge to off on a preview or sign in with the owner.

Checks: bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts are clean. The three account test files (24 tests) pass. scripts/fast.ts found 1417 files, which is over the budget, so I ran only the account tests that cover the change. The forge's check runs the full suite.

Try it: sign in as an admin and open /settings/accounts. The card appears when any account has only an edge origin and no passkey or password.

🤖 Generated with Claude Code

Closes #8947

commented

An admin can now see which accounts would be locked out if signin.edge went off. The first checklist item is done. The other two need the owner present, so I did not touch them.

What changed

  • edgeOnlyAccounts(db) in src/core/accounts.ts lists active people with no password, no passkey and no parent-forge link. They count as edge accounts if the edge made them or they are linked to an edge login. Admins come first, then oldest.
  • /settings/accounts shows a card at the top for admins, listing those people with a nudge to add a passkey at Settings → Passkeys. The card disappears when the list is empty.
  • docs/OPERATING.md describes the card, the owner-present switch-off on a preview, and the rollback in Settings → Capacity.
  • test/accounts-edge-only.test.ts checks that edge-made and edge-linked accounts are listed, that accounts with a password or no edge origin are not, that the page shows the card, and that the list empties once they have a way in.

Already in place: the refusal for an admin who has neither a password nor a passkey is in src/actions/capacity.ts (hasOwnCredential). Rollback needed no code either.

Not done

  • The card is on the Accounts page only. I added no MCP or API list for it, and no way to nudge a person from there.
  • I did not set signin.edge to off on a preview or sign in with the owner.

Checks: bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts are clean. The three account test files (24 tests) pass. scripts/fast.ts found 1417 files, which is over the budget, so I ran only the account tests that cover the change. The forge's check runs the full suite.

Try it: sign in as an admin and open /settings/accounts. The card appears when any account has only an edge origin and no passkey or password.

🤖 Generated with Claude Code

commented

The check failed because no commit message had a "Try:" line, and I've fixed that. I added an empty commit with the line and pushed it. No code changed. bun scripts/gate.ts now passes locally, and I haven't seen the forge's re-run.

What changed: Settings → Accounts, for an admin, now shows a card at the top. It lists accounts that can sign in only through the platform edge (no password, passkey or parent-forge link) and nudges each to add a passkey. docs/OPERATING.md covers the card and the rollback. A new test, test/accounts-edge-only.test.ts, covers the list and the page.

Not done: the two items that need the owner present. I did not set signin.edge to off on a preview or sign in afterwards, and the rollback in Settings → Capacity is untested. The list is on the Accounts page only, with no MCP or API version.

Try: sign in as an admin on the preview and open /settings/accounts. The card at the top names accounts that sign in only through the edge.

🤖 Generated with Claude Code

Changes requested — before the latest push

The Settings → Accounts card works on the web and the list logic is sound, but it misses the bar in four places. Fix these in one round.

  1. (First) No terminal door. An admin can see who would be locked out only on the page. Add a screen in src/tui/ that lists the same edgeOnlyAccounts(db) result, admin-only. Export its entry and add one line to ENTRIES in src/tui/screens/entries.ts, as AGENTS.md says. Gate it on isAdmin like the page.
  2. No docs/features/<name>.md. The how-to went into one very long line in docs/OPERATING.md. Put the how-to in docs/features/edge-only-accounts.md (the card, the terminal screen, switching signin.edge off on a preview with the owner present, rollback in Settings → Capacity). Keep the OPERATING.md change to a short pointer.
  3. The edge-origin test is a payload LIKE '%"via":"edge"%'. It breaks if the JSON is spaced or reordered. src/core/accounts.ts already parses this payload in parseOrigin and treats via === 'edge' as EDGE_METHOD. Reuse that, for example with json_extract(e.payload,'$.via') = 'edge' or a shared helper, so the two definitions of 'edge-made' cannot drift.
  4. Test gaps. Add cases that fail without the change: a non-admin and a guest get no card or list (and, once the terminal screen exists, none there either); an account that has a passkey drops off the list; a removed account and an agent are excluded; admins sort first. The page assertion only checks the text and one name, and the existing test never exercises the passkey NOT EXISTS branch.

Boxes 2 and 3 of the issue need the owner present and were not done, so they stay open.

commented

All four review points are fixed and pushed. The new tests, test/accounts.test.ts and test/palette-archived.test.ts pass, and tsc and scripts/gate.ts are clean. I didn't run scripts/design-lint.ts (no src/web/ change) or the full suite. scripts/fast.ts named 1,493 of 2,014 test files for this diff, so I ran only the ones that cover it.

Changes

  • Terminal screen: src/tui/screens/edgeonly.ts lists edgeOnlyAccounts(db). It is offered to admins only (not agents), and a non-admin who somehow opens it gets an empty list. Its entry is one line in ENTRIES, after access.
  • One definition of edge-made: the LIKE is now json_extract(e.payload,'$.via') = ?, guarded by json_valid. parseOrigin and the query share one EDGE_VIA constant. Spaced or reordered JSON now matches.
  • Docs: the how-to is now docs/features/edge-only-accounts.md: the card, the terminal screen, switching signin.edge off on a preview with the owner present, and rollback in Settings → Capacity. docs/OPERATING.md keeps a one-line pointer.
  • Tests: these cases are added to test/accounts-edge-only.test.ts:

- Dropped from the list: an account with a passkey, a removed account, an agent, and a payload whose via isn't edge. - Matched and ordered: a spaced/reordered payload is still listed, and admins sort first. - No card or entry: a non-admin and a guest get no card on the page, and the terminal entry isn't offered to a non-admin or a guest.

Left out

  • I didn't add a case for an unparseable payload, because the event table rejects malformed JSON on insert. The json_valid guard stays as a safeguard.
  • I didn't test the terminal screen's rendered rows. The tests cover the entry's gating and the shared list, not the drawing.
  • Boxes 2 and 3 of the issue need the owner present and stay open.

To try: sign in as an admin on the preview and open /settings/accounts for the card, or open the palette and pick "Edge-only accounts" in the terminal.

commented

I added an empty follow-up commit whose message carries the "Why now:" line the gate asked for, and pushed it. Locally bun scripts/gate.ts passes. I haven't seen the forge's check re-run on the push.

The failure was only about the commit message, so no code or tests changed. The earlier test run still stands.

The line reads: "the reviewer asked for a terminal door beside the Settings card, so an admin can see who an edge switch-off would lock out before it happens."

I used a new commit instead of rewriting the pushed ones, since force pushes are refused.

commented

The failing test is fixed and pushed. The forge's check hasn't re-run on it yet.

test/palette-roster.test.ts pins the admin palette entry by entry, so it failed when I added "Edge-only accounts". The test was out of date, not the code. I added the new entry to the admin roster only, since non-admins don't get it. That file now passes locally (15 tests) and scripts/gate.ts passes.

I didn't re-run the whole suite. test/tui-audit.test.ts also mentions "Who can do what", but it doesn't list the full roster, so I left it alone.

Approved

All four earlier asks are met. The query now uses json_extract with a shared EDGE_VIA constant. The terminal screen exists and is registered in ENTRIES. The how-to is in docs/features/edge-only-accounts.md, with a one-line pointer in OPERATING.md. The tests now cover non-admin, guest, passkey, removed, agent and ordering. I read the diff and did not drive the preview as an admin, so the card is unseen there. The web card uses card() with the attn tone, links with <a>, and needs no JavaScript.

Next, both non-blocking:

  1. Non-blocking: add a test that renders the terminal screen rows, not only the entry gating.
  2. Non-blocking: once the owner is present, run the preview switch-off and rollback described in the how-to.
commented

Left #8947 open: 2 unticked items remain (The list is empty, or the owner chose each leftover, then signin.edge is set off on a preview and the owner signs in.; Rollback: set it back on from Settings → Capacity.).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs