EFFECT LIFECYCLE

Effect contracts

The action leaves once, and success is what a second pinned tool observed.

Effect contracts govern the actions that change something outside the gateway — a payment, a ticket transition, a deployment, a database write. A contract pins one action tool and a different verifier tool to their exact descriptor digests, and a successful action response never completes the run on its own: the verifier is called afterwards, its observation has to be fresh and post-dispatch, and every postcondition and invariant has to match it before Token Observe records the effect as committed. Before a byte reaches the action tool, Token Observe takes a durable unique claim on the business idempotency value the request already carries, so a duplicate delivery is refused rather than dispatched twice and a lost response is reconciled by calling the verifier again rather than by replaying the action. Where the contract carries a compensation pair, rollback is attempted only when a separate set of failure conditions matches fresh evidence, and the run reads compensated only after a distinct pinned compensation verifier proves the rollback happened. The boundary is narrow and stated rather than implied: this is at-most-one dispatch from Token Observe, not distributed exactly-once execution, and the downstream system still has to honour the idempotency key it is sent.
Contract
Pinned action, pinned verifier, optional compensation pair
Language
JSON Pointers and six operators; no code, no templates
Claim
One durable row per effect name and idempotency digest
Receipt
Ed25519, verified from its bytes and a key you supply
Not exactly-onceThe downstream must enforce the idempotency key you send
On this page
the problem

A tool that returned 200 has not proved that anything happened

The inline decision point answers whether an agent may attempt an action. It does not answer whether the action happened, whether it happened once, or whether it stayed inside the bounds the person who approved it had in mind. Those are different questions and the gap between them is where the money is: a refund API can acknowledge a request before the ledger write is durable, a deployment can report accepted and roll back thirty seconds later, and a ticket transition can succeed against a stale cache. Treating the response as completion proves the transport worked and nothing else.

The dangerous case is not failure, it is ambiguity. A timeout hides two outcomes that need opposite responses — nothing happened, or everything happened and the acknowledgement was lost — and the agent framework’s default behaviour is to retry. Retrying a read is free. Retrying a payment is a second payment. Every gateway that offers a retry policy for tool calls is, on a consequential tool, offering to duplicate the consequence, and the agent making that decision is the component most susceptible to a paragraph of retrieved text telling it to try again.

The obvious fix is not available. A remote action system cannot join Token Observe’s database transaction; MCP tools expose no common transaction protocol and most systems of record could not enlist in one if they did. Anything that promises exactly-once across that boundary is promising something it does not control. What can be built is a durable local claim taken before egress, a second tool that reads the world afterwards and a state machine in which unknown is a state you can see rather than an error somebody swallowed.

The second temptation is to make the verification step programmable — a small expression language, a template, a hook. That is flexible and it converts your governance configuration into code execution: an unbounded runtime and a second plugin platform, sitting inside the boundary that exists to bound things. The contract language here is deliberately small enough to read in one sitting and to refuse anything it does not recognise.

the mechanism

How it actually works

Step 9 of the request path, and the durable recovery after it. The order is load-bearing rather than incidental: it is encoded in the evaluator, and the reason each step sits where it does is the reason the guarantee holds.

  1. 01

    The tool is classified effect-required

    Consequential tools carry an effect-required classification on their descriptor pin, independent of any contract’s lifecycle. A tool marked that way fails closed whenever its contract is draft, retired, missing, corrupt or no longer matching the pinned descriptor — so retiring a contract blocks the action rather than quietly re-enabling raw execution of it. You cannot even create a first contract until the action tool is pinned, unquarantined and classified.
  2. 02

    A contract is drafted against exact descriptor digests

    The draft names an action tool and a different verifier tool by server id, tool name and the exact SHA-256 of the approved descriptor, plus optionally a compensation tool and — mandatory when compensation exists — a distinct compensation verifier. It names a non-root pointer to the business idempotency value, a non-root pointer to the verifier’s own observation timestamp, a freshness bound, and all five predicate sets, of which only the postconditions have to be non-empty. Unknown fields are invalid, not ignored.
  3. 03

    Activation revalidates everything, atomically

    Activation refuses unless a stable effect key is configured, every binding is present in the live tool catalogue, pinned, and matching its recorded descriptor digest, and every pinned input schema compiles. A verifier or compensation tool that is itself classified effect-required is refused, because those stages run inside the parent contract and must not be able to bypass their own. Activating a replacement atomically retires the incumbent revision for that action in the same transaction.
  4. 04

    Preconditions run, then the claim is taken

    Identity, delegation and tool visibility settle first. The active contract is then resolved, its stored digest rechecked, preconditions evaluated against the original arguments and the action arguments mapped by pointer — so policy, data controls and payload-bound approval inspect the exact payload the contract will send. The approval’s hash covers the contract id, version, digest, idempotency key digest and action-arguments digest, so an approval cannot survive the contract changing underneath it. The claim on the business key is taken last, after redaction and before the first byte leaves.
  5. 05

    The action is dispatched once

    Reservation writes the run row, an encrypted recovery envelope and a durable outbox row in one transaction, re-reading the contract’s status and digest inside the same lock so a revision retired milliseconds ago cannot win the claim. A second delivery of the same key is refused with a non-retryable conflict and the action is not sent again. A transport timeout moves the run from executing to verifying — never back to dispatch.
  6. 06

    The verifier is called and the predicates are evaluated

    The pinned verifier is invoked with arguments mapped from the original request, and its result must carry a canonical observation timestamp that is post-dispatch, within a five-second clock-skew tolerance and no older than the contract’s freshness bound. Every postcondition and every invariant must match. A mismatch or a pending answer is polled again inside a cap of eight bounded attempts, with exponential backoff from one second capped at thirty.
  7. 07

    Commit, compensate, or park it on a human

    Positive evidence is appended to the audit chain before the terminal state changes, the run commits, and a portable receipt is issued where a signing key exists. Automatic compensation requires a separate, non-empty set of failure conditions to match fresh evidence; anything else is manual review. The agent gets the action’s response only if the effect committed — otherwise it gets a refusal telling it explicitly not to retry.

What a contract can say, and what it cannot

A contract is one immutable, integer-versioned document. It binds two mandatory roles and an optional pair: a descriptor-pinned action, a different descriptor-pinned verifier whose observation supplies the postconditions, invariants and terminal-failure conditions, and — together or not at all — a compensation action and a distinct compensation verifier. Values move between stages only by RFC 6901 JSON Pointer copies, and conditions are expressed with exactly six operators: exists, equals, not_equals, greater_than, less_than and contains. There is no expression language, no template interpolation and no way to run code, because the alternative considered and rejected was a second sandbox platform inside the governance boundary.

Pointer resolution is deliberately unforgiving, and each refusal has a reason. Only own data properties are read, so a getter cannot execute and an inherited property cannot be selected; segments spelling __proto__, prototype or constructor are refused outright; array indexes must be canonical decimal, so 0 resolves and 00 does not; and only the two escapes the standard defines are accepted, because permitting a malformed one would let two spellings select the same value and a broken contract survive review. Predicates do not coerce types either — comparing the string "3" against the number 3 is a type mismatch and fails closed rather than matching.

The bounds are stated in the code rather than left to operational luck: at most 64 predicates in each set and 64 argument mappings in each binding, pointers at most 512 characters and 32 segments, copied values at most 32 deep and 10,000 nodes, and a verification freshness window between one second and seven days. A contract that exceeds any of them is invalid at creation, not slow at runtime.

The single most consequential rule in the language is what verification is forbidden to read. No stage may consume the action’s response. Argument bindings accept the original arguments, and the compensation action alone may additionally read the verifier’s result; on the predicate side, preconditions read the original arguments and every set evaluated after dispatch — postconditions, invariants, failure conditions, compensation postconditions — reads the verifier’s result and nothing else. The reason is the failure this whole capability exists for — the action response is exactly what a timeout loses. Anything verification or compensation needs must therefore be independently observed or bound into the action arguments Token Observe already digested, and validation refuses a verifier or compensation mapping that reads an original value the action does not carry. That is what makes a governed retry’s recovery context comparable without ever storing the raw customer payload.

Editing a contract’s semantics creates version N+1; it never rewrites version N. Revisions for the same action tool must preserve the immutable effect name and advance by exactly one, and the digest that identifies a revision hashes the whole document with mapping and predicate arrays canonically sorted, because their ordering carries no meaning. Lifecycle status is deliberately excluded from that digest, so retiring a revision blocks new dispatch without destroying the digest historical runs and approvals name.

Six operators, and one trick
exists, equals, not_equals, greater_than, less_than, contains. An exists predicate with the value false is how a contract states a forbidden field or a forbidden side effect without introducing a not language.
Five predicate sets
Preconditions read the original arguments before reservation. Postconditions and invariants read the verifier and must all match. Failure conditions are separate positive proof of terminal failure. Compensation postconditions must be non-empty whenever a compensation exists.
Pinned, not named
Each stage names a server id, a tool name and the exact descriptor digest. Descriptor drift is therefore a contract mismatch, not an invitation to call whatever now holds that name.
Four distinct tools
Validation refuses a contract whose action and verifier are the same tool, and refuses a compensation or compensation verifier that targets the action, the verifier or each other.
The whole contract language, in one screen
action                  serverId + toolName + descriptorHash + mappings
verifier                a DIFFERENT pinned tool
compensation?           optional, and requires the next line
compensationVerifier?   a fourth distinct pinned tool

idempotencyKeyPointer          /request_id     non-root
verificationTimestampPointer   /observed_at    non-root
maxVerificationAgeSeconds      1 .. 604800     (seven days)
onVerificationFailure          compensate | manual_review

preconditions               over original_arguments
postconditions              over verifier_result, at least one
invariants                  over verifier_result
failureConditions           over verifier_result, >=1 to compensate
compensationPostconditions  over verifier_result, >=1 with a comp.

operators  exists  equals  not_equals
           greater_than  less_than  contains

no expressions   no templates   no code   unknown fields invalid

One claim per business effect, taken before egress

Token Observe deduplicates on a value your own system already knows. A non-root pointer names the business idempotency value inside the original arguments, and the contract is invalid unless the action, the verifier and both compensation stages all carry that same value into their own arguments — a verifier addressable by a different identity than the action it is verifying is not a verifier. The resolved value is canonicalised, namespaced by the contract’s immutable effect name and reduced to an HMAC-SHA-256 digest under a stable effect key. The raw value is never stored, so a database-only reader cannot enumerate predictable business keys from what is retained.

That digest is the claim. A unique index on the pair of effect name and key digest holds across agents, teams and contract revisions, and it is acquired after every governance decision and after redaction, but before the first byte can leave for the action tool. Redaction is checked against it in both directions: a plan that would rewrite the idempotency value the upstream will see is refused rather than dispatched with a mangled key, and that check runs once on the previewed redaction before the approval gate is enacted and again on the outbound payload. Reservation itself is a single transaction writing the run row, an AES-256-GCM-sealed recovery envelope and the outbox row, re-reading the contract’s status and digest inside the same lock so the prepare-then-retire race cannot let a superseded revision win.

The effect key is deliberately not the audit MAC key, and the reason is worth stating: rotating audit protection must never make an already-used business key executable again. The first run stamps a non-secret fingerprint of the key into the database, and every later reservation compares it inside the same locked transaction, so a restored snapshot or a replica holding the wrong key fails closed before action egress rather than making an old key look unused. A database that contains runs but no fingerprint is refused pending an explicit offline migration, because guessing which key created them is exactly the wrong instinct. There is no dual-key lookup and no online re-key ceremony, and that is a consequence of the privacy choice rather than an oversight — the raw business keys needed to perform one were never kept.

What a duplicate actually gets is a typed refusal that says the action was not executed again, marked not retryable so an agent does not treat it as a transient error. A collision — the same key presented with a different action-arguments digest or against a different contract revision — is refused separately and named as a collision, because those are different mistakes. A caller from another team is told only that an effect with this key exists: it never learns the run id or state of another team’s run, and the run API withholds the key digest from its own responses, since a deployment that ignored the key requirement would make that digest dictionary-testable.

The key is yours
A client-known identity the action system can be queried by. A correlation id that only the server generates is not a usable input here, because the verifier has to find the same effect by the same name after a lost response.
Reserved means not started
A run still in reserved proves dispatch never began, so a governed retry may resume it — but the duplicate caller never inherits the reservation’s subject or approval. The recovery worker restores the original authenticated authority instead.
Digest-only evidence
Action arguments, action result, verifier result and both compensation results are retained as SHA-256 digests. Predicate evidence records the index, whether it matched and how it failed, and deliberately never echoes the value, because verifier output can contain secrets.
Erased when it is over
The encrypted recovery envelope exists only while automatic recovery might need it and is deleted at the terminal transition. A failure to erase it is propagated rather than swallowed, so the stage is retried instead of being marked complete with the context still there.

The verifier decides, and it is a read rather than a claim

After the action returns, Token Observe calls the pinned verifier with arguments mapped from the original request and requires its result to carry a canonical timestamp at the contract’s own observation pointer. Evidence that is not a fresh post-dispatch observation is rejected: not earlier than the moment verification began, less a fixed five-second clock-skew tolerance; not more than that tolerance into the future; and not older than the contract’s freshness bound. Cached pre-action state therefore cannot commit a new run, which is the failure a naive read-after-write check walks straight into.

Success requires every postcondition and every invariant to match. A mismatching or pending observation is explicitly not terminal failure — an eventually-consistent system that has not caught up yet is a normal condition, not a rollback trigger — so the verifier is polled again inside a cap of eight bounded attempts, with exponential backoff from one second capped at thirty, and exhaustion parks the run for manual review rather than replaying a mutating stage. Only verifiers are ever polled. The action is never re-dispatched by recovery, and neither is a compensation.

The agent does not receive the action’s response until the effect is proved. If verification does not pass, the tool result is withheld and the caller is refused with an instruction not to retry; if the result came back uninspectable and verification nonetheless proved the effect, the caller is told the run committed and that its output remains withheld. This is the part most integrations get wrong in the other direction: a model handed a success body has already concluded the work is done and moved on, and no amount of downstream reconciliation retrieves that decision.

Ambiguity has exactly one answer, and it is to look. A transport timeout leaves the remote outcome unknown, so the run goes from executing straight to verifying — re-dispatching blindly and assuming failure are both unsafe, and the state graph does not offer either. A retry may legitimately finish the last hop from verified to committed after a lost local acknowledgement, because the expensive, irreversible part already happened and was already proved. Terminal duplicates never re-dispatch the action.

The honest reading of the word verifier is narrower than the marketing version. It means a contract-bound read-after-write check performed by a different pinned tool. A separate tool name is separation of contract role; it does not by itself establish an independent trust authority, and nothing here proves that a different organisation or an attested system controlled the observation. A deployment that needs that claim has to put the verifier behind a separately controlled or attested evidence source, and doing that properly is named as follow-on work rather than quietly implied.

Freshness, three ways
Post-dispatch, within a five-second skew tolerance in both directions, and inside the contract’s own maximum age. All three are checked before a single predicate is evaluated.
Eight attempts, then a human
Bounded exponential backoff from one second to a thirty-second cap. Exhaustion enters manual review; it never escalates itself into an automatic compensation.
Every transition is versioned
Transitions are optimistic compare-and-swap on a generation counter, so a stale outbox delivery cannot advance a run a newer delivery already moved, and terminal states cannot be reopened.
Reasons are mandatory
The five states that expose a failure needing attention — compensation required, compensation failed, manual review and both operator attestations — refuse a transition that arrives without one.
The durable run graph, and the only legal moves in it
pending -> reserved -> executing -> executed -> verifying
                                                  |
                                    verified -> committed

executing -> verifying          action response ambiguous

reserved..verified -> compensation_required -> compensating
                                     -> compensation_verifying
                                        -> compensated
                                        -> manual_review_required
                                     -> compensation_failed

reserved..verified -> manual_review_required

manual_review_required -> operator_attested_committed
                       -> operator_attested_not_committed
compensation_failed    -> operator_attested_committed
                       -> operator_attested_not_committed

terminal   committed   compensated
           operator_attested_committed
           operator_attested_not_committed

A rollback that was claimed is not a rollback that happened

Automatic compensation is opted into twice, and the second gate is the interesting one. The contract has to set its verification-failure mode to compensate, carry both a compensation tool and a distinct compensation verifier, and declare a separate, non-empty set of failure conditions; compensation then fires only when those failure conditions all match fresh verifier evidence. Absent that positive proof of a terminal negative outcome, the run goes to manual review — because compensating an effect that has merely not appeared yet is not a recovery, it is a second unauthorised action against the same account.

A successful compensation tool response is recorded as accepted, never as an observation that the rollback occurred. Token Observe records the response digest, moves the run into compensation-verifying and invokes the pinned compensation verifier; only when that fresh, independently mapped observation satisfies the contract’s non-empty compensation postconditions does the run read compensated. Pending, stale, unavailable or mismatching evidence is retried inside the same bounded polling window and can never advance the run. The reasoning is symmetrical with the main lifecycle: a system that can return a misleading success for the action can return one for the undo.

Failed compensation and manual review are visible states with named exits rather than swallowed errors. Both leave only by an operator attestation — committed or not committed — which requires the operator role and an evidence scope covering the run’s team, carries a mandatory reason, and is appended to the audit chain before the state changes, so an audit outage makes adjudication fail closed and leaves the run reviewable rather than producing an unattributable decision. Adjudicating never releases the used business key for replay.

The commercial consequence deserves saying in the same register as the claim, and the product’s own readiness document says it first: an external effect is reversible only when its activated contract has a tested compensation and compensation verifier. Where that pair does not exist, a failed effect is a manual review and a forward fix, and the correct time to discover that is while you are writing the contract rather than during the incident.

Two proofs, not one
Failure conditions prove the action terminally failed. Compensation postconditions prove the rollback landed. Neither substitutes for the other, and both read fresh verifier evidence rather than a tool’s own account of itself.
Retirement is fail-closed
An effect-required tool cannot execute raw while its contract is draft, retired, missing or invalid, and a contract with runs still in any of nine recoverable states cannot be retired or replaced until that recovery finishes.
Stage calls are governed too
Verifier and compensation arguments pass through the same data-class and injection policies as the action, and a payload whose sensitive JSON key would have to be redacted is refused rather than silently rewritten.
Recovery is leader-owned
One elected background leader drains a bounded page of due outbox rows; every stage then takes its own database-backed fenced lease, so a second replica cannot run the same stage behind it.

Portable receipts, and the coverage they do not prove

Every terminal run appends a hash-chained audit entry before its final transition, carrying the contract id and digest, the trace id, the tool, the run state and version, the approval payload hash and the digests of the action arguments, action result, verifier result and both compensation results. Digests, not payloads. Where an anchor signing key is configured, Token Observe then issues at most one canonical, Ed25519-signed receipt per run, and only for a committed or compensated outcome.

The receipt binds the installation and receipt identifiers, the run id, the authority that acted — subject, team, trace and the payload hash of the approval, with its id where there was one — the contract id, digest, effect name and tool, the payload digests, the outcome state with its run version and predicate-evidence digests, and the audit entry’s sequence number and hash. Verifying it needs nothing from Token Observe: a caller supplies the bundle and a public key they already trust, and verification recomputes the statement digest, checks the signature, requires the statement to be exactly the canonical JSON that was signed, and then checks internal coherence — a committed receipt carrying compensation digests, or a compensated one missing its compensation predicate evidence, is rejected as inconsistent. There is no database read, no configuration and no network call, and a receipt is never permitted to nominate its own trust root.

With no signing key there is no receipt, and the run API reports that absence rather than promoting an HMAC or an ephemeral key to a trust root. The limit inside the receipt is equally explicit. Its anchor block is a signed reference, not an offline inclusion proof: it names the anchor sequence, the chain head sequence, the head hash and the key id, and the version-one bundle deliberately does not carry the separate signed anchor bytes or the intervening chain entries. An auditor who needs to prove coverage must obtain and verify that anchor and chain evidence independently. Provisioning, rotation, historical-key retention and independent custody of the receipt key are deployment duties, and they remain unproved.

An activated contract is also a piece of configuration, and it travels as one. Effect contracts are exported as their own resource kind in the per-resource SHA-256-locked configuration bundle, alongside agents, seats, roles, policies, providers, routes, servers, tool pins and webhooks. Importing a bundle means planning, never applying by surprise: the plan compares the declared bundle against deployed state and, where a CycloneDX 1.6 observed inventory is supplied, deployed state against what was observed, classifying each resource as in sync, missing, modified, unmanaged or unverified. A matching identity with no configuration digest is reported as unverified rather than as equality, on purpose.

Promotion between environments is a digest-locked copy that emits a new artifact and an audit record, and refuses outright if the source bundle changed after the review it was based on. What this proves is stored effective-configuration equality — that the contract revision named in one environment is byte-identical to the one named in another. It does not prove that a workload binary or a cloud runtime loaded it, and observation stays bounded by the inventories and governed paths you actually supply.

Committed or compensated only
An operator-attested terminal run is an attributable local decision recorded in the audit chain, not a portable verified-effect receipt. The distinction is deliberate and the API does not blur it.
Caller-supplied trust key
The verification endpoint takes the bundle plus the public key the caller trusts, and requires the auditor role. Verification proves the bundle was signed by that key — nothing about who ought to hold it.
Signed reference, not inclusion proof
The anchor status is signer-asserted coverage. Proving the entry is genuinely inside the anchored chain needs the anchor and chain evidence obtained separately.
One receipt, ever
Issuance is idempotent per run, and a lost acknowledgement after a proved commit is repaired by the durable stage row rather than by re-running anything. Audit entries may duplicate after such a repair; the action never does.
What one signed receipt statement binds
signing    ed25519 + keyId
authority  subjectId, team, traceId,
           approvalPayloadHash, approvalId | null
policy     contractId, contractDigest, effectScope, toolName
payload    actionArgumentsDigest
           actionResultDigest              | null
           verifierResultDigest            | null
           compensationResultDigest        | null
           compensationVerifierResultDigest| null
outcome    state: committed | compensated
           runVersion, predicateEvidenceDigest,
           compensationPredicateEvidenceDigest
audit      entrySeq, entryHash,
           anchor: signer_asserted_covered | unavailable

verify(bundle, publicKeyYouAlreadyTrust) -> trusted | reason
the limits

What this does not do

Stated here rather than discovered during an evaluation. Every line below closes off a reasonable assumption a reader would otherwise carry into a proof of concept.

  • No distributed exactly-once. Token Observe claims at most one dispatch of the action from its own side; whether the action happened exactly once also depends on the downstream system honouring the idempotency key the contract sends it, and that half is outside the boundary.
  • A different pinned verifier is not an independent authority. It is separation of contract role. Nothing here proves that a different organisation or an attested system controlled the observation, and putting verifier evidence behind separately controlled or attested infrastructure remains follow-on work.
  • Nothing is reversible unless you built the reverse. An external effect is reversible only where its activated contract carries a tested compensation and a tested compensation verifier; without that pair, a failed effect is a manual review and a forward fix rather than a rollback.
  • Receipt-key custody is unproved. Verification accepts a public key the caller already trusts, and provisioning, rotation, historical-key retention and independent custody of that key are deployment duties this product does not evidence. The anchor block inside a receipt is a signed reference, not an offline inclusion proof.
  • Multi-replica concurrency is not qualified. The PostgreSQL schema, locked migrations, fenced stage leases and a leader-elected recovery worker exist and carry SQLite and integration coverage, but the product’s own known-issues register is explicit that those tests are not retained evidence of real replicas racing, losing leadership, failing over and recovering against a live downstream action system. That soak and failover evidence remains a release gate.

If one of those limits is the thing that decides it for you, say so and you will get a straight answer about whether it is on the roadmap or out of scope.

Talk it through

How is this different from retrying with an idempotency key?

An idempotency key asks the downstream system to deduplicate. An effect contract deduplicates before the request is sent and then checks what happened. Token Observe takes a unique durable claim on the business key after every governance decision and before the first byte leaves, so a duplicate delivery is refused rather than dispatched, and it refuses any redaction plan that would change the key the upstream sees. Then it calls a different pinned tool to observe the result and evaluates the contract’s postconditions and invariants against that observation. The key still has to be honoured downstream; the difference is that a lost response now leads to a read rather than a retry.

What happens when the action times out?

The run moves from executing to verifying and the pinned verifier is called. Nothing is re-dispatched, because a timeout hides two opposite outcomes and both blind retry and assumed failure are unsafe — the state graph does not offer either move. The verifier is addressed by the same business identity the action carried, its observation must be fresh and post-dispatch, and the contract’s predicates decide. If it proves the effect, the run commits and the caller is told the response was lost but the effect is committed. If it cannot resolve the outcome inside eight bounded attempts, the run enters manual review with the ambiguity visible rather than swallowed.

Can Token Observe undo a payment it should not have made?

Only where you built and pinned the undo. Compensation is optional, and when it exists the contract must also carry a distinct compensation verifier. Automatic rollback fires only when a separate, non-empty set of failure conditions matches fresh verifier evidence, so an effect that is merely slow to appear is escalated rather than reversed. A successful compensation response is recorded as accepted, not as proof: the run reads compensated only after the pinned compensation verifier observes that the contract’s compensation postconditions hold. Where no compensation pair exists, the outcome is manual review and a forward fix, and the honest place to learn that is while writing the contract.

What stops someone writing a verifier that always says yes?

Nothing in the product, and it is worth being direct about that. A verifier is a contract-bound read-after-write check performed by a different pinned tool; that is separation of role, not an independent trust authority. What Token Observe enforces is narrower and still useful: the verifier is a distinct tool pinned to an exact descriptor digest, it cannot read the action’s response, it must be addressed by the same business identity, its observation must be fresh and post-dispatch, and its result is digested into the run’s evidence and any signed receipt. Needing a genuinely independent authority means putting the verifier behind a separately controlled or attested evidence source.

Can a receipt be verified without trusting your database?

Yes, for the part it actually proves. A receipt is a canonical JSON statement signed with Ed25519, and verification recomputes the statement digest, checks the signature against a public key the caller supplies, requires the bytes to be exactly the canonical form that was signed, and rejects statements whose outcome and evidence do not cohere. No database read, no configuration and no network call are involved, and a receipt can never nominate its own trust root. What it does not prove offline is chain coverage: the anchor block is a signed reference, so an auditor who needs inclusion must obtain the anchor and chain evidence separately.

What happens if the effect key is lost or rotated?

Reservations fail closed. The key exists so the raw business idempotency value never becomes control-plane data, and it is deliberately separate from the audit MAC key, because rotating audit protection must never make an already-used business key executable again. The first run stamps a non-secret fingerprint of the key into the database and every later reservation compares it inside the same locked transaction, so a restored snapshot or a replica holding the wrong key refuses before action egress instead of making an old key look unused. There is no dual-key lookup and no online re-key ceremony, because the raw values needed to perform one were never retained.

Ask about this capability
Ask how this one actually works, where it sits in the request path, or what it will not do. Answers stay inside what this page claims.

Prefer to ask a person? Write to us →

get in touch

Bring us the agent you are least comfortable with.

Write to hello@tenhaw.com with what your agents do, which providers they call and what would have to be true for you to put something in front of them. James Rooney replies. You will get a straight answer about whether Token Observe fits, including when it does not.

no form · no qualification step · no sales desk · the other three ways in