Revised 2026-08-22. The original version collapsed two distinct boundaries into one sentence, called the overdraft guard a sieve (it isn't), and overstated what the tropical eigenvalue does under composition. Those are fixed below; the change log at the end lists them.
In Earned Compression, we showed that a three-party payment cycle exhibits the same core-observer decomposition as tic-tac-toe — the core is a timed event graph with tropical eigenvalue λ = 1, and the observer (overdraft guards) reads state catalytically without feeding tokens back.
But we treated that network as a fixed object. Real settlement systems aren't fixed. Channels are added, split, merged. Parties join and leave. The interesting question isn't "what are the properties of this network?" — it's "how do properties compose when we build networks from parts?"
That question has a precise answer in the language of open Petri nets — provided we are careful about which of two boundaries we are talking about.
Settle isn't one payment network. It's the symmetric monoidal subcategory of OPetri — the category of open Petri nets (Baez & Master, 2020) — generated by two transition shapes, send and settle. It is the category of all settlement networks built from those primitives.
send or settle, a channel, a whole meshThe three levels matter, because the original version of this post conflated them. The three-party cycle from Earned Compression is not a category and not an object: it is a morphism — and because the cycle closes, an endomorphism on its glued boundary. A two-party channel is another morphism. A five-party mesh is another. They are all built by composing the same generators, which is what puts them in the same category.
An open Petri net is a net with designated boundary places — exposed interfaces along which subnets compose. Each payment channel is an open subnet:
The channel ch_ab (Alice → Bob) is an open net with input boundary {a} and output boundary {b}. Internally it has two transitions (send_ab, settle_ab) and an internal place (pending_ab). The boundary places — alice_bal and bob_bal — are the wires that stick out, available for composition.
The full network is the composition of three open nets, glued by identifying shared boundary places:
cycle = (ch_ab ;_b ch_bc) ;_c ch_ca
where ;_b means "compose along shared boundary place b." The output boundary of ch_ab is {b}, and the input boundary of ch_bc is {b} — gluing them identifies Bob's balance as the shared wire. The cycle closes when ch_ca's output {a} feeds back to ch_ab's input {a}, which is what makes cycle an endomorphism.
This is sequential composition in a symmetric monoidal category. Each channel is a morphism. Composition is wiring outputs to inputs. The monoidal product ⊗ would give us parallel, independent channels — two settlement cycles running side by side with no shared accounts.
The core of Settle is a timed event graph: every place has exactly one producer and one consumer. This gives us a precise definition of an event. A token passing through a place isn't "something happened" — it's a specific morphism in the category: one transition fired, one token moved, one marking transformed to another. The place is the channel. The token is the evidence.
An event is a generating morphism in Settle, witnessed by a token traversal, provable in zero knowledge.
The incidence matrix records the effect. The tropical eigenvalue gives the throughput. The ZK proof certifies the event was valid — in 128 bytes. What the proof hides depends on the application: in a game, which move was played; in a settlement network, the amounts and balances.
The string diagram isn't a visualization of the incidence matrix. It is the incidence matrix, rendered as topology. Every wire is a token type (account balance or pending amount). Every box is a transition. Reading the diagram left to right gives the sequential composition; reading it top to bottom gives the parallel structure. The incidence matrix C is what you get when you compile the diagram down to a table.
This means the diagram is simultaneously:
a + b + c + p_ab + p_bc + p_ca = const follows from the conservation law visible in the wiring: every wire consumed on the left is produced on the rightThe original version of this post called the observer a sieve — a subfunctor of a representable presheaf that selects which morphisms are admitted. That word doesn't survive a literal reading. A sieve on an object X is a set of morphisms into X that is closed under precomposition: if f is in the sieve, so is f ∘ g for every g. Admission of a payment depends on the current marking — whether the guard passes for send_ab after some history depends on that history. That is path-dependent, not closed under precomposition, and so not a sieve.
What the overdraft guard actually is, in the vocabulary the stack has since settled on, is R in the zipper decomposition W(M) = 𝓛 × M × 𝓡: a predicate over the current marking, recomputed fresh at every step, that the step function consults before committing. The guard is a signal; the step function is its handler; "the morphism is deleted, never enters the event log" is just the handler declining to continue. This post was arguing for R-as-algebraic-effect five months before the Dirac thread made that explicit — it just reached for the wrong categorical noun.
If you want to keep a sieve somewhere, there is one: the image of the guard in the reachability graph. The set of marking-to-marking edges whose guard passes is a genuine subgraph of the unguarded reachability graph, and that subgraph is closed under the things a subgraph needs to be closed under. But that is a statement about the state space, not about Settle-the-category.
A candidate payment is any morphism the incidence matrix structurally permits. R reads the core's state via a read arc (catalytic: it tests a place's marking without consuming tokens) and decides whether this firing happens at all. If the overdraft guard passes (balance ≥ amount), the event is committed. If not, nothing is committed; there was never a write to roll back.
The distinction from tic-tac-toe is precise, and the table is worth reading carefully because its second row is the seed of the next section:
| TTT Observer | Payment Observer | |
|---|---|---|
| Reads | cell history places | account balance places |
| Mechanism | consuming (multi-fan input, game ends) | catalytic / read arc (system continues) |
| Timing | post-hoc (after move) | pre-hoc (before commit) |
| On failure | game continues | event not committed |
| In C? | yes — a column of C | no — a read arc is not an incidence entry |
| ρ | 5 or 10 | 1 |
The TTT observer consumes — it is an ordinary transition with many input arcs and one output, a perfectly good column of C. The payment observer reads — it is a contextual arc with no column at all. Both are "observers" in the Earned Compression sense. They are observers for different reasons.
The original version of this post ended its categorical section with: "The core-observer boundary is, precisely, the boundary between ordinary and contextual arcs." That sentence is false for tic-tac-toe, and the table above shows why. The TTT win detector uses only ordinary arcs, lives entirely in C, composes fine in the free SMC — and is still an observer, because it has ρ = 5 and breaks the timed-event-graph property. The two definitions of "observer" that the stack has been using pick out different sets of transitions.
There are two boundaries, and they are stacked, not identical:
The ρ boundary is algebraic and lives inside C. A transition with ρ = 1 (every token consumed is produced elsewhere, one producer and one consumer per place) is part of a timed event graph. A transition with ρ > 1 breaks the event-graph property. Crossing this boundary breaks the uniform R1CS encoding (the multi-fan input needs non-uniform witnesses) and breaks the tropical eigenvalue (λ is only defined on the event-graph core). It does not break composition: Meseguer & Montanari (1990) proved that ordinary nets, multi-fan inputs and all, form a free symmetric monoidal category. TTT's win detector crosses this boundary and nothing else.
The contextual boundary is categorical and lives outside C. A read arc (test for presence) or inhibitor arc (test for absence) couples a transition to a place without an incidence entry. Montanari & Rossi (1995) showed that contextual nets do not form the free SMC — the transition's enablement depends on state that C cannot see, so the morphism is not determined by its source and target boundaries. Crossing this boundary breaks composition. It does not break ρ: a read arc adds nothing to either count, so the guarded send keeps its ρ = 1.
| ordinary arcs only | has contextual arcs | |
|---|---|---|
| ρ = 1 | core (send, settle) |
overdraft guard — categorically outside, algebraically harmless |
| ρ > 1 | TTT win/draw — algebraically outside, composes fine | crosses both |
The four quadrants are all inhabited in principle, and the two examples this series has leaned on sit in the two off-diagonal cells. That is why collapsing the boundaries into one sentence went unnoticed for five months: each example breaks exactly one thing, and in isolation each looks like "the" observer. The guard is categorically an observer and algebraically core; the win detector is algebraically an observer and categorically core. Only a guarded multi-fan transition — a win detector with a read arc on a turn place, say — would cross both, and the payment network doesn't have one.
I think this is a stronger story than the one it replaces. ρ tells you what the algebra can see: where uniform circuits and the tropical eigenvalue stop. Contextuality tells you what the category can see: where free composition stops. A transition can be invisible to one and not the other, and the design consequences are different in each case.
The two-boundary picture answers a practical design question more precisely than the one-boundary picture did: what goes inside a zero-knowledge proof and what stays outside?
The ρ = 1 core — ordinary arcs, columns of C, one producer and consumer per place — compiles directly to uniform R1CS constraints. A Groth16 proof of a send or settle transition says "a valid state transition occurred and tokens were conserved" in 128 bytes, without revealing which channel moved or what amount. The P-invariant a + b + c + p_ab + p_bc + p_ca = const is enforced by the circuit structure itself. Double-entry bookkeeping isn't a policy you audit — it's a theorem the proof system guarantees.
The contextual observer — the overdraft guard — sits naturally outside the circuit. It is R: a policy decision about whether this firing is admitted. A smart contract or network validator can enforce it without the cost of proving it in ZK. You can change the guard threshold, add new compliance rules, or swap the observer entirely — without recompiling the circuit.
This is earned compression applied to system design: prove exactly what the incidence matrix requires, defer the rest.
But we are not limited to this split. If privacy demands it — if you don't want to reveal that a payment was attempted and rejected — you can pull the guard into the circuit. The overdraft check is a range proof (bal ≥ amt), straightforward in R1CS. The contextual arc becomes an auxiliary witness: the prover supplies the current balance, the circuit checks it against the state commitment and verifies the range.
The original version of this post cited Vogler, Semenov & Yakovlev (1998) as the reason the guard cannot live in C, and then pulled it into the circuit anyway without noticing that the circuit changes the question. It is worth being exact about what VSY proved. Simulating a read arc as a self-loop — consume the token, produce it back — is inequivalent under partial-order (unfolding) semantics: the self-loop serialises firings that the read arc would have allowed to be concurrent, so the unfolding and the finite prefix change. Under interleaving semantics the reachability set is identical; the self-loop is a faithful encoding of one firing at a time.
A Groth16 proof certifies exactly one transition firing. Interleaving is the only semantics the circuit has — there is no concurrency inside a single-step proof to get wrong. So inside the circuit, the self-loop is faithful, and the auxiliary-witness range proof is literally that self-loop: the balance is read in, checked, and written back unchanged in the same step. The contextual boundary dissolves at the circuit boundary.
The ρ boundary does not. Pulling a ρ > 1 transition into a circuit still costs the non-uniform gadget, because that is a fact about C, and the circuit is a compilation of C. Which is the practical content of having two boundaries: the circuit erases the categorical one and leaves the algebraic one standing.
The key property of the OPetri framework: adding a fourth party is composing another open subnet along new boundary places. Splitting a channel into two hops is refining one morphism into a composite of two.
Three of the invariants this series has used compose cleanly:
The tropical eigenvalue does not compose this cleanly, and the original version of this post claimed that it did: "if we know each channel's circuit weight, we know the composed system's throughput without re-analyzing from scratch." That is wrong. λ is the maximum over cycles of mean weight. Gluing along a boundary place creates new cycles that pass through the glue and belong to neither component. What you actually get is
λ(A ;_p B) ≥ max(λ(A), λ(B))
with equality exactly when no cycle through the glue beats the best local cycle. The settle cycle is the counterexample to its own claim: λ = 1 is a property of the closed three-party loop, and no single open channel ch_xy has a cycle at all — each component's λ is undefined (or −∞) and the composite's is 1. Throughput is created at the glue.
The honest version is still a real compositionality result, and still cheap: the components give a lower bound for free, and only the cycles through the new boundary need to be examined. That is Karp's algorithm restricted to the glue, not rerun over the whole mesh. Monotone bound plus Karp-on-the-glue is what "compositional throughput" means here.
So: a payment network doesn't get designed all at once. Channels are added, modified, split, merged. Each modification is a compositional operation in Settle. Conservation and the circuit update by construction; throughput updates by a bounded local recomputation; and the guard layer composes by conjunction. The three-party cycle and the five-party mesh live in the same category, analysed by the same tools, because they're built from the same generators.
For readers of the March version:
For the core-observer decomposition and the three-formalism convergence that motivates this: Earned Compression
For R as a handled effect rather than a held value: Structuralism, Not Objects
For the categorical structure underneath: Symmetric Monoidal Categories
For the tropical algebra that gives us throughput from topology: Tropical Petri Nets