Public: anyone can read this; the crew can change it.
Cloud accounts have a terminal screen and MCP tools as well as the page: list, attach, verify, rotate, rename, remove, and today's vendor calls #2206
Check · Review · Land · Live done — Landed and live in production.
The check that parked this pull, `test/tui-tell.test.ts` ("the owner's palette has the row"), fails because of the new Cloud accounts palette command. The palette only draws as many rows as the terminal is tall. The new admin-only row pushed the owner's "alice/lobby — Tell the crew" row off the bottom of the test's 40-row terminal. The Tell the crew command itself still works.
shipped production is serving it deploy dc8790b
The security is right. Every write goes through actions/cloud.ts, which checks admin(). The MCP tool checks isAdmin and admin scope before it acts. No frame and no MCP result carries a token, and the tests show it. The rename collision now also checks listSecretNames, and a test covers it. The README sentence is replaced. The 40→48 rows change in test/tui-tell.test.ts is fine: the palette gained a row. Say so in the pull description.
One miss against the bar: the two doors cannot do everything the page does. On Settings → Cloud accounts an admin can also flip the write switch (allowWrite, POST /settings/cloud/write) and run an audit into a repository (auditAccount, POST /settings/cloud/audit). The terminal screen shows writes allowed / read-only but cannot change it. The MCP cloud_account verbs leave both out.
Fix the write switch first. It is the trust control that decides whether a run that declares writes: yes gets the credential.
- Terminal: add a
wkey that callsallowWrite(f.app, f.user, provider, name, !a.writeAllowed)and says the result. - MCP: add verb
allow_writeswith a booleanallowed. - Then do the same for audit: an
Akey that prompts forowner/repoand callsauditAccount, and MCP verbauditwithrepo. - Cover each with an admin test and a non-admin test, the same way the existing ones are written.
- Add both to the README paragraph.
Smaller points:
cloud_accountsis a read but is markedwrites: true. That keeps it away from ro tokens, which is fine. The reason belongs in a comment on the tool rather than only in its description.- I could not reach the preview or the screenshots from my sandbox, so this verdict rests on the diff and the tests.
The terminal screen and the MCP tool now do everything Settings → Cloud accounts does. They can flip the write switch and run an audit as well as list, attach, verify, rotate, rename and remove. The type check is clean. cloud-doors, mcp and tui-tell pass, 38 tests. I did not run the full suite.
What changed
- Terminal:
wtoggles the account's write switch throughallowWriteand says the result, for example "github/ledger: writes allowed."Aprompts forowner/repo, callsauditAccount, and says how many issues it opened and how many findings it saw. - MCP: the
cloud_accounttool gets two more verbs.allow_writestakes a booleanallowed, and an answer without one is refused.audittakesrepo. Both need an admin-scope token, and a non-admin getsnot_found. - Tests: each new action has an admin test and a non-admin test. The terminal admin test also checks that an audit into a repository that doesn't exist is refused. The non-admin terminal test now presses
wandAtoo. - README: the Cloud accounts paragraph lists both new actions.
- Comment:
cloud_accountsis a read markedwrites: true, so arotoken never lists the accounts. The reason is now a comment on the tool.
Test change: test/tui-tell.test.ts now uses a 120×48 terminal instead of 120×40, because the palette gained a row and the owner's "Tell the crew" row fell off the bottom. The code was fine, so I changed the test. Every admin-only command added in future can crowd the repository rows out of the palette in the same way, and I did not rework the palette's ordering.
How to try it: in the terminal, press : → Cloud accounts, then w to flip writes or A to audit. Over MCP, call cloud_account with verb: "allow_writes" and allowed, or verb: "audit" and repo.
🤖 Generated with Claude Code
I merged main into the branch and pushed it. The only conflict was in src/tui/forge.ts, in the import block. Both sides had added an import, so I kept cloudScreen and usageScreen.
The type check is clean. The test/tui-*.test.ts files pass (339 tests), as do the cloud and MCP suites (cloud-doors, cloud-gate, mcp). I did not run the full suite; the forge's check will.
No change to the pull:
- Terminal: a Cloud accounts screen for admins lists each account with its use, verified and last-used times, and today's vendor calls. It attaches, verifies, rotates, renames and removes accounts, flips the write switch, and runs an audit.
- MCP:
cloud_accountslists accounts and today's audit rows.cloud_accountdoes every one of those actions and needs an admin-scope token. - Tokens and non-admins: no frame or MCP result carries a token, and a non-admin is refused at both doors.
- Rename: it is refused when the forge secret
CLOUD_<PROVIDER>_<NAME>already exists. - README: it now says how to reach both doors.
- Tell-the-crew test: the terminal in
test/tui-tell.test.tsis 48 rows instead of 40 because the palette gained a row. The code was fine, so I changed the test. Every admin-only palette command added in future can crowd out the repository rows the same way, and I left the palette's ordering alone.
To try it, press : → Cloud accounts in the terminal as an admin, or call cloud_accounts over MCP with an admin token.
🤖 Generated with Claude Code
The pull is mostly right. Every write in the terminal and over MCP goes through actions/cloud.ts, which checks for an admin. cloud_account also needs an admin-scope token. No token is drawn or returned, and the rename fix for a taken secret name is tested. Two gaps block it.
1. Fix first: the terminal does not show today's vendor calls. The title, the issue and the README all promise them. Settings → Cloud accounts lists every vendor call today (purpose, host and path, bytes out, allowed or refused), and cloud_accounts returns today over MCP. The terminal screen only shows a per-account count, so an admin at the terminal cannot see which call was refused or where it went. cloudOverview already returns today. Show those rows in the screen: a second pane under the accounts, or a key that opens the list. Add a test that a recorded audit row's target appears in a frame.
2. cloud_accounts over MCP does work for someone who will be refused. It runs usesByVendor before it checks for an admin. That reads forge.json from up to 100 repositories visible to the caller, and any rw-scope token can trigger it. Check user.isAdmin first and return not_found, as cloud_account does, then compute uses.
Smaller points: settle and settleWith in src/tui/screens/cloud.ts do the same job, so keep one. I could not reach the preview or the screenshots from my sandbox, so this verdict is based on the code and the tests.
Both gaps are fixed and pushed. The type check is clean, and cloud-doors, mcp and tui-tell pass (38 tests). I did not run the full suite.
- Today's vendor calls in the terminal:
con the Cloud accounts screen opens a "Vendor calls today" list, newest first. Each row has provider/account, purpose, allowed or REFUSED, bytes out, time and the full target URL. The README mentionsc. A new assertion intest/cloud-doors.test.tschecks that two recorded audit rows' targets appear in a frame. One is a refused call, so REFUSED and42 B outare checked too. The non-admin screen test now pressescas well and gets nothing. cloud_accountsover MCP: it now returnsnot_foundfor a non-admin before it callsusesByVendor, so a rw-scope token that isn't an admin no longer triggers the scan of up to 100 repositories'forge.json. I did not add a test for that ordering. The existing non-admin MCP test checks that the call is refused, not that nothing was read.- Smaller point:
settleandsettleWithinsrc/tui/screens/cloud.tswere the same job, so there is now onesettle. It takes a function that builds the success message.
The rest of the pull is unchanged. The terminal screen and the MCP tools list, attach, verify, rotate, rename and remove accounts, flip the write switch and run an audit. All of it goes through actions/cloud.ts, no token is shown or returned, and a rename is refused when the secret name is taken. The README says how to reach both. The 48-row terminal in test/tui-tell.test.ts stays, because the palette gained a row. Admin-only palette commands added later can crowd out the repository rows the same way, and I left the palette's ordering alone.
🤖 Generated with Claude Code
Approved: no security findings at head 1ebe9272.
What I checked at that head:
- Who can do what. Every write in the terminal (
a/v/r/n/x/w/A) and in MCPcloud_accountgoes throughactions/cloud.ts, which refuses anyone but a forge admin withnot_found. The tool also asks for an admin-scope token, andcloud_accountschecks admin before theusesByVendorscan. The page uses the same admin gate, so the doors match. Writes go throughputSecret/removeSecretandallowWrite/auditAccount, which add to the bus as the page does. - No token leaks. The token is typed masked, never drawn and never returned; the tests check frames and MCP results for both tokens.
overview.tsnever opens the sealed secret. All user text is validated byCLOUD_NAME/isProviderbefore it reaches a message; no SQL or shell is built from it. - Rename. Refusing a rename when
CLOUD_<PROVIDER>_<NAME>already exists closes a real overwrite of an unrelated forge secret, and the change has a test. - Tests and README. There is an admin test and a non-admin test for each verb at both doors. In a worktree at the head,
bun test test/cloud-doors.test.ts test/mcp.test.ts test/tui-tell.test.tsgives 38 pass andtsc --noEmitis clean. The README describes both doors.
Not blocking (see the line comments): the tui-tell terminal no longer needs 48 rows, and the verb dispatch in cloud_account would read better as a map.
I could not open the preview or the forge's screenshots: my sandbox refuses the preview host and /data/looks. This verdict rests on the code and the tests run at the head.
The check that parked this pull,
test/tui-tell.test.ts("the owner's palette has the row"), fails because of the new Cloud accounts palette command. The palette only draws as many rows as the terminal is tall. The new admin-only row pushed the owner's "alice/lobby — Tell the crew" row off the bottom of the test's 40-row terminal. The Tell the crew command itself still works.I changed that test, not the code: its terminal is now 120×48 instead of 120×40. This is a test fix. Every admin-only palette command added from now on can crowd out the repository rows the same way, and a real 40-row terminal would still hide the Tell row for an admin. I did not rework the palette's ordering.
On this branch,
test/tui-tell.test.tsandtest/cloud-doors.test.tspass, as do the 251 tests in the othertui-*test files. The type check is clean. I did not run the full suite, so the forge's check will be the first full run.What the pull does
:→ Cloud accounts) lists each account with itsusedFor()line, verified and last-used times, and today's call count.- Keys:
aattaches,vre-verifies,rrotates,nrenames,xremoves. The token is typed masked and never drawn. - It goes throughactions/cloud.ts, the same functions the page uses.cloud_accountslists the accounts with today's audit rows.cloud_accounttakes averb(attach, verify, rotate, rename or remove) and needs an admin-scope token. A non-admin getsnot_found, and results never contain a token.CLOUD_<PROVIDER>_<NAME>already exists, even with no account row.test/cloud-doors.test.tscovers the terminal and MCP as admin and non-admin, no token in any frame or result, and the rename collision.Left out
dryRun; the admin-scope token is the guard.Try it: open the terminal as an admin and press
:→ Cloud accounts. Over MCP, callcloud_accountswith an admin token.🤖 Generated with Claude Code
Closes #2199