skip to content

Identity lifecycle

Register, resume, sign up, record proof and revoke or suspend an agent sub-identity, and what each state allows.

awaiting reviewupdated view markdown ->
for humans

An agent sub-identity moves through a small number of states. This page is the map: what each tool does, what it changes, and what still works after revocation.

Register and resume

Two tools can create the first agent, and they differ.

identity_register is explicit. It takes an optional name and harness (defaults opencode-agent and opencode-v2) and returns the profile.

identity_whoami is the read path, and it auto-provisions a default agent when the session has none. It returns the profile, the verified human owner, the recent signups and the latest acceptance per service, plus documentation links.

identity_register resumes in this order:

  1. The agent bound to this OAuth session, if one exists.
  2. Otherwise the most recent agent for this human with the same name and harness.
  3. Otherwise a new agent with those values.

identity_whoami and the inbox tools resume differently:

  1. The agent bound to this OAuth session, if one exists.
  2. Otherwise the most recent agent for this human, whatever its name.
  3. Otherwise a new default agent.

The difference matters once a human has more than one agent. In a session that already resolved an agent, identity_register returns that same agent whatever name you pass; a distinct named identity needs a new OAuth session, where the name and harness match picks it.

All of them are idempotent: reconnecting the same client resumes the same identity, and calling one twice does not create a second agent.

A sub-identity contains:

Field Example Notes
id agt_01JAB2C3D4E5F6G7H8J9K0MN1P Stable. The sub claim and the provider's attribution key.
name opencode-macbook Display name you chose, or the default.
slug opencode-macbook-1a2b3c Derived from the name plus a suffix, used in the address.
harness opencode-v2 The client label. Part of the identity_register resume match.
email_address opencode-macbook-1a2b3c@agents.agentregistrar.dev The dedicated inbox.
scopes tos:accept, signup:free_tier, inbox:read, inbox:send Delegated permissions, embedded in the assertion.
status active, revoked or suspended The kill switch sets revoked; an operator protective action sets suspended.
human_owner id, name, email, master agreement version, verified flag The accountable principal.

Prove and sign up

identity_signup is the default path. It fetches discovery, verifies the optional provider verification credential, applies the human's trust/policy, checks accepted_tos_text against the published hash, records an acceptance if the terms are new, mints the assertion, and returns a prepared signup request. There is no manual discovery, JWT decode, or attestation handling.

identity_prove is the advanced path: it takes the audience and ToS hash directly, verifies that accepted_tos_text hashes to accept_tos_hash, runs every gate, records an acceptance if the terms are new, and mints the assertion. It may take one explicit RP discovery source (rp_discovery_url or rp_discovery plus rp_origin); a require policy rejects an agent-supplied document.

Gates, in order, all before any reuse lookup: the human is active, the human email is verified, the master agreement is signed, the agent is active, tos:accept is granted, and every requested scope is granted. Then discovery is validated, the attestation is verified, and the human's policy is applied: allow is silent, warn proceeds with an advisory warning, require refuses anything but verified, and block never proceeds. A failure returns a non-retryable error with the side effects that did not happen.

The acceptance dispatches the ledger receipt at most once, and the minted assertion lives for five minutes.

Acceptance versus assertion. The acceptance is the durable legal event. The assertion is a short-lived proof that points at it. Re-proving is not re-accepting.

Situation reuse.reason New acceptance New ledger mail New JWT
First prove for this agent and terms new_acceptance yes yes yes
Identical call, assertion still unexpired existing_assertion no no no, the same bytes are returned
Identical call after the assertion expired fresh_assertion_existing_acceptance no no yes, same acceptance and consent time
Different terms URL or hash new_acceptance yes yes yes
force_new: true forced_new_acceptance yes yes yes
Same terms, different requested scopes fresh_assertion_existing_acceptance no no yes

force_new exists for a genuine second consent event, for example a human who wants a new record after a dispute. Leave it unset for normal use.

Two rules that matter when you write code against the result:

  • Never decode the JWT. assertion.claims is the exact signed payload; use it for inspection. Send assertion.jwt verbatim.
  • Retry the same call, not a new one. A retry with identical inputs cannot create a second acceptance. Changing the inputs can.

If the acceptance is recorded but the ledger mail fails, the error is ledger_email_failed with retryable: true and side effects that show the acceptance exists. Retrying re-sends the mail and reuses the acceptance.

Record the signup

identity_record_signup takes either the provider's RP-signed v1.2 signup_token (optionally with the exact rp_attestation) or a weaker self_report. For a token it verifies the historical attestation named by service.attestation_jti, verifies the receipt signature with the key named by service.key_id, links it to the acceptance, and stores one proof row. There is no broker-key fallback and no substitution of a newer attestation.

It is idempotent per token and per assertion, and it refreshes rather than duplicates when a provider resolve issues a fresh token for the same account. A self-report has no token, assertion, origin, or verification row; it records agent_self_reported and cannot overwrite a stronger proof.

Error Meaning What to do
signup_token_invalid Signature, shape, historical window, or conflict failed Get an RP-signed v1.2 receipt and the exact historical attestation; do not retry the same one
signup_token_not_linked The receipt references an acceptance this broker has not recorded Run identity_signup/identity_prove for that audience, then retry
signup_token_ownership_mismatch The token belongs to another human owner Stop; use the token issued for your account
rp_attestation_invalid The exact attestation is invalid or unavailable Submit the exact attestation, or omit it so the broker fetches discovery and requires that jti
rp_broker_untrusted The attesting broker is outside the trust list Ask the human owner to review the broker trust list
rp_broker_unavailable Transient broker/JWKS outage Retry; recording is idempotent

The result always includes proof_source, deduplicated, rp_verification_id (nullable), context_url, context_sha256 and the handoff block. The broker never stores the credential and never fetches the guide.

Revoke, suspend and re-register

Revocation is per agent, from the portal kill switch. It sets the agent status to revoked with a reason and a timestamp. An operator protective action can set suspended after a verified, receipt-linked abuse report; the human cannot clear that suspension with the kill switch, and only an operator restore returns the agent to active.

What stops: identity_signup and identity_prove fail with agent_revoked (a suspension uses the same code with a suspension-specific message), the identity and inbox tools reject the agent at the gate, and no new assertion can be minted. identity_record_signup is the one exception: it still accepts a receipt the provider already issued, because the proof belongs to the human owner, not to the agent's status.

What remains: provider accounts, provider credentials, accepted terms, recorded proofs, and any assertion already in a provider's hands until it expires.

Reactivating a revoked agent in the portal restores the same sub-identity and its ledger. A suspension is operator-only to restore. Re-registering is the other path: in a new session, identity_register with a different name creates a new sub-identity, because the resume match is name plus harness. The revoked identity is never reused.

States

State How it is reached What works Next
No sub-identity A session with no agent yet The first call auto-provisions a default identity_register to choose a name
Active, no acceptance Registered Register, whoami, inbox identity_signup
Acceptance recorded, no signup A successful mint Re-running reuses the acceptance Post the assertion to the provider
Signed up, recorded Signup plus Contract F/identity_record_signup Whoami shows the proof; the credential is yours Use the credential
Acceptance recorded, mail failed A failed ledger dispatch Re-running retries the mail only Retry with the same inputs
Revoked Human kill switch No new assertions and no identity or inbox calls; identity_record_signup still records a receipt the provider already issued Reactivate, or register a new identity in a new session
Suspended Operator protective action after a verified abuse report Same tool blocks as revoked; the human cannot clear it Operator review/restore

Next

sections

type to search.

up/down navigateenter selectesc close