Short-lived delegated keys for runners and agents from their OIDC identity ('keyless') issue #241 closed: completed andymccall opened this · 8 comments
andymccall set labels security ·
andymccall referenced by change 52ae6422e5a1 on main ·
Design proposed in docs/keyless.md (decisions for Andy at its end: our own composite-signed certificate vs X.509; build before the transparency log with the hub's acceptance time; agent grants replacing long-lived agent keys; the hub's own OIDC issuer first). Not built.
andymccall referenced by change 6ba07678ae86 on main ·
Andy, 2026-10-17: docs/keyless.md accepted as recommended: our own composite-signed CBOR certificate; build steps 1-4 now on the hub's acceptance time; agent grants replacing ~/.oxy-agents keys; the hub's own OIDC issuer first.
referenced by 2 changes on 17 Oct 2026
Steps 1-3 done (2026-10-17): the certificate object (oxygen e6b4d3a2, spec 14.7); POST /api/v1/keyless/certify (hub 08e528b0, spec 13.5.2); receipts carry certificates (oxygen 473387f7), the hub keeps receipts signed by keys it certified for that job checked at acceptance (c3af7885), and the runner certifies a fresh key per receipt when keyless = true (oxygenflow 585276b4). Live on earth-1 since 16:47: oxygenflow run 137's four earth-1 jobs show 'signed receipt by earth-1 (short-lived key)'. plex-1 and mars-1 still sign with their keys. Note: oxy receipt verify on a keyless receipt needs the next oxy release (0.4.4 reads four-element receipts only). Next: step 4, policies naming subjects.
All three runners keyless since 17:05 (run 138: earth-1, mars-1 and plex-1 receipts all signed by short-lived keys). Step 4a done: policy rules grant subjects an issuer certifies (oxygen e7eba99d, spec 14.5, oxy policy grant-subject). Next: 4b, enforcement on push and fetch with certificates carried in pushes; 4c, the hub's acceptance attestation as trusted time.
referenced by 2 changes on 17 Oct 2026
Step 4c built (oxygen dbdb86b4, oxygenflow b5544f1f pin, hub 3eca6e99; deploy waits on CI).
- Acceptance: when a push moves a bookmark with an attestation signed by a key the hub certified, the hub signs an
acceptedattestation (spec §14.7) with its own clock, keeps it beside the attestation and serves it with the bookmark (refs entry key 5, only when there is one). - Fetch: checks the certificate at the acceptance's time when the certificate's issuer signed it for exactly that attestation; otherwise at the attestation's own time, as before. Another key's acceptance changes nothing.
- gc fix found on the way: gc kept certificates only for receipt signers. A gc could leave a certified bookmark that a fresh clone couldn't check. It now keeps the certificate and issuer of every short-lived key that signed a kept change or attestation.
Next: step 5 (agent grants).
andymccall referenced by change 8aa5ef384ef1 on main ·
Step 5 built: agent grants (oxygen efeb3588 + 6b00f379, oxygenflow 653790c3, hub 98cdd4ea; deploy waits on CI).
Authorship follows the decision taken today: the owner's key signs a grant.
- Grant: grantor, subject
agent:NAME@ACCOUNT, issuer, scope and window, signed in domaingrant. It rides inside each certificate issued under it (certificate field 8). - Verification: the author-or-delegate rule accepts a certified key whose grant verifies with the change's author, so verifiers need not trust the hub for authorship.
- Hub:
oh agent grant NAME [--scope] [--for 30d]signs the grant locally and prints the agent'soxyagent_token once.oh agent listandoh agent revoke IDmanage grants (/api/v1/agents/grants, migration 0075). - oxy: with
oxy auth grant(keeps the token) andauthor.signer = keyless, oxy certifies a fresh in-memory key whenever it signs. A push seals the owner's changes with it. - Test: end to end in the hub, on SQLite and Postgres. An agent with no key on disk pushes in its owner's name, the update is countersigned, and the agent gets no certificate after revocation.
Left: moving the real agents over and retiring their delegate keys (needs Andy's key), then step 6 (transparency log time).
referenced by 2 changes on 17 Oct 2026
Agents are keyless in production (2026-10-17).
- Releases: oxy 0.4.5 and 0.4.6 are out on apt.
- Pushes: the hub accepts an agent's grant token as its push credential (hub afcc618b).
- Agent homes: Claude Code and Antigravity sign with
author.signer = keylessunder grants issue #3 and issue #4, and hold no key at all; the old delegate secrets are deleted. - Policy: oxygen's policy (serial 7) grants agent:claude@andymccall and agent:antigravity@andymccall for changes,refs.
- First self-push: oxygen 940218f6, sealed by agent:claude@andymccall.
- Runners: all three were rebuilt with the current oxy-core, since a policy with subjects broke older runners' fetch.
- Release trust: counts receipts by hub-certified keys.
Left: step 6 (the transparency log's time); renew the grants before 2026-11-16.
Board tidy (2026-10-19): every step in docs/keyless.md's order of work is built: (1) the certificate object, (2) the certify endpoint, (3) Flow jobs certify a fresh key per job (all four runners have keyless = true), (4) policy subjects (oxy policy grant-subject), (5) agent grants (oh agent grant, grant tokens; Claude and Antigravity keyless since 2026-10-17), (6) trusted time, now the transparency log's integrated time (issue #240, 2026-10-18). Closing.
andymccall closed ·
Compared with Sigstore (Fulcio), 2026-10-16: our keys live long; a stolen key works until revoked. The hub issues minutes-long delegated keys (a delegation signed by a hub CA key, scope and expiry in it) to runners and agents that prove an OIDC identity (a Flow job's token, an agent's token), so nothing long-lived sits on a runner or in an agent's home. Policy can then name identities ('this workflow on main', 'Claude delegated by andymccall') rather than keys. People: passkeys (issue #237).