Pair a computer with OpenAgents from the CLI #16

Closed AtlantisPleb opened this 9d ago

Outcome

Pair a machine with OpenAgents from the CLI: the owner approves a short code in the browser, the machine claims its token exactly once, and that token lives in the credential store this CLI already uses.

Current behavior

The server side exists. A controller registers a pending pairing at POST /controller/pairings, receives a pairing id, a display code, and a poll secret, and polls GET /controller/pairings/:id with an x-pairing-secret header until the signed-in owner approves it at POST /api/v3/computers/pairings/:id/approve; the machine token is handed back exactly once and stored server-side by digest, and DELETE /api/v3/computers/:id revokes it and drops the controller's socket.

The only client for this is the separate sarah-computer-controller, which stores the token in its own file under its own config directory, keeps its own endpoint default, and has no relationship to openagents auth. This CLI already has CredentialStore with an OS-keychain adapter, PendingDeviceAuthorizationStore, profile and endpoint resolution, and a device-authorization flow whose shape — display code, browser approval, poll, claim once — is the same shape as pairing.

Contract

  • Add computer pair and computer logout, and report pairing state in computer status.
  • Pairing records the endpoint, tier, and roots the owner chose, registers the machine, prints the approval URL and the display code, and waits at the server's stated interval until approval, expiry, or refusal.
  • Store the machine token in the same OS-backed credential store this CLI uses for API tokens, keyed by endpoint so staging and production do not collide. Never write it into the config file, and never print it.
  • Treat the poll secret as ephemeral: held in memory for the pairing exchange, never persisted, never logged.
  • Claim the token exactly once. A resumed or repeated pair must not silently overwrite a live pairing's token or leave a half-paired state that status reports as paired.
  • Make revocation local and remote: logout removes the local token, and a machine revoked from the server is reported as unpaired the next time the CLI looks rather than retried forever.
  • Resolve the endpoint through this package's profile handling, defaulting to OpenAgents production, with the same overrides other commands accept.
  • Distinguish a server without the Computer surface enabled, an expired pairing, a refused pairing, and a network failure as separate outcomes with distinct exit codes.

Acceptance criteria

  • A fresh machine pairs end to end: registration, printed code and approval URL, owner approval, token claimed once, status reporting paired.
  • The token is in the OS-backed credential store, absent from configuration files, absent from stdout, stderr, and the journal.
  • The poll secret is never written to disk.
  • A pairing that expires, is refused, hits a Computer-disabled server, or fails on the network produces a distinct message and exit code.
  • logout leaves no usable local token, and a server-side revoked machine is reported as unpaired.
  • Pairing against a staging endpoint does not disturb a production pairing.

Verification

Run the package gates: pnpm run fmt:check, pnpm run lint, pnpm run typecheck, pnpm run test, pnpm run build. Add tests for the pairing exchange against a stubbed transport, the four failure outcomes, credential placement and removal, secret-absence assertions, and per-endpoint isolation.

Dependencies

Follows the local computer surface slice. Precedes the live channel.

  1. AtlantisPleb opened this issue 9d ago
  2. AtlantisPleb closed this as completed in 5e94778 9d ago
Sign in with GitHub to comment on this issue.