Fourcast is an agent operating under a mandate — making constrained decisions from pre-match evidence, sealing each one into a hash-bound receipt, and reconciling against an independently verifiable on-chain outcome. The route is one unfolding system: Mandate Control → Proof Theatre → Diligence.
Who this is for. Prediction-market operators and the allocators assessing them. They need more than a claimed P&L: they need a pre-outcome record of evidence, risk limits, decisions, and results. Retail can audit; we do not optimize for them.
What it does.
- Mandate Control (
/agent) — a live VPS worker operates under a versioned policy, decides from pre-match TxLINE evidence alone, and seals each decision into a SHA-256 receipt. The hero shows the current mandate, the proof timeline crossing from "outcome withheld" into "proof available," and the real on-chain Solana verdict. A self-serve Mandate Builder lets a prospect configure the four policy knobs in-browser, run a dry-run against the canonical France–Sweden fixture, save their mandate, and get a public Track Record URL (/agent/<operatorId>) — no account, no VPS, no wallet.- Proof Theatre (
/world-cup) — the final act of an autonomous decision: a vertical 6-stage evidence timeline (pre-match evidence → seeded simulation → versioned policy gates → immutable receipt → TxLINE Merkle proof + Solana validation → reconciliation) for any fixture.- Allocator Diligence (
/positions) — policy adherence, receipt coverage, discipline rate, and calibration after resolution — computed from the same public receipts, not self-reported. Per-operator Track Record URLs (/agent/<operatorId>) surface the same data scoped to one operator — the public surface a concierge DM points a prospect at.- On-chain parametric settlement — a Solana program (
match-escrow) CPI-calls TxLINE'stxoracle::validate_statto verify match outcomes and release locked SOL trustlessly. Verified end-to-end on devnet.
Fourcast's flagship route is one unfolding system — Mandate → Proof Theatre → Diligence — supported by Markets, Signals, and Labs:
/agent— Mandate Control: the flagship hero. A live VPS worker's current decision, proof timeline, and on-chain verdict. The manual runner is demoted to an Operator Controls drawer. A Mandate Builder panel lets a prospect self-serve configure a mandate, run a dry-run preview, and save it to get a public Track Record URL./agent/[operatorId]— Per-operator Track Record: the public URL a concierge DM points a prospect at. Shows the operator's mandate knobs and scoped track record (forecasts, resolved, Brier score). Public by design — the OG share card target for Warpcast/X./world-cup— Proof Theatre: a vertical 6-stage evidence timeline for any fixture, from sealed pre-match evidence to Solana-anchored reconciliation. Cross-venue edge detection and on-chain settlement remain as fixture-card capabilities./positions— Allocator Diligence: mandate adherence, receipt coverage, discipline rate, and calibration as the hero; positions/P&L demoted to a secondary section./markets— Prediction Markets Hub: three tabs for different market types. Sports & Events (Polymarket/Kalshi with weather-aware AI analysis), Crypto/Finance & More (long-tail discovery with ML edge detection), and Canton Markets (private settlement showcase with cBTC/cETH). Progressive disclosure reveals markets in batches of 10.
| Primary customer | Prediction-market operator plus the allocator who must diligence that operator |
| Distribution | Verifiable decision history → allocator trust → operator/allocator concierge conversion |
| Headline product | Mandate Control: a flight recorder for autonomous capital — policy-bound decision receipts, risk controls, and reconciled outcomes |
| Technical highlight | Custom Solana program CPI-calls TxLINE's txoracle::validate_stat for parametric insurance settlement; SHA-256 receipts reconciled against on-chain Merkle roots |
| Data source | TxLINE (primary) — fixtures, odds, scores, proofs. Polymarket as secondary comparison venue |
Fourcast implements the full proof chain end to end: TxLINE data ingestion → pre-match evidence → seeded simulation → versioned policy gates → decision receipt (SHA-256) → TxLINE Merkle proof → Solana PDA verification → reconciliation → on-chain settlement via match-escrow CPI calling validate_stat. Every step is deterministic and independently verifiable.
Sports applications rely on opaque feeds and trusted operators for settlement. Even when match data is "live," end users have no way to verify the score happened as reported. Prediction-market traders lack a trusted consensus reference to spot mispricing. And anyone who wants to settle a wager trustlessly needs an oracle — which reintroduces the trust problem.
Fourcast uses TxLINE as its single primary data layer and builds the flagship route on top:
- Mandate Control — a headless VPS worker advances a replay clock, decides from pre-match TxLINE evidence, seals each decision into a SHA-256 receipt, withholds final proofs until the replay outcome time, and posts authenticated status to
/agent. The hero eagerly fetches the canonical verification chain so the proof timeline shows real reconciliation + on-chain Solana verdict. - Proof Theatre — finalised matches surface a vertical 6-stage evidence timeline: pre-match evidence → seeded simulation → versioned policy gates → immutable receipt → TxLINE Merkle proof + Solana validation → reconciliation.
/api/worldcup/verifywalks the full chain in one call. - Allocator Diligence — mandate adherence, receipt coverage, discipline rate, and calibration computed from the same public receipts.
- Parametric on-chain settlement — a custom Solana program (
match-escrow, deployed on devnet atAMT4n3imwTgHEpafKhsjfhfM5tKPXmTBVKvMCW4ohrvQ) CPI-calls TxLINE'stxoracle::validate_statto verify match outcomes and release locked SOL trustlessly. Verified end-to-end: a 0.1 SOL policy on France–Sweden (Round of 32) was settled on-chain via CPI, no intermediary involved.
TxLINE is the exclusive data source for fixtures, odds, scores, and proofs. Polymarket serves as a secondary comparison venue for edge detection only.
┌─────────────────────────────────────────────────────────────────────────┐
│ TXLINE DATA LAYER (devnet) │
│ fixtures/snapshot · odds/snapshot · scores/snapshot · proofs │
└───────────────────────────────┬─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ FOURCAST WORLD CUP INTELLIGENCE │
│ services/txline/txlineService.js │
│ · normalises PascalCase schema -> unified fixture shape │
│ · auto-refreshes guest JWT on 401 │
│ · falls back to cached replays after July 19 cutoff │
└───────┬──────────────────┬──────────────────┬───────────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────────┐ ┌──────────────────────────┐
│ Live odds + │ │ Cross-venue edge │ │ Verifiable receipt │
│ score panel │ │ (Polymarket YES) │ │ · Merkle proof integrity │
│ │ │ │ │ · PDA derivation & fetch │
└──────────────┘ └──────────────────┘ │ · On-chain root compare │
└───────────┬──────────────┘
│
┌────────────────────────────▼──────────────┐
│ Autonomous Historical Lab (VPS) │
│ PM2 worker · replay clock · receipts │
│ signed heartbeat → /api/agent/historical-lab │
└────────────────────────────┬──────────────┘
│
┌────────────▼──────────────┐
│ Solana Match-Escrow │
│ CPI → txoracle │
│ validate_stat │
│ (settlePolicy flow) │
└───────────────────────────┘
- Primary data: TxLINE devnet (free World Cup tier, service level 1)
- Settlement/verification: Solana devnet, TxLINE
txoracleprogram - Secondary enrichment: Polymarket gamma API (cross-venue pricing), Kalshi (optional)
- Frontend: Next.js 16, React 19, Tailwind CSS
- Runtime: Node.js 20+, standard Next.js webpack build
| Endpoint | Purpose | Called by |
|---|---|---|
POST /auth/guest/start |
Issue renewable guest JWT | services/txline/txlineService.js, scripts/txline-subscribe-and-activate.mjs |
POST /api/token/activate |
Activate API token after on-chain subscribe | scripts/txline-subscribe-and-activate.mjs |
GET /api/fixtures/snapshot?competitionId=72 |
List World Cup fixtures | getLiveFixtures() |
GET /api/odds/snapshot/{fixtureId} |
Consensus odds snapshot | getLiveOdds(fixtureId) |
GET /api/scores/snapshot/{fixtureId} |
Score/event snapshot | getLiveScores(fixtureId) |
GET /api/scores/stat-validation?fixtureId=X&seq=Y&statKeys=1,2 |
Merkle proof for stats | getMerkleProof(fixtureId, seq) |
All data requests send Authorization: Bearer ${jwt} and X-Api-Token: ${apiToken} per the TxLINE Quickstart. The service auto-refreshes the guest JWT on 401 and retries once.
| Field | Value |
|---|---|
| Network | Devnet |
| TxORACLE Program ID | 6pW64gN1s2uqjHkn1unFeEjAwJkPGHoppGvS715wyP2J |
| Match-Escrow Program ID | AMT4n3imwTgHEpafKhsjfhfM5tKPXmTBVKvMCW4ohrvQ |
| TxL Token Mint | 4Zao8ocPhmMgq7PdsYWyxvqySMGx7xb9cMftPMkEokRG |
| TxORACLE version | txoracle (v1.5.6) |
| Free-tier service level | 1 (sampling interval = 0 — effectively real-time on devnet) |
| Settlement mechanism | CPI from match-escrow → validate_stat on txoracle |
The on-chain verification flow:
- Proof verification —
solanaVerify.jsextracts the Merkle proof (eventStatRoot,statProofs,mainTreeProof,subTreeProof,statsToProve) from a cached fixture replay - PDA derivation — derives the
daily_scores_rootsPDA from the match timestamp using seeds[b"daily_scores_roots", epoch_day as u16 LE](reverse-engineered from the txoracle program) - On-chain comparison — fetches the PDA account via Solana JSON-RPC, reads the 32-byte Merkle root, and compares against
eventStatRoot - Settlement (CPI) — the match-escrow program (
settlementService.js) builds and submitssettle_policytransactions that CPI-call TxLINE'svalidate_statto trustlessly release escrowed SOL to winners
Verdicts: verified (on-chain root matches), onchain-mismatch (root differs), onchain-error (PDA unreachable), proof-present (components valid but no timestamp for PDA derivation).
The match-escrow program at AMT4n3imwTgHEpafKhsjfhfM5tKPXmTBVKvMCW4ohrvQ implements a parametric sports insurance flow:
createPolicy— a user locks SOL in a policy PDA specifying{fixtureId, minTs, paysRecipientOnHomeWin, amount}settlePolicy— a keeper bot submits the TxLINE Merkle proof; the program CPI-calls txoracle'svalidate_stat, and if the condition is met, SOL is transferred to the designated recipient; otherwise refunded to the locker
The attestation won the daily_scores_merkle_roots seed pattern — it was not present in the published IDL at hackathon start but was obtained through program analysis. See services/txline/settlementService.js for the full Borsh-serialised instruction builders.
TxLINE hackathon access ends July 19, 2026 23:59 UTC. The adapter detects this automatically and switches to cached replay mode, serving deterministic snapshots of completed matches so the deployed demo keeps working for judges. The VPS worker runs those snapshots as an Autonomous Historical Lab: its replay clock creates a receipt from pre-match evidence first, withholds the final proof, and reconciles only when the simulated outcome time arrives. This preserves the decision-before-outcome ordering without claiming post-cutoff live coverage.
The lab is deliberately scoped as an operator process rather than another dashboard:
- It runs headlessly under PM2 on the VPS with no public port.
- It uses the same
services/domain/decision/policy, simulation, and receipt hashing modules as the app. - It posts a bearer-authenticated heartbeat to
POST /api/agent/historical-lab; the app stores only the latest non-secret status inhistorical_lab_status. - The Mandate Control hero (
/agent) pollsGET /api/agent/historical-laband eagerly fetches/api/worldcup/verifyfor the latest receipt, rendering the current decision, proof timeline, and on-chain Solana verdict. The supporting HistoricalLabPanel below the hero shows the replay phase, agent clock, and receipt hash.
To snapshot a real fixture (with verifiable Merkle proof):
# After onboarding (TXLINE_API_TOKEN, TXLINE_GUEST_JWT in .env.local)
node scripts/txline-snapshot-fixture.mjs 18175981 991This writes cache/txline/replays/18175981.json containing the fixture, score events, and the full Merkle proof from /api/scores/stat-validation. The /world-cup UI then surfaces this fixture as final with a "Verify on Solana" button.
The demo fixture (18175981, France 3-0) is a real World Cup match with a real, verifiable Merkle proof anchored on the TxLINE devnet program.
git clone https://github.com/thisyearnofear/fourcast.git
cd fourcast
npm installGenerate a Solana keypair and run the on-chain subscribe + activate flow. Devnet SOL is required for transaction fees.
# Generate wallet (saves secret key to .env.local, chmod 600)
node scripts/txline-generate-wallet.mjs
# Fund the printed public address with devnet SOL (https://faucet.solana.com),
# then run subscribe + activate in one step:
node scripts/txline-subscribe-and-activate.mjsThe script:
- Submits an on-chain
subscribetransaction (service level 1, 4 weeks) to the TxLINE devnet program - Calls
POST /auth/guest/startfor a guest JWT - Signs
${txSig}::${jwt}with the wallet keypair - Calls
POST /api/token/activateto receive the API token - Saves
TXLINE_API_TOKEN,TXLINE_GUEST_JWT,TXLINE_LAST_TX_SIGto.env.local - Smoke-tests
/api/fixturesand prints the response shape
To renew the JWT later without re-subscribing:
node scripts/txline-subscribe-and-activate.mjs --reactivate-onlynpm run devOpen http://localhost:3000/world-cup — TxLINE is now the primary source for fixtures, consensus odds, and verifiable receipts.
npm run buildThe /world-cup route is statically prerendered; the API routes under /api/worldcup/* are server-rendered on demand. Deploy to Vercel as usual — env vars (TXLINE_API_TOKEN, TXLINE_GUEST_JWT, TXLINE_API_ORIGIN) propagate via the Vercel dashboard.
services/txline/
txlineService.js # Adapter: live + replay modes, auto JWT refresh
solanaVerify.js # On-chain Merkle proof verification (PDA derivation + root comparison)
settlementService.js # On-chain settlement: createPolicy + settlePolicy (CPI → validate_stat)
crossVenueEdge.js # TxLINE consensus vs Polymarket YES prices
reconciliationService.js # Receipt/proof reconciliation state machine
receiptAdapter.js # Canonical decision receipt -> TxLINE reconciliation view
services/domain/decision/
decisionPolicy.js # Five-gate mandate policy
decisionReceipt.js # Canonical receipt/hash/verify helpers
simulation.js # Deterministic Monte Carlo and seed derivation
historicalLab.js # Replay-clock phase and no-lookahead checks
scripts/
txline-generate-wallet.mjs # Generate Solana keypair
txline-subscribe-and-activate.mjs # On-chain subscribe + activate
txline-snapshot-fixture.mjs # Snapshot a fixture's proof to cache
fourcast-agent-worker.mjs # Headless autonomous worker
app/api/worldcup/
fixtures/route.js # GET /api/worldcup/fixtures
fixtures/[fixtureId]/route.js # GET /api/worldcup/fixtures/{id}
replay/route.js # GET /api/worldcup/replay?fixtureId=X
edge/route.js # GET /api/worldcup/edge?fixtureId=X
verify/route.js # GET /api/worldcup/verify?fixtureId=X
status/route.js # GET /api/worldcup/status
app/api/agent/
historical-lab/route.js # GET/POST latest signed VPS worker heartbeat
runs/route.js # GET persisted decision ledger
app/world-cup/
page.js # Server entry, metadata
WorldCupClient.js # Client UI: cards, replay viewer, verify panel, edge panel
components/
MandateControl.js # /agent flagship hero — live worker state, proof timeline, dossier trigger
DecisionDossier.js # Right-side drawer — 5 allocator questions from canonical receipt
ProofTheatre.js # /world-cup vertical 6-stage evidence timeline
HistoricalLabPanel.js # /agent VPS telemetry panel (supporting surface below hero)
AgentDashboard.js # Manual runner (demoted to Operator Controls drawer)
AgentRunLedger.js # Persisted decision ledger
MandatePanel.js # /positions allocator diligence hero
cache/txline/replays/ # Cached fixture snapshots for replay mode
idl/txline/
txoracle.mainnet.json # Mainnet IDL (program 9ExbZjAapQ...)
txoracle.devnet.json # Devnet IDL (program 6pW64gN1s...)
Fourcast was originally built on Bright Data + Polymarket/Kalshi aggregation. Those integrations remain and are surfaced as secondary enrichment around the TxLINE-primary World Cup experience:
- Bright Data (SERP API, Scraping Browser, Web Unlocker) — web intelligence for the broader
/marketsdiscovery flow - Polymarket CLOB + gamma API — used by
/api/worldcup/edgeto compute cross-venue discrepancies against TxLINE consensus - Kalshi API — secondary sports markets (optional in the World Cup view)
- Venice AI — LLM for evidence synthesis in the broader agent loop
The /markets and /signals routes continue to provide the original Bright Data-powered experience as supporting capability. The flagship route — Mandate (/agent) → Proof Theatre (/world-cup) → Diligence (/positions) — is the TxLINE-primary surface.
| Endpoint | Purpose |
|---|---|
GET /api/worldcup/fixtures |
World Cup fixtures (live or cached replay) |
GET /api/worldcup/fixtures/{id} |
Single fixture with odds + scores merged |
GET /api/worldcup/replay?fixtureId=X |
Cached event timeline for replay viewer |
GET /api/worldcup/edge?fixtureId=X |
TxLINE vs Polymarket cross-venue edge |
GET /api/worldcup/verify?fixtureId=X |
Solana verification result for cached proof |
GET /api/worldcup/status |
Adapter mode (live vs replay), cutoff, replay count |
GET /api/agent/historical-lab |
Latest VPS historical lab heartbeat for /agent |
POST /api/agent/historical-lab |
Authenticated worker heartbeat receiver |
- 0:00–0:30 — Problem. Agent-managed prediction-market capital lacks an audit layer; claimed P&L does not prove mandate discipline.
- 0:30–1:30 — Mandate Control. Open
/agent; show the live VPS worker, current mandate decision, proof timeline crossing from "outcome withheld" to "proof available," and on-chain Solana verdict in the telemetry strip. - 1:30–2:45 — Decision dossier. Click "Inspect decision dossier"; walk the 5 allocator questions: what it knew, what it decided, what prevented overreaching, when the result was unavailable, what later verified it. Show the raw receipt JSON.
- 2:45–3:30 — Proof Theatre. Open
/world-cup; click "Open proof theatre" on a final fixture; walk the vertical 6-stage evidence timeline from pre-match evidence to Solana-anchored reconciliation. - 3:30–4:00 — Allocator Diligence. Open
/positions; show mandate adherence, discipline rate, and calibration — computed from the same public receipts. - 4:00–4:10 — Close. The route is one system: Mandate → Proof Theatre → Diligence. Verify without trust.
See docs/DEMO_SCRIPT_PROOF.md for the full judge-path script with fallbacks and pre-demo checklist.
What worked well:
- The normalised JSON schema across competitions is genuinely pleasant to consume — one normaliser handles fixtures, odds, and scores with no per-league special-casing.
- The free World Cup tier activates cleanly once the on-chain
subscribetx confirms; no payment, no KYC. - Historical replay via
/scores/snapshot/{fixtureId}returns the full event stream as an ordered array, which makes building a deterministic replay UI trivial. - The
stat-validationendpoint returns a complete Merkle proof bundle (eventStatRoot,statProofs,mainTreeProof,subTreeProof,statsToProve) in a single call — no client-side tree walking required.
Where we hit friction:
- The onboarding flow has six steps (wallet, SOL, on-chain subscribe, guest JWT, sign message, activate) — a one-shot CLI helper would reduce setup time from ~15 minutes to ~30 seconds.
- The published IDL does not include the seed pattern for the
daily_scores_merkle_rootsPDA. We reverse-engineered it from the deployed program —[b"daily_scores_roots", epoch_day as u16 LE]— but this should be documented upstream. - Team display names appear in
/fixtures/snapshotbut not in/scores/snapshot/{fixtureId}— score records only carryParticipant1Id/Participant2Id. We had to cross-reference against the fixtures list (and older fixtures fall off the list, leaving team names unresolvable). - The free devnet tier depends on the public Solana devnet RPC for SOL, which is heavily rate-limited. A bundled devnet SOL faucet (or a one-line
requestAirdrophelper in the SDK) would smooth the very first step.
AG Grid (free Community edition) could make Fourcast's Allocator Diligence table (/positions) and per-operator Track Records sortable/filterable — letting allocators slice by adherence rate, Brier score, or receipt coverage without custom table code. The diligence surface is fundamentally tabular decision data.
MIT