Repository navigation
Crew: accounts own workspaces, setup keeps servers current, and chats can do what Crew does - #475
Draft
Broccolito wants to merge 23 commits into
Draft
Broccolito wants to merge 23 commits into
Broccolito wants to merge 23 commits into
Conversation
A Crew workspace now belongs to server accounts instead of computers: whoever signs in over SSH as an account is that person on every computer, and the hosting account owns the workspace from any computer. The contract also covers setup reporting a server's workspaces, keeping Crew current there, adding a computer again, pruning chat-access records of removed connections, and chat tools that do what the Crew view does. Revised after a four-lens adversarial review.
A server account is now the person (design AO-1 to AO-7): - auth.account lets a computer that signs in as an account add its own device: the host from any computer, a member's new computer with no approval, and an invited account with no device code. State-first checks, one account lookup, a 32-device cap that evicts the oldest, and the member journal share with a daily pass. - account.status answers what the workspace knows about the caller; device.revoke removes one of the caller's own devices by fingerprint. - hello advertises account_identity_v1 (signed) and its version (unsigned); runtime.json records the version. - biorouter-crew workspaces lists what the account hosts or belongs to on the server, with a bounded, decoy-resistant scan of other accounts. - start and restart by --workspace-id; a journal is created only with --create or a bootstrap key. - The bridge refuses device changes from inside a full-access job. Contract tests cover each path, and a v1.94.1 broker replays a journal holding every new record.
… current Design AO-8 to AO-12, daemon side: - Setup: a server target that needs no device key, the server's workspaces in every job with where this computer stands, older Crew copies replaced by default (--keep-older keeps one), outdated workspaces restarted by ID only when nobody else is connected, stopped workspaces started by ID, and hosting finished in one job with a name-in-use refusal and configure only for a new workspace. - Joining: the account path behind account_identity_v1 (ready and removed join states, auth.account only through its own door), adding this computer again with a staged key that keeps the connection ID, and the new refusals crew_device_key_missing, crew_device_not_recognized, crew_account_unsupported and crew_join_removed. - New devices since last seen are reported until acknowledged. - Chat-access records of removed connections are swept, failing closed. - A scoped chat admits exactly crew__connections and crew__request, and a per-chat read consent store backs the chat tools.
… consents
Design AO-8 to AO-12 over HTTP, each route behind the person's proof:
- POST /crew/connections/{id}/rejoin adds this computer again in place.
- GET /crew/connections/{id}/account answers what the workspace knows
about this account; GET /crew/connections/{id} returns one connection.
- The connection view carries new_devices and account_identity, and
POST /crew/connections/{id}/devices/acknowledge clears the notice.
- GET /crew/setup lists this computer's setup jobs; the setup routes
document the server target, the workspace list, restarts and
crew_host_name_in_use.
- The grants listing carries a chat's read consents, and
DELETE /crew/sessions/{session_id}/reads/{connection_id} revokes one.
- Every new refusal code is declared in CrewErrorCode; the refusal for
an older broker says whether you host the workspace in is_host.
- The daemon sweeps removed connections' chat-access records at start.
The OpenAPI spec and the desktop's generated client are regenerated.
…removal guard - biorouter crew open NAME --server LOGIN opens a workspace found on the server, joining by account with no invitation or code. - biorouter crew rejoin adds this computer again; profile devices lists your devices (--acknowledge clears new-device notices) and profile revoke-device removes one, with the SSH-access warning. - setup and doctor use a server target with no device key, list the server's workspaces and where this computer stands, replace older Crew by default (--keep-older), restart outdated workspaces (--restart, --no-restart) and start stopped ones (--start NAME). - host runs as one setup job and reports what is left if a step fails. - join joins on ready and explains removed; auth --setup-job signs in for a waiting setup; status shows new devices; grants list shows a chat's read consents and grants revoke --reads revokes one. - connections remove keeps the host-only refusal only where the broker lacks account_identity_v1, and then says to update Crew there first.
Design AO-12, core crate side: - crew/tool_host.rs defines the person tools' actions and the CrewToolHost interface the daemon installs, which runs each action with the same functions the HTTP routes use. - The crew extension offers crew__read (read-only) and crew__change (destructive) only in a person's own chat with no Crew grant, and crew__change only where a person's approval can be given; a granted chat is offered exactly crew__connections, crew__request and todo__*. The same rule holds in the roster, at dispatch and in the tool. - The instructions gain the chat-without-access part; the scoped part and its RV-D5 sentences are unchanged. - The two tools and their instructions cost 808 tokens, under the 1,200-token budget a test holds.
Design AO-12, daemon side. routes/crew_tools implements CrewToolHost
with the same functions the HTTP routes and the CLI flows use, for all
72 crew__read and crew__change actions:
- every change parks one proof-backed approval (Allow once, Deny) built
from targets resolved before parking, shows the payload whole, and
rechecks that the chat still has no Crew grant before acting; the
generic permission inspector no longer adds a second card;
- the first read of a workspace's content asks the person once
('Let this chat read {workspace} as you');
- reads follow a grant's privacy rules per channel on the call's
sampled capability (public models get only public-safe, unrestricted
content and withheld counts; private models pass the institution
check), and the chat is raised Private before content returns;
- writes from a Private chat go marked Private, crossings are said on
the card, and a task asked from a Private chat cannot use a public
model;
- jobs answer within 20 seconds and keep running; sign-in steps are
named, never asked for in the chat;
- POST /agent/call_tool refuses the person tools, which need a card.
Also: the daemon keeps the broker's reported version and serves it as
server_version on each connection; institution::check_provider is
reachable through CallCapability::check_crew_institutions; the privacy
census counts the one chat-listing filter the tools use.
…devices and read consents The Crew view for account ownership (design Desktop section): - Open a workspace on a server, beside Join and Host and in Add a workspace: verify the server, check it, and Open, Start or Update Crew on the workspaces found, asking first when others are connected. - Joining shows no code against a capable workspace server; removed members are told so; pending joins read 'Waiting for @bob to open the workspace'. - Add this computer again in the connection bar when this computer's key is missing or no longer recognized. - Keys lists every device with Remove, with the warning to take away the lost computer's SSH access first; new devices are announced until acknowledged. - Setup shows the workspaces and restart steps; a setup a chat started appears with its Sign in button. - The connection bar says when the server runs an older Crew than this app carries, comparing the broker's reported version. - The chat access pane lists a chat's read consents with Revoke. - The removal confirmation follows the account model where the workspace server supports it. The OpenAPI spec and generated client carry server_version.
The user manual describes the account model: you are you on every computer that signs in as your server account, opening a workspace on a server, Add this computer again, devices and revoking one after taking away the lost computer's SSH access, setup's workspace list, default updates, restarts and --start, the chat tools with a GUI, CLI and chat parity table, and every new command, flag and code. The design docs carry in-place amendments for account ownership (naming design D8, D16 and M2, open workspaces, CLI guide, UI spec) and the built-in capabilities index lists the person tools.
…d routes Fixes from the adversarial review of the account-ownership branch, each with a test that fails without it: - Broker: scanned answers with oversized or malformed fields are skipped and never crowd out later entries; start and restart by ID refuse a workspace kept in two folders; a member adding a device by account ends any add-device invitation; the host rename conflict says what to do; a missing program is program_missing, not start_failed; entries from different brokers sharing an ID are never merged. - Setup: listings keep entries from different hosts or keys apart, so a copied workspace ID cannot replace the real one; restart_workspace_ids restarts exactly the workspaces a person agreed to; Stop is honoured between restart and host steps; name-in-use words use the listed name; a second request attaches only when every choice matches; a stopped workspace you host is named with its --start command; the listing is current after a restart. - Daemon: a half-saved rejoin answers crew_device_not_recognized so the person can add the computer again; a host on an older broker gets crew_account_unsupported instead of waiting forever; new devices are learned at connect; acknowledging takes the fingerprints shown. - Chat tools: names never leave a withheld workspace in refusals, cards or grant labels; a public call only reaches listed channels; the scope is recomputed after a consent card; changes act on exactly what the card showed (connection settings, shared file); open, join and setup cards name the right connection, host and restarts; Code Execution's catalogue drops tools this process can never run. - Routes: the live view carries the two device-key codes; the acknowledge route takes the fingerprints shown; the OpenAPI text names is_host and documents restart_workspace_ids. The OpenAPI spec and generated client are regenerated.
Fixes found by the second adversarial review and by the live run with two accounts on leo.ucsf.edu, each with a test: - CLI: open and join say by role when the broker is older, instead of waiting; a stopped host step names exactly what is left; the invite mentions no code on a broker with account_identity_v1; an outdated workspace is restarted alone with --restart-workspace NAME; a member who rejoined is not labelled a former member; suggested commands keep the port and jump host; full access warns that the agent can add a computer to the account and post in Crew as the person. - Desktop: Update Crew works for an old broker and restarts only the workspace agreed to; a chat shows its read consent with Revoke (ChatReadsBar); the consent card says the permission lasts; the new-device notice names the computer; "This computer" wording; focus after Acknowledge and Revoke. - Chat tools: every change card names the workspace and server; a declined read consent is not asked again until the person writes; describe examples use placeholders; a chat holding one institution's private content cannot read another's; Code Execution offers each chat only the Crew tools it can run. - Six server functions are split into helpers to stay within the 100-line clippy baseline, with no change in behaviour. The OpenAPI spec and the generated client are regenerated.
Two tests failed only on a loaded machine, the first on main as well. - keepalive: the W2-DMN-6 tests assumed a dial and the request around it fit in a fixed 1.5 s gap. Under load that took 1.6 to 51 s, after which dialling at once is the right behaviour, so the request was carried. The fresh-redial test now sets the gap from what it measured, and the two tight-loop tests follow the daemon until it holds a dial back, checking every dial against the gap and never counting spawns while a dial may be on its way. Mutation runs show the tests still fail when the throttle, the gap or the Disconnect is broken. - broker: the restart-lock test re-took the lock right after dropping it. A flock belongs to the open file description, and another test forking in that moment kept it alive until its child exec'd, which reproduced at 5 in 2,000 runs. The re-lock now waits up to 5 s; the busy check while the lock is held stays strict. The restart command holds the lock single-threaded, so the product is not affected.
- Full access: every CLI path that grants it (connections set-access full, host, setup and open with --agent-access full) now says Crew treats anything running as the account as the person, and on the host's own account that it means host authority. One helper holds the text. - Open a workspace on a server: when the Crew installed in the account is too old to list workspaces, the setup status says so in workspaces_unlisted, the sentence names the setup command, the dialog offers Update Crew (install only, no restart) and the step list no longer shows Ready for a listing that failed. A look-only result no longer says a workspace "wasn't restarted". - Chat read-consent bar: a consent given again in the same open chat shows again after a Revoke. - Desktop: "computer" wording for a person's machines; the new-computer notice appears while the Crew view stays open. - CLI: member counts count current members only; a re-admitted account is one former-member line; the new-computer hint names connect first when disconnected; setup prints each stage once, names the workspace it starts or restarts, and prints no Ready for a listing that failed. - Chat: the grants_revoke card quotes the chat title and says whether it takes back a read consent or Crew access; full-access cards add the host sentence on the host's own account. - Manual and design docs follow the code. The OpenAPI spec and client are regenerated.
…d desktop
- A person's machine is a "computer" in every sentence a person reads
(CLI, daemon and broker refusals, chat cards, desktop); "device key"
stays for the key itself, and codes, flags and fields keep their names.
- How a computer was added reads the same on the CLI and desktop ("by
signing in to the server"), and the status notice matches the
desktop's.
- The full-access warning and its host clause are one text everywhere,
now also on the chat's set-access card and the task card; the chat
cards say "your agent" like the CLI and desktop.
- A look at a server never says a workspace "wasn't restarted": the CLI
and the Open dialog say what updating would do, per row.
- The CLI decides the host clause from the connection that gets full
access, counts current members, and keeps former accounts that only
share a username apart.
- The daemon's too-old hint repeats the route the check used; the chat
names its own setup action instead of a terminal command; chat cards
escape every invisible formatting character in a chat title; the host
check before a setup card waits only briefly.
- Desktop: the Open dialog offers Update Crew exactly when the daemon
says the installed Crew is too old to list, and members count current
members only.
The manual, design docs and the OpenAPI client follow.
…e actions, live change feed Chat parity, part A (owner rulings D1 and D6). - A CrewStep card (sign in, unlock or set up the vault, a server's host key) that carries no value and that nothing can resolve as success. The desktop opens the person's own Sign in window or the system vault prompt; the shared-daemon terminal asks first, then runs ssh in raw mode or a hidden prompt. No password, code, passphrase or host key reaches the model, the transcript, logs or the session row. Cards draw buttons only while the daemon can still be waiting on them. - Sign in from a chat for setup jobs and saved connections (crew__change auth), with an adoption signal so the chat carries on after the person signs in, and person time not counted against the job wait. - Host-key card: the daemon reads the fingerprint itself (direct hops only), watches known_hosts, and a retry reuses the person's approval of the identical card once. D6: sensitive_ops criterion 7 asks first in Fully Automatic mode before an agent shell trusts a host key (StrictHostKeyChecking, KnownHostsCommand, known-hosts files, ssh-keygen -R, wrappers and every shell's variable spelling). - Vault from a chat: status, unlock, set up and lock, and a vault card before any call that needs the locked vault. - New chat actions: setup_list, connections_invitation, files_pending, context_draft, profile_devices_acknowledge, files_pause, files_resume, files_forget, with the same read gates, consent and approvals as the routes; one naming rule for transfers. - Person tools stay directly callable under Code Execution with a stable idempotency key; terminal cards in session attach and watch (shown whole, never answered from a pipe or an agent's shell); honest read-consent wording; ACP offers no proof tools. - Live change feed: GET /crew/changes (IDs only, proof required) and renderer subscriptions, so what a chat does shows in the Crew view without a reload. - One wording per concept across the CLI, daemon, chat and desktop; the dismiss route asks session_reach. - Fix the scheduled-run stack overflow CI hit on ubuntu: box Agent::reply's prologue future and the scheduler's awaits (peak about 2.0 MB to 1.15 MB), with a size test. - Docs: set Crew up from a chat, the step cards, the change feed, AO-12 amendments for D1 and D6, criterion 7 in permission-modes.md.
WI-5 adds advanced join choices and complete multi-file send approvals. WI-8 gives coding agent chats proof-backed Crew person tools. WI-9 runs interactive terminal chats on the shared daemon by default. WI-10 carries machine capabilities into workflows with an explicit opt-out. WI-11 refuses Crew changes from an agent's own shell. WI-2b adds proof-backed trust for unknown host keys. WI-16 names Crew actions in chat rows and opens their Crew destination. WI-17 replays parked cards and carries their endings to live observers. Repair the Windows SSH path fixture, escape bidi test inputs in source, and supply Vite's environment in the standalone list-marker browser test. Surface browser initialization errors directly without relaxing assertions. Set the private profile rejection fixture mode independently of umask.
Broccolito
force-pushed
the
claude/crew-account-ownership
branch
from
November 7, 2026 21:20
7ed3e98 to
5e7171f
Compare
Preserve short valid child call IDs and hash all other IDs in full so distinct long or filtered IDs cannot share an idempotency key. Keep request binding, retry stability and the final step within the existing key limit. Explain the desktop requirement in browser host key prompts without offering Trust or attempting the desktop-only fingerprint read. Add regressions for long, punctuation and Unicode call IDs and for both browser host key paths. The regressions fail before their respective fixes and pass afterward.
Keep main's off frame reply construction and preserve the Crew future size guard. Retain the local decision model affiliation guidance alongside the Crew chat instructions. Regenerate OpenAPI from the combined routes.
Cache eviction used whole second timestamps and broke ties by request key. A burst could evict newer requests before an older request and unexpectedly reject reuse of an aged out key. Record the journal revision with new cached results and use it for actor and workspace eviction. Keep expiry timestamps and human result priority. Older journal records retain their previous encoding and are evicted first. Add deterministic eviction regressions and verify that real mutations record increasing cache revisions.
Box the dispatch future before constructing the returned scope so nested task locals retain a bounded state. Preserve tool call identity and generic future compatibility. Add size, polling, cancellation and exactly once drop regressions.
Replace the obsolete ungranted worker request requirement with a named no invocation contract. Validate the exact initial prompt, fixed assistant response and completion while retaining the process and profile audits. Keep effective tool roster and forged request denial explicitly unobserved in native output and require separate Rust evidence.
Keep the Crew phase headless and selectable while checking the fixed response, zero tool calls and explicit native evidence limits. Check that all five configured handles reach the rendered checker command.
Allow an absent input echo while retaining exact initial echo validation and strict response, completion and zero tool checks. Cover native shaped output and document the limit of stdin submission evidence.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
On 2026-11-05 the lab workspace
baranzini-labonleo.ucsf.educould no longer be reached from any computer. Its host account (wanjun) still ran it, but the only two computers that held its keys were gone, and nothing let a new computer become one of the host's devices: that took an add-device invitation approved by an existing device.setup --checkcalled the server "Ready" all the same, old brokers stayed on old builds, and a chat could not do what the Crew view does.The owner's rule, now the design (
docs/crew/design/account-ownership.md): identity is the SSH account on that server. Whoever signs in aswanjunonleois@wanjunin every workspace onleo, from any computer, and the account that hosts a workspace is its owner and admin from any computer.What changed
Broker (
biorouter-crew)auth.account: a computer adds itself to its own account. The host gets a device with no bootstrap key, a member adds a second computer with no code, and an invited account joins with no code. A removed member, a renamed account or a stranger is refused.account.statusreports what the workspace knows about you: your role, your devices, and whether others are connected.device.revokeremoves a device by fingerprint, including this one. The capabilityaccount_identity_v1is signed, andhellocarries the broker version.biorouter-crew workspaceslists the workspaces on the server that you host or belong to, with caps and deadlines.startandrestart --workspace-idact on one workspace by ID. Full-access jobs cannot callauth.accountordevice.revokethrough the bridge (agent_session_refused).Daemon, routes and CLI
biorouter crew open NAME --server LOGINreaches a workspace you host or belong to from a new computer.rejoinadds this computer again after its key was lost or revoked.profile devicesandprofile revoke-devicemanage your computers.--restart,--restart-workspace NAME).--start NAMEstarts a stopped workspace. Hosting runs as one job, through to joining as host and configuring the workspace.crew_device_key_missing,crew_device_not_recognized,crew_account_unsupported(worded by role on an older broker),crew_join_removed,crew_host_name_in_use,crew_read_consent_required.Chat tools: whatever Crew can do, a chat can do
crew__read(24 actions) andcrew__change(48 actions). Each action is named by itsbiorouter crewpath, anddescribereturns any action's parameters and an example. This includes setup, hosting, opening and joining a workspace from the chat.grants revoke. A declined consent is not asked again until the person writes.tasks_askcarries its origin.crew__connectionsandcrew__request./agent/call_toolrefuses the person tools, and Code Execution offers each chat only the Crew tools it can run.Desktop
Docs: the Crew manual and design docs are rewritten for the account model, and
scripts/check-crew-manual.mjsholds them to the code.Security position
Under this model a process running as an account is that account: a full-access agent on the host's account has host authority, and can add a device or post as the person with no agent mark. The owner accepted this. Every surface that grants full access now says it, and the bridge refuses the two account-changing calls from inside a job as an accident guard. You see and can revoke your devices, folder grants stay sandboxed workers, scoped chats never act as the person, and accounts nobody invited stay out. Details are in the design doc's Security position section. ⚠ Person authority and approvals: needs human review.
Tests
Run locally on this branch before pushing (what CI runs, plus more):
cargo test --workspace --lib --bins: 5,621 passed. The one failure was the keepalive redial-gap test, which also fails on main under load; it is fixed here (below).crew_tools_host,crew_tools_actions,crew_tools_grants,crew_join_routes,crew_worker_allowlist,privacy_guard_wiringand everybiorouter-crewcontract suite.rust:1.92-bookworm, as CI's ubuntu job): 5 full runs, each with 367 passed and 0 failed.cargo clippy --all-targets -D warningsand thetoo_many_linesbaseline: clean.lint:checkand Prettier pass too.check-crew-manual.mjsand its mutant suite, the retired-name guard and the version check: pass.Two tests that failed only on a loaded machine are fixed in
d7d17f1bc, each with the cause proven before the change:flockjust after dropping it, while another test's fork could keep the open file description alive (reproduced 5 times in 2,000 runs). It now waits up to 5 s for the lock to come free, and the check that the lock is busy while held stays strict. The product's restart command holds the lock single-threaded, so it is unaffected.Every
biorouter crewcommand maps to a chat action, except the person-only controls AO-12 lists: daemon start and stop, the credential vault, SSH sign-in, transfer pause and resume, the internal prepare steps, and granting a chat Crew access.Live tests on leo.ucsf.edu (wanjun and gbellucci, isolated profiles, Versa gpt-5.5)
In progress: the re-test of the last two fix rounds is running against leo now. This section will carry its results.
First run (frozen build of
e5b9f3b52, broker cross-built from the same commit), two accounts and four isolated profiles (two computers each):setup --checkon a server with no Crew says so, never "Ready"@gbellucci, who joins with no device codesetup --checkand opens it as hostrejoinadds it again--restartmoves a broker still on the old binary67ed97b2767ed97b27🤖 Generated with Claude Code