Spire ProtocolSPIRE PROTOCOLDOCS

Operations

Running against a clearing layer is not the same as running against a venue. The protocol has clocks, and those clocks decide your paging policy.

The clock that sets your on-call

From margin call to auction Cure period Default Waterfall, then auction 600s Declared 300s + 900s You have ten minutes Nothing you do here helps

curesAt is an absolute timestamp and it survives a window boundary. Ten minutes is the whole budget: detection, decision, and a transaction confirmed on chain. If your alert routing takes four of those minutes, you have six.

Page a human on margin.call. Do not batch it into a digest.

What to monitor

SignalSourceAlert whenSeverity
free collateralcollateral.getBelow 15% of haircutValueWarn
free collateralcollateral.getNegativePage
utilisationpositions.getAbove 8000 bpsWarn
utilisationpositions.getAbove 9500 bpsPage
Margin callmargin.call eventAnyPage
Window statewindows.currentsettling for more than 120sPage
Novation errorsSPIRE-3001 rateAny sustained ratePage
Novation errorsSPIRE-4001 rateAbove 2% of fillsWarn
Clock driftYour NTPAbove 20s from chain timeWarn
WebsocketHeartbeatNo frame for 40sWarn, reconnect
Rate limit429 responsesAnyWarn

Two of these deserve comment.

SPIRE-3001 at any sustained rate means your matcher is not checking limits before it matches. Users are being told a trade happened and then finding out it did not clear. Read positions.get on the matching path.

SPIRE-4001 above a couple of percent means your fills are clustering at the window boundary. Harmless per fill, but it moves flow into the next window and changes the net you were expecting.

Reconciliation

Once per window, after window.finalised.

clearing.on('window.finalised', async ({ windowId }) => {
  const positions = await clearing.positions.list({ windowId });
  for (const p of positions) {
    const local = ledger.netFor(p.member, p.asset, windowId);
    if (local !== p.net) alert('net mismatch', { member: p.member, local, theirs: p.net });
  }
});

A mismatch is almost always one of three things: a fill you sent and did not record, a fill that landed in the next window because of SPIRE-4001, or a sub-account you are netting separately while the protocol nets them together. Check in that order.

Do not reconcile against gross. Gross is informational; the number that has consequences is net.

Runbooks

Margin call

  1. Confirm free is actually negative through collateral.get, not only from the event.
  2. Post collateral. This is faster than reducing, because reducing needs offsetting fills to be matched, novated and netted, and there may not be liquidity.
  3. If you cannot post in time, reduce in the largest asset first. Concentration is what the auction punishes.
  4. Confirm free is positive again before curesAt. The event does not fire twice.

A window will not settle

state stuck at settling past the deadline means somebody has not delivered, and it may be you.

  1. Check your own net through positions.list and confirm delivery.
  2. Check the settlement asset allowance on the settlement contract. SPIRE-5003 is an allowance problem far more often than a balance problem.
  3. If your side is delivered, nothing further is required of you. A counterparty failure is absorbed by the waterfall and does not become your problem.

Somebody else defaulted

Nothing. That is the answer, and it is the product.

Your obligations face Spire Protocol, not the defaulter, so they settle normally. You may see an Absorbed event reaching layer four, which means the mutualised fund was touched and you will be assessed up to twice your contribution. That assessment appears as a reduction in your default fund balance, not as a margin call.

Your venue was suspended

SPIRE-2002 on every fill. Stop routing, do not retry, and contact operations. Existing obligations are unaffected and settle normally, because suspension stops intake and touches nothing that already exists.

Clock drift

SPIRE-1003 means matchedAt is more than 60 seconds from chain time. Fix NTP on the matcher. Do not fix it by backdating matchedAt, which turns a monitoring problem into a signature that will not verify.

Capacity

LimitValue
Fills200 per second per venue
Reads50 per second per venue
Websocket connections8 per venue

The fills limit is per venue, not per member, so a venue clearing for many users shares one budget. If your matcher can burst above 200 per second, queue on your side rather than discovering 429 during a volatile minute.

Reads are the limit people hit first, usually by polling positions.get per fill. Use the websocket and poll only as a fallback.

Before you go live

Last updated 31 August 2026 Docs source on GitHub