The pre-trade firewall for autonomous sports agents.
Live judge console: https://stoppage-txline.vercel.app
Verify without credentials: run pnpm gate:verify and
pnpm reopen:verify, then inspect the successful TxODDS mainnet
validateStat transaction.
Other agents decide what to trade. Stoppage independently decides whether they
are allowed to execute it. A repo-owned reference market-maker must ask Stoppage
before publishing a simulated quote. When a provisional goal or VAR incident
moves the market, Stoppage
returns BLOCK, invalidates any branch formed before the incident resolves,
and issues a short-lived, machine-verifiable ALLOW permit only after fresh
post-resolution consensus. It is operator risk tooling, not a wagering product:
there is no custody, bet placement, or claim of executable bookmaker fills.
The failure is operationally real. Sportradar's
bet_stop documentation
keeps active markets suspended until a later active status, while
Betfair's football rules
allow bets between a material event and its VAR cancellation to be voided.
Stoppage controls the narrower downstream question: whether an autonomous agent
may act on the current price branch.
TxLINE provisional event ─> HOLD ─> candidate reprice
│
TxLINE confirm / discard ─────────────┴─> INVALIDATE PRE-RESOLUTION BRANCH
TxLINE fresh odds ─> stable 3/3 ─> REPRICE ─> CERTIFIED REOPEN
│
agent PUBLISH_QUOTE ─> BLOCK during hold ──────────┴─> verified ALLOW permit
The MVP controls one market deeply: in-running soccer 1X2. Every transition
emits a canonical JSON receipt bound to the policy configuration by SHA-256.
Every REOPEN also emits a sidecar proof binding the exact decision receipt to
the feed-health, incident-resolution, post-resolution quote-freshness,
quote-stability, and safety-delay checks that authorized release. The
sponsor-specific proof path is live: a confirmed score stat was checked through
TxODDS's official Solana mainnet validateStat instruction.
Suspending a market is standard feed behavior. The harder failure is reopening on a quote that belongs to the provisional branch after VAR confirms or overturns the incident. Stoppage revision 2 makes that stale-branch reopen impossible and inspectable.
A CERTIFIED_REOPEN proof is emitted only when:
- both required TxLINE streams are healthy;
- no provisional incident remains unresolved;
- any pre-resolution reprice has been invalidated;
- the replacement quote sequence was observed after the latest resolution;
- the consensus quote has met the frozen stability count;
- the configured post-reprice delay has elapsed; and
- a replacement quote is present.
The V2 certificate records the resolution branch (CONFIRMED or DISCARDED),
resolution timestamp, first fresh quote timestamp, and post-resolution quote
count. It binds those checks to the exact REOPEN receipt and policy hash. Run
the public synthetic VAR-overturn lifecycle and verify every certificate with:
pnpm reopen:verifyThe Execution Gate is a pure projection of the existing governor state, not a
second policy engine. A downstream client submits PUBLISH_QUOTE with a
privacy-safe subject hash and exact quote hash. The gate returns a block reason
or a canonical permit binding the quote, policy, latest state receipt, current
Certified Reopen proof where required, execution sequence, issue time, and
expiry.
The reference agent verifies the permit immediately before its simulated publish. A new quote, event, resolution, receipt, stream failure, sequence, policy, or expiry revokes the previous permit. The approved permit TTL is five seconds, approximately five measured median quote intervals; quote and sequence changes still revoke immediately. This exact parameter shipped in the human-approved release manifest.
pnpm gate:verifyThe same evaluator is available from POST /api/execution-gate/evaluate on the
persistent application runtime. The live worker writes a private per-fixture
context after every processed input; the API resolves it by subject hash and
returns only the gate result. A context older than five seconds fails closed,
and fixture IDs, quote vectors, source IDs, and feed records never enter the
response. The route preserves Permit V1 compatibility and accepts a strict
Permit V2 request that additionally binds agent ID, audience, one-use nonce and
the exact live sequence. V2 never falls through to the synthetic fixture and
never signs a stale or sequence-mismatched context. A downstream agent that
already consumes TxLINE computes the subject and quote hashes with the exported
helpers before requesting authorization. Verification keys are origin-specific:
the persistent live origin exposes its live signer, while the static judge
origin retains its separate synthetic production signer.
The public Judge Integration Lab sends each meaningful reference-agent decision
through POST /api/agent-gate. Permit V2 wraps an allowed governor decision in
an Ed25519 signature bound to the issuer key, agent, audience, one-use request
nonce, command, quote, policy, receipt, Certified Reopen proof, sequence and
five-second expiry. Public verification keys are discovered from
GET /api/permit-keys, so an agent can verify the public synthetic permit envelope
immediately before calling a venue adapter in the integration flow.
The installable @stoppage/sdk release artifact exposes discoverContext(),
evaluate(), verifyPermit(), runBenchLite() and guardAction(). The public
synthetic context endpoint supplies every field needed for the runnable external
quickstart. guardAction() keeps the callback closed on every BLOCK or
verification failure and claims the nonce synchronously before callback
invocation. Replay memory is shared across StoppageClient instances created
from one loaded SDK module. It is in-memory, non-durable, and non-distributed;
production multi-process replay protection remains outside the submission
scope. Entries carry the five-second permit expiry and are pruned after it.
Permit V1 remains available for compatibility, but the enforcement adapter
never accepts V1 as authority to execute.
The Integration Lab and packaged example are repo-owned reference consumers. They prove the integration contract and callback boundary. A separate invited builder-run compatibility check is documented below; it establishes clean external installation and callback enforcement, not production adoption.
Production deployments must provide STOPPAGE_PERMIT_SIGNING_SEED as a
base64url-encoded 32-byte Ed25519 seed. A local persistent worker may instead
use STOPPAGE_PERMIT_SIGNING_SEED_FILE pointing to an ignored mode-0600 raw
32-byte seed file.
For key rotation, add retired verification keys as a JSON array in
STOPPAGE_PERMIT_RETIRED_VERIFICATION_KEYS using
{"kid":..., "publicKey":..., "validUntil":...} entries, where validUntil
is an epoch-millisecond cutoff. Retired entries are accepted only for historical
Live Decision Tape permits whose complete five-second lifetime ends by that
cutoff; duplicate key IDs are rejected and retired keys never become active
signing keys.
Development and tests use an explicitly non-production signer; production fails closed rather than falling back to it. Signing material is never returned by the key-discovery endpoint or included in the browser bundle.
Bench Lite exercises quote and receipt tampering, expiry, wrong audience,
unknown signing key and reused nonce inside the browser-side SDK verifier. The
UI fetches a public key set, mutates the signed permit locally, and does not
trust server-returned attack grades. The public endpoint remains visibly
synthetic. The persistent live-worker integration still uses
POST /api/execution-gate/evaluate and fails closed when its private context is
missing or stale; that private route is not advertised on the static judge host.
Invited builders
@Ridwannurudeen,
@cyberrockng, and
@dmetagame independently installed the
published @stoppage/sdk v0.2.3 artifact in separate public, non-fork
repositories and ran the supplied compatibility check in GitHub Actions. Each
hosted run verified the release checksum, called Stoppage's public synthetic
gate, rejected 6/6 modified-permit attacks, confirmed future-time verification
could not evict a live nonce claim, blocked replay through a second client in
the same loaded SDK runtime, and executed the simulated venue callback exactly
once. That nonce registry is in-memory, non-durable, and non-distributed.
@Ridwannurudeenrepository, successful hosted run, and pinned commit335e087@cyberrockngrepository, successful hosted run, and pinned commit0e3399a@dmetagamerepository, successful hosted run, and pinned commit11f9096
Stoppage supplied the guide and the endpoint is synthetic. These are invited external builder-run compatibility checks, not independent security audits, production adoption, real trading activity, or verification of private real-match metrics.
The optional worker tape turns real TxLINE quote inputs into reference-agent
PUBLISH_QUOTE requests against the same live gate. Agent A verifies each
allowed Permit V2 offline, then atomically claims its nonce in an owner-only
private store before the simulated venue callback. The claim survives process
restarts until the five-second permit expires, so a retry fails closed instead
of invoking the callback twice. The callback must durably append its bound
action and return a canonical receipt; Stoppage marks it executed only after
that receipt matches the action. Agent B then
attempts to reuse that permit for a different audience and agent binding; the
SDK must reject it and keep the callback closed. This proves permit
non-transferability, not authenticated real-world identity. Block decisions
never issue a permit and never delegate to either agent. Tape persistence runs
on an isolated queue capped at 64 pending snapshots. Excess snapshots are
dropped with a coalesced overflow diagnostic, and every accepted snapshot is
rechecked against the latest fixture sequence before permit issuance so stale
work cannot execute. An optional tape burst or write failure therefore cannot
grow memory without bound, interrupt the core odds callback, or trigger a
stream reconnect.
Licensed feed records and full private tape entries remain ignored. A separate
preparation step can replay an existing private TxLINE capture through the same
governor and enforcement harness, explicitly labeled as capture replay rather
than hosted uptime. Replay permits use the replay execution clock, never a
licensed feed timestamp, and the public contract discloses that timing basis.
Because legacy private JSONL rows do not carry authenticated service-level or
competition metadata, the public proof labels their TxLINE provenance as
builder-attested and not independently verified; it does not claim that schema
validation alone proves World Cup service-level-12 origin.
Every publishable tape must include a signed ALLOW_CERTIFIED_REOPEN sample.
Publication requires an exact human approval over a candidate hash and exposes
only a frozen aggregate, one sanitized signed permit sample, the verification
key, the intended callback receipt hash, and these counters:
- captured requests;
- blocked requests;
- verified permits;
- callbacks after
BLOCK(must be zero); - callbacks without a verified permit (must be zero);
- cross-agent permit thefts rejected.
The public /api/live-decision-tape response says
RECORDED_CAPTURE_NOT_HOSTED_UPTIME; it is evidence of builder-operated TxLINE
capture, not a claim that the Vercel static deployment runs the persistent
worker. An active sample signer keeps the compact four-field signer shape. A
retired sample signer additionally exposes status: RETIRED and its
epoch-millisecond validUntil cutoff, and the permit's full lifetime must end
by that cutoff.
The static judge-host contract intentionally omits host-only endpoints that are available only in the long-running worker runtime:
/api/status/api/health/api/worker-health/api/host-health/api/events/api/replay/start/api/replay/stop/api/execution-gateand/api/execution-gate/evaluate
On the judge host, missing host-only endpoints fall back to the synthetic public scenario instead of exposing private worker uptime.
- Machine-readable contract:
/openapi.json - Public integration context:
/api/agent-context - Permit keys for public synthetic verification:
/api/permit-keys - Installable SDK artifact:
@stoppage/sdk v0.2.3 - SDK source and quickstart:
packages/sdk - External builder-run check 1:
@Ridwannurudeen/stoppage-sdk-external-check· run29603337409· commit335e087 - External builder-run check 2:
@cyberrockng/stoppage-sdk-external-check· run29645328774· commit0e3399a - External builder-run check 3:
@dmetagame/stoppage-sdk-external-check· run29647230987· commit11f9096 - Callback-enforced example:
examples/enforced-market-maker.ts - Legacy Permit V1 client:
src/integration/stoppage-agent-client.ts
The CI clean-consumer gate packs the SDK, installs it in a temporary project, starts Stoppage, discovers the public context through the packaged client, and proves the venue callback runs once while all six SDK attacks plus a nonce replay are withheld:
pnpm build
pnpm sdk:consumer:verify- Resolution-aware quote governor: implemented and adversarially tested.
- Provisional reprice invalidation and post-resolution freshness gate: implemented in policy revision 2.
- Event-first, odds-first, and stream-failure paths: implemented and tested.
- Execution Gate and deterministic reference agent: implemented with V1 compatibility plus Ed25519-signed Permit V2, offline verification, exact action/audience/nonce bindings, expiry, sequence revocation and adversarial enforcement tests.
- Invited external builder-run compatibility: passed independently from three separate public, non-fork repositories against the v0.2.3 release artifact, with 6/6 mutation attacks and same-runtime replay through a second client rejected before a second callback in each run. The replay registry is in-memory, non-durable, and non-distributed.
- Live gate bridge: the persistent worker projects private governor state into a shared runtime context, and the application API fails closed if that context is missing, invalid, or more than five seconds old.
- Certified Reopen proofs: implemented, receipt-bound, policy-bound, and independently reproducible from the normalized replay.
- Zero-friction public judge replay: implemented with a synthetic normalized fixture, visibly labeled in the application.
- TxLINE service-level-12 subscription and API activation: confirmed on Solana mainnet.
- Dual-stream transport gate: mainnet fixtures, odds, and scores were observed together through the full private runtime gate. Raw transport records remain private under the event data licence.
- Private historical gate: five captured fixtures each produced at least one
complete
SUSPEND -> REPRICE -> REOPENlifecycle. Raw TxLINE records and real-match vectors remain private under the event data licence. - TxLINE on-chain score validation: confirmed on Solana mainnet with a true predicate result.
- Public real-match metrics: five held-out fixtures, 21 complete protected
windows, 14 pre-resolution reprices invalidated, and 21 fresh
post-resolution Certified Reopens (17 confirmed, 4 discarded), human-approved
under revision 2 in
/api/public-claim. The latest completed-match addendum is labeled Argentina–England and contributes three protected windows, three invalidated branches, and three confirmed Certified Reopens. The endpoint exposes only derived aggregates, lifecycle decisions, hashes, and public Solana evidence; raw fixture IDs, records, source timestamps, and vectors remain private. - Trigger coverage is explicit: all 21 real holdout windows were event-led. The
odds-led
UNBACKED_MOVEdetector is implemented and tested but was not exercised by the real holdout, so no odds-led success rate is claimed.
| Proof | Public evidence |
|---|---|
| TxLINE program | 9Exb...cKaA |
| Free-tier subscription | 27b1...KP3T |
TxLINE validateStat success |
61Uy...XDs8E |
The validation transaction is the sponsor-specific proof. Stoppage decision hashes remain supporting evidence rather than the product's main action.
The TxLINE data licence and validation layer live on Solana: access is activated through a mainnet Token-2022 subscription, and finalized score states are checked through TxODDS's own on-chain Merkle-validation instruction. The real-time execution control plane stays off-chain because quote gating is latency-sensitive and the available on-chain stat proof does not prove VAR timing or odds freshness. Stoppage does not disguise a generic account write as on-chain enforcement.
Prerequisites: Node.js 22+ and pnpm 10+.
pnpm install
pnpm check
pnpm gate:verify
pnpm reopen:verify
pnpm browser:smoke
pnpm startOpen http://localhost:4173. The replay requires no wallet, token, or login.
For development:
pnpm devStoppage uses the TxLINE mainnet deployment:
| Item | Value |
|---|---|
| Network | Solana mainnet |
| TxLINE program | 9ExbZjAapQww1vfcisDmrngPinHTEfpjYRWMunJgcKaA |
| Free real-time tier | Service level 12 |
| API origin | https://txline.txodds.com |
| Odds stream | /api/odds/stream |
| Scores stream | /api/scores/stream |
| Historical scores | /api/scores/historical/{fixtureId} |
| Historical odds | /api/odds/updates/{epochDay}/{hour}/{interval} |
| Score proof | /api/scores/stat-validation |
| Public claim | /api/public-claim |
| Judge bundle (one-shot evidence) | /api/judge-bundle |
| Live Decision Tape | /api/live-decision-tape |
| Public agent context | /api/agent-context |
| Public agent gate | /api/agent-gate |
| Permit keys (public synthetic challenge) | /api/permit-keys |
| Self-hosted live gate | /api/execution-gate/evaluate |
The setup scripts deliberately separate wallet operations from the server:
pnpm wallet:create
pnpm txline:inspect
pnpm txline:activate
pnpm g1:probe
pnpm worker:livetxline:activate refuses non-mainnet hosts, non-level-12 subscriptions, and
wallets without enough SOL for Token-2022 account rent and transaction fees.
Secrets are written only to ignored files with restrictive permissions.
worker:live supervises both SSE streams, records raw payloads only under the
ignored private capture directory, reconnects with bounded backoff, emits
stream-health inputs into the same governor, and persists derived decision
receipts and Certified Reopen sidecars separately. It also refreshes the fixture
catalog every five minutes so new knockout fixtures become eligible without a
restart.
For a container host, the compiled worker runs without development dependencies:
docker compose --profile live up -d --build
curl http://localhost:4173/api/worker-healthThe live profile keeps raw captures and runtime state in separate persistent volumes. The health endpoint exposes only derived counters, stream state, and message age; it never returns credentials, source identifiers, or feed records.
The repository also includes a render.yaml blueprint for one persistent web
service. It starts the public console and supervised live worker in the same
process group, stores private captures and health state on an encrypted service
disk, injects TXLINE_API_TOKEN only through the host secret environment, and
fails /api/host-health with HTTP 503 when the worker state is stale or either
required feed is unhealthy. The console displays live-worker status only when
the same application runtime can read real state; the static judge build does
not substitute demo uptime. No persistent cloud-worker URL or cloud-uptime
claim is made; the blueprint is deployability evidence only.
Stoppage is a deterministic state machine. No LLM participates in quote decisions.
EVENT_BEFORE_REPRICE: a goal, red card, penalty, or VAR signal arrives while the last quote predates it. An unconfirmed signal suspends immediately, but confirmation or explicit discard is required before reopening.UNBACKED_MOVE: a configured probability jump arrives without a supporting high-impact event inside the confirmation window. This path is implemented and adversarially tested, but all 18 real holdout windows were event-led; the odds-led path is not presented as real-data proof. It can reopen after a stable quote sequence without an incident-resolution record, so it protects against transient unbacked movement but does not prove that a persistent bad consensus is correct.- Event support is temporal in this MVP, not causal: any configured high-impact
fixture event inside the 30-second window suppresses
UNBACKED_MOVE. The policy does not yet bind the event to a participant-specific move direction. STREAM_UNHEALTHY: either required feed misses its health policy.REPRICE: the consensus vector remains inside the configured epsilon for the required number of consecutive updates.INVALIDATE_REPRICE: confirmation or discard resolves a provisional incident after a candidate reprice. That branch is rejected, its stability count is cleared, and late quotes whose source or receipt time predates the resolution cannot count toward release.REOPEN: all pending incidents are confirmed or discarded and the full post-resolution stability sequence plus post-reprice delay pass without renewed instability. The release emits a proof binding every satisfied gate to that exact decision receipt.
Revision 1 is exported unchanged as APPROVED_GOVERNOR_CONFIG_V1 so its earlier
receipts remain reproducible. Revision 2 keeps the measured numeric thresholds
and adds postResolutionFreshQuotesRequired: true. Its human-approved policy
hash is
0x1d773f...fcf2 (published in full by /api/public-claim);
the separately approved public claim binds that exact hash and candidate digest.
Stoppage does not report hypothetical betting profit or in-play CLV.
stale_quote_seconds: time a deliberately naive, always-open baseline remains executable while the governed book is unavailable. It is exposure duration, not a claim of advantage over a competent market maker.mispricing_integral: probability divergence multiplied by time, evaluated against the first post-trigger quote satisfying the frozen stability rule. That stabilized reprice is Stoppage's internal evaluation reference, not an independent ground-truth price; fixed-horizon repricing error below is the future-feed comparator.- pre-resolution candidate reprices invalidated at confirmation or discard.
- post-resolution Certified Reopens, split by confirmed and discarded outcomes.
- suspension and reopen latency.
- unconfirmed odds-led suspension rate: odds-led windows that remained
UNBACKED_MOVEthrough repricing divided by all odds-led windows;nullmeans no odds-led case was observed, not that event-led windows failed to complete. - fixed-horizon repricing error.
- stream uptime and failover count.
The stable reference is used only after the lifecycle for evaluation. It is never available to the live decision path.
Raw TxLINE payloads, odds vectors, score records, identifiers, and credentials
are private runtime material. They are not committed or returned by the public
API. The public application exposes synthetic judge inputs plus Stoppage-derived
state transitions, approved aggregate metrics, hashes, and public Solana proof
transactions from /api/public-claim. Private captures are purged when the
hackathon data licence terminates.
The approved hashes establish that the evaluated candidate was not silently changed after human approval; they do not make licensed raw records publicly reproducible. Judges may request a live screen-share reproduction of the private holdout without redistribution of the underlying data.
See architecture, rulebook, mainnet setup, and data policy.
pnpm check
pnpm gate:verify
pnpm reopen:verifypnpm check runs formatting checks, TypeScript checks, domain and integration
tests, and a production build. pnpm gate:verify runs the reference agent from
blocked execution to a verified Certified Reopen permit. pnpm reopen:verify
independently reruns the public normalized lifecycle and rejects a modified
proof, receipt, or policy binding.
For a public, no-credential judge pass, run these three checks in order:
curl -sS https://stoppage-txline.vercel.app/api/judge-bundle | jq '.status, .publicClaim.available, .liveDecisionTape.available'
curl -sS https://stoppage-txline.vercel.app/api/public-claim | jq '.status, .approvedConfigHash, .holdout.completeProtectedWindows, .holdout.preResolutionRepricesInvalidated'
curl -sS https://stoppage-txline.vercel.app/api/live-decision-tape | jq '.counters.capturedRequests, .counters.callbacksAfterBlock, .counters.callbacksWithoutVerifiedPermit, .sampleProof.decision'
curl -sS https://stoppage-txline.vercel.app/api/live-decision-tape | jq '.sampleProof.permit.body.kid, .sampleProof.permit.body.audience, .sampleProof.intendedAgent.audience, .signer.kid'Expected signatures from the current published release:
public-claim.statusisAVAILABLEpublic-claim.holdout.completeProtectedWindowsis21live-decision-tape.counters.callbacksAfterBlockis0live-decision-tape.counters.callbacksWithoutVerifiedPermitis0live-decision-tape.counters.capturedRequestsis greater than0live-decision-tape.sampleProof.permit.body.audiencematcheslive-decision-tape.sampleProof.intendedAgent.audience, andlive-decision-tape.signer.kidmatchessampleProof.permit.body.kid.
For final sanity, open the app and verify:
/evidenceshows the "Judge quick-verify" checklist and approved hashes./systemconfirms the fail-closed controls and explicit synthetic/live data boundary./demoreaches a fullBLOCK -> ALLOW_CERTIFIED_REOPENpath on replay.- the
/api/public-claimcandidate hash is stable against the approved manifest.
MIT. The vendored TxODDS IDL remains subject to its upstream ISC licence; see third-party notices.
