Private betaZynth Auth is currently in private beta testing.New organizations are created by invitation only, and no plan can be purchased yet.Request early access

Operating autonomous response

The model behind this — the levels, the blast-radius ceilings, and the record a class has to earn — is Autonomous response. This page is the operating manual: what to set, what to watch, and what to do when the engine has acted.

Everything here is in the console under Command Center → Autonomy, and everything the console does is this API.

Availability. On a self-hosted install the engine ships dormant: while AUTONOMY_EXECUTION_ENABLED is off (the default) it records what it would have done and executes nothing, whatever your levels say. See Self-hosted deployments.

Permissions

Four permissions, deliberately split so an incident responder can stop the engine without gaining the power to reconfigure it:

PermissionGrants
autonomy:readRead the decision ledger, engine metrics, graduation status, and the kill-switch state
autonomy:approveApprove or deny a proposal, and undo an executed action
autonomy:disableEngage or release the emergency stop
autonomy:manageReserved for configuring response policy. No endpoint requires it today — the levels below are organization settings behind tenant:manage. It is not inert, though: see the note under Step 1.

autonomy:approve is not sufficient on its own. Approving an action also requires the permission to perform that action yourself — session revocation needs tenant:manage — and the same rule governs undoing one. An approver can never unlock authority they do not have.

Grant these with roles in Access Control, like any other permission.

Step 1 — choose a level per response class

Levels are organization settings, one per blast-radius class, every one defaulting to manual:

Setting keyClass
autonomy.level_contain_sourceContain source
autonomy.level_contain_non_humanStop an AI agent
autonomy.level_raise_assuranceRaise assurance
autonomy.level_revoke_accessRevoke access
autonomy.level_disable_principalDisable principal

Set one in Settings → Organization, or over the API (requires tenant:manage):

curl -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"value": "recommend"}' \
  https://auth.example.com/api/v1/tenants/config/autonomy.level_revoke_access

The accepted values are the ones the class's ceiling permits, so each key offers a different set — you cannot store a level the engine would not honour:

Setting keyAccepted values
autonomy.level_contain_sourcemanual · recommend · graduated · full
autonomy.level_contain_non_humanmanual · recommend · graduated
autonomy.level_raise_assurancemanual · recommend · graduated · full
autonomy.level_revoke_accessmanual · recommend · graduated
autonomy.level_disable_principalmanual · recommend

Anything past the ceiling is a 400 naming the allowed set — full on revoke_access, or anything unattended on disable_principal, is refused rather than silently reduced.

One cap is still applied at decision time and cannot be seen here. A detection judged attacker-inducible holds its response at recommend however its class is configured (see the inducibility cap). That is a per-detection judgement, not a per-class one, so no setting can express it — a class may legitimately sit at full while one of its detections still only ever proposes.

Start at recommend. It is also the only way to build the record a class needs before it can act unattended — nothing accrues at manual, because nothing is proposed.

Holding autonomy:manage, autonomy:disable or agents:kill makes you un-targetable. The engine never acts on a principal who could stop it, so these permissions carry a second meaning beyond what they let you call. Owners are excluded for the same reason: an owner holds every permission, including the off-switch. Grant them to the people who run the engine, not as a way to shield an account from response.

Step 2 — work the proposal queue

At recommend, a firing detection raises a proposal and waits. Read the ledger:

# Everything awaiting a verdict (requires autonomy:read)
curl -H "Authorization: Bearer $TOKEN" \
  "https://auth.example.com/api/v1/autonomy/decisions?mode=proposed"

Each decision carries the plan's effect sentence — one line saying exactly what would happen — plus the detection that triggered it, the target, the blast class, and two flags the server computes for you: actionable (still awaiting a verdict and inside its window) and revertible.

Approve or deny:

curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"approve": true, "reason": "Confirmed token theft — the session set is not the user's"}' \
  https://auth.example.com/api/v1/autonomy/decisions/$DECISION_ID/adjudicate
  • The verdict is recorded before the action is attempted, so a decision is never lost to a downstream failure — and your verdicts are the evidence the graduation gate counts.
  • reason is optional on approval and worth writing anyway: it is the oversight record an auditor reads. It is required when undoing an action.
  • A decision that already has a verdict returns 409. Retrying is safe; it will not double-act.
  • Proposals expire. Past its window a proposal lapses unexecuted rather than acting on facts that have aged out — approving a lapsed one is refused, and the ledger says so.

Nothing pings you when a proposal is raised. The detection behind it alerts on your normal channels; the pending proposal itself does not. Watch the queue — see Is anyone actually driving this? below.

Step 3 — undo an action

Reverting session revocation restores the previous state, so a wrongly-signed-out user's existing devices resume working without signing in again.

curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"reason": "False positive — the second device was the user travelling"}' \
  https://auth.example.com/api/v1/autonomy/decisions/$DECISION_ID/revert
  • Guarded exactly like approval: you cannot undo what you could not have done yourself.
  • It refuses (409, nothing changed) when the effect was superseded — for example if a person revoked the same sessions again after the engine did. Undoing then would silently discard their decision.
  • A revert is honest about being partial: it restores what this action changed. Sessions ended by something else — a logout, a password change, an IdP deprovision — stay ended.
  • Reverting feeds the circuit breaker. Any reversal in the trailing 30 days holds that class below unattended operation until the window is clean. That is intended: a reversal is the signal the engine got something wrong.

Undoing an agent stop

Reverting a stop an AI agent decision releases the agent's kill switch: it authenticates and works again, immediately, with nothing to re-provision. Two things differ from the session case, and both are deliberate:

  • Tokens issued before the stop stay dead. The containment cut-off only ever moves forward, so a token that was already stolen cannot come back to life because the engine changed its mind. Your agent gets a new token on its next request; the response says so (epoch_retained). Read the revert as "the agent can work again", never as "as you were".
  • Only a person can do it. Undoing a containment restores a non-human principal's authority, which is the same act as releasing its kill switch by hand — and that has been people-only since v0.33.0. An AI agent that tries is refused with 403 and the attempt is recorded as a security event, whatever permissions it holds.

Watch: is it working, and is anyone driving it?

curl -H "Authorization: Bearer $TOKEN" https://auth.example.com/api/v1/autonomy/metrics

Four numbers matter:

  • Decisions by mode — what the engine has been deciding.
  • Time to act — decision raised → action executed, mean and p95.
  • Age of the oldest pending proposal — the one to alert on. A rising oldest-age means proposals are not being adjudicated: no record grows, nothing ever graduates, and recommend has quietly become shadow mode with extra steps. Lapsed proposals stay counted; they are the strongest form of the signal, not noise.
  • Cap utilisation — how much of each class's hourly and 24-hour blast-radius budget is spent. Hitting a cap halts that class and raises a critical detection.

Check what a class has earned

curl -H "Authorization: Bearer $TOKEN" https://auth.example.com/api/v1/autonomy/graduation

Per class: whether it is eligible, every criterion with its numbers, the unmet ones named, and ≈how many more clean approvals would clear the confidence bar. This is the answer to "why is autonomy still asking me every time?".

Two things to expect from the numbers:

  • Graduation is always about your own organization's adjudications. There is no cross-organization view of a track record, by construction.
  • disable_principal has no row. Its ceiling is Recommend, so there is nothing to graduate to, and inventing a row would imply a path that does not exist.
  • contain_non_human has a row, and its bar is 0.90 rather than revoke-access's 0.95. It is the only class in which a false positive harms no person — an AI agent stops, and any human holding agents:kill starts it again in one call.

What one denial would cost

Each class also reports it, as two fields:

fieldmeaning
lower_bound_after_denialthe confidence bound if the very next adjudication is a denial
recovery_approvals_after_denialconsecutive clean approvals needed after it (0 = still above the bar, null = not recoverable soon)

The bar is a statistical bound, not a count, so near it the margin is one adjudication wide and the cost is asymmetric: a denial both lowers the observed rate and adds a sample, while the streak that undoes it is long. A class sitting at 80 of 80 against a 0.95 bar reads 0.954 today and 0.933 after one denial — 29 clean approvals to get back. Plan anything that might produce a denial (a deliberate test, an unfamiliar rule, a new on-call rotation) against that number rather than against the impression that one "no" is a small thing.

On an empty record the answer is arithmetically real and practically meaningless — the bound is already 0, so nothing is at risk. The console omits the sentence there rather than printing a true number attached to a false story, and an API consumer should do the same: guard on total > 0 before rendering it.

The emergency stop

# Read the current state (autonomy:read)
curl -H "Authorization: Bearer $TOKEN" https://auth.example.com/api/v1/autonomy/kill-switch

# Stop everything for 8 hours (autonomy:disable)
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"enable": false, "hours": 8}' \
  https://auth.example.com/api/v1/autonomy/kill-switch
  • hours must be 1, 8, or 24, and the stop auto-re-arms when it lapses. The real-world failure is the switch nobody turns back on.
  • Omitting hours means indefinite, which is refused without a reason — the unbounded option is deliberately the awkward one.
  • {"enable": true} releases it.
  • Every change raises a critical detection. Disabling is both a legitimate emergency action and an intruder's first move; the platform does not guess which, only makes it visible.

Detections and proposals continue while the switch is engaged — what stops is acting.

Self-hosted deployments

Set these in your instance's environment file and recreate the backend and the workers:

VariableDefaultWhat it does
AUTONOMY_EXECUTION_ENABLEDfalseThe global execution gate. Off means nothing executes and nothing is proposed, whatever your levels say — the engine only shadows. Turning it on is a deliberate operator act, never a side effect of deploying.
AUTONOMY_SHADOW_ENABLEDtrueThe response pass itself. false stops the engine even recording what it would have done.
AUTONOMY_PROPOSAL_TTL_SECONDS3600How long a proposal stays open — the window for a verdict and for the approved action to run. Past it the decision expires unexecuted.
AUTONOMY_DECISION_RETENTION_DAYS90How long unadjudicated shadow rows are kept. Anything adjudicated, proposed, executed or reversed is never purged — that is the evidence and the action record.

Set COMMAND_ALERT_EMAIL and/or COMMAND_ALERT_WEBHOOK_URL before raising any class above recommend. Unattended executions notify on those channels, and an install with neither set takes actions nobody is told about. The notifier is fail-open by design — a broken channel never blocks or rolls back a response — which is exactly why an unconfigured channel is silent rather than loud.

Next