Skip to main contentPASS
Read the docs

Execution

Preflight

How a proposed execution is normalized, evaluated against policy, and resolved into a decision before capital moves.

A proposed execution arrives as a call and its context. It is decomposed into something policy can reason about, measured against the mandate that governs the actor, and leaves as a decision: AUTHORIZED, or BLOCKED with a Reason Code. Nothing in the proposal is assumed to be true on arrival. The evaluation first establishes what a call actually does, and only then establishes whether the call is permitted.

The evaluation is deterministic. The same Execution Intent, the same mandate state and the same accounting state produce the same outcome and the same Reason Code. That property is not a convenience. It is what makes a decision reviewable months later by someone who was not present when it was made. An authorization system whose answer depends on unspecified ordering, or on state it did not record, can report what it did but cannot explain why.

Where the check belongs

Autonomous actors can increasingly form, price and submit financial instructions without a person in the loop. The layer that produces an instruction is the wrong place to enforce constraints on it. A model that consults its own limits before acting is producing a statement about its intentions. The statement may be entirely accurate. It is still not a control, because the component that generates the check and the component that generates the action are the same component, and they change together. Prompts are edited, tool definitions are extended, and one model is swapped for another between releases. A constraint that lives inside the thing being constrained inherits every change made to it.

Enforcement therefore belongs at the last point where the action can still be withheld: after the instruction is fully formed, and before it is submitted for settlement. Earlier than that, there is nothing definite to evaluate — asset, venue and value may still change as the actor revises its plan. Later, there is nothing left to decide. A settled transaction can sometimes be compensated, but it cannot be withdrawn. Preflight consumes a concrete call rather than a stated intention for exactly this reason.

A preview is not an enforcing check

Simulation answers a question about outcome: given current state, what would this call do, and would it revert. That question is worth answering, and simulation tooling answers it well. It is a different question from whether the actor was permitted to make the call at all. An intent can be entirely valid, fully funded and certain to succeed, and still be one the actor has no authority to submit.

The structural difference is where the answer sits relative to the execution path. A preview is a prediction. An enforcing check is a precondition. If execution can proceed while the preview is ignored, unavailable or stale, then the preview is advice, and the authority claim rests on the caller choosing to honour it.

Two properties separate the two in practice. First, the evaluation must be produced by the component that is able to withhold the execution, not by a component that reports to it. Second, it must be evaluated against the state under which the execution is admitted, not against state read at some earlier moment. Otherwise a gap opens between check and use: a credential is revoked in between, or a concurrent intent consumes the remaining daily budget, and the execution proceeds on an answer that was true when it was given and false when it was used.

PASS defines both and keeps them distinct in the interface itself. A non-committing evaluation exists as a rehearsal, so a front end can explain to a person why an action would be refused before an actor attempts it. It runs the same rules in the same order, and it carries no authority of its own: an execution admitted on the strength of a rehearsal has not been authorized.

interface ExecutionIntent {
  actor: Address;
  mandate: MandateId;
  action: Action; // BUY, SELL, TRANSFER, BRIDGE, ...
  asset: AssetId;
  value: Uint256; // in the denomination of the mandate
  venue: Address;
  adapter: AdapterId;
}
 
interface Decision {
  outcome: 'AUTHORIZED' | 'BLOCKED';
  reason: ReasonCode; // AUTHORIZED when the outcome is AUTHORIZED
  evidence?: Evidence; // failing rule, observed value, limit
}
 
interface Preflight {
  // Rehearsal. Same rules, same order, no state change.
  evaluate(intent: ExecutionIntent, at: Timestamp): Decision;
 
  // Enforcing path. Commits periodic accounting when, and
  // only when, the outcome is AUTHORIZED.
  authorize(intent: ExecutionIntent): Decision;
}

The four stages

STAGE 1  PROPOSED EXECUTION
         call + context
              |
              v
STAGE 2  NORMALIZE / VALIDATE   <- adapter derives
         Execution Intent          asset, action,
              |                    value, venue
              |
              +--> no adapter validates:
              |    INVALID_EXECUTION_CONTEXT (stop)
              v
STAGE 3  POLICY CHECK           <- ordered rules,
         against the mandate       first failure wins
              |
              v
STAGE 4  DECISION
              |
              v
AUTHORIZED    |    BLOCKED + REASON CODE

Stage one: the proposed execution

The input is a call plus its context: the acting address, the mandate it claims to act under, the target contract, the calldata, any attached value, the chain, and the evaluation timestamp. At this stage none of it has meaning to policy. A target address is an address. Calldata is bytes. The proposal is a claim about what the actor wishes to do, recorded so the decision can later be tied to a specific input.

Stage two: normalize and validate

An adapter decodes the call into an Execution Intent: which asset, which action, what value, which venue. This is the step that turns bytes into facts policy can evaluate. The adapter model exists because calldata cannot be trusted to describe itself. A function selector is a convention, and a mandate that limits exposure to AAPL has to determine for itself that a given call moves AAPL rather than accept that assertion from the caller.

If no registered adapter can decode and validate the call, evaluation stops with INVALID_EXECUTION_CONTEXT. No policy rules are consulted, because there is nothing reliable to consult them about. Failing closed here is deliberate: an undecodable call is not a call known to be harmless, it is a call whose effects are unknown, and unknown effects cannot be measured against a limit.

Stage three: policy check

The derived intent is evaluated against the governing mandate in a fixed order. Ordering is part of the specification rather than an implementation detail, because the returned Reason Code is the code of the first failing rule, and an unspecified order would make that code arbitrary.

Every rule reads from one snapshot of state: mandate status, credential status and accounting figures as of the evaluation timestamp. Facts that were never simultaneously true must not be combined into a single decision, and no rule may depend on the outcome of a rule that follows it.

Stage four: decision

The result is AUTHORIZED, or BLOCKED with exactly one Reason Code. A decision names one reason, not a list of them. When several rules would fail, the first in evaluation order is reported, so the reason given is the most fundamental one: an actor who is not bound to the mandate is told that, rather than being told about a limit that was never relevant.

Evaluation order

StepCheckReason Code on failure
0Intent derivable from the callINVALID_EXECUTION_CONTEXT
1Actor is bound to the mandateACTOR_NOT_AUTHORIZED
2Mandate is liveMANDATE_EXPIRED, MANDATE_SUSPENDED
3Required credentials present and currentMISSING_CREDENTIAL, CREDENTIAL_EXPIRED, CREDENTIAL_REVOKED
4Action is permittedACTION_NOT_ALLOWED
5Asset is in scopeASSET_NOT_ALLOWED
6Venue routes through a permitted adapterADAPTER_NOT_ALLOWED
7Value is within the transaction limitTX_LIMIT_EXCEEDED
8Value fits the remaining periodic budgetDAILY_LIMIT_EXCEEDED

Step zero belongs to stage two rather than to policy: it is the gate that produces an intent at all. The remainder moves from binding, to standing, to scope, to magnitude. Identity questions come first because they determine whether the mandate applies at all. Credential checks read the passport of the subject and precede scope checks, because a revoked credential removes the standing of the actor regardless of what was proposed. Limits come last because they are the only checks that read accounting state, and a call that fails for a structural reason should never touch that state. The policies page describes the rule categories themselves; this page describes only the order in which they are asked.

Periodic accounting

A transaction limit is stateless: the value of one intent is compared against one number. A periodic limit is not. It requires the enforcing layer to hold a running figure, and the definition of that figure has to be exact.

What counts is the value of intents that were authorized and admitted for execution, denominated in the unit of account of the mandate and measured at evaluation time. Blocked intents contribute nothing: a refused call must not consume budget that a permitted call would need. A mandate also has to state what happens when an authorized execution later fails downstream, because the two defensible answers differ in consequence. Releasing the reservation treats the budget as a measure of completed exposure. Retaining it treats the budget as a measure of authority spent. PASS requires that choice to be explicit in the mandate rather than implied by an implementation.

The window is equally explicit. A fixed calendar period resets on a boundary, so a mandate can be fully consumed one minute before that boundary and fully available one minute after. A rolling window has no such edge, but it requires per-intent history to compute. A daily exposure limit of $10,000 means something different under each, and the mandate states which one it means.

The accounting is maintained by the enforcing layer, never reported by the actor. A limit checked against a self-reported total is not a limit; it is a request that the actor keep its own score. The same reasoning covers concurrency. If two intents are evaluated against the same remaining budget before either commits, both can pass a check that only one of them should have passed. Reservation and commitment therefore happen inside the enforcement boundary, atomically with the decision.

Worked evaluation: authorized

Actor 0x81...29F acts for subject 0x3A...F02 under a mandate whose permitted assets are AAPL and NVDA, whose maximum transaction value is $2,500, whose daily exposure limit is $10,000, whose venues are approved adapters only, which requires the credential STOCK_TOKEN_ELIGIBLE, and which blocks bridging. That subject's passport 0x7F...94A carries IDENTITY_VERIFIED, NON_US_PERSON, EEA_ELIGIBLE, AML_CHECKED and STOCK_TOKEN_ELIGIBLE. The actor has used $4,200 of the current window and proposes to buy $1,800 of AAPL.

{
  "actor": "0x81...29F",
  "subject": "0x3A...F02",
  "passport": "0x7F...94A",
  "intent": {
    "action": "BUY",
    "asset": "AAPL",
    "value": 1800,
    "denomination": "USD",
    "adapter": "STOCK_TOKEN_SPOT"
  },
  "accounting": { "window": "CALENDAR_DAY", "consumed": 4200, "limit": 10000 }
}

The adapter decodes the call, so evaluation proceeds. The actor is bound to a live mandate, and STOCK_TOKEN_ELIGIBLE is present on the passport, neither expired nor revoked. BUY is permitted, AAPL is in scope, and the venue resolves to an approved adapter. The value of $1,800 is below the $2,500 transaction limit, and $4,200 plus $1,800 is $6,000, below the $10,000 daily limit.

{
  "outcome": "AUTHORIZED",
  "reason": "AUTHORIZED",
  "evidence": { "window": "CALENDAR_DAY", "consumedAfter": 6000 }
}

Worked evaluation: blocked

The same actor, with $6,000 now consumed in the window, proposes to buy $4,200 of NVDA.

{
  "outcome": "BLOCKED",
  "reason": "TX_LIMIT_EXCEEDED",
  "evidence": {
    "rule": "maxTransactionValue",
    "observed": 4200,
    "limit": 2500,
    "consumedAfter": 6000
  }
}

Steps one through six pass unchanged: the actor is bound, the mandate is live, the credential holds, BUY is permitted, NVDA is in scope, and the venue is approved. Step seven fails, because $4,200 exceeds the $2,500 maximum transaction value. Evaluation stops there. The daily limit is never consulted, and the consumed figure remains $6,000, because a refused intent does not spend budget.

Order also determines which reason is returned. Had the actor proposed $2,500 of NVDA with $8,500 already consumed, step seven would pass — the value sits exactly at the transaction limit — and step eight would fail, since $8,500 plus $2,500 is $11,000 against a $10,000 daily limit. The decision would then be BLOCKED with DAILY_LIMIT_EXCEEDED. Same asset, same actor, same mandate, a different binding constraint, and a Reason Code that names which one bound.

What a decision does not assert

AUTHORIZED means the actor was permitted to submit this intent under this mandate at this moment. It is a statement about authority and nothing more. Market, smart-contract, oracle and governance risk are real and sit outside the question preflight answers, and the security model sets out that boundary in full.