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

Merged opened this

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

Merged

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

opened this

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.ts and test/cloud-doors.test.ts pass, as do the 251 tests in the other tui-* 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

  • Terminal: an admin-only "Cloud accounts" screen (: → Cloud accounts) lists each account with its usedFor() line, verified and last-used times, and today's call count.

- Keys: a attaches, v re-verifies, r rotates, n renames, x removes. The token is typed masked and never drawn. - It goes through actions/cloud.ts, the same functions the page uses.

  • MCP: cloud_accounts lists the accounts with today's audit rows. cloud_account takes a verb (attach, verify, rotate, rename or remove) and needs an admin-scope token. A non-admin gets not_found, and results never contain a token.
  • Rename: it is refused when the forge secret CLOUD_<PROVIDER>_<NAME> already exists, even with no account row.
  • Docs and tests: the README sentence now says how to reach both doors. test/cloud-doors.test.ts covers the terminal and MCP as admin and non-admin, no token in any frame or result, and the rename collision.

Left out

  • The page's write switch and audit-to-issues action have no terminal or MCP door, since the issue didn't ask for them.
  • MCP changes run immediately, with no dryRun; the admin-scope token is the guard.
  • An admin agent account is not excluded, matching the page's gate.

Try it: open the terminal as an admin and press : → Cloud accounts. Over MCP, call cloud_accounts with an admin token.

🤖 Generated with Claude Code

Closes #2199

Changes requested — before the latest push

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 w key that calls allowWrite(f.app, f.user, provider, name, !a.writeAllowed) and says the result.
  • MCP: add verb allow_writes with a boolean allowed.
  • Then do the same for audit: an A key that prompts for owner/repo and calls auditAccount, and MCP verb audit with repo.
  • 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_accounts is a read but is marked writes: 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.
commented

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: w toggles the account's write switch through allowWrite and says the result, for example "github/ledger: writes allowed." A prompts for owner/repo, calls auditAccount, and says how many issues it opened and how many findings it saw.
  • MCP: the cloud_account tool gets two more verbs. allow_writes takes a boolean allowed, and an answer without one is refused. audit takes repo. Both need an admin-scope token, and a non-admin gets not_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 w and A too.
  • README: the Cloud accounts paragraph lists both new actions.
  • Comment: cloud_accounts is a read marked writes: true, so a ro token 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

commented

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_accounts lists accounts and today's audit rows. cloud_account does 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.ts is 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

Changes requested — before the latest push

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.

commented

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: c on 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 mentions c. A new assertion in test/cloud-doors.test.ts checks that two recorded audit rows' targets appear in a frame. One is a refused call, so REFUSED and 42 B out are checked too. The non-admin screen test now presses c as well and gets nothing.
  • cloud_accounts over MCP: it now returns not_found for a non-admin before it calls usesByVendor, 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: settle and settleWith in src/tui/screens/cloud.ts were the same job, so there is now one settle. 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

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 MCP cloud_account goes through actions/cloud.ts, which refuses anyone but a forge admin with not_found. The tool also asks for an admin-scope token, and cloud_accounts checks admin before the usesByVendor scan. The page uses the same admin gate, so the doors match. Writes go through putSecret/removeSecret and allowWrite/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.ts never opens the sealed secret. All user text is validated by CLOUD_NAME/isProvider before 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.ts gives 38 pass and tsc --noEmit is 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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs