| name | run-demo |
| description | Start the Surf prediction market demo. Use when the user wants to run, restart, or show the demo (benchmark dashboard, MV app, re-query app). |
| user_invocable | true |
Run Surf Demo
Start the prediction market demo with live Polymarket data: two app instances (MV vs re-query mode) for side-by-side comparison.
Prerequisites
- Docker installed
- Demo app built (
npm run demo:build)
Steps
1. Start RisingWave
Always ensure RisingWave is running. Start it and wait for it to accept connections:
docker start surf-risingwave 2>/dev/null || docker run -d --name surf-risingwave -p 4566:4566 risingwavelabs/risingwave:latest
Wait for it to be ready (retry up to 30 seconds):
for i in $(seq 1 30); do pg_isready -h localhost -p 4566 && break; sleep 1; done
If it doesn't become ready after 30 seconds, stop and report the error.
2. Build the demo app (if needed)
npm run demo:build
3. Kill any existing instances and reset schema
pkill -f "tsx demo/server.ts" 2>/dev/null
pkill -f "tsx benchmark/index.ts" 2>/dev/null
lsof -ti:8081 -ti:8082 -ti:3002 -ti:3003 | xargs kill -9 2>/dev/null
sleep 2
Reset stale schema (required for clean MV creation):
psql -h localhost -p 4566 -U root -d dev -c 'DROP SUBSCRIPTION IF EXISTS "_surf_sub_marketPrices";' 2>/dev/null
psql -h localhost -p 4566 -U root -d dev -c 'DROP SUBSCRIPTION IF EXISTS "_surf_sub_marketTrades";' 2>/dev/null
psql -h localhost -p 4566 -U root -d dev -c 'DROP MATERIALIZED VIEW IF EXISTS "_surf_marketPrices" CASCADE;' 2>/dev/null
psql -h localhost -p 4566 -U root -d dev -c 'DROP MATERIALIZED VIEW IF EXISTS "_surf_marketTrades" CASCADE;' 2>/dev/null
psql -h localhost -p 4566 -U root -d dev -c 'DROP TABLE IF EXISTS "trades" CASCADE;' 2>/dev/null
4. Start the MV server first (it inserts trades)
The MV server connects to Polymarket's WebSocket for real-time trades and inserts them into RisingWave. It must start first.
nohup npx tsx demo/server.ts --mode mv --live > /tmp/surf-live-mv.log 2>&1 &
Wait ~15 seconds for it to fetch markets, backfill trades, and connect WebSocket. Verify:
tail -3 /tmp/surf-live-mv.log
curl -s -o /dev/null -w "MV (3002): %{http_code}\n" http://localhost:3002
5. Start the re-query server (read-only)
The re-query server reads from the same shared table but polls every 5s instead of 200ms. It does NOT insert trades.
nohup npx tsx demo/server.ts --mode requery --live > /tmp/surf-live-rq.log 2>&1 &
Wait ~10 seconds, then verify:
tail -3 /tmp/surf-live-rq.log
curl -s -o /dev/null -w "RQ (3003): %{http_code}\n" http://localhost:3003
6. Report to the user
Tell the user to open these side by side:
Both show the same 20 live Polymarket markets with real-time trade data streaming via WebSocket.
Ports
| Port | Service |
|---|
| 4566 | RisingWave |
| 3002 | Demo app (MV mode, HTTP) |
| 3003 | Demo app (re-query mode, HTTP) |
| 8081 | Surf WebSocket server (MV mode) |
| 8082 | Surf WebSocket server (re-query mode) |
Stopping
pkill -f "tsx demo/server.ts"
lsof -ti:8081 -ti:8082 -ti:3002 -ti:3003 | xargs kill -9 2>/dev/null
Troubleshooting
- Port in use: Kill existing processes with the stop commands above
- RisingWave not ready: Wait a few seconds after
docker start, then retry pg_isready
- Stale data / MV errors: Reset schema (step 3) and restart
- Demo app not built: Run
npm run demo:build first
- No trade updates: Check
tail /tmp/surf-live-mv.log for "WS: inserted" messages. If none, Polymarket WebSocket may be down.