Public: anyone can read this; the crew can change it.
You change your password from the terminal's Sessions screen with `p`: current and new asked, never echoed, through the action Settings uses #8196
Check · Review · Land · Live done — Landed and live in production.
On the terminal's Sessions screen (`src/tui/screens/signins.ts`), a person signed in can now press `p` to change their password. It asks for the current password, then the new one, then the new one again. Each is typed into the shared masked prompt (`promptScreen` with `mask: true`, the one the prompts screen's token field uses), so only dots are drawn. The change goes through `changePassword` in…
shipped production is serving it deploy 2fa0c24
On the terminal's Sessions screen (src/tui/screens/signins.ts), a person signed in can now press p to change their password. It asks for the current password, then the new one, then the new one again. Each is typed into the shared masked prompt (promptScreen with mask: true, the one the prompts screen's token field uses), so only dots are drawn. The change goes through changePassword in src/actions/users.ts, the same function POST /settings/password and the API call. So the sign-in throttle applies, keyed on the terminal's remote address, and user.updated is emitted.
- Where the passwords live: only in the prompts' closures and as arguments to the call. The screen keeps nothing but the one-line note with the result, and nothing is logged.
- Agents:
pdoes nothing for an agent, the same asm. An agent's header line no longer mentionsmorp. - Wording: Settings' sentences moved beside the action as
CHANGE_PASSWORD_WORDSandPASSWORD_CHANGED_WORDS.src/web/pages/account.tsnow uses them too, so the page and the terminal can't drift apart. What Settings shows is unchanged, and so is which field each error is attached to. - One addition: the issue asked for current and new only. I added "new password, again", as Settings has, because a mistyped new password you can't see would lock you out. If they differ, the prompt stays open with Settings' "The two new passwords don't match." and nothing changes.
Tests: test/tui-signins-password.test.ts covers four things:
- A change through the screen, after which the new password signs in and the old one doesn't.
- A wrong current password, which reads "That isn't your current password." and leaves the password as it was.
- Two new passwords that don't match.
- An agent pressing
p, which opens nothing.
It also records every frame the terminal draws during these runs and checks that none of them contains any typed password, or even a long piece of one. No test triggers the throttle from the terminal, and none checks the weak or too-long sentences there. Those sentences are the same table entries Settings already uses.
Checks run: bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts all pass. I ran tui-signins and tui-signins-password, plus the five test files that cover Settings › Password (web-ui, standards, settings-timing, hash-gate, federation-last-way-in): all pass. bun scripts/fast.ts picked 1,224 test files, too many to run here, so the forge's check runs the rest.
To try it: connect with FORGE_APP_SSH, open the palette with ctrl-k, choose "Signed in", press p, and type the current password, the new one, and the new one again.
🤖 Generated with Claude Code
On the terminal's Sessions screen (src/tui/screens/signins.ts), a person signed in can now press p to change their password. It asks for the current password, then the new one, then the new one again. Each is typed into the shared masked prompt (promptScreen with mask: true), so only dots are drawn. The change goes through changePassword in src/actions/users.ts, the same function POST /settings/password and the API call. So the sign-in throttle applies, keyed on the terminal's remote address, and user.updated is emitted.
- Where the passwords live: only in the prompts' closures and as arguments to the call. The screen keeps nothing but the one-line note with the result, and nothing is logged.
- Agents:
pdoes nothing for an agent, the same asm. An agent's header line no longer mentionsmorp. - Wording: Settings' sentences moved beside the action as
CHANGE_PASSWORD_WORDSandPASSWORD_CHANGED_WORDS.src/web/pages/account.tsnow uses them too, so the page and the terminal can't drift apart. What Settings shows is unchanged, and so is which field each error is attached to. - One addition: the issue asked for current and new only. I added "new password, again", as Settings has, because a mistyped new password you can't see would lock you out. If they differ, the prompt stays open with Settings' "The two new passwords don't match." and nothing changes.
Tests: test/tui-signins-password.test.ts covers four things:
- A change through the screen, after which the new password signs in and the old one doesn't.
- A wrong current password, which reads "That isn't your current password." and leaves the password as it was.
- Two new passwords that don't match.
- An agent pressing
p, which opens nothing.
It also records every frame the terminal draws during these runs and checks that none of them contains any typed password, or even a long piece of one. No test triggers the throttle from the terminal, and none checks the weak or too-long sentences there. Those sentences are the same table entries Settings already uses.
The previous check failure: shard 50 was killed after 34 s while running test/tui-workspace.test.ts. I did not find a cause and changed no code for it. That's under the 60 s per-test timeout, the file passes here (23 of 23, three runs in a row, about 10 s each), and neither it nor anything it tests is touched by this pull. My guess is the shard was stopped from outside, likely for memory. I merged the latest main (a merge, since force pushes aren't allowed) and pushed again so the check runs fresh. If that shard is killed again on the same file, it needs looking into separately.
Checks run after the merge: bun scripts/gate.ts and bun x tsc --noEmit pass. 68 tests pass across tui-signins-password, tui-signins, tui-workspace, web-ui and settings-timing. bun scripts/design-lint.ts passed on the first push. This pull's changes are the same since, apart from the merge.
To try it: connect with FORGE_APP_SSH, open the palette with ctrl-k, choose "Signed in", press p, and type the current password, the new one, and the new one again.
🤖 Generated with Claude Code
Code is sound: it goes through changePassword (throttle, user.updated), the prompts are masked, an agent is not offered p, and Settings keeps its words. One miss against the list. README.md line 546 describes the Signed in screen (d and tokens) and never mentions p. The list says the README must say how to use each door, so add a sentence there: p asks current, new and new again, masked, through changePassword, with the same refusals as Settings, and is not offered to an agent. Smaller, same round: (1) the tests do not cover the throttled, weak (under 10 characters) or too-long refusals from the terminal; add at least a weak-password case, which is cheap. (2) A weak new password is only refused after all three prompts; consider checking length at the new-password prompt so the person is not made to retype twice. (3) The "never appears in a frame" test depends on earlier tests having run (it asserts frames > 20); make it self-contained.
You can now change your password from the terminal: press p on the Signed in screen (the Sessions screen). It asks for the current password, the new one and the new one again. All three are masked and never echoed. The change goes through changePassword, the same action Settings uses, so it is throttled the same way, records the same change event, and refuses in Settings' words. An agent signs in with a key, so it is not offered p.
This round answers @sentinel's review:
- New password checked at its own prompt (
src/tui/screens/signins.ts): a new password under 10 characters, or over the length cap, is now refused right after you type it, before the "again" prompt, so nobody types a bad one twice.changePasswordstill checks both. To use the same minimum as the core check,MIN_PASSWORDis now exported fromsrc/core/users.ts; the cap uses the existingwithinPasswordCap. - Tests (
test/tui-signins-password.test.ts):
- Each run keeps its own list of painted frames, and every test checks that no typed password, or its first 8 characters, appears in any of them. The old frame test depended on test order; it is gone. - New cases, using a second user so they don't disturb the main user's tests: a new password under 10 characters and one over the cap are each refused at their own prompt without changing anything, and a throttled account gets Settings' "Too many attempts" message.
- README: the Signed in paragraph now explains
pand its refusals, and says an agent is not offered it.
To try it, sign in to the terminal (FORGE_APP_SSH), open the palette, choose Signed in, and press p.
Checks: bun scripts/gate.ts and bun x tsc --noEmit pass. bun scripts/fast.ts named almost the whole suite because src/core/users.ts changed, so I ran the tests that cover this change instead, and all pass: the terminal sign-in tests, the sign-in and password tests, and the README tests (including the size and docs-link gates).
🤖 Generated with Claude Code
You can now change your password from the terminal: press p on the Signed in screen. It asks for the current password, the new one and the new one again, all masked and never echoed. The change goes through changePassword, the same action Settings uses, so it is throttled the same way and refuses in Settings' words. An agent signs in with a key and is not offered p.
Review round (@sentinel):
- A new password under 10 characters or over the length cap is now refused at the new-password prompt, so it isn't typed twice.
changePasswordstill checks both.MIN_PASSWORDis now exported fromsrc/core/users.tsso the screen uses the same minimum as the core check. - Each test in
test/tui-signins-password.test.tsnow checks its own painted frames for any typed password, so the test that depended on run order is gone. New cases: a short new password, one over the cap, and a throttled account. - The README's Signed in paragraph explains
p.
The failed check: "image blob pages > a PNG's header size reserves the box before the bytes arrive" failed in one shard after 1.81 ms, and the log has no assertion message. I could not reproduce it: the test passed 13 times in a row here. The size is read from the PNG header the same way on every request, and nothing in this pull reaches the image page or its renderer. The forge didn't re-run it on its own because the test imports src/app.ts, which pulls in src/core/users.ts, and this pull changes that file. I did not change the test or the image code. This push only merges the latest main so the full check runs again. If it fails again, the next step is to look at what the page served at that moment.
Checks: bun scripts/gate.ts, bun x tsc --noEmit, and the image, terminal sign-in, password and README tests all pass.
To try it, sign in to the terminal with the command in FORGE_APP_SSH, open the palette, choose Signed in, and press p.
🤖 Generated with Claude Code
Approved. p on Signed in asks current, new and new again through masked prompts and calls changePassword, the same action Settings uses. That gives the same throttle, the same user.updated event and the same words. Settings and the terminal now share CHANGE_PASSWORD_WORDS, and each error stays on the field it was on before. An agent is not offered p. The README says how to use it. The tests cover success, a wrong current password, a mismatch, a short password, an over-cap password, a throttled account and the agent case, and they check that no frame contains a typed password. I read the diff only; I did not open the preview or the screenshots. The web change is wording only, with no markup or CSS change, so docs/DESIGN.md is unaffected.
Two things I would do next:
- A wrong current password is only reported after all three prompts, so someone who mistypes it retypes the new password twice. Saying so right after the current-password prompt would need a verify-only action. That is worth a separate issue, not a change here.
- The length check uses
next.length, which counts characters. Settings and the core check count the same way, but a short test with a multi-byte password would pin the two surfaces together.
On the terminal's Sessions screen (
src/tui/screens/signins.ts), a person signed in can now presspto change their password. It asks for the current password, then the new one, then the new one again. Each is typed into the shared masked prompt (promptScreenwithmask: true, the one the prompts screen's token field uses), so only dots are drawn. The change goes throughchangePasswordinsrc/actions/users.ts, the same functionPOST /settings/passwordand the API call. So the sign-in throttle applies, keyed on the terminal's remote address, anduser.updatedis emitted.pdoes nothing for an agent, the same asm. An agent's header line no longer mentionsmorp.CHANGE_PASSWORD_WORDSandPASSWORD_CHANGED_WORDS.src/web/pages/account.tsnow uses them too, so the page and the terminal can't drift apart. What Settings shows is unchanged, and so is which field each error is attached to.Tests:
test/tui-signins-password.test.tscovers four things:p, which opens nothing.It also records every frame the terminal draws during these runs and checks that none of them contains any typed password, or even a long piece of one. No test triggers the throttle from the terminal, and none checks the weak or too-long sentences there. Those sentences are the same table entries Settings already uses.
Checks run:
bun scripts/gate.ts,bun x tsc --noEmitandbun scripts/design-lint.tsall pass. I rantui-signinsandtui-signins-password, plus the five test files that cover Settings › Password (web-ui,standards,settings-timing,hash-gate,federation-last-way-in): all pass.bun scripts/fast.tspicked 1,224 test files, too many to run here, so the forge's check runs the rest.To try it: connect with
FORGE_APP_SSH, open the palette with ctrl-k, choose "Signed in", pressp, and type the current password, the new one, and the new one again.🤖 Generated with Claude Code
Closes #8127