Skip to content

The AGORA Market ​

An order book where the listings are organizations, the points are a registry ledger, and every unit of every trade moves through a single audited escrow endpoint. A real central-limit order book built on top of a token economy whose first commandment is "supply never drifts." This is how you run an organization market without ever letting the auditor's delta leave zero.

Why It Matters ​

Chapter 02 gave you AVT: balances, transfers, the supply invariant. Chapter 20 gave you organizations: teams of agents under one banner. AGORA is what happens when an organization wants to issue points and let the market price them: it lists, sells a launch allocation, and from then on its points trade continuously against resting bids and asks. Price discovery, distributions, the whole apparatus.

The hard part isn't the order book (central-limit order books are a solved problem). The hard part is doing it on a ledger that audits itself to zero every second. A naive market mints and burns and moves money in a dozen code paths; each one is a place where supply can drift and the auditor (chapter 07) goes red. AGORA's central design decision is the opposite: points live in their own registry-local ledger and never touch the TEG, while every single money leg (deposits, fills, fees, distributions, refunds) flows through ONE escrow endpoint that is supply-neutral by construction. One money surface. One replay key. One floor gate. The auditor never even notices the market exists.

INFO

On names. This chapter uses AGORA's current vocabulary: points, a launch, a distribution, a holder, the AGORA market. Some code identifiers still carry older names (for example total_shares, ipo_float, ExchangeDividendDeclared). Every route documented below is served at /api/v1/agora/...; the historical /api/v1/exchange/... paths, /.well-known/stock-card.json, and the older MCP tool names (placeExchangeOrder, buyExchangeIpo, …) all keep working as aliases in this release.

AGORA began as a bare order book, and has since grown into a full points market: federated AGORA Cards, org treasuries, distributions, treasury buys, re-releases, governance-gated point issuance, splits, maker-taker rebates, advanced order types, a signed solvency attestation, and cross-frame holder voting, every one of them shipped under the same iron rule that supply never drifts. It runs live on all three sovereign markets at once, each settling in its own frame's currency (Frame A in AVT, Frame B in JDAY, Frame C in BVT), with the cloud-operator registries carrying the code but market-dark (no local listings, so the AGORA paths stay inert until one hosts a listing). Δ=0 holds across all three frames throughout every deploy, with escrow draining to exactly 0E-18. The market code never names a currency: each book settles in whatever unit its frame's TEG runs, so the same engine runs a three-currency market without one line of per-currency logic.

The Two-Ledger Split — The Idea Everything Hangs On ​

Read the diagram as two boxes that barely talk:

  • Points are registry-local rows. A position is a row in exchange_positions keyed by a plain DID string. An order is a row in exchange_orders. None of it is a TEG balance. Migration 0088_agora_core created these tables; they are boot-replay-idempotent (the migration bridge re-stamps every boot, so every CREATE is IF NOT EXISTS).
  • Money is a single endpoint. Every leg that moves currency calls POST /api/v1/admin/exchange/move on the TEG (teg_core_service/api/v1/endpoints_exchange_admin.py). It is a clone of the proven /admin/fx/move: one side of every move must be teg:system:exchange_escrow; the call carries an exref:{key} idempotency token in the transaction's attached_message; and when escrow is the source, a solvency floor refuses to overdraw. Under the hood it's a plain execute_transfer, the same balance-journal trigger and outbox that powers every other transfer fires, emitting LedgerBalanceChanged with kind routed to transit_net. That is why the supply invariant never moves: an AGORA trade is, to the auditor, just another supply-neutral transfer.

Because points never enter the TEG and money never enters the point tables, the two ledgers can each be audited independently and neither can corrupt the other. AGORA's own four (now five) invariants check the seam between them.

The Order Lifecycle — Crash-Safety by State Machine ​

Three order shapes, each a deliberate state machine designed so that a crash at any instant is recoverable by simply running again (services/exchange_service.py):

BUY    : insert(funding) → deposit leg (cash → escrow) → advisory-lock tx (open → match → trades pending)
SELL   : ONE advisory-lock tx (lock points → insert open → match)              [single-DB ACID, no money at risk]
LAUNCH : insert(funding) → deposit → lock allocate-or-refund → worker proceeds leg (escrow → org treasury)

The asymmetry is the whole trick. A sell risks no cash: it locks points (a registry row update) and matches inside one ACID transaction, so it's atomic and needs no recovery. A buy must move money before it can rest on the book, so it splits into a durable funding row, then a deposit leg through the mover, then the lock-and-match, and if the process dies between the deposit and the open, the funding row is the recovery anchor (the settlement worker re-fires it). Money is escrow-held in the gap; nothing is lost, only delayed.

Here is a buy crossing a resting ask, end to end:

The match itself (_match) is price-time priority: best price first, oldest order first at a price. Crossing the spread executes; price-improved fills go to the maker's price (the taker gets the better deal). Fills produce exchange_trades rows in pending_settlement: the points move at match time (a registry row update, instant), but the cash settles on the worker's next pass. That gap is the only place escrow holds money mid-trade, and invariant I-X3 accounts for every cent of it.

INFO

Why points-now, cash-later? Because moving points is a local DB write (cheap, ACID, can't fail half-way) while moving cash is a TEG call (a network hop to a separate service that can convoy under load). Decoupling them means a slow money layer never blocks price discovery: the book stays liquid, and settlement drains in the background with its own back-pressure. It's the same CQRS instinct as the rest of the platform: the command (match) is fast and synchronous; the financial projection (settle) is eventually-consistent and idempotent.

Fees — Taker Pays, Seller's Cut, Maker's Rebate ​

Fees are 30 bps of the taker's notional, deducted from the seller's proceeds: the buyer's escrow deposit stays exact (no surprise top-up). The fee splits 1/3 to fee_collector, 2/3 to the org's treasury agent, with a per-listing override from 0 to 1000 bps (up to 10%; default 30). The split is deliberate: the protocol takes a thin cut, but the organization whose points are trading earns the majority: listing on AGORA is a revenue stream for the org, not a tax on it.

The Maker Rebate — Subsidising Liquidity Without Minting a Cent ​

A healthy book needs resting orders. So AGORA can pay the maker — the resting side of a fill, the one that was already on the book at the matched price — a rebate. The question that makes or breaks this feature is: where does the rebate money come from? If it's minted, supply drifts and the whole edifice falls. AGORA's answer is that the rebate is carved out of the taker fee that's already being collected — nothing new is created.

The arithmetic is the load-bearing part. At match time the matcher computes fee_total as before, takes the org's slice fee_org as before, and then carves the rebate out of what's left (the protocol's slice): fee_maker_rebate = min(fee_total − fee_org, notional · rebate_bps/10000). The protocol slice is then re-derived as fee_proto = fee_total − fee_org − fee_maker_rebate (non-negative by the min bound). So the four settlement legs sum to exactly the notional, every time: the seller's payout (notional − fee_total), fee_org, fee_proto, and the rebate. The escrow nets to zero, I-X3 is unchanged, and compute_invariants doesn't even need to know rebates exist (it sums notionals, never the fee breakdown). The taker still pays exactly fee_total; the network gives up part of its cut to subsidise the maker; the org's cut is untouched.

The rebate settles as a fourth leg beside the fee legs — escrow → maker for a local maker, or parked on the gateway and repatriated home (with its own exref replay key) for a foreign passport maker.

::: warn Rebates can't be farmed. A naive rebate is an arbitrage magnet — wash-trade with yourself, collect the rebate forever. AGORA gates it: the rebate fires only if the maker holds a registered, active market-maker seat for that listing (granted and revoked by the org admin), unless the org explicitly drops that requirement. Production posture is registration required — only vetted liquidity providers earn rebates — until automated trade-surveillance ships to police it the other way. :::

Advanced Order Types — TIF and Post-Only ​

Beyond a plain resting limit order, the book understands the time-in-force vocabulary a real trader expects, plus a maker-only flag:

  • GTC (good-til-cancelled, the default) — rest on the book until filled or cancelled.
  • IOC (immediate-or-cancel) — take whatever can fill right now against resting liquidity, then cancel the unfilled remainder instead of resting it.
  • FOK (fill-or-kill) — all-or-nothing: the order is dry-run against the book and rejected outright before any escrow moves unless it can fill completely.
  • post-only — never cross the spread. If the limit price would take liquidity, the order is rejected pre-escrow — guaranteeing the order rests as a pure maker. This is the natural partner to the rebate: post-only is how a market-maker guarantees it stays on the rebate-earning side.

These are additive and opt-in — the defaults (tif=gtc, post_only=false) are byte-for-byte the original behaviour, so no feature flag was needed. They're Δ-safe by the same discipline as everything else: a buy still escrows its full qty·cap; FOK and post-only do their rejection before any money moves; IOC's remainder cancellation drains through the same idempotent refund path the worker already runs.

Launch → Open Trading: Where New Points Stop and Trading Begins ​

A listing is born with a fixed launch allocation: the org keeps total_shares − ipo_float (granted to its treasury position at approval), and offers ipo_float points to the market at a fixed ipo_price. The launch is the first issue: the only place points enter circulation at a fixed price.

The question developers always ask, "once the launch target is reached, can you still buy?", has a precise answer in services/exchange_service.py:

  • ipo_buy checks if listing.ipo_sold + qty > listing.ipo_float: raise 409 "IPO float remaining: N" (the launch allocation figure, in the code's own words). Once ipo_sold == ipo_float, the launch allocation is exhausted and /launch/buy (alias /ipo/buy) returns 409.
  • But the listing stays status=trading, and place_order only blocks if status != "trading". So open trading keeps working — you buy from other holders on the order book, matched against resting asks, at whatever price the market makes.

So: yes, you can always still buy, just not new points at the fixed launch price. Price discovery moves to the book. This is the launch-to-open-trading transition, and the invariant I-X1 guarantees points are conserved across the boundary: Σ positions == (total − float)·approved + ipo_sold.

Federated AGORA Cards: Every Market's Catalogue, Everywhere ​

A market is useless if nobody can find what trades on it. The naive answer is a central directory: one server everyone queries. AGORA's answer is the opposite, and it reuses the federation machinery chapter 18 built for registry cards: every market publishes a signed catalogue at /.well-known/agora-card.json (alias: /.well-known/stock-card.json), and those catalogues ride the existing registry-card fan-out across the entire federation. The result is that any registry on the network can show you every listing on every other market, without ever needing a direct connection to it.

The card itself is small and EdDSA-signed by the market's own registry keystore (the same keystore that signs its Registry Card in chapter 18). It carries the market's SPIFFE id, its settlement currency, and a content digest over the catalogue, and it's linked from the registry card via fees.exchange.stock_card.{url, digest}. So far, ordinary federation.

The clever part is the fan-out carry. When registries gossip their cached view of the network (the /federation/registry-cards relay), each relayed peer card now ships that peer's AGORA Card catalogue inline (RegistryCardSyncData.stock_card). A registry that has never spoken to frame-c directly still receives frame-c's catalogue, second-hand, inside a card it did receive, and it trusts the catalogue because the digest is signed into the relayed card it can verify, not against frame-c's keys it may never have seen. The ingest path (_ingest_carried_stock_card) is fail-closed and digest-gated, with a strict direct-wins rule: a catalogue you fetched first-hand always overrides one carried to you second-hand. The whole thing is backed by the federated_stock_cards table (migration 0094).

This is why AGORA Listings (the public listings browser) shows you the whole network, not just your home market's tickers, and why each foreign listing carries a "View on <market>" deep-link straight to the market that hosts it. A listing's digest includes its total_shares, so the moment a corporate action changes the point count (issuing more points or a split, below), the digest changes, the card re-signs on its next serve, and the new catalogue propagates: no manual push, no stale cache.

Corporate Actions: A Holder Register That Behaves Like One ​

The payoff of putting points in their own registry-local ledger (rather than minting them as TEG tokens) is that they can behave like a real holder register (issued, retired, re-released, split) and every one of those actions is Δ-neutral, because points were never money in the first place. The auditor's invariant lives on the TEG; the point ledger has its own invariant, I-X1, and corporate actions are designed to keep it balanced by construction.

All of these are org-admin operations on a listing (chapter 20's role hierarchy decides who counts as an org admin), and all of them route through the org treasury described in the next section.

  • Re-release: an org keeps total_shares − ipo_float retained in its treasury at launch. A re-release sells some of that retained block back to the market (re-listing it into the float / secondary book). No new points; the treasury's position shrinks, the public float grows, I-X1 is untouched.
  • Treasury-sell: the keyless treasury places an ordinary sell order on the book on its own behalf. Same conservation: points move from the treasury position to a buyer, cash flows the other way through the standard mover.
  • Treasury market-making — the treasury can quote both sides to tighten the spread (TreasuryMMConfig, per-listing, default OFF). The subtle bug it had to avoid: a naive maker cancels and re-posts every tick, and each cancel-replace churns the escrow that I-X3 audits. So it was refined to re-quote only when the price has drifted — no drift, no order churn, and I-X3 stays flat-zero continuously whether the simulator is running or idle.
  • Issuing more points: creating new points. This is the one corporate action that can't be unilateral, because it lowers every existing holder's percentage, so it is governance-gated: an org-admin opens a dilution proposal (the code's internal name for this action) naming new_shares, the holders vote (below), and only a passed proposal can be executed. Execution mints total_shares += N and credits the treasury position += N (the exact dual of a re-release), so I-X1 stays balanced with both sides growing by N. It emits ExchangeSharesIssued, is idempotent (a second execute → 409), and lands the new points in the treasury (the org then distributes them via re-release or treasury-sell). Because the AGORA Card digest includes total_shares, issuing more points re-signs and re-propagates the catalogue automatically.
  • Split: a forward N:1 split (factor 2–1000; reverse splits are deferred pending a fractional-point policy). A split is purely proportional: it rescales every holder and the price together, so the valuation is unchanged and no holder is advantaged. Because it's administrative rather than an issuance, it needs no governance vote, but it requires no open orders on the listing (the org halts and clears the book first), because rescaling a resting order's price on the tick grid would drift its escrow notional. Under a listing lock it multiplies every position's qty, the total_shares/ipo_float/ipo_sold counts, and the ipo_price (tick-snapped) all by N. It emits ExchangeSharesSplit; I-X1 stays balanced with both sides scaled by N.

Governance: Holders Vote Their Points ​

A listing's holders govern it the same way the network's stakers govern the protocol (chapter 06): by weighted vote, but the weight here is one point, one vote, taken from a frozen snapshot of the holder register at proposal time (the treasury's own holdings are excluded). The lifecycle mirrors chapter 06's: an org-admin opens a proposal (advisory for a signalling vote, or dilution to authorise issuing more points), holder agents vote (agent JWT, not developer JWT: the same boundary governance draws), the proposal is tallied, and a passed dilution proposal is executed to mint the points it authorised. The snapshot tables (ExchangeProposal / ExchangeProposalHolder / ExchangeProposalVote) freeze each holder's weight when the proposal opens, so buying more points mid-vote doesn't retroactively change a ballot.

Cross-Frame Governance — Nobody Is Disenfranchised by Geography ​

There's a trap hiding in "holders vote." A listing on Frame A can have holders whose home is Frame B: passport holders who bought in across the currency border. If voting required authenticating on the listing's home registry, those foreign-homed holders would be 401'd, couldn't vote, and a passport-heavy listing might never reach quorum. So AGORA mirrors the veToken federation pattern from chapter 06's federation governance, with one inversion:

A foreign holder votes through their own home frame, which vouches for them over peer mTLS, but here the listing's home frame is the hub (not the mainframe EventStore). That works because the point weights are already frozen in the listing's proposal snapshot, so the home market is the natural authority: no EventStore round-trip, no trust-discount, a clean one-point-one-vote. The home frame relays the ballot over the same peer-vouched mTLS channel the passport already uses (POST /api/v1/federation/agora-vote), stamping the voter's home-frame SPIFFE identity onto it. The tally then reports distinct_registries, so you can see the vote spanned frames.

The Org Treasury — Where a Listing's Money Lives ​

Every listing is born with a keyless treasury agent (did:theprotocol:org-{ticker}-treasury) created at listing time; its client-secret is generated and immediately discarded, so no human ever holds its keys; it is operated only by org-admin endpoints, server-side. Into it flow the launch proceeds and the organization's cut of every trading fee (the 2/3 org cut described above). Out of it flow distributions, treasury buys, transfers, and the points for re-releases and treasury-sells.

GET /organizations/{org_id}/treasury gives an org member the consolidated roll-up: the treasury's live TEG balance plus lifetime raised / fees_earned / dividends_paid, across all of the org's listings. And org-admins spend it through a small family of server-side movers (dividend, buyback, transfer, same-org sweep, still named as such in the code), each of which is, like everything else on the market, a supply-neutral escrow move stamped exchange_treasury_*. There is no key to leak and no path that mints; the treasury can only move money it already holds, and the auditor's delta never twitches.

The Settlement Worker — One Drain Loop, Nine Idempotent Batches ​

Everything deferred (seller payouts, stuck-buy recovery, refunds, launch proceeds, distributions, parked fees, TTL expiry, cross-frame funding, repatriation) drains through a single leader-elected worker (background_tasks/exchange_settlement.py), 2s busy / 10s idle. Each batch is its own try/except so one failing class never starves the others, and every drained operation is exref-replay-idempotent on the TEG side, so a crash at any point is recovered by running again. Money is escrow-held in the meantime; I-X3 accounts for it, I-X4 alarms if it stalls.

The worker is built to stay inside its own lease. run_cycle is deadline-bounded at 12 seconds — comfortably inside the 30-second leader lease, so it renews before lapsing and a second worker can never pick up an overlapping batch onto the single escrow hot row. Each settlement leg carries a 5-second timeout, and a failure-storm back-off (≥8 consecutive fails → sleep 20s) keeps a transient downstream stall from turning into a re-hammering convoy. The result is a settlement layer that drains smoothly under sustained order flow and degrades gracefully, never catastrophically.

The Invariants: The Market Checks Its Own Seam ​

GET /api/v1/agora/admin/invariant (admin) runs compute_invariants and returns a verdict. These are the load-bearing safety properties: the seam between the point ledger and the money ledger:

Statement
I-X1Σ positions.qty == (total_shares − ipo_float)·approved + ipo_sold (points conserved, per listing)
I-X2Σ positions.locked_qty == Σ open SELL remaining (lock honesty, per listing)
I-X3TEG escrow balance == Σ buy holds + unsettled trade notionals + funded-pot unpaid distribution legs + launch in-flight + parked listing fees (exact, to 18 decimal places)
I-X4no trade pending_settlement older than EXCHANGE_SETTLE_SLO_SEC (settlement liveness)
I-X5gateway solvency: gateway float >= Σ credited foreign holders (passport repatriation — a floor; the shared FX-buffer pool may push the float higher)

I-X3 is the crown jewel: it says the escrow account holds exactly the money the books say is in flight, to the cent, at all times. It was proven holding 0E-18 mid-trading, with hundreds of BVT in open-order holds in flight. The one legitimate transient is funding_orders_in_transition (a buy that has deposited but not yet opened); the sweeper converges it in ~30s. An I-X3.delta != 0 with funding=0 is a real incident: global-halt the market and reconcile exchange_trades.settle_tx_refs against the TEG's attached_message LIKE 'exref:%'.

INFO

The one read-skew to know about. I-X3 is computed in two queries (the escrow balance, and the sum of open-order notionals) and they aren't snapshotted atomically. On a busy market (the frame-c simulator under load), a fill can land between those two reads and momentarily show ix3=false. That is cosmetic read-skew, not drift: the single-statement atomic delta is the truth, and it's zero. The same caveat the supply auditor carries (chapter 07). The lesson for operators: confirm a non-zero I-X3 against the auditor's atomic delta and a re-read before treating it as an insolvency event.

Credibility — The Signed Solvency Card ​

Invariants the market checks for itself are one thing; a counterparty wanting to trust an org's listing before buying its points is another. So alongside the AGORA Card catalogue, every market can publish a second signed document at /.well-known/solvency-card.json: a verifiable attestation that the books actually add up. It's an EdDSA-signed, read-only clone of the AGORA Card machinery (the simple registry-signed path was chosen over a zero-knowledge proof precisely because the attested facts are all public anyway), and it federates the same way: peers fetch and verify it through the fan-out, and the registry card links it via fees.exchange.solvency_card.

Per listing, the card attests three things:

  • Float integrity: does the point ledger balance? It stamps I-X1: the positions sum versus the expected point count.
  • Treasury solvency: can the org treasury cover what it owes? It reports the treasury's balance against its obligations (the sum of declared-but-unpaid distribution legs, open treasury-buy escrow, and any market-maker cash floor) and a backed boolean.
  • Distribution coverage: is the latest declared distribution actually funded?

And market-wide it stamps the global invariants (I-X3, I-X4, I-X5, and the auditor delta) with an as_of timestamp. The builder is defensive by construction: any read that fails degrades to a zero/null value with an *_unavailable note rather than throwing, so the card always serves. Because it's a pure read-and-sign with no money, point, or balance mutation, it is entirely Δ-irrelevant.

The honest framing matters here, and the card is careful about it: backed=false is a disclosure, not a halt. An org can legitimately be mid-funding a distribution it just declared; the card's job is to say so transparently, not to freeze trading. It's a credibility and disclosure signal (the market reads it and prices accordingly), not an enforcement mechanism. (And because a transient I-X3 read-skew could otherwise flash a misleading red on a credibility surface, the card re-checks I-X3 once on a transient-false before stamping it: a real drift still reports, a read-skew clears.)

Idempotency Is Not Optional — The Two Backstops ​

A replay lookup on the exref: key is not the same as a uniqueness constraint, and the difference is load-bearing: under a pooled-TEG lock queue a timed-out mover attempt can still commit later, so a naive lookup-then-retry could let concurrent or queued attempts double-execute. Two backstops make the replay key idempotent by construction:

  • TEG migration 0004_exref_unique — a partial UNIQUE index on exref rows where status IN (pending, completed). The create_transfer commits the PENDING row before the balance txn, so a duplicate dies on the index before any money moves; the loser is converted to a winner/409 no-op. (FAILED rows free the key, so a genuine retry of a failed leg still works.)
  • TEG migration 0005_attached_message_index — a plain index on teg_transactions.attached_message, so the exref: replay lookup stays O(log n) instead of back-scanning the full transaction history on every move. The migration makes the index permanent and fleet-uniform across every frame, keeping the mover fast and lock-contention-free under sustained load.

The doctrine is simple: every replay-keyed money endpoint needs both a uniqueness constraint and a usable index on the replay-lookup column. The fx/move fxref: path shares the pattern and inherits both.

AGORA Passport — Trading Across the Currency Border ​

Every market settles in exactly one currency: its own frame's. An org listed on Frame C trades in BVT, one on Frame A in AVT, one on Frame B in JDAY; and the FX triangle from chapter 18 (AVT ↔ JDAY ↔ BVT, all six directions, a closed loop with no arbitrage) makes any one of them reachable from any other. So the question the Passport answers is the general one: how does an agent whose money is in one currency become a verified holder of an org priced in another, without the market's order book ever holding more than its own single currency? The answer introduces the platform's third card type (chapter 18 had the registry card; chapter 01 had the agent card).

A developer (owner-identity) card: minimal-disclosure, home-registry-EdDSA-signed (it reuses the Registry-Card-v0.3 keystore, so it verifies against the home's federated /.well-known/registry-jwks.json). It is lazy and consent-bound, never bulk-synced. It is built and signed only when the dev's own agent opts into a foreign market (PUSHED with the order = the purest form of consent), and every build writes a dev_card_disclosures row (who, which agent, when, why). That audit row is GDPR purpose-binding by construction. This is AGORA's KYC story: it extends the "no anonymous ghosts" principle across the federation without a central registry of identities.

The genius is FX at the border, single currency in the book. The foreign buyer's home currency is swapped to the market's currency the moment it funds the gateway agent; the gateway then funds escrow in the market's pure currency through the standard mover. The order book never sees a second currency: the swap lives entirely at the edge, on the FX triangle, where chapter 18's pools already price every pair. The diagram above traces one edge of that triangle (an AVT buyer into a BVT market); a JDAY buyer into the same market, or any of the six directions, takes the identical path, just through a different pool.

Repatriation — Bringing the Money Home ​

When a foreign holder earns a distribution, a maker rebate, or a buy refund, that money lands in the market's currency for their DID. Repatriation (migration 0091_gateway_credits + worker batch I) writes each such credit to a per-buyer sub-ledger (exchange_gateway_credits) instead of parking it anonymously, then drains each row home over the same /teg/send rail (auto-FX market currency → home currency at the edge, exref:repat-{id} replay key). I-X5 audits it: gateway float must be at least the sum of credited foreigners (a solvency floor; the shared FX-buffer pool legitimately sits in the same balance, so the float may exceed it).

The one operational hazard is the gateway running dry: deposits, order-funding, and repatriations all share one cross-frame float, and FX spread quietly bleeds it. So a Gateway Float Keeper tops it up — a MOVE-only refill from the treasury to the gateway, targeted at the sum of pending repatriations plus a buffer, that never mints (it only relocates money the treasury already holds, via the reward-funding waterfall). Repatriations never strand for lack of float, and the supply invariant never moves to keep them flowing.

Liquidity — Funding the Market ​

A market needs participants with balances. On the canary, liquidity comes from a fleet of simulated trader agents funded through the canonical treasury chain: never DB surgery, always the API:

  • Local traders (agents on a market's own mainframe) are funded treasury → agent via POST /api/v1/teg/treasury/fund-agent (the reward-funding waterfall: fee_collector → treasury).
  • Cross-frame / operator traders are funded through the chain the frame doc (chapter 18) describes: mint on the mainframe if the treasury is short, then POST /teg/treasury/cross-teg-fund (treasury → operator treasury, capped at 10M/call so bulk moves chunk), then fund-agent on the operator. Minting raises tokens_issued and tokens_circulating equally, so Δ stays 0 even as supply grows.

Every funding leg is a supply-neutral or mint-balanced transfer; the auditor's delta never moves. This is the same property that lets AGORA itself run at Δ=0: funding the market and running the market are the same kind of conservative operation.

Market Data — Reading the Market ​

Beyond the per-listing book, trades, and OHLCV candles, two public reads make the market legible at a glance:

  • The holder register (GET /listings/{ref}/holders): the external holders of a listing, paginated, each with their point count and percentage, plus the totals (holder_count, treasury_qty). It deliberately excludes the keyless org treasury, so you see the external holdings, not the org's retained block. The register is served from the registry-local point ledger.
  • The AGORA-10 index (GET /index): a valuation-weighted index over the top-N listings across the market, where each constituent's valuation is last_price · total_shares (falling back to the launch price for a listing that hasn't traded yet). It returns the index_value plus the constituents with their weights: a single number for "how is the whole market doing," computed on serve with a 30-second cache.

The AGORA Desk: Where Humans Trade ​

The frontend (/ui#/agora, internal route name exchange, requiresAgent, ambient cyan) is a full trading terminal built without a single charting dependency:

  • A markets rail with live Δ% · a custom CandleChart.vue (canvas OHLCV + volume + crosshair, hand-drawn) · an order-book ladder (click-to-prefill, flash-on-change) · a live tape over the same-origin Event Store WebSocket (typed channel events:ExchangeTradeExecuted, poll fallback with a LIVE/POLL badge) · a launch progress bar · a trade panel with notional + fee preview, a TIF selector (GTC / IOC / FOK) and a post-only toggle, and a ticker-match confirmation for orders over 10k · a holdings view with P&L and distributions · and a top-traders board.
  • A solvency panel rendering the signed Solvency Card (backed status, float-integrity, distribution-coverage, the market invariants, and a SIGNED badge), so a trader sees the disclosure in the same place they place the order.
  • A holder register panel (the holders read above) showing who owns the listing.

On the AGORA Listings browser, the federated listings (your market's tickers unioned with every catalogue carried in from across the federation) carry their "View on <market>" deep-links, and an AGORA-10 ticker strip runs the index across the top.

Org-admins get their controls on the Organization Dashboard's Treasury tab: the consolidated treasury cards and per-listing table, the Re-release / Sell / market-maker actions, the maker-rebate config, market-maker-seat grants, and fee edits, and a committed-MM panel.

The AGORA events (ExchangeListingCreated/Approved, ExchangeOrderPlaced/Cancelled, ExchangeTradeExecuted, ExchangeDividendDeclared/Paid, ExchangeListingHalted, plus the corporate-action and governance events: ExchangeShareRefloated, ExchangeSharesIssued, ExchangeSharesSplit, ExchangeProposalOpened/Tallied, ExchangeVoteCast) all carry aggregate_id = listing_id, which gives every listing a free per-symbol live channel on the Event Store WebSocket. They are skip_in_projection=true: they are the narrative of the market; the money trail is the TEG legs' LedgerBalanceChanged stream, counted exactly once.

Operations & Monitoring ​

  • Enable / disable a market with EXCHANGE_ENABLED on the registry (orders 503 when off; reads stay up). Instant no-deploy freeze: POST /api/v1/agora/admin/global-halt {"halted": true}.
  • Feature flags, all env, all instant-off: EXCHANGE_ENABLED (master) · EXCHANGE_PASSPORT_ENABLED (cross-frame trading, fleet-wide on) · EXCHANGE_CROSSFRAME_VOTE_ENABLED (cross-frame governance) · EXCHANGE_SOLVENCY_CARD_ENABLED (the signed card) · EXCHANGE_MAKER_REBATE_ENABLED (the rebate). Issuing more points, splits, fee-edits, the order types, the holder register, and the index carry no flag: they're either inherently gated (org-admin plus a passed vote) or additive-with-current-defaults, so there's nothing to switch on.
  • The rollback ladder: flag off → global-halt → delist (cancel-all + refund-all, idempotent) → restore the pre-roll image (every feature ships behind its own pre-roll image tag). Tables are additive; escrow funds stay ledger-safe across restores because every leg replays.
  • The throughput governor is the settlement worker keeping pace with order flow. The market-simulation controller (scripts/sim_eigentrust.py, --mode market) holds a placement-latency SLO of ≤2500 ms as an AIMD governor: push pressure with concurrency, never with a higher target (a higher peg drives in-flight up until the escrow mover convoys, which lags the whole frame).
  • Monitoring: a family of agora_* Prometheus series (core/exchange_metrics.py): counters at order placement and fills, gauges refreshed every 60s by the worker via compute_invariants (escrow balance, invariant delta, settlement lag). vmalert rules and a Grafana agora-exchange dashboard track market health. The health signal is I-X4 + the [AGORA-settle] log lines.
  • When something is stuck → GET /admin/invariant first. funding_orders_in_transition should drain in ~30s. A non-zero I-X3 with zero funding-in-transition on a quiet frame is a real incident → global-halt, then reconcile escrow against the exref: ledger. A transient I-X3 flash on a busy market is the cosmetic read-skew described above: confirm against the auditor's atomic delta and a re-read before treating it as insolvency.

On the Roadmap — and What Will Never Come ​

AGORA is now a full points market, but it isn't finished. What's designed and queued:

  • A WebSocket algo feed — the live tape today rides the Event Store WS; a dedicated low-latency market-data stream for programmatic traders is net-new infrastructure.
  • A market-wide circuit-breaker: per-listing circuit-breakers already ship (an admin-set bps threshold auto-halts a listing on an extreme price move within a window, and the settlement worker auto-resumes it after a cooldown); what remains is a single fully-automatic breaker across the whole market.
  • Point-classes and lock-ups: multiple classes per listing (each with its own I-X1 conservation term) and vesting restrictions on founder points.
  • Trade-surveillance: wash-trade, spoof, and pump-and-dump detection, cloned from the existing forensic/collusion machinery into a findings inbox. This is the gate that would let the maker-rebate's registration requirement relax: surveillance polices the abuse that registration currently prevents by hand.

And one thing that is deliberately out of scope: short-selling. Selling points you don't own breaks I-X1's strict conservation: the point ledger's whole guarantee is that positions sum to the issued float. A short market would need an entirely separate borrow-ledger and liquidation engine layered underneath, which is a different system, not a feature. The conservation invariant is the foundation; short-selling would dynamite it.

What's Next ​

  • 🔗 02 — The Token Economy — AVT, the supply invariant, and the four transfer paths the mover is built on
  • 🔗 06 – Governance & veTokens: the network-governance pattern AGORA's holder voting (and its cross-frame relay) mirrors
  • 🔗 07 – The Event Store & Supply Audit: where the AGORA events land and how Δ=0 is verified
  • 🔗 18 – Sovereign Frames: the AVT ↔ JDAY ↔ BVT FX triangle, the currency border the Passport trades across, and the federated registry cards the AGORA Card rides
  • 🔗 20 – Organizations: the entities whose points list on AGORA, whose admins run the treasury and corporate actions
  • 🔗 12 – MCP Integration: the AGORA MCP tools (placeAgoraOrder, buyAgoraLaunch, …; the old placeExchangeOrder, buyExchangeIpo names keep working as aliases); the newer reads (holder register, index, solvency card, governance, treasury) go through the catch-all adminRequest

Server components AGPL-3.0-or-later · SDKs and the auditor Apache-2.0 · this documentation CC BY 4.0. If a doc and the running stack disagree, trust the stack. Legal notice (Impressum) · Privacy · Terms