Skip to content

feat(agents): server-managed Agent keyrings and key-bearing tokens - #1102

Merged
nimish-ks merged 3 commits into
feat--agentsfrom
feat--agent-token-keys
Oct 29, 2026
Merged

nimish-ks merged 3 commits into
feat--agentsfrom
feat--agent-token-keys

Conversation

@nimish-ks

@nimish-ks nimish-ks commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Each Agent gets an Ed25519 keyring when it is created, wrapped for the server (server-side key management, as for service accounts) and shared by all of its Workflows.
  • Tokens stay scoped to one Workflow and are minted as pss_agent:v1:{token}:{kx_pub}:{share_a}:{wrap_key}. This is the first released Agent token form, so it starts at v1. The server stores only the bearer digest and the wrapped second share. Authentication is unchanged (Bearer Agent <token>), and the GraphQL and REST mint endpoints both return the full token.
  • The Agent, not the Workflow, is the cryptographic principal. Every Workflow's tokens carry shares of the same key, so Workflow isolation is enforced by the server.
  • Deleting an Agent wipes its server-wrapped keyring. Retiring a token (token, Workflow or Agent deletion, or member retirement) clears its wrapped share, so an old token string can't rebuild the key from a copy of the row. The Agent page shows "Server-side KMS".
  • The REST mint returns 403 invalid_agent_keyring when the keyring doesn't open, rather than a 500.
  • The new fields mirror ServiceAccount, so client-side key management can be added later without changing the token format.
  • Nothing reads the key shares yet; this keeps the option open for app secrets and end-to-end credentials.

CLI side: phasehq/cli#327.

Migration

0144_agents is edited in place (unreleased). To update a local database: delete AuditEvent rows with resource_type='agent_request' (the rollback shrinks that column back to 10 characters), then migrate api 0143 and migrate api. This wipes local Agent data.

Testing

  • Backend suite passes, apart from two failures that also occur without this change (tests/test_auth_password.py, and a file-permission test that cannot pass as root in the container).
  • New tests: a token's two shares rebuild the Agent's key; Workflows of one Agent share a key and different Agents don't; minting refuses another Agent's keyring; deleting an Agent wipes its keyring; every token segment is 64 hex characters, as the CLI requires.
  • New tests for the REST mint endpoint, which had none: a personal access token mints a working full token; Owners mint without an assignment; unassigned members, other Workflows, missing permission, Agent tokens and service account tokens are refused; input validation; unknown, disabled and foreign Agents; and an invalid keyring.
  • Every retirement path clears the token's share, and other tokens keep theirs.
  • Agent frontend jest suites and ESLint on touched files.
  • A token minted by this code was parsed by the CLI, and the bearer header matched.

…g tokens

Agent tokens were bearer-only (pss_agent:v1), so letting them read app
secrets or end-to-end credentials later would have meant a new token
format and reissuing every token.

Each Agent now gets an Ed25519 keyring at creation, wrapped for the
server like a service account with server-side key management, and
shared by all of its Workflows. Tokens stay scoped to one Workflow and
are minted as pss_agent:v2:{token}:{kx_pub}:{share_a}:{wrap_key}; the
server keeps only the bearer digest and the wrapped second share.
Authentication still uses the bearer part alone. Deleting an Agent wipes
its server copies, and the Agent page shows server-side KMS.

The fields mirror ServiceAccount so client-side key management can be
added later without changing tokens.
No release has ever minted pss_agent tokens, so the first released form
is pss_agent:v1:{token}:{kx_pub}:{share_a}:{wrap_key}. The prefix alone
selects Agent authentication, so the version does not need to match
Service Account tokens.
- The REST mint now returns 403 invalid_agent_keyring when the Agent's
  keyring does not open, and 404 workflow_not_found if the Workflow is
  retired mid-request, instead of a 500. Adds endpoint tests, which did
  not exist (the CLI's Agent SDK uses this endpoint).
- Retiring a token clears its wrapped key share, on every path: token,
  Workflow and Agent deletion, and member retirement. An old token
  string can no longer rebuild the Agent key from a copy of the row.
- Documents that the Agent, not the Workflow, is the cryptographic
  principal: Workflow isolation is enforced by the server.
@rohan-chaturvedi
rohan-chaturvedi self-requested a review October 29, 2026 09:55
@nimish-ks
nimish-ks merged commit bcdba0c into feat--agents Oct 29, 2026
@nimish-ks
nimish-ks deleted the feat--agent-token-keys branch October 29, 2026 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants