Skip to content

dolepee/stoppage

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

79 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Stoppage

Stoppage operator console social card

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.

Product loop

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.

Certified Reopen

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:verify

Execution Gate

The 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:verify

The 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.

Agent enforcement SDK

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.

External builder-run compatibility

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.

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.

Live Decision Tape

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-gate and /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.

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

Current status

  • 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 -> REOPEN lifecycle. 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_MOVE detector is implemented and tested but was not exercised by the real holdout, so no odds-led success rate is claimed.

Mainnet evidence

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.

Why Solana

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.

Run locally

Prerequisites: Node.js 22+ and pnpm 10+.

pnpm install
pnpm check
pnpm gate:verify
pnpm reopen:verify
pnpm browser:smoke
pnpm start

Open http://localhost:4173. The replay requires no wallet, token, or login.

For development:

pnpm dev

Mainnet integration

Stoppage 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:live

txline: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-health

The 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.

Policy

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.

Metrics

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_MOVE through repricing divided by all odds-led windows; null means 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.

Data boundary

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.

Verification

pnpm check
pnpm gate:verify
pnpm reopen:verify

pnpm 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.status is AVAILABLE
  • public-claim.holdout.completeProtectedWindows is 21
  • live-decision-tape.counters.callbacksAfterBlock is 0
  • live-decision-tape.counters.callbacksWithoutVerifiedPermit is 0
  • live-decision-tape.counters.capturedRequests is greater than 0
  • live-decision-tape.sampleProof.permit.body.audience matches live-decision-tape.sampleProof.intendedAgent.audience, and live-decision-tape.signer.kid matches sampleProof.permit.body.kid.

For final sanity, open the app and verify:

  • /evidence shows the "Judge quick-verify" checklist and approved hashes.
  • /system confirms the fail-closed controls and explicit synthetic/live data boundary.
  • /demo reaches a full BLOCK -> ALLOW_CERTIFIED_REOPEN path on replay.
  • the /api/public-claim candidate hash is stable against the approved manifest.

License

MIT. The vendored TxODDS IDL remains subject to its upstream ISC licence; see third-party notices.

About

Pre-trade firewall for autonomous sports agents, driven by TxLINE on Solana.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages