Decisions fail closed
An agent with no active policy denies everything, with the ruleno_policy. The three
intrinsic rules do not count as a policy. An agent also arrives with an active starter policy
unless you ask for it not to, so the default path never passes through no_policy at all.
What this removes: the window between creating an agent and writing its rules, during which
a misconfigured agent would otherwise have unlimited authority. The common failure in
policy-engine deployments is a default-allow gap at setup time. There is none here, in either
direction: an agent with no rules refuses, and an agent you have not got to yet already has
some.
The first denial wins, and cannot be overridden
Rules are evaluated in a fixed order: intrinsic rules, then each active policy oldest first, with imported modules ahead of a policy’s own rules. The first denial short-circuits the entire evaluation. What this removes: a permissive rule silently re-enabling something a stricter rule refused. Anallow never overrides a deny, and a require_approval never converts a denial into a
question for a person. Rule order cannot produce a weaker outcome than the strictest rule that
matched.
Scopes are checked before policy
An agent key carries an action-type allowlist enforced by an intrinsic rule, so a request outside it is refused asruntime_scope before any policy is consulted.
What this removes: a policy misconfiguration granting an agent authority over action types it
was never issued for. Scope is a property of the credential, not of the rules attached to it.
Reservations are released, not stranded
Budget is reserved when an action is authorized or held, and released when it fails, is denied, or its approval is refused. What this removes: budget stranded by an action that was refused, failed, or turned down by a person. What it does not remove: an intent leftpending because nobody called complete or
fail holds its reservation for good. A budget has no expiry and no close endpoint, so nothing
sweeps that up later. Reporting the outcome is part of the contract.
Replay returns the original decision, not a new one
Each(agent, idempotency_key) maps to exactly one intent forever. A replay returns the
stored intent and the stored decision, at the original HTTP status.
What this removes: a retry under network failure double-reserving budget, and a retry after a
policy change silently getting a different answer than the one already acted on. The decision is
the record, not a recomputation.
The key is matched on its own, though. Reuse one with a different amount and you get the first
decision back, not an error, so the key has to be as specific as the action it stands for.
Concurrent spend contends without queueing
A budget is claimed with a single conditional update rather than a lock. What this removes: both the race where parallel sub-agents each read sufficient budget and both succeed, and the serialization bottleneck that a lock would impose on a fan-out. Exactly one claimant wins each contested update. The same applies to spend tokens: if two payments cite one token at once, exactly one consumes it, and the loser releases its reservation and fails withspend_token_unusable.
Every decision is recorded, including refusals
A denied action still writes an intent, in statedenied, alongside an audit event. The audit
log is append-only and rejects deletion at the model layer.
What this removes: “we don’t know what it tried to do.” A refusal that leaves no trace is a
refusal you cannot audit, and the interesting question after an incident is usually what the
agent attempted, not what it completed.
Every role can read the audit trail, including member.
The ledger is never in the authorization path
Where a workspace has the ledger enabled, reservations are mirrored into it asynchronously. What this removes: a slow, unavailable, or absent ledger delaying or changing a decision. A projection can lag; it can never gate. Authorization behaves identically on a workspace with no ledger at all. The gap is observable asunprojected_cents on the workspace funds endpoint.
Revocation is not retroactive
Suspending or revoking an agent stops new authorizations. It does not rewrite decisions already made, and it does not invalidate a cosignature already issued. What this removes: nothing. This is a limit, stated so you can size it. A cosignature’s default lifetime is five minutes, so revoking a compromised agent leaves at most that window in which an already-issued signature still verifies offline. If that window matters to you, verify throughPOST /control/v1/cosign/verify rather than locally: it re-checks the intent’s state and will report
already_settled for anything spent.
Test and live never mix
Every credential is bound to one mode. Keys are prefixedkrt_test_ or krt_live_, and a test
key cannot reach live data.
What this removes: a load test or a demo consuming real budget or producing real payments.
What Kordio does not guarantee
A control layer that overclaims is worse than one that does not exist, so here is what this one will not do for you.It cannot govern a path that does not ask
Kordio holds no funds, no accounts, and none of your credentials. An executor that never calls us and never verifies a cosignature is not governed by us, no matter what your policy says. The control layer binds the paths you route through it, and the completeness of that routing is your responsibility, not ours.It has no opinion on what you do when we are unreachable
If the authorize call times out, nothing on our side decides what happens next. Your code does. Fail closed: treat a transport failure as a refusal and do not execute. That is the only choice consistent with having a control layer at all. An agent that spends whenever Kordio is unreachable has a control layer exactly as long as the network is up. Retry with the same idempotency key, which is safe by construction: if the original request did land, you get the original decision back rather than a second reservation.A cosignature proves authority was granted, not that it is unused
Offline verification checks the signature, the claims, and the expiry. It cannot see settlement state. If double-execution is a risk in your architecture, verify through the endpoint, which re-loads the intent and refuses anything already settled.Simulation is not free of side effects
POST /control/v1/agent/actions/simulate creates no intent and reserves no budget, but it does write an
action.simulated audit event. Policy previews on the dashboard side write nothing at all, so
use those if you need a genuinely silent evaluation.
Next steps
How a decision is reached
The evaluation order these properties come from.
Where the ledger fits
What the projection promises, and what it does not.