Skip to content

Pre-release. v0.1 is not out yet, so there is nothing to install and no public source to clone — the quickstart builds from a checkout.

Register an agent

The registration is the most an agent may ever do. Nothing it does at runtime can exceed it.

Keep registrations in a repository and change them through review. Two commands do that:

Terminal window
SubactId.Server agent init jira-triage --out agents/
# fill in allowed_scopes and allowed_audiences in agents/jira-triage.yaml
SubactId.Server agent apply agents/*.yaml --server https://subactid.internal.example.com

init writes the registration, the agent’s key pair and its public key set, with tight defaults and two empty lists you must fill in. apply reconciles the files with a running control plane through the admin API. The command line has the details.

The same registration over HTTP, which is what apply sends:

Terminal window
curl -s -X POST https://subactid.internal.example.com/admin/agents \
-H "Authorization: Bearer $SUBACTID_ADMIN_KEY" \
-H 'Content-Type: application/json' \
-d '{
"agent_id": "jira-triage",
"display_name": "Jira triage agent",
"sponsor_required": true,
"allowed_scopes": ["jira:read", "jira:comment"],
"allowed_audiences": ["https://jira.internal"],
"max_task_ttl": "PT30M",
"max_token_ttl": "PT5M",
"max_delegation_depth": 1,
"jwks_uri": "https://agents.internal.example.com/jira-triage/jwks.json"
}'
Field What it does
agent_id Required. The agent’s name in its assertion, in act.sub as agent:<id>, and in the ledger
display_name Required. For people reading the registry
sponsor_required Must be true in v0.1, and is true when omitted
allowed_scopes Required, at least one. The ceiling: a token’s scope is the intersection of this, the user’s scopes and the request
allowed_audiences Required, at least one absolute URL. Any other audience is invalid_target
max_task_ttl How long one task may last. Optional; SubactId:Tokens:DefaultTaskTtl when omitted
max_token_ttl How long one token may last. Optional; SubactId:Tokens:DefaultTokenTtl when omitted, cut to the task if shorter. Never more than max_task_ttl
max_delegation_depth Required, 1 to 5. v0.1 issues depth 1 only, so the value does not change what is issued
high_risk_audiences Audiences whose tokens carry introspect_required. Empty by default
jwks The agent’s public keys, held and served by the control plane. Set this or jwks_uri
jwks_uri An absolute HTTPS URL the control plane fetches the agent’s keys from

Durations are ISO 8601: PT5M is five minutes, PT6H six hours. Both lifetimes must fall within the server’s SubactId:Agents:* bounds: a task from a minute to a day, a token from thirty seconds to an hour, by default.

  • allowed_scopes: narrow. It limits what a compromised agent can do. List what the agent does, translate that into scopes, and register exactly those.
  • max_token_ttl: five minutes is usually right. It is how long a revoked task’s tokens stay usable for audiences validated locally; see revocation. Longer means fewer refreshes and a longer window for a leaked token.
  • max_task_ttl: as long as the work takes. A token never outlives it, and a task that runs longer than needed is a grant left open.
  • max_delegation_depth: 1.
  • high_risk_audiences: every audience you would not want reached after you revoke a task. A token for one carries introspect_required, and @subactid/server and @subactid/mcp then introspect it on every call, with no configuration on the tool server.

The control plane authenticates an agent by a short-lived signed assertion, checked against the agent’s registered public keys. Set exactly one of:

  • jwks: the key set inline, as agent init writes it. The control plane serves it at /agents/{agent_id}/jwks.json, so the agent needs no public endpoint and the control plane fetches nothing. A key with a private member is refused.
  • jwks_uri: a URL the control plane fetches the keys from. It must be absolute HTTPS. The keys are cached for up to 10 minutes, so a removed key can keep working that long. To stop a key at once, hold it inline or disable the agent.

The assertion is a compact JWS signed with RS256, PS256 or ES256:

Claim Value
iss and sub The agent_id
aud The control plane’s issuer URL, or that URL plus /oauth2/token
jti Unique, at most 256 characters. A reused one is invalid_client
exp At most five minutes ahead. 60 seconds of clock skew is allowed

@subactid/client builds and signs the assertion; see Build an agent.

Terminal window
curl -s -X PATCH https://subactid.internal.example.com/admin/agents/jira-triage \
-H "Authorization: Bearer $SUBACTID_ADMIN_KEY" \
-H 'Content-Type: application/json' \
-d '{"allowed_scopes": ["jira:read"]}'

PATCH takes any subset of the fields plus enabled, and validates the merged registration as a whole. A change applies from the agent’s next request: a narrowed allowed_scopes at the next refresh of each live task, and {"enabled": false} on the next exchange or refresh. Tokens already issued stay valid until they expire.

Every change writes agent.registered, agent.updated or agent.deleted to the ledger in the same transaction.

A validation failure answers 400 with the fields named:

{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "The request is invalid.",
"status": 400,
"errors": {
"display_name": ["is required."],
"max_delegation_depth": ["is required."]
}
}

Deleting an agent, revoking its tasks and the admin key are covered in the admin API.

Subact ID Pre-release. v0.1 is not out yet.

© 2026 Nikola Živković PR Agencija za programerske usluge Novi Sad. Subact ID is its product.

LegalTermsPrivacy