Skip to content

Contracts That Cross Frames

A contract lives where its escrow lives. What crosses the frame boundary is a signed copy of the facts and a rail to ask the owner to act. The copy is never the authority, and it is never merely hoped to be current.

Why It Matters

Two agents on different registries want one agreement. The obvious design is to put a copy of the contract on both frames and keep them in step.

That design is wrong, and it is wrong for a reason worth stating plainly: the escrow can only be in one place. Money sits in exactly one TEG. If two frames both believe they may release it, you have not built replication, you have built a double-spend waiting for a network partition. The Contract Spine already settled this question one level down — a dispute ruling and a contract resolution cannot both drive the same escrow, so one of them is the settlement authority and the other defers. Crossing frames is the same rule, one level up.

So the contract does not travel. It has a home frame — the frame holding the escrow — and that frame is the only place a transition can happen.

What travels is a replica: a read-only, signed record of the contract's facts, materialised on the counterparty's own registry so their interfaces, their agents and their dispute filings can see the agreement without pretending to own it.

HOME frame                                  PEER frame
  contracts, escrow, authority       ──▶      contract_replicas  (read-only, signed, inert)
  ▲                                                 │
  └────────── relayed command ─────────────────────┘

A replica has no escrow reference and no fee or settlement columns. Not "we do not use them" — the columns are not there, and the test suite asserts their absence. A replica cannot move money because there is nothing in it to move.

What Makes a Copy Trustworthy

Two things, in this order.

It is signed. The home frame signs a digest of the contract's material facts — id, home, status, sequence, both parties, currency, amount, title — with Ed25519, using the same keypair that signs its registry card. No new key material, no new trust root; a peer already knows how to verify that signature.

The digest deliberately covers material facts only. If a presentation hint changes, the signature must not break. If the amount changes, it must.

Verification runs cheap-check-first: the digest gate, then the signature. A tampered payload is rejected before any crypto is consulted. An envelope that does not verify is dropped and counted — never stored. A replica whose provenance you cannot establish is worse than no replica, because the interface will present it as fact.

It is reconciled. This is the part that matters, and it is where most designs quietly fail.

Cross-frame event fanout is best-effort by design. That is an entirely reasonable property for a cache and an entirely unacceptable one for a legal object. So the peer does not trust delivery. Every sixty seconds it takes each open replica it holds, asks the home frame for that contract's authoritative digest, and compares. On a mismatch it takes home's signed envelope as truth — or, if it cannot verify anything, marks the replica stale so the surface can say "I am not currently sure" instead of showing a number it can no longer stand behind.

The event is the optimisation. The reconciler is the guarantee.

That sentence is the whole design. Events make replication fast; the reconciler makes it true. If every event were lost forever, every replica would still converge — slower, but correctly.

Two Things The Ledger Taught Us

Push alone cannot bootstrap. Mirrors carry a deterministic idempotency key, so re-emitting an unchanged transition is correctly deduplicated by the EventStore. Correct — and it means a registry that starts listening after a mirror was published never hears about that contract at all. The reconciler cannot rescue it either, because the reconciler only repairs replicas it already holds. The gap is invisible: no error, no retry, just a contract the peer has never heard of.

So the peer also pulls. It asks each peer registry "which cross-frame contracts are addressed to me?" and adopts the signed envelopes it gets back. The caller's mTLS identity decides which rows come back; a name in the request is never trusted. This doubles as the recovery path if a replica set is ever lost entirely.

A peer has more than one name. The field naming the counterparty's home is written by several call sites, and production holds four different spellings of the same registry: the bare name, its base URL, its operator SVID, and its federation-license SVID. Three of the four were discovered by a real call failing to resolve. The resolver now recognises all four — and checks every candidate against the known-peers table before returning it. This widens what the system can recognise, never what it will trust.

Acting From The Other Side

A provider on the peer frame still needs to submit a milestone. They act on their own registry, and their registry relays the request to the home frame.

The peer registry is a courier, never a principal. Its mTLS identity authenticates the channel; it does not authorise anything. The principal is the named agent, and that agent must be a party to this contract, checked against home's live state. A trusted peer cannot act as an agent that is not theirs, and cannot touch a contract that is not homed there.

The relay accepts a closed set of actions: accept or decline an offer, submit or accept a milestone, file a dispute. Anything else is refused by construction rather than by validation, because a relay that accepts arbitrary actions is not a relay, it is a remote procedure call with a trust boundary drawn in the wrong place.

Retries are safe: the idempotency key is recorded in the contract's own event trail — the same trail the digest is derived from — so a repeated relay is a no-op, not a second transition.

How It Ships

One flag, three positions: off, shadow, enforce.

Off is byte-inert: the worker idles, the reactor ignores everything, the endpoints return 404. Shadow runs replication for real — mirroring, adoption, reconciliation all live — while a relayed command is authenticated, authorised, deduplicated, audited, and then explicitly not applied. That is the posture that lets the whole rail be exercised against production traffic before it can change anything. Enforce lets commands apply, and is a deliberate per-registry decision.

Live in shadow across the frame-b and frame-c tier and their cloud-ops. Frame A is untouched.

Nothing here can affect the supply invariant, because nothing here moves money.

Server components AGPL-v3 · client SDK Apache-2.0. If a doc and the running stack disagree, trust the stack.