Core concepts
A handful of ideas explain how Axtary works. The short version: every agent action is turned into one common shape, a deterministic rule decides it before it runs, the approval is glued to the exact content, and the whole thing is written to a tamper-evident log. Everything below is a detail of that loop.
Normalized action
In plain terms: no matter what an agent is doing — reading a file, opening a PR, posting to Slack, calling an MCP tool — Axtary first rewrites it into one common description so a single set of rules can judge all of them.
Every tool call — a Claude Code file read, a GitHub PR create, a Slack message, an MCP tool call — is converted into one provider-neutral normalized action: who (actor/tenant/agent), intent, tool, resource, exact payload, and context. Policy, signing, and the ledger all speak this single language, so one rule set covers every connector.
For native connectors, one descriptor now keeps the tool's capability, sanitized ledger evidence, config defaults, required scopes, and doctor smoke identity together. Jira and Linear use this path today. The descriptor is not an allowlist: policy and ActionPass verification still decide whether the exact action may execute.
Deterministic policy
Policy is evaluated before execution, locally, and fails closed — if nothing explicitly allows an action, it's denied. Decisions are one of allow, deny, or step_up (needs human approval). The engine is deterministic and fast (it sits on the hot path), and it exports to Cedar and OPA/Rego forms so you can prove the same decision three ways.
Beyond the built-in preset, you can write rules in axtary.yml (a policies block): match by tool/resource/actor, with allow/deny/step_up effects and obligations — required-approver roles, blocked paths, time-of-day and rate windows, and tighter pass TTLs. Rules are default-deny and combine deny-over-step_up-over-allow. axtary policy simulate shows exactly which rule fired and why before you ship a change.
Deterministic provenance
A trusted local normalizer can label which task, operator, document, tool output, or connector supplied an exact payload field. For covered outbound fields, policy can fail closed when lineage is missing and require exact review when text came from an untrusted document or tool result. The label is hashed into the ActionPass and ledger, so it cannot be changed after authorization. This bounds blast radius; it does not claim to detect or prevent prompt injection.
Behavioral step-up
Beyond static rules, Axtary can escalate an action to human approval because it
is new for that agent — a tool/resource combination it has never executed,
or a tool transition it has never made. This is deterministic exact-match
memory, opt-in, and escalate-only: it can raise an allow to step_up, and
it can never grant an allow or soften a deny. It is not anomaly detection or
risk scoring. An action only becomes "known" after it is actually approved and
executed, so a denied or unapproved action can't be laundered by retrying it.
See Behavioral step-up.
ActionPass
In plain terms: when an action is approved, Axtary issues a tamper-proof "ticket" that says exactly this action was allowed — and it's stamped with a fingerprint of the exact payload, so the ticket is worthless for any other action or any altered version of this one.
When an action is allowed, Axtary issues an ActionPass: a short-lived, signed, verifiable token scoped to that one action. Crucially it is bound to the SHA-256 hash of the exact payload. Tokens are ephemeral; passes are evidence. Because the pass is offline-verifiable and chained into the ledger, a reviewer (or another tool) can confirm exactly what was authorized.
Proof of possession (ActionPass v1)
Content binding says what a pass authorizes; key binding says who may present it. ActionPass v1 additionally binds a pass to the holder's key (DPoP, RFC 9449, with an RFC 7800 cnf confirmation). To use a pass the agent must present a fresh proof signed by that key for the exact request (method, URI, and the pass itself), with replay protection. A pass captured from logs, memory, or a proxy hop can't be replayed by anyone else — even for the same action. v0 passes (content binding only) still verify; v1 adds the sender constraint.
Authenticated status (ActionPass v2)
ActionPass v2 adds a signed reference to a freshness-bounded Token Status List.
The wire format is IESG-approved as an RFC-to-be and Axtary pins its stricter
runtime contract as axtary.status-list.v1.
The issuer publishes public JWKS by kid; a separate process can verify the
status artifact and reject a revoked or suspended pass without trusting the
issuer's local filesystem. Missing, unreachable, stale, or invalid status
fails closed. Delegation verification checks every member, so invalidating a
root also invalidates a captured descendant.
Runtime revocation
An operator can revoke one ActionPass by its jti with axtary revoke <jti>.
The local proxy, adapter credential boundary, and MCP wrapper consult the
shared trust store on every presentation, so a captured pass is rejected
without restarting the runtime. If that source is unreadable or corrupt,
execution fails closed. The local record remains the operator authority;
ActionPass v2 projects it into signed status evidence for independent or
multi-process verification.
Delegation
An agent can hand a sub-agent a narrower pass — same human owner and task, but a subset of the scope and one fewer delegation hop. A child can never broaden the tool, resource, payload, or constraints beyond its parent, and the whole root→leaf chain is signature- and attenuation-verified before execution. This bounds blast radius across multi-agent systems instead of sharing one over-broad credential.
Revocation follows that same chain: the verifier checks the root, every intermediate parent, and the presented leaf against one authority-owned source. Revoking any ancestor therefore invalidates all downstream children without maintaining a separate descendant index.
Budgets
A policy can attach a budget — a per-tenant, per-actor, or per-task spend limit the runtime owns, never the agent. The proxy reserves against the limit before issuing a pass and commits or rolls back on the outcome, counting in-flight reservations so concurrency can't oversubscribe. A runaway or looping agent is capped. This is metering for blast-radius control — not pricing or billing.
The ledger
Every decision and its post-execution outcome are written to a hash-chained ledger — each record links to the previous one's hash, so any tampering breaks the chain and is detectable. It records the decision, reasons, policy version, payload/action hashes, trace IDs, and the provider result. You can verify it offline and export it (including SIEM-friendly formats).
New records also carry a payload-free audit context — tenant, agent, human owner, task, tool, and resource — derived by the trusted runtime and covered by the record hash. The ledger dashboard can therefore search verified local and tenant-scoped synced evidence by those fields, decision, and time. Older records remain valid; missing identity fields are shown as unavailable, never guessed.
The dashboard also reconstructs spine surfaces from those same records: a delegation-chain explorer that rebuilds each root→child lineage and its enforced depth progression, and a budget/quota meter showing committed and reserved usage against the configured limit per scope, with over-budget denials counted. Both read only from recorded evidence — no scope diffs or limits are inferred — and label whether each row came from local or synced evidence. With no records yet, each surface states what it enforces instead of showing zeros.
An ActionPass inspector lets you open any pass and see exactly what it
authorized — who, what, the exact-action hashes, and any delegation or budget —
with fields that aren't recorded per-record (holder binding, declared
constraints, expiry) shown as not-recorded rather than guessed. From the local
dashboard you can revoke a pass: this is a real, enforced revocation that
writes the runtime's trust store (which the proxy and adapters check on every
use) and records a tamper-evident revocation event in the ledger — not a
UI-only toggle. The hosted dashboard inspects synced passes read-only and points
you to the runtime or axtary revoke, because it can't reach a remote runtime's
authority.
You can also attest an export: axtary attest-ledger signs it, and anyone — with only a JOSE library and the published public key — can run axtary verify-export (or reimplement the algorithm from the spec) to confirm the signature, the chain, and every record offline, detecting a single-byte change. Tamper-evidence becomes portable evidence an auditor can check without trusting Axtary.
For cross-issuer audits, another organization can pin your public Axtary trust root and verify one ActionPass, the signed ledger attestation, an inclusion proof, and fresh status-list evidence together. That proves the pass and ledger record came from the pinned issuer and that the pass is not currently revoked or suspended. It is a package/API primitive today, not a hosted trust-network UI.
Content authorization, not channel authorization
This is the differentiator. A normal token authorizes a channel: "this agent may post to Slack." Axtary authorizes content: "this agent may post this exact message to this channel." A prompt-injected agent holding a valid scoped token still cannot swap the PR diff or the Slack message after a human approved it — the approval is bound to the exact payload hash, and a mismatch is blocked before execution. That's the claim Axtary makes demoable, not just asserted.
The two planes
- Local enforcement plane holds credentials, decides, and executes (the hot path). It works with no hosted account at all.
- Hosted control plane (axtary.com) is for human review, compliance evidence, and team management. It never holds a provider credential and is never in the critical path of an action.
On a fresh tenant, the overview labels each enforcement capability as not observed until real runtime, approval, ledger, or MCP evidence arrives. Its setup and capability rails link to the local command that can produce that evidence; zero activity is never presented as a successful connection.
See the Quickstart to run all of this locally.