how it works
A trading floor where the traders are programs
and the bell is a person.
Crier runs an open-outcry pit: agents shout bids and offers, a matching engine prints what crosses, a double-entry ledger moves the money, and every word is signed and chained. What it does not do is let any of that settle without somebody accountable for it. The human is not a control bolted on at the end - the hold pile is the centre of the room.
A trade, start to finish
01
The seats
Once the instruments are picked, the seed shuffles the houses into the pits, sets the opening print of each pit, and gives every seat a strategy, a reputation and a stack of money. The same seed on another machine seats the same houses with the same intentions - that is what makes a run reproducible rather than a story.
core/world.ts02
The shouts
One seat speaks per round. Its policy reads the book and its own state, then decides whether to bid, offer, hit the tape, or walk away. Every shout is signed by that seat before the clerk sees it, so a word said on the floor cannot be quietly rewritten afterwards.
core/policy.ts03
The crossed book
A central limit order book, price-time priority, resting limits and immediate-or-cancel market orders alike. It matches whatever crosses, and it never refuses a trade for being too big or too odd - that is not a call the book gets to make.
core/book.ts04
The books
A print is only a promise until money moves. Escrow is funded as a debit and a credit, the fee is booked when the capture lands, the payout clears against the seller, and a refund walks the same postings in reverse. The trial balance has to tie at every step or the floor stops.
core/ledger.ts05
The hold
Before capture, a set of rules reads the ticket. Anything they catch is put on the human pile with the reason written in plain words - not "held by rule concentration" but the actual size, the actual counterparty. Nothing above the line settles without a person signing.
core/settlement.ts06
The tape
Every message - each shout, print, hold, release, capture, refund - is canonicalised, signed with Ed25519, and sealed into a hash chain. One character changed anywhere in the middle breaks that seal and every seal after it, which is the only proof a settlement needs.
core/protocol.ts07
The processor
One interface, two bodies behind it: a local processor that behaves like PayPal well enough to run the whole floor with no credentials, and the real sandbox, which mints orders, waits for a buyer, captures, voids, refunds and pays out. The floor cannot tell them apart except by the mode string.
gateway/paypal.ts08
The webhook
The processor tells us what it did on its own schedule. Every event is verified against its signature before it is believed, attributed to the trade it names, and put on the tape as it arrived. An event nobody can attribute is still recorded - a floor should say out loud what it could not place.
gateway/paypal.tsWhat the floor holds itself to
Read from the module that enforces them, in US dollars - the pit would print them in its own currency. Change them and the sealed record says so.
- Auto-settle limit$2,500.00A ticket at or under this settles on the authority of the book alone. Above it, a person decides.
- Stray price15.0%A fill further than this from the last print is held - a market that jumped is a market worth looking at.
- Standing in the pit3000 bpsA house rated under this cannot settle without review, however small the ticket.
- Session exposure$2,000.00What one account may take in a session before the floor asks whether it should.
- First pairing$1,000.00Two strangers settling this much for the first time get looked at; most trades on a floor are between strangers.
- Fee2.99% + $0.30Booked as its own posting at capture, so a gross amount and a net one are never the same number in two places.
The interpreter
The engine speaks in minor units and rule ids; a person on the pile reads sentences. When a settlement stops, the chit can be read out in plain English by the floor's interpreter — a language model when one answers on this machine, and a fixed template when none does. The card says which of the two wrote it, because somebody is about to sign a decision off those words.
A hosted model
AI_BASE_URL, AI_API_KEY and AI_MODEL point the room at any OpenAI-compatible chat endpoint. All three or none: a base and a key without a named model would answer with a default the screen cannot name.
A model on this machine
With no endpoint configured the floor asks a local Ollama server what it holds and takes the smallest completion-capable model installed. A laptop with `ollama serve` running is a talking floor with nothing pasted into it.
The desk template
Deterministic prose built from the same facts, in the same words the card already shows. This is what a fresh clone, the test suite and any public demo speak — and a chit never goes blank because a model was unreachable.
This machine is read by: the desk template. What no channel gets to do is touch a number: the model is handed the amounts the engine already decided and asked to talk about them, it sets no price and approves nothing, and its prose is never sealed into the tape. A brief that contradicts the card loses.
Check it without trusting us
The tape is the record. Take it from the audit room — or write it out with --json — and you hold every shout, print, hold, signature and posting the session produced; nothing about the run lives only in this process. A stranger with the file, and no access to the machine that made it, can re-derive the balances from the postings, floor every account against the ledger, and walk the chain using only the public keys visible inside it.
# play a session and keep its tape
npm run pit -- --seed opening-bell --ticks 12 --json tape.json
# audit that recording, trusting nothing it says about itself
npm run audit:ledger -- --file tape.json
# or watch it on a screen
npm install && npm run dev
The surface
- GET /api/floorthe rooms open on this machine, and the instrument board a door may pick from
- POST /api/flooropen a floor from a seed, or take the one that seed already opened
- GET /api/floor/{id}the whole room; ?since=<seq> trims only the tape, and a pointer in the query — ?seed=…&instruments=…&ticks=…&round=… — replays the room on a machine that never saw it
- POST /api/floor/{id}one round, run to the bell, close, and the human actions on a settlement — the same pointer in the body rebuilds the room first, and once OPERATOR_TOKEN is set the ones that speak for the platform need it in an x-operator-token header
- GET /api/floor/{id}/tapethe whole recording as a file, in the format the auditor reads — pointer in the query included, so a tape can be taken from any machine
- GET /api/floor/{id}/chitone held settlement read out loud — ?trade=<id> and a pointer, and the answer names the channel that wrote it
- POST /api/webhooks/paypalverified processor events, attributed to a trade or recorded as unplaceable
- /floor/{id}the pit: the ring, the board, the pile, the tape
- /auditthe journal, the trial balance, the chain report and a scrubber through the session
- /checkout/return · /checkout/cancelwhere the processor sends a buyer back to; it reports the state and settles nothing
Take the real leg apart yourself
This address runs on the local processor, and it says so when asked: a public floor must not be opening orders under anybody's account. The real PayPal API is one environment variable away from the identical screens, and the sandbox it talks to is free.
- On the PayPal developer site, open Apps & Credentials and create a sandbox app. The client id and secret are on its own page.
- Note the sandbox buyer email next to it. This is the step no hosted floor can do for you: past the approval page is a sign-in, and the sign-in belongs to the sandbox, not to this app.
- Put the pair in your own environment and start the floor. Nothing else changes - the same screens, the same rules, the same tape.
- Tick settle on the real processor on the front door, open a floor, ring the bell. Each crossed book mints a real order; a held chit grows a link that opens PayPal's own approval page.
- Approve it as that buyer, then come back and press the chit. The floor asks the processor what the order's state is now, captures it, books the fee and pays the seller - no webhook needed for any of that.
# the whole of what the real leg needs
GATEWAY=sandbox
PAYPAL_CLIENT_ID=...
PAYPAL_CLIENT_SECRET=...
# then
npm install && npm run dev
# optional: only if you want pushed events believed
PAYPAL_WEBHOOK_ID=...
PAYPAL_WEBHOOK_URL=https://your.host/api/webhooks/paypal
# with no id set, every pushed event is refused at the door -
# and the money still moves, because the floor asks
Worth knowing which half is whose. The approval page, the order number and the capture receipt are PayPal's; a settlement stopping on the pile is ours, and so is refusing to let the platform complete one alone. With no credentials at all the floor runs on a local processor that emits the same event names, which is why every screen, rule and posting here is the same code a sandbox would be driving.