Revoke a session or a grant
A grant ends on its own when it expires. To end access sooner, revoke the grant, or end the session that holds it: ending a session revokes every grant it held and cancels every request it still had pending. Ending a grant stops the session reading its values again; a command already running with a value keeps it.
Revoking a grant in Dispatch ends it at once. A grant someone approved is asked for again from its approver at the session’s next request. When you revoke a grant your own session got without asking, you withhold its secrets from that session: the session’s other grants that got them without asking end with it, and its next request for them is sent to their owner for approval (to anyone signed in, for a shared secret) instead of being granted at once, so the session asks before it gets them again; your other sessions still get them at once. The withhold is the session’s, not the grant’s, so it holds even when the grant had already ended by the time you clicked Revoke (the session revoked it itself, or it expired, after the page loaded). Only the session’s own person withholds: a person who revokes a grant they approved on someone else’s session ends that grant alone. To end every session’s access to a secret, change its tags. To end every session a machine started, and stop it starting more, revoke its machine login.
Revoke a grant in Dispatch
Section titled “Revoke a grant in Dispatch”Dispatch’s Settings page lists your Live grants: every live grant of a session you operate,
whether it was granted automatically or approved by someone, and every grant you approved on
anyone’s session. Each row names its enrollment, its secrets, how it was Granted
(Automatically, or Approved by an email), and when it was created and expires. Click
Revoke to end one at once; the session’s next read of it is refused GRANT_NOT_LIVE. The
broker allows it when you are the grant’s approver or its session’s operator, and refuses anyone
else NOT_APPROVER.
$ curl -s -X POST -H "Authorization: Bearer $AGENT_SECRETS_UI_TOKEN" -d '{"approver":"ada@example.com"}' "$AGENT_SECRETS_URL/v1/grants/<grant id>/revoke-by-approver"{"state":"revoked"}(That is the call Dispatch’s server makes with the broker’s UI token, the credential only Dispatch’s server holds; on the local stack you can make it yourself.)
Revoke a grant from your shell
Section titled “Revoke a grant from your shell”From your own shell on a machine that is logged in (not from inside an agent session),
agent-secrets grant list lists the same grants as Live grants, and agent-secrets grant revoke
ends one as Revoke does, withholding included. Both call the broker under the machine’s login:
the helper signs each call with the machine’s credential, and refuses a process inside a registered
session’s process tree (IN_SESSION), so an agent’s own commands cannot act as you. The check
covers that process tree only. A process a session sends out of it, with ( cmd & ), setsid -f
or a tmux server the session started, passes it, and any process running as your user can stop the
helper anyway. Through these commands such a process can list and revoke your own machine logins,
never a service’s machine login, and your grants, which include grants you approved on any
session, a service’s pod among them. agent-secrets enroll --helper is open to any process of
your user, so such a process can also enroll a box and read your agent secrets.
$ agent-secrets grant listGRANT_ID SECRETS GRANTED APPROVER SESSION OPERATOR EXPIRESf26b6b78-2819-4b1d-abbd-d6990ca1a776 DEMO_API_KEY approval ada@example.com session/example-host-laptop:1989345:35362384 ada@example.com 2026-10-10T15:56:07Zcc63d479-ddae-4052-b93b-b4b5df37adf0 DEMO_READ_TOKEN automatic - session/example-host-devbox:1989344:35362384 ada@example.com session-end$ agent-secrets grant revoke f26b6b78-2819-4b1d-abbd-d6990ca1a776revoked f26b6b78-2819-4b1d-abbd-d6990ca1a776GRANTED is automatic or approval, and APPROVER is - for an automatic grant. EXPIRES is
session-end for a grant that lasts until its session ends, and a time for one its request gave a
--ttl. --json prints the broker’s answer, the same body the Live grants page reads, whose
expires_at is null for a grant that lasts until its session ends.
Revoke a grant from its session
Section titled “Revoke a grant from its session”A session can end any of its own grants, by the grant id agent-secrets self lists. A session
ending its own grant withholds nothing: its next request is decided as before.
$ agent-secrets revoke b51e58bc-3b7b-4144-a2ed-5821f232d612revoked$ agent-secrets selfenrollment_id: 2b8627f4-9a2a-4c12-85bf-910144bc0b5ckind: sessionoperator: ada@example.comlease_expires_at: 2026-10-03T03:36:40ZEnd every session’s access to a secret
Section titled “End every session’s access to a secret”Retag the secret so its sessions no longer get it automatically: set its tier to human
(agent-secrets secret retag NAME --tier human), so every request for it needs a person’s
approval, or give it another owner, so the sessions that had it are no longer the owner’s own.
Deleting the secret (agent-secrets secret delete NAME), or moving it out of
BROKER_SECRETS_PREFIX, ends access altogether
(manage a secret). Once the broker rereads the secret, at
once after a write through agent-secrets secret and, after one made any other way, within about ten
minutes on AWS Secrets Manager (on Google Secret Manager usually as soon, but Google bounds its
listing’s lag only at a few hours, so ask for a reread after a gcloud write), it refuses each read of a grant given automatically under the old tags with
GRANT_NOT_LIVE, and the session’s next request for the secret waits for a person’s approval. The
change applies to every session that got the secret automatically, where revoking a grant of your
own session in Dispatch withholds it from that session alone.
Cancel a pending request
Section titled “Cancel a pending request”A session that no longer needs what it asked for withdraws the request, which takes it off the
approver’s list. It names the request by the request id agent-secrets printed when it asked
(agent-secrets request prints it, and so does a command whose --wait ran out):
$ agent-secrets cancel 715ea84a-6f5c-4230-83c9-1cf258acd88bcancelled$ agent-secrets status 715ea84a-6f5c-4230-83c9-1cf258acd88bstate: cancelleddecided_by: session:2b8627f4-9a2a-4c12-85bf-910144bc0b5cEnd a session
Section titled “End a session”Ending a session’s enrollment revokes all of its grants and cancels all of its pending requests in one step, and the approver’s Inbox drops them. A session ends in one of three ways:
- Its launcher revokes it. A machine session ends when its process exits: the helper sees the
registered process go and revokes its enrollment (it logs
session retired … why="process exited").agent-secrets-helper sessionslists the live ones. A box’s launcher revokes it withagent-secrets unenroll --helper --enrollment <id>. - Its lease lapses. A session that stops renewing (a pod that is gone, a box whose
agent-secrets renewstopped, or a machine that went away) is refusedENROLLMENT_NOT_LIVEon its next renewal from the moment its lease lapses,BROKER_LEASE_SECONDSafter its last renewal. Other session calls are refusedPROOF_INVALID. The broker’s sweep ends it on its next run, withinBROKER_SWEEP_SECONDS, and logsbroker sweeper: ended an enrollment whose lease lapsed. The configuration reference gives both settings’ defaults. - Its machine login is revoked. Whoever may revoke the machine login that enrolled it — the person who approved a person’s machine login, or anyone signed in for a service’s — revokes that login, which ends every session it enrolled (end a machine’s login).
End a machine’s login
Section titled “End a machine’s login”A person’s machine login lasts BROKER_LAUNCHER_CREDENTIAL_SECONDS from its approval, but its
sessions outlive it: a session renews its lease with its own key, never with the machine’s
credential, so a box keeps working after the machine’s credential expires, and so does a machine
session while the helper that renews it runs. A login can be ended, with every session it enrolled,
at any time: for a machine that is lost, compromised or no longer used, or a Legion daemon whose pods
must lose their secrets now. Dispatch’s machine-login page (/credentials/machine) lists Machine
logins: every machine logged in as you, and every service’s login, such as the Legion daemon’s,
shown as legion-daemon on <host>, each with its Credential id, who approved it (No one:
and the ServiceAccount that logged in, for a daemon’s token login), when its login was issued and
when it expires. The host is the name its machine reported, so two logins can share it; the
credential id is the one every enrollment the login made names, and it tells them apart. A login
that has expired stays listed, marked expired, sessions still running, while a session it
enrolled still runs, so revoking every row a machine has ends every session it started, those of
its earlier logins included. Click Revoke on its row and confirm. The broker then, at once:
- refuses the login’s credential, so it enrolls no more sessions; and
- ends every session it enrolled (machine sessions and boxes, or every worker pod of the Legion
daemon’s login) as ending a session does: each one’s grants are revoked and its
pending requests cancelled, and its next call is refused
PROOF_INVALID.
Only the person who approved a person’s machine login may revoke it; the broker refuses anyone else
NOT_APPROVER. Anyone signed in may revoke a service’s login. A revoked login stays revoked: the
machine runs agent-secrets machine login again and its person approves the new code, while the
Legion daemon logs in again with its own Kubernetes token, which no person approves, and enrolls
the pods it launches from then on. A worker pod already running when its login was revoked stays
without secrets until the daemon replaces it. To keep a daemon from logging in again, remove its
account from the broker’s BROKER_SERVICE_LOGINS and redeploy the broker
(Operate).
From your own shell on a logged-in machine, agent-secrets machine list lists your own machines’
logins, row for row as the machine-login page lists them, and agent-secrets machine revoke ends
one, under that machine’s login. A service’s login is not listed there, and machine revoke of
one is refused NOT_FOUND as for an id it does not know. Revoking the machine’s own login (its id
is the one machine revoke warns about) ends that machine’s access too: its next command is
refused LAUNCHER_INVALID until it logs in again.
$ agent-secrets machine listCREDENTIAL_ID HOST APPROVED_BY ISSUED EXPIRES STATEd4187389-953c-4156-90c1-1dd937e9bf0b example-host-devbox ada@example.com 2026-10-10T14:56:01Z 2026-10-17T14:56:01Z oka2df6d16-adc1-434e-b479-496defb3a5c8 example-host-laptop ada@example.com 2026-10-10T14:55:53Z 2026-10-17T14:55:53Z ok$ agent-secrets machine revoke 8f4c8ae4-b73e-4a60-a591-725667480f1aagent-secrets machine revoke: no such machine login (NOT_FOUND)$ agent-secrets machine revoke a2df6d16-adc1-434e-b479-496defb3a5c8revoked a2df6d16-adc1-434e-b479-496defb3a5c8The machine-login page lists the services’ logins, the Legion daemon’s, beside them, and revokes
them. This one a person approved before services logged in with their own Kubernetes token; a
daemon’s token login is listed the same way with approved_by empty and subject naming its
ServiceAccount:
$ curl -s -H "Authorization: Bearer $AGENT_SECRETS_UI_TOKEN" "$AGENT_SECRETS_URL/v1/launcher-credentials?approver=ada@example.com"{"credentials":[{"credential_id":"d4187389-953c-4156-90c1-1dd937e9bf0b","host":"example-host-devbox","service":null,"subject":null,"approved_by":"ada@example.com","issued_at":"2026-10-10T14:56:01.121955Z","expires_at":"2026-10-17T14:56:01.122701Z","expired":false},{"credential_id":"8f4c8ae4-b73e-4a60-a591-725667480f1a","host":"example-host-cluster","service":"legion-daemon","subject":null,"approved_by":"bob@example.com","issued_at":"2026-10-10T14:55:59.906427Z","expires_at":"2026-10-17T14:55:59.9071Z","expired":false}]}$ curl -s -X POST -H "Authorization: Bearer $AGENT_SECRETS_UI_TOKEN" -d '{"approver":"ada@example.com"}' "$AGENT_SECRETS_URL/v1/launcher-credentials/8f4c8ae4-b73e-4a60-a591-725667480f1a/revoke-by-approver"{"state":"revoked"}(Those are the calls Dispatch’s server makes, as above.)
STATE is expired for a login past its expiry whose sessions still run. --json prints the
broker’s answer, the machine-login page’s rows for your own machines. On the machine whose login was
revoked, every machine and grant command is refused from then on:
$ agent-secrets machine listagent-secrets machine list: the launcher credential is not valid (LAUNCHER_INVALID); this machine's login is expired or revoked (run: agent-secrets machine login)Nothing tells the machine’s helper; it finds out the next time it calls the broker. A session’s
renewal, within a third of BROKER_LEASE_SECONDS, is refused; revoking that session’s enrollment is
refused too, and the helper drops the credential and logs at ERROR the broker refused the launcher credential (…); cleared: no session can enroll until a human approves a new machine login. From
then agent-secrets machine login-status exits 1:
$ agent-secrets machine login-statusexpiredagent-secrets machine login-status: the broker refused the launcher credential (expired or revoked, or a proof it could not verify, such as clock skew or an AGENT_SECRETS_URL mismatch); run: agent-secrets machine loginA helper with no session running finds out when it next enrolls one. The Legion daemon finds out
when it next enrolls or ends a pod: the broker refuses its credential LAUNCHER_INVALID, and the
daemon logs in again with its Kubernetes token, under a new credential, unless its account is no
longer in the broker’s BROKER_SERVICE_LOGINS. The pods it had enrolled do not wait for that:
their sessions ended with the revoke, so their agents’ agent-secrets calls are refused
PROOF_INVALID from then on. The daemon does not enroll a running pod again: each stays without
secrets until the daemon replaces it, and only the pods launched after its new login are enrolled.
Stopping the helper also stops the machine from enrolling sessions, since its credential lives only
in the helper’s memory, but it ends none. A machine session lapses within BROKER_LEASE_SECONDS once
the helper stops, since the helper is what renews it. A box renews itself (agent-secrets renew),
so it keeps working after the helper stops and after the machine’s credential expires. Revoking the
machine login ends both, and its row stays on the machine-login page while either runs, marked
expired, sessions still running once the login has expired. Without Dispatch, end each box with
agent-secrets unenroll --helper --enrollment <id>, run against a helper logged in as the box’s
operator, or stop its agent-secrets renew, after which it lapses within BROKER_LEASE_SECONDS.