AIP v0.9 · Ratified 25 July 2026 · Open, MIT

A person grants authority once.
A machine is held to it
on every action.

Observer Protocol is the open layer that refuses and proves. A mandate signed by the principal and out of the agent's reach. A request outside it that produces no instruction at all. A signed record of what was decided, verifiable by anyone without asking us. On any rail.

RAILS  ·  ACH  ·  Faster Payments  ·  SEPA Instant  ·  PayNow  ·  Lightning  ·  Base / USDC  ·  Solana  ·  TRON

The control is designed to be the same on every one of them, and it has been demonstrated on Lightning. It has never run on a fiat rail. The register below says exactly where each one stands.

Your systems record what was decided.
Nothing records what was authorised.

Every institution deploying agents already has two systems that look like they cover this. Neither one does, and neither can stop the other.

What you have · the decision log

Your application writes down what the agent did and when. It is a record produced after the fact, by the same system that took the action, and it can be reconstructed, backfilled, or simply be wrong. It answers what happened. It cannot answer whether the thing that happened was permitted.

What you have · the payment record

Your rail writes down that value moved. It is authoritative about settlement and silent about everything before it. By the time the instruction reaches the rail the decision is already made, and on the instant rails there is no state left in which it can be recalled.

What nothing records · the authority

Which human granted this machine the right to act, bounded how, for how long, and against which counterparties. That fact exists at the moment of delegation and nowhere afterwards. It is not in the decision log, because the log is written by the thing being constrained. It is not in the payment record, because the rail never saw it. When a supervisor asks whether an agent acted inside its authority, there is no artifact to hand them.

Two systems record two things, and neither one can stop the other. Observer Protocol records the third, and refuses before either of the other two is reached.

What refuses, and what proves.

Three artifacts. Each one is a signed object, each one is checkable by someone who does not trust us, and the second one is the product.

01 · THE MANDATE

Signed by the principal, out of the agent's reach.

A credential the human signs, not the machine. It states a ceiling, a counterparty set, a window, and whether a decision must be cited before the action is permitted. The agent holds a key that can spend inside it and no key that can widen it. Rewriting the mandate is not a permission the agent has to escalate to; it is a signature it cannot produce.

02 · THE REFUSAL

A request outside mandate produces no instruction.

Not a warning. Not a flag on a dashboard for someone to review on Monday. No instruction is emitted, so there is nothing downstream to recall, reverse, or dispute. The check fails closed: if the mandate cannot be read, or the revocation status cannot be established, the answer is no. And the refusal is itself a signed record, which is the part nobody else publishes.

03 · THE ATTESTATION

What was decided, under which policy, over which inputs.

A signed statement naming the decision, the policy version and its content hash, a digest of the inputs it ran over, the outcome, and the party that decided, kept separate from the party that issued the mandate. It verifies offline, against public keys, with nothing from us. A determination is independently verifiable by a party who was not there. The set the published engine reads is delegation credentials, refusal records, lapse records and decision attestations. It does not include policy evaluation credentials, resolutions, instructions or releases. It travels with the counterparty rather than living in our database.

Everyone shows you the payments that succeeded.

A successful payment demonstrates that a system can move money. It says nothing about whether the system could have stopped. So here is the other record: a payment that was requested, was fully fundable, had a route, and did not happen. One entry, on a live rail, with the money there.

Entry 001 · Bitcoin mainnet · Lightning 27 July 2026
Enforcement pointLND RPC middleware · lnd v0.20.1-beta
Triggermacaroon caveat
lnd-custom op-mandate maxi-l402-v1
Requested200,000 sat
Mandate ceiling100,000 sat per transaction
Local balance at the time481,500 sat
Routelive ACINQ channel, available
OutcomeREFUSED · ceiling
Control · same destination, same channel, same RPC method10 sat → SETTLED

The payment could have succeeded. There was more than four times the balance required, a live channel to a well-connected peer, and a working node. Nothing about the network prevented it. The mandate did.

The control matters as much as the refusal. Ten sats to the same destination, over the same channel, by the same RPC method, settled. So the denial is the mandate acting on the amount, not a broken node, a closed channel, or a route that was never there. A refusal you cannot distinguish from a failure is not evidence of anything.

Stripping the caveat does not help: it dies on the macaroon HMAC chain. A hand-rolled gRPC client running as a different user with a sanctioned macaroon was refused by the node the same way, and the middleware defaults to deny across all 70 RPC methods and fails closed if it dies.

What this entry does not show. The mandate issuer was a demonstration did:key, not Observer Protocol's production issuer. · One node, ours, so this is enforcement at a node and not closure of the Lightning network. · The macaroon caveat is a sender-side trigger in the payer's own credential; it is not visible to the payee, who sees an HTLC and a preimage like any other payment.

This register has one entry because one refusal has happened on a live rail with funds present. It will grow as more do. We would rather show you one you can check than a list you have to sort.

No API key. No bearer token. No account.
No call back to us at verification time.

The strongest claim we make is that you do not need our cooperation to check our work. That claim is worth nothing unless you can run it, so here it is, against a credential that is live on this domain right now. What you can check without us: delegation credentials, refusal records, lapse records and decision attestations. What you cannot: policy evaluation credentials, resolutions, instructions and releases.

install · fetch · verify
$ npm install @observer-protocol/policy-engine

$ curl -O https://observerprotocol.org/credentials/\
maxi-0001-trading-mandate-2026-08.json

// verify.mjs
import { verifyCredentialObject }
  from '@observer-protocol/policy-engine';
import fs from 'node:fs';

const credential = JSON.parse(
  fs.readFileSync('./maxi-0001-trading-mandate-2026-08.json'));

const config = {
  credentialPath: 'maxi-0001-trading-mandate-2026-08.json',
  // pin the issuer. a verifier that trusts
  // whoever the credential names is not
  // verifying, it is agreeing.
  issuerDid: 'did:web:bitcoinsingularity.ai',
  schemaAllowlist: ['https://observerprotocol.org'
    + '/schemas/delegation/v2.2.json'],
  revocation: { maxStalenessHours: 24,
    onUnreachable: 'cache-then-deny',
    fetchTimeoutMs: 5000 },
  didCache: { maxStalenessHours: 24 },
  cacheDir: '/tmp/op-cache',
  auditLog: '/tmp/op-cache/audit.log',
  rails: {}, allowContractCalls: false,
};

console.log(await verifyCredentialObject(
  credential, config, Date.now()));

$ node verify.mjs
{ allow: true, reason: 'credential verified' }

What it reads, and from where.

Everything the verifier checks is public: the issuer's DID document and the revocation status list, which it fetches over ordinary HTTPS from the origins the credential names, and the schema, which it checks by $id against your allowlist without fetching anything. There is no Observer Protocol API in the path, nothing to authenticate to, and nothing we can withhold to make a credential stop verifying.

Point offline.didDocumentPath at a local copy of the issuer's DID document and it makes no network call at all. That is the mode an examiner uses.

It fails closed, and it will refuse us too. onUnreachable: 'cache-then-deny' is the only accepted value: if the revocation list cannot be fetched, a cached answer is used and then the credential is denied. A status list hosted on an origin other than the pinned issuer's is refused until you allowlist it, and Observer Protocol's own clause-zero revocation demonstration is exactly such a pair, so it does not verify out of the box. That limit is published in the package.

package · @observer-protocol/policy-engine 1.0.0-rc.12
licence · MIT · self-hostable · no account

Without installing anything: a decision attestation or an enforcement refusal record can be pasted into /check and checked in your own browser, which also derives, from the record you paste, which of its fields that signature actually reaches. A delegation credential is the one artifact the box above is still the route for, because verifying it resolves a did:web issuer over the network and that page makes no request.

Enforcement before the instruction is the only kind that survives instant settlement.

This is not a design preference. It falls out of the message specifications for the real-time rails, and it is the reason the control sits where it does.

What the specifications say

Faster Payments, SEPA Instant, FAST and PayNow carry no state in which a submitted instruction can still be stopped. There is no pending window, no recall leg that a supervisor can reach, no status the receiving institution is obliged to honour a reversal from. Settlement is the first and last state.

Where a hold actually lives

Where an institution does offer a hold, it sits in that bank's own API, above the clearing rail and inside one perimeter. It is a product feature of a single participant, not a property of the rail, and it does not travel to a counterparty at another institution.

If the only stoppable moment is before the instruction exists, then that is where the control has to be. Everything after it is reporting.

One field, one parser, fiat and chain.

A rail identifier that needs a branch for fiat is a rail identifier that will grow a parallel schema. ClearingSystemCode:PaymentProduct mirrors CAIP-2's namespace:reference, so one field carries both and eip155:8453 already parses under it unchanged. The left half resolves against the ISO 20022 external clearing system code set, which appears in every pain.001, so it is not a vocabulary we invent or maintain.

IdentifierRailWhy the right half exists
USABA:ACH.CCDACH credit, United StatesACH and Fedwire share a clearing system and are different products
GBDSC:FPSFaster Payments, United KingdomDistinguishes FPS from Bacs and CHAPS on the same code
SGIBG:PAYNOWPayNow, SingaporeDistinguishes PayNow from GIRO and FAST
eip155:8453BaseCAIP-2 already; no change, no branch

Status: proposed. This grammar is a design decision, not yet a published schema. Credentials issued today carry rail as a free-text string, and the shipped delegation/v2.6 schema documents it that way. Published schema URLs are immutable, so adopting this convention means a new schema version rather than an edit to an existing one. Until that version ships, this is what the field is becoming, not what it currently contains.

Open primitives. Operated surface.

OP is what refuses and what proves. Agentic Terminal is how an institution operates it. The line between them is not open-versus-paid; it is primitive-versus-operation.

Observer Protocol · MIT · self-hostable

The primitives that refuse and prove

Mandate issuance
Policy evaluation
Fail-closed refusal
Signed attestations
Offline verification
Rail adapters
Schemas & engine

Signed attestations for decisions, refusals and approvals. Decisions and refusals verify with nothing from us. An approval does not, at the version npm install serves, and section 08 carries the band. Spec-governed, and what verifies you can run yourself without telling us you have.

Agentic Terminal · commercial · in your perimeter

The operating surface for an institution

Request/approve separation
Approvals queue
Entitlement & roles
Audit export
Evidence operations

The approver cannot set the terms of what they are approving. Deployed inside your infrastructure, operated by your team or ours. Never holds funds. Never takes custody of credentials — the enforcement point reads the mandate it enforces, and holds no key that could issue, alter or re-sign one.

Agentic Terminal →

The register, stated plainly.

Written so that a reader who checks it finds it accurate rather than generous. Where a claim is weaker than we would like it to be, the claim moves toward the evidence.

Refusal enforced on a live rail, funds present
Bitcoin mainnet Lightning, 27 July 2026. Our node, demonstration issuer, one profile. Not closure of the rail. Section 04 above carries the full scope.
Demonstrated
Enforcement at a wallet signing boundary
Engines published for Tether WDK (wdk-op-policy), MPP/Tempo (mppx-op-account) and OWS (ows-op-policy), from a shared core. The Aqua demonstration runs in an instrumented build on Liquid; the hook is not in shipped Aqua.
Published
Offline verification with nothing from us
@observer-protocol/policy-engine 1.0.0-rc.12 on npm, MIT. Section 05 is a transcript of it running against a credential served from this domain.
Live
Credentials published here that verify against that engine
Two of the five. The two PolicyEvaluationCredential artifacts carry no credentialSchema and are refused by our own verifier on structure; one older trading mandate is missing authorizationConfig.policy. Re-issuing them is a signing operation, not an edit, and it has not happened yet.
2 of 5
Settlement attested by the protocol
One. The verified_events table holds 56 rows; 55 of them predate the current cutover and carry proof_strength = 'unverified', which is what that column exists to record. We do not count them as verifications and neither should you.
1
Enforcement demonstrated on a fiat rail
None. The rail grammar in section 06 is a design decision; no fiat instruction has been refused or settled under a mandate. The argument for why the control belongs before the instruction is drawn from the message specifications, not from our own traffic.
Not yet
A reader can tell they have been shown every artifact
Every artifact here verifies individually. A firm can hand over a hundred clean determinations and withhold the hundred-and-first, and all hundred verify. That is the whole problem, and no property of the signatures addresses it. The cryptography for transparency logs is solved. What is unsolved is who runs the log, who is obliged to publish to it, what a gap in it legally means, and how a firm demonstrates completeness without exposing commercially sensitive volume. Those are governance and regulatory questions, and we cannot answer them by ourselves or by shipping anything.
Not established
The decider is independent of the enforcement point
Section 03 says the decider is kept separate from the issuer, and that is true: the decider in our demonstrations is a separately-keyed did:key, cryptographically distinct from the enforcement point, and you can verify that separation offline from its own DID. What it is not is a second party. It is a fixture we generated and control — not a published organisation and not an independent third party with its own interests. The separation is real as cryptography and currently means nothing as governance. Proving a decider is genuinely independent needs a real second party, and until one exists this row says fixture rather than independent.
Fixture
Signed records an outside party can rebuild
Of 11,432 signed records across 13 record stores we hold, written between 2026-08-05 and 2026-08-15, 3,089 of them (27.0%) cannot be checked by anyone. They are instructed, report records, and no version of the engine ever published rebuilds their signed bytes, so there is nothing to verify a signature against. This is not a version pin and it is not a gap that a reader can work around by installing something else: those records carry a signature that no counterparty, and no one here, can check. Principle 04 below says the evidence is portable or it isn't evidence. For better than a quarter of what we sign, it isn't. A further 122 resolution records are rebuildable at some published version but not at the one npm install serves; section 04 of /verify carries that. Every figure in this row is read from results/ and fails the build if the copy and the measurement disagree.
27.0% unverifiable
A determination re-checked by a party who was not there
verifyDecisionAttestation is exported by the version npm install serves, and verify-samples/ppp-determination-refused-outcome.json is a published decision attestation it returns attested on. The record carries the document that was signed as base64 beside a documentHash, and sha256 over those stored bytes reproduces that hash, so the input to the signature is rebuildable from the record alone, by someone who holds nothing else and was present for none of it. Checked in CI in both directions, so a regression and a silent repair both break the build. What it establishes is narrower than the word verifier suggests, and the narrowness is stated where the check is run rather than here: a named key signed those exact bytes and nobody has altered them since, while policyRef.hash, vocabularyRef.hash and deciderArtifactDigest.value are confirmed present and never resolved against anything. The field-level account is on the docs page.
Verifiable
Determination records written before the document bytes were kept
The property above rests on the raw document and signature being preserved through ingestion. That is true of deployments running the determination-records path. It is not true of what came before it: those deployments verified an attestation once on receipt and did not keep what they verified, so a determination recorded on one of them cannot be rebuilt by us or by anyone else, permanently. This repository cannot tell you which records those are. The boundary is a property of a deployment, it is not readable from any artifact published here, and no version string in this estate marks it. Read the row above as a statement about a determination you are holding, not as a statement about every determination that exists.
Not reconstructible
A public surface where an outside party performs the check
/check, published 17 August 2026. No account, and the check runs in the visitor's own browser against a key recovered from the decider's own identifier, so nothing pasted into it reaches us. The page carries no script of its own and, for a period after it was published, the CDN in front of this domain added one anyway. That setting was disabled on 17 August 2026 and the absence corroborated across two runs an hour apart, so the page states again that it loads nothing, and the audit that watches it is invoked with nothing disclosed, which means the absence is proven on every run rather than asserted once. The row below carries what that layer is able to add. It covers one artifact type, a decision attestation. A delegation credential and a refusal record verify against the published engine already and neither has a box on that page, so an outside party holding one of those still installs the package. And a public surface is not a public corpus: it checks the artifact somebody hands you, and there is no address that will hand you one you were not given. That is the completeness row above, unchanged by this. The page's verdicts are run against the published sample and against deliberately broken variants on every build.
One artifact type
What the page is served through, and what that layer is able to add to it
This domain is served through a CDN, and a CDN in front of a static origin can change what a visitor receives without any change to what we publish. That is a property of the arrangement rather than an accusation about a setting, and it is in this register because a page that tells a visitor what it loads is only as good as the layer between it and them. Measured on 17 August 2026: /check was published carrying no script of its own, and the page a browser received loaded static.cloudflareinsights.com/beacon.min.js. Nothing in our repository had changed, and it was injected only for requests carrying browser headers, so a build check reading the file and a plain fetch of the same URL both reported it clean. It was disabled at the zone the same day. Two runs of the audit an hour apart, on 17 and 18 August 2026, both found it absent, which is what corroborated means here and is the whole of what it means: it rules out a transient and rules out nothing else.

The surface, not the instance. A zone of this kind is capable of injecting or rewriting a page in at least these ways: an analytics beacon on every HTML response; email address obfuscation, which rewrites addresses and adds a decoder script; a script loader that defers and rewrites the page's own script tags; bot detection JavaScript served under a CDN-owned path; and a tag manager, which can load third-party tags configured entirely outside this repository. An edge function on a route can rewrite the HTML arbitrarily. Each of those is a capability of the surface and none of them is a statement about how this zone is configured today, which is not readable from this repository: the configuration lives in a dashboard and changing it produces no commit.

So the control is a measurement rather than a document. scripts/served-page-audit.mjs fetches the page as a browser and compares what it carries against what the page discloses, failing when something undisclosed appears, when something disclosed is no longer there, and when one of the page's own scripts is missing or altered. That third direction is the one a subtraction hides in: an injected script is loud, and the page's own verifier being removed leaves a page that renders and decides nothing. It also refuses to treat an absence as established on one observation: a thing that is disclosed and not there may have been removed at the zone or the edge may transiently have stopped adding it, so that answer is held open until a second run separated in time sees the same thing. It is a standalone file with no dependency on this repository, so it can run on a clock rather than on a commit, which is the only shape that catches a change nobody committed.

What that control does not reach, said here rather than left to be discovered. It reads markup and executes nothing, so it establishes what was delivered, not what is performed. It sees every script the page carries, external or inline, and it cannot see what any of them does once it runs: a runtime request made by a script already disclosed is outside it, and disclosing a tag manager would not tell you which third parties that manager then loads. An earlier version of it read only external references, so an injected inline script issuing a request on load came back clean and printed that the page loaded nothing. It was exactly accurate about the comparison it made, and that accuracy is what stopped the next question being asked. And the same limit runs along a second axis, which is the one that produced this row. Until 17 August 2026 the beacon was configured to be added for some visitors and not others, decided at the edge by where the request came from. So what was delivered varied by reader, and a fetch from one place could have found nothing while another found the beacon, with neither result wrong about itself. The audit establishes what was delivered to one client, from one origin, at one time. Edge configuration can vary delivery by origin, by user agent and by request, and nothing here claims coverage across any of those. This is the original finding on a second axis: the first version of that check could not see an inline script, and no version of it can see a reader somewhere else.
Not readable from here
A refused payment joined to the determination it cited
On a corpus of 908 refusals, the determination-side join is closed for 7. A refusal that fires before the attestation is read records no citation, and the record says so positively rather than leaving the field blank, so the gap is visible in the data rather than inferred from its absence. So a refused payment mostly cannot be connected to the determination it cited, and the party asking which determination a refusal was about will not get an answer from these records. Provenance: measured on the payment stack and relayed here on 17 August 2026. Unlike the figures in the rebuildable-records row above, nothing in this repository re-derives it, and the record stores it was taken over are not published here.
7 of 908
Specification
AIP v0.9, ratified 25 July 2026, tagged v0.9, review record in the public repository. Additive over v0.6 through v0.8; an implementation conforming to any of those remains conforming.
Ratified
Decision attestation in a published schema
Published 8 August 2026 as delegation/v2.7.json, carrying requiresDecisionAttestation, served at a URL that is immutable from the moment it went live. Note precisely what this does and does not change: the shape is now pinned and public, and there is still no verifier path for a PolicyEvaluationCredential. A published schema is not a verifier.
Published

Don't trust. Verify.

Four commitments that do not bend to convenience, including when the convenience would be ours.

01

Don't trust claims. Verify behaviour.

What a system did is checkable. What it says about itself is not. Every artifact this protocol produces is built to be checked by someone who has no reason to believe us.

02

Authority is granted, never assumed.

A machine holds no authority it was not explicitly given by a human who signed for it. Absence of a mandate is not permission to proceed carefully; it is refusal. The default is no.

03

Open source. Always.

Verification logic is public, reproducible and auditable. No authority required. Self-hostable by design: OP does not custody funds, execute payments, or control access.

04

The evidence is portable, or it isn't evidence.

An attestation that only means something inside our system is a log entry. Ours verify against public keys, in your hands, after we are gone. We are not the custodian of the proof.

The first one, and what it actually proved.

On 22 February 2026 two agents built by different developers, running on different stacks, settled a payment on Bitcoin mainnet against authority that had been granted in advance. The point was never that no human was in the loop. The point is that the human was in the loop first, at the moment the authority was granted, and that what happened afterwards can be checked by anyone against the payment hash.

date · 22 February 2026 · Lightning · Bitcoin mainnet
payment hash · 6a30ba7fff332c4eb8f368da804b663a20bf59ae1362d76ac1d10c298d4cd875
The genesis record →