Identify and delegate agent work¶
Configure OIDC identity to bind agent work to a verified human or a declared service principal. Public core v0.6.0 supports this local broker workflow on macOS and Linux. Process classification alone cannot identify a human: an agent can invoke the same CLI.
The daemon owns login and token validation. Agent-session creation requires its configured approval factor; login alone does not approve an operation. See agent integration for session setup and deployment patterns for custody requirements.
Principals and roles¶
| Kind | Established by | ID prefix | Authority |
|---|---|---|---|
| Human | Verified OIDC issuer and subject | hum_ |
Admin-assigned roles; first human bootstraps admin/approver/operator outside managed mode |
| Agent | Workload identity | agt_ |
No independent principal authority |
| Service | [[identity.service_principals]] in daemon configuration |
svc_ |
Explicit roles and autonomous policy |
Roles are admin (identity administration), approver (approval), operator
(operations) and auditor (read-only inspection). The broker reads current roles
and session state from its store; tokens do not preserve revoked roles. Client
classification is a separate policy matcher.
Neither classification nor roles replace an operation's approval requirements.
Access modes¶
| Mode | Subject | Approval |
|---|---|---|
delegated |
Authenticated human | Required out-of-band approval |
autonomous |
Config-declared service principal | Applicable operation and policy requirements |
break_glass |
Authenticated human | A distinct approver; fails closed without a supported factor |
Configuration¶
Register a native/public IdP client with loopback redirect URIs. The daemon binds the listener, exchanges the code with PKCE and validates the ID token's signature, issuer, audience, nonce and expiry. The CLI displays the login URL; it does not receive the authorization code. Login assurance still depends on the IdP and browser environment.
[identity]
issuer = "https://your-org.okta.com"
client_id = "opaque-cli"
allowed_email_domains = ["example.com"]
required = true
# audience = "opaque-cli" # defaults to client_id
# redirect_port = 8721 # if the IdP requires an exact redirect URI
# session_ttl_secs = 43200 # default 12 hours
[[identity.service_principals]]
name = "ci"
roles = ["operator"]
required = true requires verified delegation for agent operations. Put daemon
settings above the first [[rules]] table and inspect opaque policy check
warnings. Managed tenant admission additionally requires an explicit
identity.allowed_subjects list.
Managed identity lifecycle¶
A provider-neutral adapter can submit membership observations through the public lifecycle contract. The broker owns admission, role mapping, authority epochs and revocation. An adapter never opens its identity database or submits roles directly.
Enable managed lifecycle only with sealed configuration, enforced tenant custody,
a tenant binding, identity.required = true, and explicit
identity.allowed_subjects. Add these settings to the reviewed configuration:
[lifecycle]
socket_path = "/run/opaque/identity-lifecycle.sock"
allowed_adapter_uids = [7383]
socket_gid = 7999
token_file = "/var/lib/opaque/.opaque/identity-lifecycle.token"
[lifecycle.group_roles]
reviewers = ["approver", "operator"]
The example assumes broker UID 7381 and a separately isolated adapter UID 7383.
Preprovision the socket parent as broker-owned, group 7999, mode 0750; grant the
adapter only traversal and socket access. Every ancestor must be broker/root
controlled without untrusted writes; root-owned sticky temporary directories are
permitted. The socket is broker-owned mode 0660. Store a separate random
32–128-character URL-safe credential without whitespace in owner-only
identity-lifecycle.token directly inside broker custody. Give the adapter its
own private copy of only that credential. Keep task, approval, provider and
lifecycle credentials distinct. Same-UID processes share a trust boundary.
The wire format is one big-endian u32 length followed by one JSON
LifecycleRequest, then one similarly framed LifecycleResponse. The public
identity_lifecycle::deliver(socket, broker_uid, credential, batch) client checks
path custody, socket ownership and the connected OS peer UID before sending any
credential or mutation bytes. The broker separately checks the adapter peer UID
against allowed_adapter_uids and authenticates the dedicated credential. There
is no TCP or HTTP fallback. Frames are bounded, connections have a 10-second
deadline, and at most 32 requests are handled concurrently.
Each version-1 batch carries the exact tenant/broker binding, configured issuer,
strictly sequential source revision starting at one, and at most 4,096 subject
updates within 2 MiB. Each subject must be explicitly admitted and may carry at
most 128 group identifiers. Core maps those identifiers through the sealed role
mapping. Unprovisioned humans cannot bootstrap admin or log in under managed
mode. Deleted subjects remain tombstoned; terminal suspend batches revoke human
authority and cannot be remotely cleared.
A successful receipt binds tenant, issuer, revision and the SHA-256 digest of the serialized batch. Adapters must verify all those values before acknowledging a source update, retain pending deliveries durably, and retry the exact batch after an uncertain result. Only exact replay of the most recent committed revision is acknowledged again. Updating authority and final task dispatch share the identity writer lock; removal/regrant does not restore old sessions, delegations or reviewer authority. A receipt cannot recall an already completed external effect.
The broker holds a lifetime endpoint writer lock. On restart it retires only an
owned stale socket after observing connection refusal; active sockets and
unrelated files remain untouched. Managed state cannot be silently disabled by
removing its configuration. Legacy [scim] configuration and persisted legacy
managed identity state fail closed with an explicit offline-migration requirement.
No automatic migration, authority reset, remote transport or retention pruning is
provided by this contract.
Delegation and policy¶
With identity configured, opaque agent run mints an Ed25519-signed opqd1
delegation binding subject (sub), acting workload (act), access mode and
session ID (jti). Delegated mode also requires an unexpired human login.
Every request checks signature, expiry and current store state. Approval hashes
include this context, so two principals' approvals are not interchangeable.
Constrain operations with the verified delegator's roles and mode:
[[rules]]
name = "agents-for-operators-only"
operation_pattern = "github.*"
allow = true
client_types = ["agent"]
[rules.identity]
require_principal = true
roles = ["operator"]
access_modes = ["delegated"]
All listed roles must belong to sub. Any identity constraint fails closed
without a verified principal. Add the target, workspace and approval constraints
required by your workflow; this example shows only identity matching.
Listing requires admin/auditor authority; assigning roles requires admin authority.
Attribution and limits¶
Operation records carry the verified principal context and role snapshot.
Approval records distinguish session-bound presence (local_bio_session),
polkit account authentication, and signature-bound device/workstation/FIDO2
responses. Login, logout, role and delegation changes are audited. The
trust_domain.posture startup record reports observed custody enforcement.
Use audit inspection and the explicit
older-store migration procedure.
Same-account processes that obtain broker keys can forge locally valid history. An enforced custody split prevents agent-account access to those files; it still trusts the host, root and broker administrator. Attribution does not establish globally complete evidence or prove an external effect.
Bounded task requests carry the same verified delegation. Logout, delegation revocation or role removal blocks further authorized access; a final dispatch fence cannot recall work already dispatched. Read the task lifecycle before retrying an interrupted request.