AI Research Library

Typed Actions and the Deterministic Authority Kernel

Rajesh Ranjan Mahapatra

A model may propose what should happen; only a small, typed, state-aware kernel should decide whether that effect is permitted now, bind it to authority, and prove what happened next.

© 2026 Rajesh Ranjan Mahapatra. 34 min read

Typed Actions and the Deterministic Authority Kernel

A model may propose what should happen; only a small, typed, state-aware kernel should decide whether that effect is permitted now, bind it to authority, and prove what happened next.

TLDR

  1. Model output should be treated as a proposal, never as authority. Fluent reasoning can select an action, but it must not supply the identity, permission or trusted state that makes the action executable.
  2. A deterministic authority kernel validates a typed proposal, derives the represented subject and actor from trusted channels, checks policy and current world state, then issues a narrow, expiring capability bound to one immutable action.
  3. The practical unit is a three-object protocol: proposal, decision receipt and effect receipt. This separation makes approval exact, retries safe, denials testable and post-incident reconstruction possible without preserving hidden reasoning.
  4. The kernel cannot repair bad policy, prove that a recommendation is wise or observe effects a downstream system cannot expose. It reduces authority errors only when every consequential path is mediated and state can be checked.
  5. Change the architecture review question from “Which tools may the agent call?” to “Which typed effects can this principal authorise, against which live state, and what evidence proves each effect occurred exactly once?”

The refund that was valid in every field

A relationship manager asks an assistant to reverse a £75 service fee after a documented complaint. The model retrieves the case, finds the ledger entry, produces the correct reason code and emits a perfectly valid tool call. Every required JSON field is present. The amount is an integer. The account identifier matches the expected pattern. The tool schema passes.

But one retrieved note contains an old customer identifier. The model selects that account instead of the account in the authenticated session. The service credential attached to the agent can reach both records, so the downstream API accepts the call. Nothing looks like a software failure: authentication succeeded, the payload was valid and the service returned 200 OK. The wrong customer received the reversal.

The defect was not that the model produced malformed syntax. It was that the system allowed model-controlled text to choose facts that belonged to the authority boundary. A valid action shape had been mistaken for a valid action.

The repair is not another instruction telling the model to be careful with account identifiers. The repair is to make the account scope come from signed user entitlement, to express the model output as a proposal, and to require an independent component to intersect that proposal with live policy and live state before any effect occurs.

The core answer is to split cognition from authority. Let the model interpret evidence, compare alternatives and propose a business action. Put a small deterministic mechanism between that proposal and every consequential resource. The mechanism must be non-bypassable, derive identity outside model-editable content, fail closed, bind approval to the exact proposal, recheck state beside the authoritative resource and emit evidence about both the decision and the effect.

A stochastic proposal stream crossing a deterministic capability membrane Indigo proposal trajectories approach a teal membrane. Invalid, unauthorised and stale paths are reflected in coral. One authorised path becomes a narrow capability, reaches the resource, and returns an evidence path. Model proposal space Authorised effect deterministic authority kernel wrong scope stale state resource local veto effect receipt one narrow capability readback and outcome evidence
Figure 1. The capability membrane. The composition is conceptual. The model may generate many plausible proposals; only a proposal that satisfies typed, identity, policy, state and evidence checks becomes executable authority. Rejected paths remain proposals, not partial permissions.

Part IA valid shape is not authority

Structured output is an important improvement over extracting a command from free text. A schema can require the action name, restrict an amount to a number, close the object against unknown fields and enumerate valid reason codes. JSON Schema explicitly describes this as validation of instance structure. It says nothing about whether the caller is entitled to act on the named account, whether the purpose is legitimate or whether the state has changed since the proposal was formed.

That distinction gives us three separate questions. Syntax asks whether the proposal is well formed. Semantics asks whether the parameters mean what the contract says. Authority asks whether this actor may cause this effect for this subject, purpose, resource and moment. A tool-call schema answers mainly the first question and a bounded part of the second.

Two identical JSON proposals with different authority outcomes Two copies of the same reverse fee JSON object appear on the left and right. The left uses an ambient service token and reaches the wrong account. The right intersects trusted user entitlement and reaches only the authorised account. Same proposal shape, different authority MODEL OUTPUT action: reverse_fee resource: ACCT-200 amount_gbp: 75 reason: SERVICE_FAILURE schema: valid authority: assumed from service token MODEL OUTPUT action: reverse_fee resource: ACCT-200 amount_gbp: 75 reason: SERVICE_FAILURE schema: valid authority: derived from signed entitlement executed on wrong account denied: outside entitlement
Figure 2. Shape is invariant; authority is not. This is an illustrative intervention. The proposal is held constant while the authority mechanism changes. The safer outcome does not depend on the model noticing its mistake.

Thought experiment one: change only ambient authority

Imagine two systems using the same model, prompt, retrieval result and tool schema. In system A, the agent process holds a service token that can reverse fees across the full portfolio. In system B, the process can only submit a proposal; a separate component derives the employee and permitted accounts from the authenticated session, then grants one action on one account.

The model makes the same cross-account mistake in both systems. System A executes it because the token has ambient authority. System B denies it because the proposed account is not in the trusted entitlement set. Nothing about model intelligence changed. The causal feature is whether designation and authority travel together.

This is the modern form of the confused deputy problem described by Norm Hardy. A deputy with legitimate power is induced to use that power for a request whose authority is missing or ambiguous. Capability-oriented designs address the problem by carrying a specific authority to a specific object rather than asking a broadly privileged deputy to interpret a name. Macaroons later showed how cryptographic credentials can be attenuated with contextual caveats about who, how and when authority may be exercised.

Published evidence AgentDojo evaluated tool-using agents over 97 realistic tasks and 629 security test cases, including attacks delivered through untrusted tool results. Its authors found that both task completion and defence remained challenging. CaMeL then demonstrated an architectural defence that separates trusted control flow from untrusted data and enforces capabilities at tool calls, solving 77 per cent of tasks with provable security versus 84 per cent utility for an undefended system in its updated evaluation. These results support the design direction, not a claim that every business action can be secured automatically.

The strongest objection: existing IAM already does this

In a mature platform, identity, OAuth, policy engines and service checks already exist. Why add another “kernel”? The objection is partly correct. The authority kernel is not a new credential format or a replacement for IAM. It is the application-level composition that makes existing controls answer the exact business question before an effect.

SPIFFE can attest which workload is calling. OAuth Token Exchange can represent impersonation or delegation across services. Rich Authorization Requests can carry structured, fine-grained authorisation details. Cedar can evaluate a typed request over principal, action, resource and context. None of those primitives alone proves that the model’s proposal is the action a person approved, that the case state is still current, that the operation has not already occurred or that the downstream system reached the intended business outcome.

The new architectural burden is the join. The proposal digest, represented subject, actor version, purpose, policy version, state version, approval, idempotency key and outcome evidence must describe one action without being assembled from model-controlled strings. Existing security components supply many of those fields. The kernel makes their relationship explicit and testable.

The argument in one passChart · flow
flowchart TD
  A["The refund that was valid in<br/>every field"] --> B["On this page"]
  B --> C["Part I A valid shape is not<br/>authority"]
  C --> D["Part II The authority kernel,<br/>cut open"]
  D -. "re-test" .-> A
  classDef first fill:#0a1424,stroke:#D95545,stroke-width:2px,color:#e6f3ff
  classDef step fill:#0a1424,stroke:#3f7da0,stroke-width:1px,color:#e6f3ff
  class A first
  class B,C,D step
A structural route through Typed Actions and the Deterministic Authority Kernel, from The refund that was valid in every field to Move autonomy into proposals, not…. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Part IIThe authority kernel, cut open

The term “kernel” is borrowed carefully. NIST defines a reference monitor as a mechanism that is always invoked, tamperproof and small enough to analyse and test. A security kernel implements that concept inside a trusted computing base. An application authority kernel seeks the same properties for consequential business effects, but it operates across distributed services rather than inside one operating system.

The analogy matters because it suggests three tests. Can any tool path bypass the decision? Can the model, orchestration layer or retrieved content alter the enforcement code or trusted inputs? Is the mechanism small and deterministic enough to test exhaustively over its action vocabulary? The analogy breaks because a distributed application cannot obtain operating-system-level completeness by naming a policy service. Every downstream mutation endpoint must still enforce locally, and the authoritative state may change between decision and execution.

Cutaway of the authority kernel and its trusted inputs A layered cutaway places the model proposal outside the trusted boundary. Inside, contract validation, trusted identity resolution, policy evaluation, state preconditions and capability issuance are shown. A local resource veto and evidence ledger sit beyond the central kernel. Authority kernel cutaway deterministic decision allow or deny proposal digest + policy version typed contract validation capability issuance and expiry trusted identity actor + subject live world state preconditions + freshness purpose + policy evidence + approval model proposal untrusted resource gate final local veto evidence ledger decision + effect
Figure 3. The trusted cutaway. This is a practitioner synthesis of reference-monitor, policy-engine and capability patterns. The dashed outer boundary is an assurance claim to be tested, not a decorative trust zone.

The proposal contract

A proposal contains only facts the model is allowed to choose or infer. For a fee reversal, those may be the action verb, target business resource, amount, controlled reason code, evidence references, expected state version and idempotency key. It should not contain the authenticated employee identity, the deployed agent identity, the set of entitled accounts, the effective policy or a Boolean named approved.

Closing the schema against unknown fields matters. Otherwise a model can add attractive but meaningless authority claims such as role: admin, user_confirmed: true or policy_exception: urgent. The fields may be ignored by a correct implementation, yet their presence can mislead logging, reviewers or later adapters. Trusted facts should be absent from the model’s writable type, not merely discouraged by prose.

A deterministic decision function

Let K be the deterministic kernel function, P the canonical typed proposal, T the trusted actor and subject context, St the authoritative state at decision time t, and Π the versioned policy. The result C is a narrow capability. K issues it only when every gate joined by the logical “and” symbol ∧ holds:

K(P, T, S_t, Π) = C iff Valid(P) ∧ Authorised(T, P, S_t, Π) ∧ Preconditions(P, S_t) ∧ Fresh(P, S_t) ∧ Evidence(P)

Valid(P) checks structure and controlled vocabularies. Authorised intersects the actor role, represented subject, purpose, action, resource and policy. Preconditions checks business facts such as “fee exists and is unreversed”. Fresh requires the proposal and any approval to refer to the current relevant state. Evidence requires the decision’s mandatory source references and human approval where policy demands it.

If any term becomes false, the result is deny, not a lower-confidence allow. If the policy version changes, the same inputs may yield a different decision by design. Within one policy version and trusted state snapshot, the result should be repeatable. Deterministic here means that identical trusted inputs produce the same decision, not that the software is free from defects or that the external world stops moving.

The ten-layer anatomy of a consequential action Ten curved strata run from intent at the top through identity, world state, context, reasoning, authority, action, evidence, outcome, and release and recovery. A vertical line separates model-editable strata from trusted and observed strata. One action, ten distinct objects model may interpret or propose trusted system must derive or observe 1 intent 2 identity 3 world state 4 context 5 reasoning 6 authority 7 action 8 evidence 9 outcome 10 release and recovery signed session and workload attestation authoritative versioned facts policy decision and capability decision and effect receipts readback, compensation, escalation
Figure 4. Do not collapse the action stack. The ten strata are conceptual, not measured. Persistent context, remembered narrative and current world state remain separate. A protocol connection or tool schema occupies only part of the action layer.

The three-object action protocol

The most useful implementation distinction is among the proposal, the decision receipt and the effect receipt. They are different records with different writers, trust levels and lifetimes.

ObjectWritten byWhat it may assertWhat it must never stand in for
Typed proposalModel or deterministic plannerRequested verb, resource, bounded parameters, evidence references, expected state, idempotency keyIdentity, entitlement, approval, policy result or proof of execution
Decision receiptAuthority kernelAllow or deny, reason code, trusted principals, purpose, policy version, proposal digest, state binding, expiryProof that the downstream effect occurred
Effect receiptResource owner or independent observerAccepted, denied, applied or unknown; operation identifier; before and after state; readback evidenceA claim that the original business recommendation was wise

A decision receipt makes review possible without exposing private chain-of-thought. It records what the system observed, which rules it applied and why it allowed or denied the proposal. An effect receipt closes a different gap. It answers whether the resource changed, not whether the API call looked successful.

The capability is a sealed answer, not a broad credential

When the kernel allows an action, it should issue a short-lived capability bound to the canonical proposal digest. The payload normally includes actor identity and deployed version, represented subject, purpose, action, resource, parameter limits, policy version, state version, expiry, audience and idempotency key. The executor verifies the signature and the binding before using it.

This capability may be represented by a signed token, a macaroon-like credential with caveats, an opaque handle to server-side state or a request evaluated directly at the resource. The representation is secondary. The invariant is that the model cannot mint, widen or retarget it. Approval authorises an immutable proposal, not an intention described in natural language.

A relationship manager clicking “approve refund” is not enough if the model can later alter the account, amount or reason code. The review event must sign or otherwise bind the digest of what was displayed. If any material parameter changes, a new proposal and new approval are required.

Delegation must narrow at every hop

Agent systems usually contain at least three acting identities: the human or organisation whose interests are represented, the deployed agent workload that proposes an operation and the service that finally executes it. Conflating them produces an audit record that says only “the agent did it”. That phrase hides the central authorisation question. The employee may be entitled to reverse a fee; the agent version may be approved to propose reversals; the fee service may be permitted to execute a valid capability. Those are three different grants.

OAuth Token Exchange distinguishes the subject of a token from an acting party and can preserve a delegation chain. That is useful plumbing, but exchange alone does not prove that authority became narrower. The application must still compare the incoming grant with the requested child grant. A system that turns a customer-service token into a portfolio-wide service credential has represented delegation while violating attenuation.

Write Cchild ⊑ Cparent when the child capability grants no more authority than its parent across resource set, action set, parameter ranges, purpose, audience, time window and acceptable state. The symbol ⊑ means “is no wider than”. Narrowing the amount from £100 to £75, the resource from a portfolio to one account or the lifetime from ten minutes to one request preserves the relation. Adding another account, purpose or mutation breaks it. The relation is partial because two capabilities can be incomparable: one may allow a larger amount for a shorter time.

The authority kernel should check attenuation mechanically whenever work passes to a sub-agent, tool broker or executor. It should also bind the parent decision identifier into the child receipt. Delegation is safe only when every hand-off can preserve provenance while reducing, never inventing, authority. In the running case, the employee’s portfolio entitlement narrows to one fee event, one £75 reversal, one reason code, one account state and one execution audience.

Keep the trusted base smaller than the agent runtime

The trusted base should contain the proposal canonicaliser, principal resolver, policy evaluator, state adapter, capability issuer and receipt linkage needed to decide one action. The model, prompt, retrieved documents, episodic memory, planner, general orchestration graph and user-interface prose remain outside it. They may supply proposals and evidence references. They cannot decide which identity is authenticated, which policy applies or whether the decision was allowed.

This boundary is operationally important. Agent runtimes change frequently, accept untrusted content and contain recovery logic that is difficult to reason about exhaustively. A small action vocabulary can be tested differently. For each verb, the kernel can close the schema, enumerate reason codes, define principal sources, name state preconditions and generate adversarial combinations. The decision core can then be released independently from prompt or model changes.

Policy selection must also be deterministic. The model may propose reverse_fee; a registry maps that verb to the applicable policy namespace and resource adapter. The model must not choose “legacy policy”, omit a disfavoured check or route the same mutation through a more permissive tool. Unknown actions, unresolved principals, missing state, unsupported evidence and policy-version mismatches should produce named denials rather than fallback behaviour.

Small does not mean centralised into one network service. Complete mediation can be realised by a shared library, signed capability, sidecar or resource-local policy check. The decisive property is that every material mutation path invokes equivalent enforcement. Batch jobs, administrator tools, browser automation and repair scripts belong in the inventory. Any exceptional path should carry separate identity, narrow operational purpose and evidence, because an undocumented bypass silently expands the trusted base to the whole estate.

Part IIIAuthority expires when state changes

An action can be authorised correctly and still become unsafe before execution. A payment beneficiary changes. A customer withdraws consent. A legal hold appears. A fee is already reversed by another process. An employee loses portfolio entitlement. A policy threshold changes. The original decision may remain historically correct while its capability is no longer applicable.

This is why the kernel must distinguish policy time, decision time, execution time and outcome-observation time. A short token lifetime helps, but duration is only a proxy for freshness. A material action should bind to a state version or digest covering the facts that justified the decision, then recheck the resource’s local invariants immediately before the effect.

Thought experiment two: change only the state version

Hold the model proposal, user identity, agent version, policy and approval constant. In world A, the account is still open, the fee is unreversed and the case remains in the complaint-remediation state reviewed by the approver. In world B, another process has already reversed the fee and advanced the account version.

A capability that says only “may reverse up to £75” succeeds in both worlds and duplicates the effect. A capability bound to state_version = 12 succeeds in world A and fails in world B, where the resource has version 13. The causal feature is not shorter reasoning latency; it is whether authority is conditional on the state that made it permissible.

State-bound approval over time A horizontal timeline shows proposal at state version 12, approval bound to its digest, then a state change to version 13 before execution. A capability with no state binding executes incorrectly; a state-bound capability is denied and returns for decision. The same approval across two worlds proposal state v12 approval digest P7 world changes state v13 execution Broad capability “reverse up to £75” still executes after the reviewed state no longer exists State-bound capability proposal digest P7 + state v12 + policy v7 resource sees v13, denies execution, and requests a new decision
Figure 5. Freshness is a predicate, not a timer. The timeline is illustrative. Binding to a version or digest does not make state immutable; it makes changed state visible before authority is spent.

After allow: execution is another control boundary

A central policy decision should not be the last check. The resource owner knows facts the central kernel may not: whether the fee event has already been reversed, whether the account is frozen, whether a transaction has settled or whether a document record is under legal hold. The executor must validate the capability, audience, proposal digest, expiry and local preconditions immediately before mutation.

This split creates an authority control plane and an execution data plane. The control plane owns action types, principal resolution, policy, approval rules, capability issuance and revocation. The data plane owns the authoritative record, resource-local veto, idempotency and effect evidence. Centralise the grammar of authority; keep the final veto beside the state that can contradict it.

The unknown outcome

Suppose the fee service applies the reversal and then the network connection fails before the agent receives the response. The client now knows that a request was sent, not whether the effect occurred. Retrying blindly can create a duplicate. Treating the timeout as failure can leave the workflow and ledger inconsistent.

HTTP distinguishes idempotent methods because they can be retried after a communication failure without changing the intended effect beyond the first application. Many business mutations use POST, so the service must create equivalent semantics with an idempotency key and a durable result record. AWS’s implementation guidance makes the same point: retry safety shifts complexity into the service, which must recognise repeated requests and distinguish them from genuinely new intent.

The action protocol therefore needs an explicit UNKNOWN state. On a timeout, the workflow must not infer success or failure. It queries the authoritative resource by operation or idempotency key, obtains the stored effect receipt, and only then decides whether to complete, retry, compensate or escalate. A transport acknowledgement is not a verified business outcome.

Receipts must form one evidence chain

Recovery works only when the three records can be joined without inference. The proposal digest identifies exactly what was requested. The decision receipt binds that digest to trusted principals, policy and state. Its identifier and the client intent key travel to the resource. The effect receipt returns the same intent key, the resource operation identifier and the observed before-and-after state. Readback uses those identifiers rather than asking the model to recognise a similar transaction.

The chain should be append-only at the event level. A later observation may supersede an earlier UNKNOWN status, but it should not erase the fact that uncertainty existed or invent a success timestamp. Status words also need resource-specific semantics. ACCEPTED, QUEUED, APPLIED, SETTLED and VERIFIED describe different boundaries. The resource owner, not the agent, defines which evidence is sufficient for each transition.

This ledger is deliberately smaller than a reasoning transcript. It records inputs, versions, decisions, effects and recovery events required to contest or replay the action. Sensitive source content can remain behind stable references and access controls. Operating evidence should reconstruct authority and outcome without treating private chain-of-thought as either necessary or trustworthy evidence.

Unknown outcome recovery state machine A state machine moves from proposed to authorised to submitted. From submitted, direct responses lead to applied or denied. A lost response leads to unknown, then readback resolves to applied, not applied, or still unknown and escalation. Unknown is a first-class execution state proposed authorised submitted applied denied unknown readback verified not applied escalate response lost after possible commit query by operation key no authoritative answer within the recovery window
Figure 6. The ambiguous-commit path. This temporal model is operational, not measured. The important branch is from submitted to unknown. Systems that omit it usually convert uncertainty into either duplicates or silent incompleteness.

Part IVA production-shaped worked case

Consider a synthetic commercial-banking assistant that prepares customer fee reversals. The scenario is designed to expose the mechanism; it is not a reported deployment. The authenticated employee is employee:RM-247. The attested workload is agent://rm-assist/refund-proposer@3.2.1. The employee may act only for a signed portfolio and only for the purpose complaint_remediation.

The model reads a complaint, a fee ledger entry and the current policy summary. It proposes reverse_fee on account:ACCT-100 for £75 using reason code SERVICE_FAILURE. It cites the complaint and fee event, states that it observed account version 12 and supplies a stable idempotency key tied to the case and fee.

The proposal does not contain the employee identity or entitlement. The host derives both from the authenticated session. It does not contain the agent role. The platform derives that from workload attestation and deployment metadata. It does not contain an “approved” flag. Under the illustrative policy, reversals up to £100 are permitted without a second person when the fee exists, the reason is controlled, the employee is entitled and the account is not restricted.

Worked fee-reversal trace through proposal, decision and effect A braided trace shows model proposal fields in indigo, trusted actor and subject fields in teal, policy and state checks in black, a decision receipt in sky, and the final effect receipt after an unknown network response. One fee reversal, end to end proposal P7 ACCT-100 · £75 · FEE-9 · state v12 actor refund-proposer@3.2.1 subject RM-247 · entitled to ACCT-100 policy + state purpose valid · fee unreversed · v12 current ALLOW policy v7 digest P7 response UNKNOWN effect readback by idempotency key APPLIED · v12 → v13 · reversal_count 0 → 1 subsequent retry replays the same receipt
Figure 7. The worked trace. All identifiers and values are synthetic. The figure shows the exact separation implemented by the executable artefact: the model proposes, the kernel decides, the resource applies, and readback verifies.

The kernel validates the proposal and computes its canonical digest. It intersects RM-247 with the trusted account entitlement, confirms the deployed agent version is allowed to propose this action, checks purpose, verifies fee FEE-9 exists for £75 and is unreversed, and confirms the account remains at version 12. It issues a capability that expires in two minutes and is bound to the proposal digest, account, amount, reason, actor, subject, purpose, policy version, state version and idempotency key.

The fee service validates the capability and repeats the local checks. It applies the reversal, increments the account state to version 13 and stores an effect receipt under the idempotency key. The network response is then lost. The agent reports UNKNOWN, not failure. Reconciliation queries the fee service, obtains the effect receipt and confirms that the reversal occurred once. A retry returns the stored receipt rather than applying a second reversal.

Now vary the case. If the model adds subject_id: employee:ADMIN, structural validation fails because identity is not a model-controlled field. If it targets ACCT-200, policy denies the proposal because that account is outside the employee’s trusted entitlement. If account version 13 appears after capability issuance, resource-local enforcement denies execution and requires a new decision. These negative paths show where authority actually lives. They matter more than a happy path that proves only that well-formed input can succeed.

Practitioner synthesis A useful release artefact is one complete action chain that proves: the workload was attested; the represented subject was entitled; the proposal was immutable; any approver saw that proposal; policy and state were current; the resource applied its local veto; retry did not duplicate the effect; and readback produced a verified outcome. A diagram of IAM roles cannot prove those facts.

Choose the control depth by consequence, authority and observability

Not every model output needs a full capability protocol. Excess control creates queues and tempts teams to bypass the system. The control depth should follow the effect, not the apparent sophistication of the model. Assess consequence, reversibility, delegated authority, sensitivity to live state, duplicate risk and the resource’s ability to expose an outcome.

Decision surface for authority-kernel control depth A two-axis field plots consequence vertically and reversibility horizontally. Low-consequence reversible actions need schema and ordinary authorization. Material or hard-to-reverse actions move through policy mediation to state-bound capability, approval and readback. Control depth follows the effect material low easy to reverse hard to reverse business consequence reversibility of harm, not only technical rollback public read or private draft external message or case transition temporary restriction with readback funds, closure, legal disposition schema + ordinary auth typed proposal + policy state-bound capability approval + one-use authority + readback
Figure 8. The action-control decision surface. The positions are illustrative. Add one level of control when the action uses delegated human authority, exposes private data, depends on rapidly changing state, can duplicate under retry or lacks reliable outcome observation.
QuestionIf noIf yes
Can the operation change external state or disclose non-public data?Use schema validation, ordinary service authorisation and rate limits.Require a typed proposal mediated outside the model.
Does the agent act for a person, customer, business role or another agent?Workload identity may be sufficient for low-risk own-authority reads.Bind subject, actor and purpose separately; never accept identity from model text.
Could live state invalidate a previously correct decision?Normal token expiry may be sufficient.Bind a state version or digest and recheck beside the resource.
Could a retry duplicate a business effect?Record the call normally.Require a client intent key, durable fingerprint and result replay.
Would harm remain after technical rollback?Automated action may be acceptable at validated thresholds.Add exact preview, approval, narrow execution window and possibly dual control.
Can the resource provide authoritative readback?Do not claim a verified outcome; retain human or compensating control.Emit an effect receipt and reconcile unknown responses.

Test the boundary, not only the policy syntax

A release gate should include user substitution, agent-version substitution, scope inflation, cross-resource use, stale entitlement, changed state, missing approval, changed proposal after approval, capability expiry, key reuse with different parameters, response loss after commit and a bypass attempt against every mutation endpoint. Each test needs a named denial or recovery result.

Cedar’s verification-guided development is instructive here. Its authors combine a formal model, mechanised proofs, differential random testing and property-based testing, and report that these methods found subtle defects in the validator and implementation. An application kernel rarely warrants the same formal investment on day one, but it should inherit the principle: keep the decision core small, specify invariants, generate hostile requests and compare implementations against a clear model.

How the claim changes under pressureChart · state
stateDiagram-v2
  [*] --> Claim
  state "Thought experiment one: change<br/>only ambient authority" as Claim
  state "The strongest objection:<br/>existing IAM already does this" as Test
  state "The proposal contract" as Revision
  Claim --> Test: expose mechanism
  Test --> Decision: evidence survives
  Test --> Revision: rival survives
  Revision --> Claim: narrow or rebuild
  Decision --> [*]
The revision loop begins with Thought experiment one, change only… and keeps counterexamples inside the reasoning path. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Failure boundary and negative control

The authority kernel solves a narrower problem than “safe agents”. It prevents a model from turning an untrusted proposal into authority outside explicitly encoded limits. It can also make stale decisions, duplicate effects and ambiguous outcomes visible. It does not establish that the model’s recommendation is accurate, fair, useful or lawful.

Bad policy remains executable policy. If the institution permits an unfair action, encodes the wrong threshold or grants an employee an excessive portfolio, a perfectly implemented kernel may faithfully allow harm. The kernel needs policy governance, representative evaluation, human contestability and incident response around it. It is an enforcement mechanism, not a source of values.

It also fails when the mediation claim is false. A second API, batch job, administrator path or browser automation route that can mutate the same resource without the kernel defeats complete mediation. Computer-use agents are especially difficult because clicks and pixels often sit above the real effect boundary. The safer interception point is the underlying transaction or HTTP request, where the action can again be typed and checked.

Finally, the kernel cannot verify an outcome the resource cannot expose. A “send email” endpoint may acknowledge queueing while final delivery remains unknown. A payment gateway may accept a request while settlement occurs later. The effect receipt must state the observed boundary honestly: accepted, queued, applied, settled or unresolved. Calling all of them success creates false evidence.

Coverage and failure boundary of the authority kernel A central teal contour covers typed proposals, trusted principals, policy, state binding, idempotency and effect evidence. Outside the contour are model truthfulness, policy justice, bypass paths, unavailable readback and downstream human consequences. What the kernel can and cannot establish inside the enforceable boundary typed proposal shape trusted actor, subject, purpose policy allow or deny state and expiry binding idempotent execution decision and effect receipts denied paths, revocation and readback model recommendation quality justice of policy unmediated bypass path unobservable effect human and social consequences after rollback
Figure 9. The explicit failure boundary. This boundary is conceptual. The kernel can make claims only about mediated operations and observed state. It should publish no broader claim about agent safety or business correctness.

A negative control: the public weather lookup

Take a tool that reads a public weather forecast, exposes no private data, changes no state and has no delegated human authority. A schema, ordinary service authentication, egress control and rate limit may be enough. Adding proposal digests, one-use capabilities and effect receipts would increase latency and complexity without changing the material risk.

This negative control matters because it prevents the authority kernel from becoming ritual architecture. The mechanism earns its cost only where a model-selected operation can spend authority, reveal protected information or create a consequential state transition. Pure computation, public reads and private drafts can often remain outside the full protocol.

Protocols and frameworks do not remove the boundary

Tool protocols can describe schemas and transport calls. Current MCP specifications also define authorisation flows and warn that tools represent arbitrary code execution, while acknowledging that the protocol itself cannot enforce all security principles. This is the correct boundary. MCP, A2A, a graph orchestrator or a cloud agent runtime can carry the action protocol, but the application still owns business identity, purpose, policy, preconditions, idempotency and outcome verification.

The same rule applies to model-level prompt-injection defences. Training an instruction hierarchy or using another model to screen tool results may reduce attacks. It cannot create deterministic authority. Architecture-level defences such as CaMeL are compelling precisely because they assume the underlying model may remain susceptible and place enforceable control around it.

Executable artefact: a small authority kernel

The following Python programme uses only the standard library and synthetic state. It demonstrates the exact claim. The model-controlled proposal cannot supply identity. Trusted context carries the actor, subject, purpose and account entitlement. The kernel validates, authorises and signs a narrow capability. The resource rechecks state, applies one mutation, records an effect receipt and safely reconciles a lost response.

The positive test commits a £75 reversal, deliberately loses the response, reads the effect back and proves that a retry does not duplicate it. Negative tests reject a model-supplied identity, a cross-account action and execution after state drift. The HMAC key is intentionally synthetic and is not a production key-management pattern.

authority_kernel_demo.pyPython 3.11+ · standard library only
"""Synthetic authority-kernel demonstration for Paper 117.

No external packages, private endpoints or real credentials are used.
Expected result: one authorised fee reversal is applied exactly once; attempts to
supply identity, cross account scope, or execute against changed state are denied.
"""

from __future__ import annotations

from dataclasses import dataclass, asdict
from datetime import datetime, timedelta, timezone
from hashlib import sha256
import hmac
import json
from typing import Any
from uuid import uuid4


POLICY_VERSION = "refund-policy/7"
KERNEL_KEY = b"synthetic-demo-key-not-for-production"
ALLOWED_REASONS = {"SERVICE_FAILURE", "DUPLICATE_FEE"}


class ContractError(ValueError):
    """The model proposal did not match the typed action contract."""


@dataclass(frozen=True)
class TrustedContext:
    actor_id: str
    actor_version: str
    subject_id: str
    purpose: str
    entitled_accounts: frozenset[str]


@dataclass(frozen=True)
class Capability:
    payload: dict[str, Any]
    signature: str


@dataclass(frozen=True)
class DecisionReceipt:
    decision_id: str
    decision: str
    reason: str
    proposal_digest: str
    policy_version: str
    capability: Capability | None = None


@dataclass(frozen=True)
class EffectReceipt:
    operation_id: str
    status: str
    decision_id: str
    proposal_digest: str
    resource: str
    before: dict[str, Any]
    after: dict[str, Any]
    executed_at: str
    replayed: bool = False


class SyntheticLedger:
    """Authoritative state plus an idempotency/effect ledger."""

    def __init__(self) -> None:
        self.accounts: dict[str, dict[str, Any]] = {
            "account:ACCT-100": {
                "state_version": 12,
                "fees": {
                    "FEE-9": {
                        "amount_gbp": 75,
                        "reversed": False,
                        "reversal_count": 0,
                    }
                },
            },
            "account:ACCT-200": {
                "state_version": 4,
                "fees": {
                    "FEE-3": {
                        "amount_gbp": 40,
                        "reversed": False,
                        "reversal_count": 0,
                    }
                },
            },
        }
        self.effects_by_idempotency_key: dict[str, EffectReceipt] = {}
        self.fingerprint_by_idempotency_key: dict[str, str] = {}


TOP_LEVEL_FIELDS = {
    "action",
    "resource",
    "parameters",
    "evidence_refs",
    "expected_state_version",
    "idempotency_key",
}
PARAMETER_FIELDS = {"fee_event_id", "amount_gbp", "reason_code"}


def canonical_json(value: Any) -> str:
    return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False)


def digest(value: Any) -> str:
    return sha256(canonical_json(value).encode("utf-8")).hexdigest()


def validate_proposal(raw: dict[str, Any]) -> dict[str, Any]:
    """Structural validation. It proves shape, not permission."""

    unknown = set(raw) - TOP_LEVEL_FIELDS
    missing = TOP_LEVEL_FIELDS - set(raw)
    if unknown:
        raise ContractError(f"unknown model-controlled fields: {sorted(unknown)}")
    if missing:
        raise ContractError(f"missing required fields: {sorted(missing)}")
    if raw["action"] != "reverse_fee":
        raise ContractError("unsupported action")
    if not isinstance(raw["resource"], str) or not raw["resource"].startswith("account:"):
        raise ContractError("resource must be an account identifier")

    params = raw["parameters"]
    if not isinstance(params, dict) or set(params) != PARAMETER_FIELDS:
        raise ContractError("parameters must contain fee_event_id, amount_gbp and reason_code only")
    if not isinstance(params["amount_gbp"], int) or not 0 < params["amount_gbp"] <= 500:
        raise ContractError("amount_gbp must be an integer from 1 to 500")
    if params["reason_code"] not in ALLOWED_REASONS:
        raise ContractError("reason_code is not in the controlled vocabulary")
    if not isinstance(raw["evidence_refs"], list) or len(raw["evidence_refs"]) < 2:
        raise ContractError("at least two evidence references are required")
    if not isinstance(raw["expected_state_version"], int):
        raise ContractError("expected_state_version must be an integer")
    if not isinstance(raw["idempotency_key"], str) or len(raw["idempotency_key"]) < 8:
        raise ContractError("idempotency_key is too short")
    return raw


def sign_payload(payload: dict[str, Any]) -> str:
    return hmac.new(KERNEL_KEY, canonical_json(payload).encode("utf-8"), sha256).hexdigest()


def authorise(
    raw_proposal: dict[str, Any],
    trusted: TrustedContext,
    ledger: SyntheticLedger,
    now: datetime,
) -> DecisionReceipt:
    """Deterministically derive authority from trusted context, policy and state."""

    try:
        proposal = validate_proposal(raw_proposal)
    except ContractError as exc:
        return DecisionReceipt(
            decision_id=str(uuid4()),
            decision="DENY",
            reason=f"CONTRACT_INVALID: {exc}",
            proposal_digest=digest(raw_proposal),
            policy_version=POLICY_VERSION,
        )

    proposal_digest = digest(proposal)
    resource = proposal["resource"]
    decision_id = str(uuid4())

    if trusted.actor_version != "3.2.1":
        reason = "AGENT_VERSION_DENIED"
    elif trusted.purpose != "complaint_remediation":
        reason = "PURPOSE_DENIED"
    elif resource.removeprefix("account:") not in trusted.entitled_accounts:
        reason = "SUBJECT_NOT_ENTITLED"
    elif resource not in ledger.accounts:
        reason = "RESOURCE_NOT_FOUND"
    else:
        account = ledger.accounts[resource]
        params = proposal["parameters"]
        fee = account["fees"].get(params["fee_event_id"])
        if account["state_version"] != proposal["expected_state_version"]:
            reason = "CASE_STATE_CHANGED"
        elif fee is None:
            reason = "FEE_NOT_FOUND"
        elif fee["reversed"]:
            reason = "ALREADY_REVERSED"
        elif fee["amount_gbp"] != params["amount_gbp"]:
            reason = "AMOUNT_MISMATCH"
        else:
            reason = "ALLOW"

    if reason != "ALLOW":
        return DecisionReceipt(
            decision_id=decision_id,
            decision="DENY",
            reason=reason,
            proposal_digest=proposal_digest,
            policy_version=POLICY_VERSION,
        )

    capability_payload = {
        "decision_id": decision_id,
        "proposal_digest": proposal_digest,
        "actor_id": trusted.actor_id,
        "actor_version": trusted.actor_version,
        "subject_id": trusted.subject_id,
        "purpose": trusted.purpose,
        "action": proposal["action"],
        "resource": resource,
        "parameters": proposal["parameters"],
        "state_version": proposal["expected_state_version"],
        "policy_version": POLICY_VERSION,
        "idempotency_key": proposal["idempotency_key"],
        "expires_at": (now + timedelta(minutes=2)).isoformat(),
    }
    capability = Capability(capability_payload, sign_payload(capability_payload))
    return DecisionReceipt(
        decision_id=decision_id,
        decision="ALLOW",
        reason="POLICY_SATISFIED",
        proposal_digest=proposal_digest,
        policy_version=POLICY_VERSION,
        capability=capability,
    )


def execute(
    decision: DecisionReceipt,
    ledger: SyntheticLedger,
    now: datetime,
    *,
    lose_response_after_commit: bool = False,
) -> EffectReceipt | dict[str, str]:
    """Resource-local enforcement, idempotent mutation and effect evidence."""

    if decision.decision != "ALLOW" or decision.capability is None:
        return {"status": "DENIED", "reason": "NO_EXECUTION_CAPABILITY"}

    cap = decision.capability
    if not hmac.compare_digest(cap.signature, sign_payload(cap.payload)):
        return {"status": "DENIED", "reason": "CAPABILITY_SIGNATURE_INVALID"}
    if datetime.fromisoformat(cap.payload["expires_at"]) < now:
        return {"status": "DENIED", "reason": "CAPABILITY_EXPIRED"}

    key = cap.payload["idempotency_key"]
    fingerprint = cap.payload["proposal_digest"]
    prior = ledger.effects_by_idempotency_key.get(key)
    if prior is not None:
        if ledger.fingerprint_by_idempotency_key[key] != fingerprint:
            return {"status": "DENIED", "reason": "IDEMPOTENCY_KEY_REUSE_MISMATCH"}
        return EffectReceipt(**{**asdict(prior), "replayed": True})

    resource = cap.payload["resource"]
    account = ledger.accounts[resource]
    if account["state_version"] != cap.payload["state_version"]:
        return {"status": "DENIED", "reason": "CASE_STATE_CHANGED"}

    params = cap.payload["parameters"]
    fee = account["fees"][params["fee_event_id"]]
    if fee["reversed"]:
        return {"status": "DENIED", "reason": "ALREADY_REVERSED"}

    before = {
        "state_version": account["state_version"],
        "reversed": fee["reversed"],
        "reversal_count": fee["reversal_count"],
    }
    fee["reversed"] = True
    fee["reversal_count"] += 1
    account["state_version"] += 1
    after = {
        "state_version": account["state_version"],
        "reversed": fee["reversed"],
        "reversal_count": fee["reversal_count"],
    }
    receipt = EffectReceipt(
        operation_id=str(uuid4()),
        status="APPLIED",
        decision_id=cap.payload["decision_id"],
        proposal_digest=fingerprint,
        resource=resource,
        before=before,
        after=after,
        executed_at=now.isoformat(),
    )
    ledger.effects_by_idempotency_key[key] = receipt
    ledger.fingerprint_by_idempotency_key[key] = fingerprint

    if lose_response_after_commit:
        return {"status": "UNKNOWN", "reason": "RESPONSE_LOST_AFTER_COMMIT"}
    return receipt


def readback(idempotency_key: str, ledger: SyntheticLedger) -> EffectReceipt | None:
    """Independent reconciliation after a timeout or lost response."""

    return ledger.effects_by_idempotency_key.get(idempotency_key)


def base_proposal() -> dict[str, Any]:
    return {
        "action": "reverse_fee",
        "resource": "account:ACCT-100",
        "parameters": {
            "fee_event_id": "FEE-9",
            "amount_gbp": 75,
            "reason_code": "SERVICE_FAILURE",
        },
        "evidence_refs": ["case:CASE-7", "ledger:FEE-9"],
        "expected_state_version": 12,
        "idempotency_key": "CASE-7:FEE-9:REVERSE",
    }


def run_demo() -> None:
    now = datetime(2030, 1, 15, 12, 0, tzinfo=timezone.utc)
    trusted = TrustedContext(
        actor_id="agent://rm-assist/refund-proposer",
        actor_version="3.2.1",
        subject_id="employee:RM-247",
        purpose="complaint_remediation",
        entitled_accounts=frozenset({"ACCT-100"}),
    )

    # Positive case: authorised action commits, but the network response is lost.
    ledger = SyntheticLedger()
    proposal = base_proposal()
    decision = authorise(proposal, trusted, ledger, now)
    assert decision.decision == "ALLOW"
    first_attempt = execute(decision, ledger, now, lose_response_after_commit=True)
    assert first_attempt["status"] == "UNKNOWN"
    observed = readback(proposal["idempotency_key"], ledger)
    assert observed is not None and observed.status == "APPLIED"

    # Safe retry: the stored effect is replayed and the fee is not reversed twice.
    retry = execute(decision, ledger, now + timedelta(seconds=5))
    assert isinstance(retry, EffectReceipt) and retry.replayed
    assert ledger.accounts["account:ACCT-100"]["fees"]["FEE-9"]["reversal_count"] == 1

    # Negative case 1: the model tries to supply trusted identity in its payload.
    injected_identity = {**base_proposal(), "subject_id": "employee:ADMIN"}
    identity_decision = authorise(injected_identity, trusted, SyntheticLedger(), now)
    assert identity_decision.decision == "DENY"
    assert identity_decision.reason.startswith("CONTRACT_INVALID")

    # Negative case 2: a validly shaped proposal targets an account outside entitlement.
    cross_account = base_proposal()
    cross_account["resource"] = "account:ACCT-200"
    cross_account["parameters"] = {
        "fee_event_id": "FEE-3",
        "amount_gbp": 40,
        "reason_code": "SERVICE_FAILURE",
    }
    cross_account["expected_state_version"] = 4
    cross_account["idempotency_key"] = "CASE-7:FEE-3:REVERSE"
    cross_decision = authorise(cross_account, trusted, SyntheticLedger(), now)
    assert cross_decision.decision == "DENY"
    assert cross_decision.reason == "SUBJECT_NOT_ENTITLED"

    # Negative case 3: state changes after capability issuance and before execution.
    drift_ledger = SyntheticLedger()
    drift_decision = authorise(base_proposal(), trusted, drift_ledger, now)
    drift_ledger.accounts["account:ACCT-100"]["state_version"] += 1
    drift_result = execute(drift_decision, drift_ledger, now + timedelta(seconds=10))
    assert drift_result == {"status": "DENIED", "reason": "CASE_STATE_CHANGED"}

    print("positive decision:", decision.decision)
    print("lost response:", first_attempt)
    print("readback:", observed.status, observed.after)
    print("safe retry replayed:", retry.replayed)
    print("identity injection:", identity_decision.reason)
    print("cross-account attempt:", cross_decision.reason)
    print("state-drift execution:", drift_result["reason"])


if __name__ == "__main__":
    run_demo()
Expected output and the invariants it proves
positive decision: ALLOW
lost response: {'status': 'UNKNOWN', 'reason': 'RESPONSE_LOST_AFTER_COMMIT'}
readback: APPLIED {'state_version': 13, 'reversed': True, 'reversal_count': 1}
safe retry replayed: True
identity injection: CONTRACT_INVALID: unknown model-controlled fields: ['subject_id']
cross-account attempt: SUBJECT_NOT_ENTITLED
state-drift execution: CASE_STATE_CHANGED

The programme proves only its own synthetic invariants. It does not prove the business policy correct, the cryptographic construction suitable for production, the executor non-bypassable or the state store durable under arbitrary failure. Those become implementation and assurance work.

Sources and evidence boundary

Primary research

  1. Jerome H. Saltzer and Michael D. Schroeder, The Protection of Information in Computer Systems. Foundational protection principles and capability versus access-control architectures.
  2. Norm Hardy, The Confused Deputy. The canonical account of a privileged deputy misusing ambient authority.
  3. Arnar Birgisson and colleagues, Macaroons: Cookies with Contextual Caveats for Decentralized Authorization in the Cloud. Delegable, attenuated credentials with contextual caveats.
  4. Joseph W. Cutler and colleagues, Cedar: A New Language for Expressive, Fast, Safe, and Analyzable Authorization, plus Craig Disselkoen and colleagues, How We Built Cedar: A Verification-Guided Approach.
  5. Edoardo Debenedetti and colleagues, AgentDojo, and Defeating Prompt Injections by Design. Benchmark and architecture-level capability defence for tool-using agents.

Standards and official specifications

  1. NIST, reference monitor definition and SP 800-207A for granular application and service identity policy.
  2. IETF, RFC 8693: OAuth 2.0 Token Exchange and RFC 9396: OAuth 2.0 Rich Authorization Requests.
  3. SPIFFE, Workload API specification for workload identities and verifiable identity documents.
  4. JSON Schema, Draft 2020-12, used here only for structural-validation semantics.
  5. IETF, RFC 9110, idempotent methods, and the MCP tools specification with its security guidance.

Practitioner source

  1. Amazon Builders’ Library, Making retries safe with idempotent APIs. Used for the implementation pattern around client intent, duplicate recognition and result replay, not as evidence for a general scientific claim.
The evidence conversationChart · sequence
sequenceDiagram
  participant Q as Question
  participant M as Mechanism
  participant E as Evidence
  participant D as Decision
  Q->>M: The refund that was valid in every field
  M->>E: Thought experiment one: change only ambient authority
  E-->>M: compare rival explanations
  E->>D: Move autonomy into proposals, not permissions
  D-->>Q: revise scope or act
Question, mechanism, evidence and decision exchange the burdens developed in Typed Actions and the Deterministic Authority Kernel. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Move autonomy into proposals, not permissions

An agent becomes dangerous when its ability to form a plausible sentence is allowed to spend ambient authority. The durable design move is simple to state and demanding to implement: keep the model free to propose, while making permission deterministic, narrow, current and independently evidenced.

The typed proposal preserves model usefulness. The authority kernel derives principals and purpose from trusted channels, evaluates versioned policy and live state, and issues a capability bound to one immutable action. Resource-local enforcement keeps the final veto beside authoritative state. Decision and effect receipts preserve what was allowed and what actually happened. Idempotency and readback keep a lost response from becoming either a duplicate or an invisible omission.

This changes the architecture review. Do not approve an agent because its tool list looks narrow or its prompt says it must ask before acting. Select one material operation and demonstrate a denied identity substitution, a denied cross-resource proposal, a denied stale-state capability, an exact approval binding, a response lost after commit, a safe retry and a verified readback. If any effect path bypasses that chain, the authority boundary is still inside the model.

The decision is therefore not whether to trust the model. It is where trust is allowed to become power. Place that transition in the smallest mechanism the system can make non-bypassable, inspectable and testable. Everything before it is a proposal. Everything after it must leave evidence.