NEWWatch an agent's payment get refused, and read the reason, in the live browser demo.Open the demo
AxorumAxorum
01

The Moment of Truth

Why a ledger for AI spending has to say yes or no at the exact instant of the payment.

Every control you have assumes a person is in the loop

An agent breaks that assumption. You tell it to buy the team's coffee supplies. It does, it finishes the errand, and it is gone. Nobody signed, nobody reviewed, and the charge is already on your books. There is no one left to ask.

You are probably not that company. You are building the software it runs on: an accounts payable, spend, or procurement platform on the buying side, or a billing or monetization platform on the selling side. Your customers' agents are starting to spend and get paid, and they will come to you for the controls.

Axorum is what you embed to give them those controls. It is a subledger, not a general ledger: it sits on each participant's own books, the buyer's and the seller's alike, and records agent spending in detail.

How to read "your books" in this course

Read your books throughout as the books of the company your platform serves. The controls are the ones your customers will ask you for. If you are that finance team rather than the platform, the course still reads straight. You are the customer in that sentence.

Two sides to every payment

There are two sides to every payment: the arithmetic of the books, and the authority. A customs checkpoint is the metaphor for this whole module. At a border, the goods must be declared correctly, and you must be allowed to bring them in. Fail either side and nothing crosses. That is exactly how Axorum handles every payment.

Axorum is a ledger built for this. Instead of letting the money move and auditing it later, it decides at the instant of the payment, and it records why it said yes.

The old way

Let the money move, write it in a log, and read the log next month. That works when a person chose to spend, because you can go and ask them.

The Axorum way

Both sides are checked before the payment is ever recorded: the arithmetic of the books, and the authority to spend. The agent can vanish. The decision, and the reason for it, are already on the record.

Five laws, checked at once, before the money moves

A payment is recorded only if it passes every one of the ledger's laws at the same moment. There are five today. If any one of them fails, nothing is written down, the way a single denied stamp at customs hands your bags back.

Four of the five are the arithmetic of your books: the entry balances, the budget holds, revenue matches what was earned, and the bill matches the meter. The fifth is the authority: was this allowed under the rules your team wrote. Two of them do the work on this first coffee purchase. You will meet all five up close in Module 5.

Law 1 · The entry balances

It has to balance. Every debit is matched by an equal credit, per currency. Money is not created or lost, only moved.

Law 2 · Someone authorized it

The payment was actually allowed under the written policy in force at that moment. The rules your team wrote said this agent could spend here.

Law 3 · The budget holds

The spending stays within a cumulative budget. The total ever spent against a mandate can never pass its ceiling. (Module 5.)

Law 4 · Revenue matches what was earned

Revenue recognized as earned never exceeds what was taken in advance. The GAAP matching rule, enforced. (Module 5.)

Law 5 · The bill matches the meter

What you bill never runs past what you metered, and what you meter never runs past what was actually delivered. No charge for service that never happened. (Module 5.)

All of them, or nothing

Most systems ask one question: can this payment move money? Axorum also asks whether your own rules allow it, and whether the budget still holds. It answers all of them before anything is recorded, and it answers them in one step. As your books come to need new laws, they join that same single check.

Our running example uses real rules: purchases are allowed, but any purchase over $1,000 is forbidden, and every purchase creates a duty to file a receipt. Keep those numbers in mind. They come back in later modules.

Follow one purchase: $500 of coffee supplies

The decision, the refusal or the approval, and the reason all happen before the money leaves. Here is that sequence for a single purchase of coffee supplies, $500, from the moment the agent submits it to the moment it is posted. Click through each step.

The Agent
The Gateway
The Vault
Recorded
Click “Next step” to begin
Step 0 / 7

Notice the split, because your auditor already has a name for it. The Gateway (passport control) checks who is asking and on whose authority. The Vault (the books) applies the laws. The part that checks the authority is not the part that records the money, so neither one can quietly cover for the other. Here are those same seven steps as cards you can scan:

1
The agent asks to spend

Buy coffee supplies, $500, submitted with its ID and the pass that grants it the authority to spend.

2
The Gateway checks the badge and the pass

Is this a real sender, and was this agent actually granted the authority to spend?

3
Identity and authority hold

Confirmed: a real sender, and someone authorized it. Only now does the request reach the books.

4
The Vault runs Law 1: the entry balances

Do the debits equal the credits? The $500 leaving must equal the $500 arriving.

5
The Vault runs Law 2: someone authorized it

Do the rules your team wrote allow this purchase? It is under $1,000, so it is within policy.

6
Laws 3 and 4 have nothing to check

They fire only when a purchase spends against a budget or moves deferred revenue. This plain coffee order does neither, so they pass instantly. You will see them do real work in Module 5.

7
Every law that applies holds, so it posts

The payment is posted to the books, Approved, along with the reason it was allowed.

Axorum records more than the fact of a payment

It records who authorized it, which rule allowed it, and the reason that was given. That is why the agent can disappear and you can still answer your auditor.

The agent does not simply send "pay $500." It hands over a declared package, the customs form, and every field on it is something you would want in front of you at the close: who is asking (the agent's ID badge), on whose authority (a tamper-evident digital pass, issued by the company that vouches for the agent; alter one character of it and it stops verifying), a receipt number (so a retry can never double-charge), the entries (the debits and the credits), what it wants to do (make a purchase), the evidence (any supporting documents it is relying on), the reason (in the agent's own words), and which rulebook it believes is in force. The reason is sealed at the door, so it cannot be quietly rewritten later.

In plain English

The business rule: an agent may not spend unless it declares all eight of these, in one package, up front.

Who is asking, by a unique address that names the agent.

On whose authority, carried on a tamper-evident digital pass.

A receipt number, so a retry can never double-charge.

The entries: the actual debits and credits this payment would post.

The action: what it is trying to do, for example make a purchase.

The evidence: any supporting document it is relying on, sealed so it cannot be swapped later.

The reason: why the agent says this spend is justified. Sealed at the door.

The rulebook: which version of your policy the agent believes is in force.

FOR YOUR ENGINEERS
pub struct IntentEnvelope {
    /// The agent's `agent://` URI (parsed and validated at the gateway).
    pub agent: String,
    /// The PASETO v4.public capability attestation.
    pub attestation: String,
    /// The client-minted transaction id (the substrate idempotency key).
    pub transaction: TransactionId,
    /// The accounting journal legs.
    pub entries: Vec<Entry>,
    /// The proposed deontic action and its typed bindings.
    pub action: ActionTerm,
    /// Hashes of external evidence the agent references.
    pub evidence: Vec<ContentHash>,
    /// The agent's stated reason (hashed at the gateway; the hash goes on-ledger,
    /// the text is stored off-ledger by the caller).
    pub justification: String,
    /// The policy the agent believes is in force (the staleness pin).
    pub policy: PolicyId,
}

The instant the laws are checked

The laws run in order, and the first failure ends the payment. Law 1 confirms the entry balances and lays out where each amount would be posted. Law 2 looks up the rulebook that is actually in force at that moment, and if there is no rulebook in force at all, the payment is refused: no action can be judged against rules that do not exist. Only after every applicable law holds is anything written to the books.

In plain English

Law 1, the entry balances: confirm the debits equal the credits, and lay out where each amount will be posted.

If it does not balance, the payment stops here and nothing is recorded.

Law 2, someone authorized it: look up the rulebook in force at this moment.

If no rulebook is in force, the payment is refused. Nothing can be judged against rules that do not exist.

Only after both hold does the payment reach the books.

FOR YOUR ENGINEERS
        // Conservative law (validate fully; produce the posting plan).
        let plan = self.plan_postings(&entries)?;
        check_conservation(&entries)?;

        // Normative law.
        let active_id = self
            .policies
            .active_id()
            .ok_or(SubstrateError::NoActivePolicy)?
            .clone();

Check your understanding

Each question puts you in a situation you would recognise from a close, and asks you to trace what the ledger does.

ScenarioThe entry does not balance
An agent submits a purchase whose entries do not balance: $500 leaves, but only $400 arrives. Follow it through the checkpoint. Where is it stopped, and does anything reach your books?
ScenarioExpired authority
An agent's authority to spend has expired. It submits a perfectly balanced $500 purchase. Where does this fail, at the Gateway or in the Vault? And why does it never appear in the ledger's record of what was allowed?
ScenarioThe vendor's proposal
A vendor proposes a simpler design: let the payments through, and review a log of them at month end. You are the one who signs the rep letter. Why does Axorum check at the instant of the payment instead?
Pick an answer for each question.
Up next

The part that checks who is asking is walled off from the part that records the money. In Module 2 · Meet the Cast, each character is introduced by name, and the line between the messy outside world and the tidy books turns out to be a control you already know: segregation of duties.

02

Meet the Cast

Six characters, and one hard line down the middle: the part that decides never touches the messy outside world.

The Airlock

One line runs down the middle of Axorum, and every part of it sits on one side of that line or the other. The part that deals with the messy outside world is not the part that decides whether a payment is allowed, and it is not the part that records the money.

Think of a airlock into a clean room. Outside is the messy world: wind, dust, things nobody can predict. Inside, nothing is left to chance and every action is exactly repeatable.

Outside: the messy world

Passes to verify. Clocks to read. Networks that drop messages. Nothing here is guaranteed to happen the same way twice.

Inside: the clean room

Only arithmetic and rules. Nothing in here depends on when you ask, or on which machine you ask.

The one idea for this whole module

Axorum splits in two: the edge, which handles the messy outside world, and the part that decides, which is sealed off from it. The line is deliberate. It is what lets your books be copied across machines in Module 6.

Inside, the answer is repeatable: the same question always gets the same answer, today, tomorrow, and on any machine. Hold on to that. Modules 6 and 7 both rest on it.

The Six Characters

Every part of Axorum has exactly one job, and each one lives on a known side of the airlock. Here is the cast.

On the messy side (outside the airlock)

The Gateway

The bouncer. Checks who is asking and whether they are allowed in, using the seal on their pass and the clock. This is the airlock itself. (Module 3 shows exactly how it checks who is asking.)

In the clean room (inside, where the answer is repeatable)

The Rulebook

Your spending policy, written in a short rule language. What is allowed, what is forbidden, and what creates a duty someone still has to discharge. (Module 4 opens this up.)

The Judge

Reads the Rulebook against the payment actually proposed and records the outcome: Approved, Refused, Approved by exception, Obligation opened, or Obligation closed.

The Vault

The books. The ledger that records a payment only if both sides hold: the arithmetic balances, and the rules your team wrote allow it.

The outer infrastructure (holds the building up)

The Committee

Several copies of the books, on separate machines, that must agree on the order of events before anything counts. (Module 6.)

The Turnstile

Holds real payments back from the payment rails until the Vault says yes. (Module 7.)

Segregation of duties

Each character has exactly one job, and you already know why that matters. Your auditor calls it segregation of duties. The part that checks who is asking is not the part that records the money, so neither one can quietly cover for the other.

Where the Line Is Drawn

The clock and the seal never cross the line: they are used up at the door and thrown away. Three characters live inside the clean room, the Rulebook, the Judge and the Vault, and not one of them reads a clock, checks a seal, or waits on a network. The Gateway does all of that out front. It verifies the seal on the pass, confirms the pass has not expired, strips both away, and hands the part that decides a plain request with nothing unpredictable left in it.

In plain English

The three inner rooms (Rulebook, Judge, Vault) are the clean room. Nothing in there reads a clock, checks a seal, or leaves anything to chance.

The Gateway is the airlock in front of them: it checks the seal on the pass and the expiry date.

It then hands a clean, stripped-down request to the books.

The messy world stops here, at the door, and never leaks inward.

FOR YOUR ENGINEERS
//! Everything inside the replicated state machine (`axorum-deontic`,
//! `axorum-eval`, `axorum-substrate`) is **deterministic**: no crypto, no
//! wall-clock, no randomness. `axorum-agent` is the **non-deterministic
//! boundary** in front of it: it verifies signatures and expirations, translates
//! a verified wire envelope into a deterministic
//! `TransactionProposal`, and drives `axorum_substrate::Ledger`. Crypto and time
//! live **here** and never leak inward.

That is why your books can live on more than one machine. Hand several copies of the books the same events in the same order and they arrive at the same balances, to the last cent. Module 6 shows what that is worth on the day a machine dies.

The Gateway's checks, in order

Before anything reaches the clean room, the Gateway asks four questions, in this order. Who is asking? Look up the agent's address and find the party it acts for. Is it allowed to act, and is its pass still in date? Check the seal, trace it back to a company you trust, confirm the pass has not expired, and confirm it actually covers a purchase. Are we both reading the same rulebook? The rules the agent assumed must be the rules in force. Only then does the request cross the line to be recorded.

In plain English

1. Identity. Who is asking? Look up the agent's address and find the party it acts for.

2. Authority. Is it allowed, and is its pass still in date? Check the tamper-evident digital pass, trace its seal back to a company you trust, and confirm against the clock that it has not expired.

3. Policy pin. Are we both reading the same edition of the Rulebook? The rules the agent assumed must be the ones in force.

6. Commit. Only now does the request cross into the clean room to be recorded.

FOR YOUR ENGINEERS
//! 1. **Identity** — parse + validate the `agent://` URI; resolve its registry
//!    binding to a `PartyId` and roles.
//! 2. **Authority** — verify the PASETO attestation (signature chases to a
//!    trusted root; token subject is this URI), check expiry against the injected
//!    `wall_now`, and require capability coverage for the action.
//! 3. **Policy pin** — the pinned policy must be the one in force.
//! ...
//! 6. **Commit** — the deterministic substrate commit (checks #3–4).

Step 2 is the only place the clock is read (wall_now in the listing above), the only place the seal is checked, and the place the pass must be shown to cover the purchase being made. All of it is spent here, at the door. By step 6 it is gone, and the clean room never sees any of it.

The Handoff Across the Line

By the time a payment reaches the books, nothing unpredictable is left in it. Watch the Gateway hand over to the Vault, and watch the airlock door close.

0 / 5 messages
Why the copies can agree

The Vault never learns there was a seal or a clock. By the time a request reaches it, everything unpredictable has been spent and thrown away at the door. That is what lets a second machine keep the same books and reach the same answer, to the last cent.

Think It Through

Three situations, each asking the same question: which side of the airlock does this belong on?

You add a rule: refuse payments made outside business hours, which means reading the clock. Which part should own that check?
Your books are copied onto a second machine in another data centre. Both copies are handed the same payments, in the same order. Will they close at the same balances, and what guarantees it?
An agent cannot prove who it is, and the Gateway turns it away. Does the ledger record a Refused verdict against your rules?
Pick an answer for each question.
Next up

Six months from now, your auditor will ask who approved a payment, and the agent that made it will no longer exist. Module 3, Who Are You?, stays at the door and answers that question: how an agent proves who it is, and that it was allowed to act at all, before a single dollar moves.

03

Who Are You?

How the books prove that a vanishing agent is who it claims to be, and is allowed to spend at all.

Six months later, your auditor asks

Six months from now, your auditor will ask who approved a payment. The agent that made it will not exist. Axorum answers anyway, because the proof of who authorized it was taken at the instant of the payment and travels with the record.

A person has fingerprints and a memory. A company has a headquarters and a domain name. An agent has neither. It is started, copied, moved between clouds, and shut down, often with no human watching: it finishes its errand and disappears. So who it was has to be settled while it is still standing at the counter, because afterwards there is no one left to ask.

The booth one over from Module 1

Module 1's customs officer inspected the goods: the five laws that check the arithmetic of the books and the authority to spend. This module is the booth right next to it, passport control, which settles who is asking before any of that begins.

Today's flaw: your name is your address

Most agents today are identified by where they run: a web address, a network address. Move the agent to another server and that identity breaks. The paper behind Axorum's naming puts it bluntly:

"The agent's identity was its location."

The fix is an old one from the early internet (Saltzer, 1982): names should be resolved to addresses, not used as addresses. Give the agent a stable name up front, and keep a separate lookup for wherever it happens to be running today.

Two questions, not one

Every arrival raises two separate questions, and the whole module hangs on telling them apart.

1. Who are you?

The name. A stable name that survives the agent moving anywhere. The Gateway looks it up on its roster.

2. Prove it.

The proof. A pass the Gateway can check on the spot: proof that the sender really is that name, and may act.

Both are settled at the Gateway (Module 2's bouncer), before anything touches the books.

A name, not an address

An agent's name identifies the agent, never the machine it happens to be running on, so moving it cannot break the record. A passport names you, not your house: you can move cities, switch jobs, cross borders, and the name on the passport keeps identifying you. An agent name works the same way, and it has three parts: who vouches for the agent, what it is for, and which exact one it is.

In plain English

Take one full agent name and pull it into its three parts.

Who vouches for it: anthropic.com, the company that stands behind it and the only one that can issue a pass for it.

What it does: assistant/chat, its capability path.

Which exact one: llm_chat_01h455…, a serial number, unique to this one agent.

FOR YOUR ENGINEERS · lib.rs
let uri = AgentUri::parse(
    "agent://anthropic.com/assistant/chat/llm_chat_01h455vb4pex5vsknk084sn02q"
).unwrap();

assert_eq!(uri.trust_root().host_str(), "anthropic.com");
assert_eq!(uri.capability_path().as_str(), "assistant/chat");
assert_eq!(uri.agent_id().prefix().as_str(), "llm_chat");
Authority: anthropic.com

Who stands behind this agent, and answers for it

Capability: assistant/chat

What the agent is for

Agent id: llm_chat_01h455…

Which exact one, down to the serial number

It says who and what, never where

Move the agent from one data center to another and its name does not change. A separate directory simply updates where it is running now. Your passport name survives a move; your phone number survives switching carriers.

On our roster

A valid name is not enough: the name also has to be one you already know. A bouncer with a guest list does two things, reads the name off the ID and checks it against the list, and the Gateway does exactly that. A garbled name gets nowhere. A well-formed name that is not on your roster gets nowhere either. In both cases nothing has touched the books.

In plain English

First, is this even a real agent:// name?

If it cannot be read, stop here: InvalidAgentUri. A garbled name gets nowhere.

Now look the name up in the registry (the roster).

The lookup returns which party the entry will land against, and which roles the agent carries.

If the name is not on the roster, stop: UnknownAgent. A stranger with a well-formed name is still turned away.

FOR YOUR ENGINEERS · gateway.rs
        // Check #1: identity.
        let uri = AgentUri::parse(&envelope.agent).map_err(|_| GatewayError::InvalidAgentUri {
            uri: envelope.agent.clone(),
        })?;
        let (party, roles, bound_mandate) = {
            let (party, roles, mandate) =
                self.registry
                    .resolve(&uri)
                    .ok_or_else(|| GatewayError::UnknownAgent {
                        uri: uri.canonical(),
                    })?;
            (party.clone(), roles.clone(), mandate.cloned())
        };
Two ways to fail, both at the door

uri.canonical() is the name written in one canonical form, so a formatting quirk can never make a lookup miss. Bad name or unknown name, the request is turned away before a single balance moves.

Prove it: the passport and its hologram

Being on the roster says the name is known. It does not prove the sender is that agent. The pass does that: it carries a seal only the company vouching for the agent could have made, and any officer can check the seal on the spot, without phoning the issuing country.

The tamper-evident pass is the passport

The company that vouches for the agent issues a pass saying "this agent is ours, and it may do X."

The seal is the hologram

Only the company that vouches for the agent can make it. Tamper with the pass and the seal breaks.

Checking the seal is the officer's lamp

Anyone can confirm the pass is genuine on the spot, with no call back to the issuer.

So the pass is asked three questions, and it has to answer all three: is it genuine and made out to this exact agent, is it still in date, and does it actually cover what the agent is trying to do? Fail any one and the request is turned away at the door.

In plain English

Is the pass genuine, and does it name THIS agent? Check the seal against what the vouching company published, and confirm the pass is made out to this exact name.

Is it still in date? Measured against a time handed in from outside, never a clock this part reads for itself.

Does it cover this action? The permissions written on the pass must cover what the agent is trying to do — and they are written inside the agent's own name, so a pass can narrow what this agent may do but never borrow authority from outside it.

FOR YOUR ENGINEERS · gateway.rs
        // Check #2 — authority (verification is clock-free; expiry uses wall_now).
        let claims = self.verifier.verify_for_uri(&envelope.attestation, &uri)?;
        check_expiration(claims.exp, wall_now)?;
        capability::ensure_covered(
            &claims.capabilities,
            uri.capability_path(),
            &envelope.action.action,
        )?;
Checkable by anyone, issued only by one

A pass from acme.com can be checked by anyone and issued only by Acme, the way a French passport is valid worldwide and issued only by France. Nothing about that check depends on the agent, or on the company that vouched for it, being reachable at the moment you make it.

Everything that must be true at once

A pass clears the door only if all four of these hold at the same time, and each one exists to defeat a particular cheat. A border officer does not wave a passport through just because it carries a seal.

trusted + owns namespace — Sealed by a company Axorum trusts, and one that owns this agent's namespace. A seal for acme.com cannot vouch for evil.com.

this exact agent — Made out to this name. You cannot borrow someone else's pass.

not expired — Still in date, measured against a clock handed in from outside. The part that decides reads no clock of its own.

grants this action — Coverage: the pass must actually permit what is being attempted.

The first hurdle has a subtle edge to it. The company that sealed the pass must be the same one named at the front of the agent's name:

In plain English

Is the company that sealed the pass (iss, the issuer) the same one named at the front of the agent's name?

If not, refuse it: IssuerNamespaceMismatch.

Acme may vouch for agent://acme.com/… agents only, never for a rival's.

The passport rule: France issues French passports, not German ones.

FOR YOUR ENGINEERS · verifier.rs
        if claims.trust_root() != Some(claims.iss.as_str()) {
            return Err(AttestationError::IssuerNamespaceMismatch {
                issuer: claims.iss.clone(),
                uri_trust_root: claims.trust_root().unwrap_or_default().to_string(),
            });
        }

Watch it happen: the name → the pass → the books

Every step can turn the request away, and the Vault at the end only ever meets requests that cleared all of them.

Agent
Roster
Passport check
Delegation
Vault
Click “Next step” to begin
Step 0 / 6
What "Delegation" is doing there

Between the pass and the books, the Gateway also gathers any standing authority a principal handed down: delegation. It is how "my boss said I could" becomes something the machine can check.

Turned away at the door

When the name or the pass fails, the request is turned away at the Gateway and nothing is written to the books. It is not a verdict from the rulebook, because the request never reached the rulebook: no entry, no balance moved, nothing to explain at the close.

Notice where the messy part lives. The expiry check compares two times that are both handed in from outside, which is the rule Module 2 set: the part that decides never reads a clock of its own.

In plain English

The check is given both times: when the pass expires (exp) and what time it is now (now).

If now is still before the expiry, the pass is in date.

Otherwise it has expired, and it is refused.

It never reads a clock itself. The clock stays outside, so the same question always gets the same answer.

FOR YOUR ENGINEERS · verification.rs
pub fn check_expiration(exp: DateTime<Utc>, now: DateTime<Utc>) -> Result<(), AttestationError> {
    if now < exp {
        Ok(())
    } else {
        Err(AttestationError::TokenExpired {
            expired_at: exp.to_rfc3339(),
        })
    }
}
What your auditor gets to see

The record keeps the agent's name, the company that vouched for it, the pass it presented with its seal, the authority that pass granted, and the rule that was in force at the moment. Answering "who approved this" needs none of them to still be running.

Think it through

Each question drops you into a new situation. Reason it out the way the Gateway would.

A stranger copies one of your agents' agent:// names and submits a payment instruction, but holds no valid pass. Where does it fail, and does anything reach your books?
acme.com presents a perfectly sealed pass, but it vouches for agent://globex.com/…. Is it accepted?
An agent migrates from AWS to Azure mid-workflow. Does its agent:// name change?
A pass is valid and names the right agent, but it grants assistant/chat while the agent attempts a purchase. Accepted?
Pick an answer for each question.
Up next: Rules a Machine Can Read

The Gateway now knows who the agent is and that it may act. The next question is what the rules allow: the rulebook's short rule language of "you may / you must not / you must." That is Module 4.

04

Rules a Machine Can Read

Green light, red light, yield sign: how your spending policy becomes rules the ledger applies to every payment, and reads the same way every time.

The road signs of a rulebook

Your spending policy stops being a document nobody enforces. It becomes rules the ledger applies at the instant of every payment, and you can read those rules yourself. The ceiling from Module 1, no purchase over $1,000.00, is one of them.

You write the rule the way you would say it out loud, and it is translated into a short rule language: a handful of words, chosen so that a rule can only be read one way.

Think of driving. Every rule on the road is one of exactly three signs, and your spending policy works the same way: you may, you must not, you must.

Permitted

You may. A green light: the payment is allowed to happen. In the rulebook: permitted.

Forbidden

You must not. A red light: the payment is refused, and the reason is on the record. In the rulebook: forbidden.

Obligated

You must, by a deadline. A yield sign: a duty comes due, and the ledger tracks it until it is discharged. In the rulebook: obligated.

Why this matters to you

These three words are what turn your written policy into something enforced. "Make sure receipts get filed" is a line in a manual that nobody applies to a payment. "A buyer is obligated to file a receipt, at a higher priority than the permission to buy" means exactly one thing, and the ledger applies it to every purchase.

A real policy, read top to bottom

You can read your own rulebook, start to finish. Two rules decide whether a purchase is allowed at all. The first, permit_purchase: anyone in the buyer role may make a purchase, at a weak priority of 1. The second, forbid_over_ceiling: a buyer is forbidden to purchase when the amount is over $1,000.00, at a strong priority of 10. Amounts are held in whole cents, so nothing rounds: 100000 is $1,000.00.

That is Axorum's demo policy as it stands, written in a short rule language. The text you can read is the text the ledger applies: there is no second version behind it.

In plain English

Rule one: the permission to buy.

Anyone in the buyer role may make a purchase: a green light.

It carries a weak priority of 1.

That is rule one, in full.

Rule two: the ceiling.

A buyer is forbidden to purchase: a red light,

but only when the amount is over $1,000.00. (Amounts are held in whole cents, so nothing rounds: 100000 is $1,000.00.)

It carries a strong priority of 10.

That is rule two, in full.

FOR YOUR ENGINEERS · DEMO_POLICY
  rule permit_purchase {
    permitted any_in_role(buyer) do purchase
    priority 1
  }

  rule forbid_over_ceiling {
    forbidden any_in_role(buyer) do purchase
    when { ( param(amount) > amount(100000, USD) ) }
    priority 10
  }

When two signs disagree

The higher priority wins, and only where it applies. Both rules cover the same action, a purchase, so the ceiling's priority of 10 beats the permission's priority of 1. But the ceiling only overrides the permission when the amount is actually over $1,000.00.

A $500.00 coffee order

The ceiling rule's when condition does not hold ($500.00 is not over $1,000.00), so only the permission applies. The payment is recorded as Approved.

A $2,500.00 order

Now both rules apply. Priority 10 beats priority 1, so the ceiling wins. The payment is Refused, nothing crosses the books, and the reason is on the record.

Your approval limits already read this way

An exception that beats the default is how your own spending policy already reads: generally allowed, except above a limit. The specific rule beats the general one, and only in the cases it was written to cover.

Match each word to its meaning

Drag each word of the rule language onto the road sign it belongs to.

"You may do this."
Drop here
"You must not do this."
Drop here
"You must do this."
Drop here
"When two rules clash, the bigger number wins."
Drop here
"The deadline: act within five of the ledger's own steps."
Drop here

A "yield" sign comes with a clock

Not every rule stops a payment. Some open a duty that comes due, and the ledger tracks it until it is discharged. The demo policy carries one, receipt_duty: every purchase obliges the buyer to file a receipt within five of the ledger's own steps. The ledger counts its own events, not calendar days, so the same sequence always plays out the same way. Miss the deadline and the rulebook opens a penalty duty in its place, late_penalty. The rule carries a priority of 5.

A green light lets you go and a red light stops you. A yield sign says something else: you must act, and there is a deadline.

In plain English

Rule three: the receipt duty.

A buyer is obligated to file a receipt: a yield sign.

What starts the clock? Any purchase. That is the trigger.

The deadline: within 5 of the ledger's own steps.

If the deadline passes with no receipt, late_penalty opens a new duty to pay.

It carries a priority of 5.

That is the obligation, in full.

FOR YOUR ENGINEERS · receipt_duty
  rule receipt_duty {
    obligated any_in_role(buyer) do file_receipt
    trigger purchase
    within(5)
    on_violation late_penalty
    priority 5
  }
The ledger comes to collect

An obligation is a promise the ledger remembers. In the next module, the Vault collects on this duty, and if the receipt never lands, the broken promise becomes a new penalty duty. The whole rulebook is built from a handful of words: permitted, forbidden, obligated, trigger, within, priority, and nothing else.

Check your instincts

Each question is a payment arriving at the checkpoint. Read the rulebook the way the ledger reads it, and decide what gets recorded.

A buyer submits a $999.99 purchase, then a $1,000.01 purchase. What does the ledger record for each?
You want to always allow purchases from one trusted vendor, even over the $1,000.00 ceiling. Using only the words in this rule language, what do you change?
A buyer makes a purchase but never files a receipt. Under the receipt rule, what happens after five of the ledger's own steps?
Pick an answer for each question.
Up next: The Five Laws

You have read the rulebook. Next, the Vault enforces it. There are two sides to every payment, the arithmetic of the books and the authority, and both are checked before a single balance moves: if either side fails, nothing crosses the books. That same checkpoint comes to collect on the receipt duty.

05

The Five Laws

Every payment passes five inspections in one step, or nothing is recorded.

Five inspectors, one checkpoint

Your own accounting rules and your own spending policy are checked together, before any money moves. Five inspectors stand at the checkpoint you met in Module 1, and every one of them has to stamp the payment.

Your transaction only crosses if all five inspectors stamp it OK. If any single one refuses, the payment is turned away and nothing is written down. The Vault is a scrupulous bookkeeper running five checks in a single instant.

Law 1 · The entry balances

Debits equal credits, in every currency on the entry.

Law 2 · Someone authorized it

The payment was allowed under the rules your team wrote.

Law 3 · The budget holds

The running total spent stays under the budget granted.

Law 4 · Revenue matches what was earned

You never count more as earned than customers actually prepaid.

Law 5 · The bill matches the meter

You never bill past the meter, and never meter past what was delivered.

Two sides, one check

There are two sides to every payment. Four of these laws are the arithmetic of the books: the entry balances, the budget holds, revenue matches what was earned, the bill matches the meter. The fifth is the authority: allowed or forbidden under the rules your team wrote. Every one of them is checked before a single balance moves, and new laws join the same single check as your books need them.

Law 1 · The entry balances: the scale must sit level

Debits equal credits, in every currency, or nothing posts. The first inspector keeps a balance scale. For each currency, the total debits (money out) must exactly equal the total credits (money in). This is conservation: money is never created or destroyed, it only moves.

Debits out $1,000.00 = Credits in $1,000.00 The scale is level. Inspector 1 stamps it OK.

The ledger walks every currency on the entry and compares the two totals. If one currency is off by a single cent, nothing posts, and the refusal names the currency that is off and both of its totals.

In plain English

Take each currency on the entry, one scale at a time.

If the money going out does not exactly match the money coming in...

...refuse the entry, and put the reason on the record...

...naming the exact currency that is off...

...and reporting both totals, so your team can see the mismatch.

Only when every currency balances does the entry post.

FOR YOUR ENGINEERS · conservation.rs
    for (currency, (debits, credits)) in totals {
        if debits != credits {
            return Err(SubstrateError::ConservationViolation {
                currency,
                debits,
                credits,
            });
        }
    }
    Ok(())
One scale per currency

Dollars are weighed against dollars, euros against euros. A payment in two currencies has to balance on both scales. That is the trap in the first quiz question.

Law 2 · Someone authorized it: was it allowed?

A payment can balance perfectly and still be refused. The second inspector does not look at the money at all: she asks whether the payment was allowed under the rules your team wrote, and the Judge reads the rulebook in force to return a verdict. These are the same rules you wrote in Module 4, now applied at the instant of the payment.

Approved

The rules allow it. The payment posts.

Obligation opened

Allowed, and it leaves a duty to discharge later.

Refused

The rules forbid it. Nothing crosses the books, and the reason is on the record.

Refused is the only outcome that stops a payment. A forbidden payment that a person with the authority overrode does still post to your books, recorded as Approved by exception, with the rule it broke and the person who overrode it named on the record.

In plain English

Read this payment against the rulebook in force right now, not the one from last quarter.

If the rules cannot be read at all, stop: nothing is recorded.

The payment posts on every outcome except a Refused.

FOR YOUR ENGINEERS
        let decision = self.evaluate_active(&active_id, &proposal)?;

        let posted = decision.verdict != Verdict::ForbiddenRejected;
Authority, not arithmetic

Law 1 asks whether the numbers add up. Law 2 asks a different question: was anyone allowed to do this? A perfectly balanced payment to a vendor your policy forbids is refused here, with the reason on the record. A refusal is as much a record as a payment.

Law 3 · The budget holds: the prepaid card with a limit

What gets checked is the running total, not the size of the latest charge. The third inspector holds a mandate: a prepaid debit card loaded with a budget. No matter how many small swipes an agent makes, the running total can never pass the card's ceiling. The budget is cumulative: it watches the sum of everything spent, not just the size of the latest charge.

Mandate ceiling $5,000.00 Already spent: $4,980.00 Left: $20.00 A new $40.00 swipe would push the total to $5,020.00, over the ceiling. Refused.

The ledger adds this charge to everything already spent against the mandate. If the new running total would cross the ceiling, the payment is refused before the money moves, and the refusal records the ceiling, the amount already spent, and the amount attempted.

In plain English

Would this charge be admitted? Start with the total already spent against the mandate...

...and add this new charge on top of it...

...(refusing outright if the figures are too large to add at all).

If that new running total climbs past the ceiling...

...refuse, and report the ceiling, what was already spent, and what was attempted.

The sum of everything, across every payment, can never exceed what was authorized.

FOR YOUR ENGINEERS · mandate.rs
    pub fn admits(&self, delta: i128) -> Result<(), SubstrateError> {
        let next = self
            .executed
            .checked_add(delta)
            .ok_or(SubstrateError::Overflow)?;
        if next > self.ceiling {
            return Err(SubstrateError::AuthorizationExceeded {
                mandate: Box::new(self.id.clone()),
                ceiling: self.ceiling,
                executed: self.executed,
                attempted: next,
            });
        }
Prevention, not detection

A rule that says "no single purchase over $1,000.00" is defeated by fifty small purchases. A cumulative ceiling is not: it stops the running total the instant it would cross the line. This is the control to require before anyone hands an agent a budget, and it is the difference between preventing the overspend and reading about it at the close.

Law 4 · Revenue matches what was earned: the gift card you sold

Recognized revenue can never exceed what was actually prepaid, and the ledger enforces that itself. The fourth inspector guards deferred revenue: money taken in advance but not yet earned, like a gift card you sold but the customer has not redeemed. You can only recognize (count as earned) as much as was actually prepaid.

Deferred (prepaid) $500.00 gift card sold Recognized (earned) $500.00 fully redeemed Trying to recognize $50.00 more would book $550.00 as earned against only $500.00 prepaid. Refused.

If an entry would book more as earned than was ever prepaid, it is refused, and the refusal records which deferral it touched, the prepaid total, the earned total, and the amount attempted. Your quarter cannot be overstated by an agent, or by anyone else posting through the ledger.

In plain English

If, after this entry, the earned amount would exceed the prepaid amount...

...and the entry is booking more as earned, not less...

...refuse the whole entry...

...naming which deferral is affected...

...and reporting the prepaid total, the earned total, and what was attempted.

The matching principle, enforced by the ledger itself, at the instant of the entry.

FOR YOUR ENGINEERS · deferral.rs
        if next_deferred < next_recognized {
            if recognize_delta > 0 {
                return Err(SubstrateError::RecognitionExceedsDeferred {
                    deferral: Box::new(self.id.clone()),
                    deferred: next_deferred,
                    recognized: self.recognized,
                    attempted: recognize_delta,
                });
            }

Law 5 · The bill matches the meter: the meter on the wall

Nobody can be billed for service that was never delivered, and the ledger enforces that itself. The fifth inspector reads a meter. Your water company may bill you only for what the meter read, and the meter may only read what actually came through the pipe. Axorum keeps that same discipline for every metered service an agent buys or sells, whether it is API calls, compute time, or seats.

The ledger tracks three running totals for each metered service: what was consumed (the resource the agent actually used), what was metered (the measurement of that consumption), and what was billed (the money charged for that measurement). The order can never run backward. Billing may never pass metering, and metering may never pass consumption.

Metered service: api-tokens Consumed (actually delivered)1,000 Metered (measured)1,000 Billed (charged)900 A charge for 300 more would take billing to 1,200 against only 1,000 metered. Refused.

Metering more than was consumed is phantom usage: a charge for delivery that never happened, and the fraud this law exists to prevent. Billing more than was metered is a charge with no meter reading behind it, which is what the revenue recognition rules forbid: you may not bill beyond the measured service you actually delivered.

The three totals are not required to match at every moment. Service delivered but not yet measured, and service measured but not yet billed, are ordinary and lawful: that is work in flight, and reconciliation closes the gap. What is checked at the instant of every transaction is the order, and that none of the three totals ever falls below zero. Corrections run the same gauntlet: a metering correction or a credit memo is admitted only if the whole chain still holds afterward, exactly as a refund is.

In plain English

If this transaction would measure more than was actually delivered...

...refuse it, naming the metered service...

...and reporting what was delivered, what was already measured, and what was attempted.

Then ask the same question of the bill: would it climb past the meter?

If it would, refuse, and report the metered total, the amount already billed, and the amount attempted.

No phantom usage, and no charge without a meter reading behind it.

FOR YOUR ENGINEERS · crates/axorum-substrate/src/usage.rs
        if next_metered > next_consumed {
            return Err(SubstrateError::MeteringExceedsConsumed {
                usage: Box::new(self.id.clone()),
                consumed: next_consumed,
                metered: self.metered,
                attempted: next_metered,
            });
        }
        if next_billed > next_metered {
            return Err(SubstrateError::BillingExceedsMetered {
                usage: Box::new(self.id.clone()),
                metered: next_metered,
                billed: self.billed,
                attempted: next_billed,
            });
        }
The refusal, in the ledger's own words

The charge above comes back refused, in a sentence your team can hand to a customer or an auditor as it stands: "billing exceeds metered usage for usage balance 'api-tokens': attempted billed total 1200 exceeds metered total 1000 (already billed 900; 100 metered but not yet billed)." Nothing posts. The customer is not billed, your revenue is not overstated by a single unit that was never delivered, and the reason is on the record.

One instant, all-or-nothing

Nothing posts unless everything holds. The five inspectors do not stand in separate rooms; they check the same payment in one pass. All five laws are checked in full first, and only then does anything change. Step through the checkpoint below.

Law 1 · The entry balances
Law 2 · Someone authorized it
Law 3 · The budget holds
Law 4 · Revenue matches what was earned
Law 5 · The bill matches the meter
Recorded
Click “Next step” to begin
Step 0 / 7
All five, or nothing

A commit is all or nothing, in one step. Every check runs before a single balance moves. You will never find the books half posted, with the money gone and the receipt never filed. If any law fails, nothing crosses the books, and the refusal is recorded with its reason.

The clock keeps enforcing

Some payments that pass all five laws leave a duty behind, and the ledger collects on it by itself. Passing the checkpoint creates an obligation: file a receipt, deliver by a date. The Vault files it and comes back to collect, with no human chasing it.

1
The payment posts

A purchase lands at tick 1 and passes all five laws.

2
Obligation opened

The rule attaches a duty: file a receipt, due at tick 6.

3
Tick 6: the clock sweeps

Advancing the clock runs a sweep that scans every open duty for one whose deadline just passed.

4
Obligation closed, or breached

Receipt filed in time? The duty is discharged and the obligation closes. Missed? It is breached, and a penalty duty is created automatically.

Check your understanding

Six scenarios. Each one asks you to apply the five laws to a situation you have not seen yet.

1. A payment moves both USD and EUR. The USD side balances perfectly, but the EUR side is off by one cent. What gets recorded?
2. An agent has a $5,000.00 mandate and has already spent $4,980.00. It submits four separate $10.00 purchases. Each one is tiny, under any per-transaction limit. Do they all go through?
3. A company deferred $500.00 of prepaid revenue and has already recognized $500.00. A new entry tries to recognize $50.00 more. What happens?
4. You sell an API by the call. The meter reads 1,000 calls delivered this month, and 900 are already billed. The billing run submits a charge for 300 more. What posts?
5. Law 4 refuses partway through a commit. Have any account balances or the mandate's running total changed?
6. An agent has a budget for the quarter. You want it to be unable to drain that budget through fifty small payments that each sit under your approval limit. What do you require of the system before you sign off on it?
Pick an answer for each question.
Next up: When a Machine Dies

Your books cannot sit on one machine that can die. These same five laws are about to run on several copies of the books, which must agree on the order of events before anything counts. Because the same question always gets the same answer here, those copies stay identical, to the last cent.

06

When a Machine Dies

Your books cannot sit on one machine that can die, and no copy of them can quietly disagree with the others.

Your books do not live on one machine

Machines die: a power supply, a bad disk, a spilled coffee. If your books live on that one machine, they die with it. So the Vault, the books you met in Module 5, runs as several copies, each on its own machine, and each copy holds the whole book rather than a slice of it.

Copies are only worth having if they stay identical. If one of them can quietly hold a different version of the truth, your books are not safer, they are arguable. That is the work of this module: keeping books that live on more than one machine in step, payment after payment.

A marching band in perfect step

Every musician plays the same notes in the same order, so they stay together without watching each other. One leader calls the cadence. If that leader stumbles, the next player in a fixed order picks it up: no vote, no argument. That is how several copies of the books stay identical.

Each machine holds a full copy of the books, and the copies stay together because of the property Module 2 built in: the same question always gets the same answer.

Same sheet music = the same events

Every copy is handed the identical list of events to apply.

Same cadence = the same order

They apply those events in the same order, never one musician a beat ahead.

Same performance = identical books

The same question always gets the same answer, so the same events in the same order must land on the same books, to the last cent.

Your engineers can read the same idea in the code that keeps the copies in step:

In plain English

This is the band's rulebook: a published, decades-old method for keeping several copies in agreement.

It attaches to the ledger from Module 5 and lets it be copied across machines.

Every copy holds the whole book, not a slice of it.

What the copies agree on is the order the events arrived in, and nothing else.

The same question always gets the same answer, so the same order lands on the same books.

FOR YOUR ENGINEERS
//! A **pure, deterministic Viewstamped Replication (Revisited)** replica state
//! machine, plus the binding that makes an `axorum_substrate::Ledger` replicated.
//! N replicas each hold a ledger; consensus sequences the op stream; every
//! replica applies the same ops in the same order — and the substrate's
//! determinism (proven by the VOPR) does the rest.

Nothing counts until a majority agrees

A payment does not count because one machine says so. It counts when a majority of the copies have agreed where it sits in the order. The copies that must agree are called the Committee. One of them, the copy calling the order, proposes the position of each payment, and the rest follow and confirm.

Watch them post one real payment: #42, the coffee purchase. The lead copy waits for a majority to confirm before anything is declared final.

0 / 6 messages

Three moves, and a payment is real only once the second one has happened:

Proposed

The lead copy announces the next payment and its position in the order: log this as number 42.

Confirmed

Each other copy replies that it has logged it. A majority of those confirmations is what makes payment #42 real.

Posted

Once a majority has agreed, every copy applies it. All of them post the same payment, in the same place in the order.

When the lead copy goes dark, the close carries on

If the copy calling the order fails, the other copies keep posting payments without it, and nothing a majority had already agreed on is lost. A band does not hold a vote when the leader stumbles: the next player in line picks up the cadence. The copies do the same, counting off a numbered era of leadership.

  1. The lead copy goes quiet
  2. The other copies notice it has stopped
  3. All of them move to the next era of leadership
  4. The published order, next in line, names the new lead copy
  5. Payments keep posting, and the cadence is never lost

There is no vote and no argument, because every copy already knows who is next. Your engineers can read why this method was chosen over the more common one:

In plain English

The method covers four situations, including the one that matters at close: the lead copy goes dark and another has to take over.

It was chosen over the more common method because it can recover after a crash without reading anything back off the disk.

That suits books that are held in memory and never have to trust what was saved.

Leadership is a fixed rotation, next in line, not a random election, so the handover is predictable and two copies never end up calling the order at once.

FOR YOUR ENGINEERS
//! normal operation, view change, recovery, and state transfer. Chosen over Raft
//! for its no-disk recovery (safety never depends on persisted state — a perfect
//! match for a pure in-memory core) and deterministic round-robin leadership
//! (`primary = view mod N`, no randomised election timers).

A payment is on disk before you are told it happened

Agreement keeps the copies matching while the machines are running. What carries your books through a data centre losing power is the disk: a payment is written to disk before anyone is told it went through, so a payment you were told had posted is still there when the lights come back on.

The note comes first

Each payment is written down before it is applied, so a machine that dies halfway through finishes the job when it comes back.

Onto the disk, not into memory

A payment is physically written to disk before anyone is told it went through. If the power fails one millisecond later, the payment is still there when the machine comes back.

Forcing every payment onto the disk on its own would be slow under load. So a burst of payments is bundled and pushed down together in a single write: one write per burst, not one per payment. Nothing is reported as done until that write has finished, so the guarantee does not change:

In plain English

The agreement between the copies is untouched: it still handles one payment at a time.

What gets bundled is the writing to disk, a whole burst of payments at once.

Each payment's note is queued, not pushed to the disk on its own.

Then one write forces the entire burst onto the disk.

Only then is anyone told the payments went through. Nothing is released early.

A burst of payments costs about one disk write instead of one each, and the guarantee is unchanged.

FOR YOUR ENGINEERS
//! The pure consensus core still processes one op per transition — no protocol
//! change. **Durability**, however, is batchable: the durable binding exposes
//! `propose_batch` and `on_messages`, which run a run of `propose` / `on_message`
//! transitions each with a *deferred* write-ahead, then a **single** `fsync`
//! makes the whole batch durable before any effect is released (J-WA,
//! amortized). ... a burst of N ops costs ~1 fsync, not N.
Agreement on the order, not on the answer

The copies never have to agree on the answer. They only have to agree on the order the payments arrived in. Because the same question always gets the same answer, the books then match on their own, to the last cent.

Think it through

Three situations from a close. Reason from what the band taught you.

Three copies of the books receive the same payments, but each applies them in a different order. Do the three sets of books still agree?
Your team is told a payment has posted. One millisecond later the data centre loses power. When the machines come back, is that payment still on the books?
It is 4pm on the last day of the close. The copy calling the order goes dark, and payments are still arriving. The handover rule is published in advance: next in line. What happens to your close?
Pick an answer for each question.
Next: Onto the Rails

Your books now survive a dead machine and a power cut. In Module 7, real money moves across a payment rail, and it cannot move until the payment has committed here: all five laws checked, a majority of the copies agreed, and the payment on disk. Nothing is released onto the rail before that.

07

Onto the Rails

The money cannot move without the ledger's approval. Not as a policy, not as a discipline, but as a locked gate with no way around it.

A turnstile that fails closed

No money leaves unless the ledger has already approved the payment. Picture a subway turnstile: it stays locked by default, it unlocks only when a valid ticket is stamped, and there is physically no lever to shove it open by hand.

The rail moves money only when the ledger stamps the payment as approved. No stamp, no unlock. The gate fails closed.

The metaphor for this module

A locked turnstile. The ledger holds the only ticket stamp. A payment the ledger has not approved cannot pass, and there is no manual override.

Axorum is not the rail

Keep the two apart, because this module is the easiest place to confuse them. The rail moves the money. Axorum decides whether it may. Axorum does not move money, hold funds, or settle anything: it sits on the books, underneath whichever rail is already in use, and it withholds the approval when a payment breaks a law. Everything that follows is about the approval, never the custody of a single dollar.

Where the turnstile sits

The agent no longer talks to the merchant directly. A gate sits between them, and it holds the only ticket stamp.

  1. Buying Agentwants to pay for something
  2. The Gate (Turnstile)the x402 checkpoint
  3. Merchantdemands payment first

This module uses x402 as the worked example because it is the one you can watch end to end today. The gate is not tied to it. AP2 and ACP are the other standards agents are settling on, and they attach at the same seam: the payment is held, the ledger rules on it, and only an approved payment goes out. Swapping the rail changes the paperwork at the edge, never the laws in the middle.

Watch the gate do its job

The gate never signs and pays on demand. When the merchant asks to be paid first, the gate puts that payment through Axorum's five laws before anything is signed. Step through it.

Agent
The Gate(Turnstile)
Merchant
AxorumLedger
SignedPayment
Click “Next step” to begin
Step 0 / 7

What the gate does, in one sentence

The agent points at the gate instead of the merchant, so every payment it wants to make goes through the gate first. The gate is a stand-in for the merchant: it holds the request, sends the payment to the ledger, and signs only if the ledger approves. Nothing is signed on the way in.

In plain English

The buying agent points at the gate, not at the merchant.

When the merchant asks to be paid, the gate catches the request.

Before anything is signed, the gate puts that payment through the ledger's laws.

There are two sides to it: the arithmetic of the books, and the authority to spend. Both are checked in one step, and nothing posts unless every law holds. (The comment above is written in the engineers' older wording. All five laws run today, and a simple payment like the coffee purchase in Module 1 rests on them.)

The payment posts as approved, or no authorization to pay is ever created.

FOR YOUR ENGINEERS
//! instead of at the merchant; the sidecar reverse-proxies the request to one
//! configured upstream. When the merchant answers with an [x402] `402` demand,
//! the sidecar **gates that payment through Axorum's dual-law ledger before
//! anything is signed**: the intent must commit as permitted — conservation and
//! policy, atomically — or no payment authorization is ever created.

Why nobody can force the gate

To sign a payment you need a permit, and nobody can make one. The only way to get a permit is to ask the ledger through a single door, and it hands one back only when the payment posted to the books and the rules allowed it. That is a rule the system cannot break, not a step someone has to remember to run.

A refusal gets no signature. Neither does an approval by exception: if a person with the authority overrides the rulebook and pushes a forbidden payment through, it is recorded in your books as Approved by exception, and the gate still will not sign it onto the rail. Posting to your books and moving money on the rail are two different events, and the gate holds the second one.

In plain English

Signing a payment requires a permit, and no part of the system can build one.

The one way to get a permit is to ask the ledger, through a single door.

It hands one back only when the payment posted to the books and the rules allowed it.

A refusal gets no permit. Neither does a forbidden payment that a person overrode: the override is recorded in your books, and it still is not signed.

No permit, no signature, and the merchant sees nothing. The gate fails closed.

FOR YOUR ENGINEERS · gate.rs
//! The **no-bypass invariant** lives here too. A payment can only be signed with
//! a `Permit`, and a `Permit` has no public constructor: the sole way to obtain
//! one is `authorize_payment`, which yields `Ok` **only** when the ledger both
//! posted the transaction and returned a clean `permitted` verdict. Every other
//! outcome — a forbidden rejection, or a commit carrying conditions this gate
//! will not auto-sign — returns a `LedgerRefusal`, and the upstream merchant
//! never sees a signature. The gate fails closed.
A control nobody can skip

This is not a control someone has to remember to run. There is no flag, no admin switch, and no path around it, because the part that signs a payment cannot run at all without the ledger's approval. The gate stays locked by default.

The whole system, stacked

Axorum sits under the rails, on each participant's own books. Click any block to see what it does. You have met all of them across this course.

Payment Rails (real money)
The Turnstile: gates the rail
Axorum Core: each party's own books
Select a component to see what it does.

What changes, and what does not

Add a second rail

Card payments as well? A second gate goes at the boundary. The books, the five laws, and the copies that must agree stay exactly as they are.

Rewrite the rulebook

New spending policy? Edit the Rulebook. The Judge applies whatever rules are in force at the instant of the payment, with no surgery on the system itself.

Add more copies

Need more resilience? Add machines to the Committee. Because the same question always gets the same answer, every copy of the books stays identical, to the last cent.

Think it through

Three situations. Each one asks you to use what you now know about the turnstile, not to recite it.

Which is correct?
Which is correct?
Which is correct?
Pick an answer for each question.

Remember the coffee purchase from Module 1, the one we used to ask "who approved this?" With the turnstile in place, that same purchase can be real money on the x402 rail, settled by an outside facilitator: the agent asks, the five laws are checked in one step, and only an approved, balanced, in-budget payment is ever signed.

One question remains

The ledger now checks every action, keeps copies of the books that agree with each other, and gates real money so that it fails closed. A company adopting it always asks one last question: how does all of this land in the books I already keep? That is the finale: Module 8, Books That Agree.

08

Books That Agree

How the ledger hands its records to the books you already keep, and proves the two never drift apart.

The last question

Axorum's records flow into the general ledger you already run, and reconciliation then checks the two against each other, line by line. That is the last thing a finance team asks about before adopting it: how does all of this land in the books I already keep?

Every business already runs a general ledger inside an accounting system like NetSuite, SAP, or QuickBooks, an ERP. Axorum does not replace it. It keeps the one thing that ledger has no column for: whether the spending was allowed, and on whose authority.

A record of authority, not a second set of books

Axorum records what was allowed: who was permitted to spend, under whose authority, and which duties came due. Your general ledger stays the book of record for what was posted. Reconciliation lays the two side by side and proves they agree, or names the line that does not.

Two independent scorekeepers

Two scorekeepers sit at opposite ends of the court, each keeping their own sheet. One tracks what every player was cleared to do; the other tracks what actually went on the board. Neither trusts the other blindly, and at the end you lay the two sheets side by side, line by line.

Scorekeeper A: Axorum

Records what was allowed: who was permitted, under whose authority, which duties came due. The half your books do not keep.

Scorekeeper B: Your General Ledger

Records what was posted: the actual money on the books. The official financial half.

Laying the sheets side by side

That comparison, line by line, is reconciliation. It is what proves the two sheets agree.

The envelope

Every committed transaction leaves the ledger as one sealed export record, and it carries five things: the journal entry, the outcome, which rule decided and under whose authority, every duty the transaction opened, and a plain English summary of the whole event.

It is vendor-neutral and carries a version stamp, so any system, now or years later, knows exactly how to read it.

In plain English

One committed transaction, packaged into one envelope an auditor could read cold.

The entries: the journal entry, every row of it, and it has to balance.

The outcome: was the spending allowed, and did it leave a duty behind?

Who decided: which rule applied, and under whose authority.

The duties: each obligation opened, and what became of it.

The summary: the whole event in one plain English sentence.

FOR YOUR ENGINEERS
pub struct ExportRecord {
    pub identity: RecordIdentity,        // (5) txn id, commit tick, policy hash, schema version
    pub verdict: VerdictRecord,          // (2) the normative verdict + its English sentence
    pub legs: Vec<ConservationLeg>,      // (1) the multi-row journal entry
    pub provenance: Provenance,          // (3) which rule decided, under whose authority
    pub obligations: Vec<ObligationLifecycle>, // (4) each duty created + its fate
    pub narrative: String,               // the full compliance-English narration
}

The half no general ledger has

Your general ledger records the money. It has no column for whether the action was allowed in the first place. Axorum keeps that answer, and every committed transaction carries exactly one of five outcomes: Approved, Refused, Approved by exception (a person with the authority overrode the rule, and is named), Obligation opened, or Obligation closed.

In plain English

The five outcomes your general ledger has no column for.

Approved: allowed under the rules in force, and it posts clean.

Refused: the rules forbid it, nothing crosses the books, and the reason is on the record. Or approved by exception, where a person with the authority overrode the rule and the override is recorded against their name.

An obligation opened: the transaction leaves a duty still owed...

...or an obligation closed: the duty is discharged. This is the record a plain ledger never keeps.

FOR YOUR ENGINEERS
pub enum ExportVerdict {
    Permitted,
    ForbiddenRejected,
    ForbiddenRecorded,
    ObligatedPending,
    ObligatedFulfilled,
}

One envelope, two wrappers

The record your accounting system receives and the record your data team analyzes are the same record. Nothing is re-keyed between them, and no amount is ever rounded on the way, because every figure is carried as exact whole cents.

The same record ships two ways, because rows and columns answer different questions. One record at a time, whole, for the connector that posts a single transaction into your books. Or stacked as columns, so a warehouse can total ten million of them at once. Ask what agents spent last quarter against those ten million records and you are reading the same figures your auditor would.

In plain English

One published column layout, carrying the same version stamp as the single-record form.

One step restacks a pile of envelopes into columns...

...one writes them out where a warehouse can read them directly...

...and one reads them back. The round trip changes nothing, and a test holds it to that.

The amounts stay exact whole numbers of cents, natively.

The single-record form has to write large amounts as careful text to avoid a figure that quietly rounds; here the exact figure is a column in its own right.

FOR YOUR ENGINEERS
pub fn schema() -> SchemaRef;                  // the one columnar shape, version stamped in
pub fn record_batch(records) -> RecordBatch; // envelopes restacked as columns
pub fn write_parquet(records, writer);       // ...and onto disk, warehouse-ready
pub fn read_parquet(reader) -> Vec<ExportRecord>; // and back out, unchanged

// the money column, exactly:
req("amount_minor", DataType::Decimal128(38, 0))
Two forms that cannot drift apart

The promise is the record, not the file it travels in, and tests hold both forms to it. Every field in the column layout matches the single-record form one for one, and every outcome, direction, and duty word is checked, word for word, against the same vocabulary; rename a field or retype a column and the build fails. It is this module's thesis in miniature: two independent copies of the same story, provably in agreement.

The resumable stream

The envelopes reach the other scorekeeper through a resumable feed: a numbered, append-only logbook that never gets re-paginated. Your connector reads from its bookmark, takes what is new, posts it into your books, and saves a new bookmark for next time.

Resumable

Each reader keeps a bookmark: give me everything since where I left off.

Read-only

The reader never writes back. It cannot change the ledger, only observe it.

Never loses a record

It will never skip a record, though after a crash it may hand you the same one twice; your connector recognizes the repeat by its transaction number.

In plain English

Give me everything since where I left off. That is the whole request.

It takes the ledger's committed records...

...and starts a fresh, empty batch to fill.

It walks only the new tail, from your bookmark onward. Nothing already delivered is read again.

For each new transaction...

...it writes out the plain English account of what happened and why...

...and packs the finished envelope into the batch.

It only ever reads and packs. It never writes back, so a connector cannot alter your ledger.

It hands back the new records plus an advanced bookmark. Save it, and a restart resumes exactly here.

FOR YOUR ENGINEERS
pub fn read_from(ledger: &Ledger, ctx: &ReportContext, cursor: Cursor) -> Result<Batch, CdcError> {
    let records = ledger.records();
    let mut projected = Vec::new();
    for record in &records[cursor.pos()..] {
        if let LedgerRecord::Transaction { id, .. } = record {
            let explanation = explain(ledger, ctx, id)?;
            projected.push(export(&explanation));
        }
    }
    Ok(Batch { records: projected, cursor: Cursor::at(records.len()) })
}

Two sheets, side by side

Reconciliation gives every line one of five outcomes, and one of them is a control you do not have today: it names anything that reached your books with no authorization behind it. The envelopes have been posted into your general ledger; reconciliation now lays Axorum's record of what was authorized against your ledger's record of what was posted.

AxorumLedger
ExportEnvelope
ResumableFeed
Your GL/ ERP
Reconciliation
Click “Next step” to begin
Step 0 / 6

Five outcomes on each line

Line up the two sheets and each pairing lands in exactly one bucket. Four are routine. One is a red flag.

Matched

Both sheets agree: same transaction, same amount. Nothing to do.

Amount break

Same transaction, but the money is off. Axorum says one figure, the GL posted another.

Timing break

Right amount, wrong period. The two sheets disagree on when it landed.

Posted-not-authorized

THE RED FLAG. The GL posted something Axorum never authorized: a payment nobody approved reached your books.

Authorized-not-posted

Axorum approved it, but the GL never posted it. The books are missing a line.

In plain English

Every reconciled line becomes one of these five outcomes.

Matched: it agrees. Move on.

Amount break: the two figures do not match, and the line keeps both numbers.

Timing break: same money, different period.

Posted-not-authorized: the red flag, and it carries the reason.

Authorized-not-posted: Axorum approved it, the books never recorded it.

FOR YOUR ENGINEERS
pub enum ReconOutcome {
    Matched { transaction_id, external_ref, amount_rendered },
    AmountBreak { transaction_id, external_ref, axorum_amount_minor, posted_amount_minor },
    TimingBreak { transaction_id, external_ref, amount_rendered, axorum_period, posted_period },
    PostedNotAuthorized { external_ref, amount_rendered, reason },
    AuthorizedNotPosted { transaction_id, amount_rendered },
}

The break reads like a sentence

The red flag is not a cryptic error code. It comes out as a plain English sentence naming the amount and the exact rule that was violated, ready to drop straight into an auditor's report.

In plain English

When your books posted something a rule forbade...

...the break names the amount and the exact rule that was broken.

A real figure, a real rule name, not a mystery code.

The break is already a sentence your auditor can read.

FOR YOUR ENGINEERS
PostedNotAuthorizedReason::AxorumForbade { deciding_rule: Some(rule) } => format!(
    "Governance exception: {posting} recorded {}, but Axorum forbade this action under the rule \u{2018}{rule}\u{2019}.",
    entry.amount_rendered,
),
Watch this one

Posted but never authorized is the line every finance team with spending agents should watch. A payment that reached the books with no authorization behind it is the exposure you cannot see today, and this is the control that names it.

Rules that know your books

Your approved vendor list and your closed months become rules the ledger enforces at the instant of a payment. Axorum imports your master data, the chart of accounts, your approved counterparties, and which fiscal periods are open, and keeps its own copy of each list. The rulebook from Module 4 then names those lists directly.

In plain English

A reference set is a named list Axorum keeps a copy of.

Only pay vendors on the approved list.

Only post to accounts that exist on the chart.

Only post into a period that is still open.

And here is a real rule: forbidden to post when now is not in the open periods.

The rule consults your imported list at the instant of the transaction, not at month end.

FOR YOUR ENGINEERS
pub enum ReferenceSet {
    ApprovedCounterparties, // only pay approved counterparties
    GlAccounts,             // only post to accounts on the chart
    OpenFiscalPeriods,      // only post into an open period
}

// the DSL rule it powers:
forbidden … when { ( not now in open fiscal periods ) }
Fail-closed if the list is missing

If a reference set was never imported, the answer is "no", not "maybe." A rule that asks whether this vendor is approved, against a list that does not exist, fails closed, and the payment is refused. Missing data can never become an accidental yes.

The loop closes

This is Module 4's rulebook, now speaking your accounting system's own language. "Only pay approved vendors" and "only post into an open month" stop being wishes in a policy document and become rules the ledger enforces at the instant money moves.

The whole system, end to end

Eight modules, one sentence each. Read them top to bottom and you have the whole control: an agent that spends and vanishes, stopped at the instant the money moves, with every decision on the record.

1
The moment of truth

An agent that spends and vanishes has to be checked at the instant money moves. Audit it after the fact and there is no one left to ask.

2
The cast + the line

Each part has one job. The part that decides is walled off from the part that deals with the outside world, which is segregation of duties.

3
Who are you?

A stable identity, plus a tamper-evident digital pass the gateway checks before anything moves.

4
The rulebook

Your spending policy in a short rule language a machine reads: what an agent may do, must not do, and must do next.

5
The five laws

The entry balances, someone authorized it, the budget holds, revenue matches what was earned, the bill matches the meter. All five, or nothing posts.

6
When a machine dies

Your books do not sit on one machine that can die, and no copy of them can quietly disagree with the others.

7
Onto the rails

Policy becomes a gate on real money, and the gate stays locked by default. There is no override switch.

8
Books that agree

Records flow into the ledger you already keep, and reconciliation proves the two agree, or names the line that does not.

Think it through, one last time

Five situations. Each asks you to use what you now understand about handing records to your books and proving they agree.

Which is correct?
Which is correct?
Which is correct?
Which is correct?
Which is correct?
Pick an answer for each question.
That is the whole system

You now know what your customers will demand before they let an agent spend inside your product: every payment checked against their own rules before the money moves, every refusal recorded in a sentence their auditor can read, and a reconciliation that names anything posted to their books without authorization. That is a subledger, and it is not the product you set out to build. You embed it, under your own interface, on your customers' books. Bring us one agent workflow your customers are nervous about, and we will show you the record it would leave behind.