Skip to main content

Internal Betting Feed API Reference

Version: 1.0
Last Updated: March 23, 2026
PR: #1065

Overview

The Internal Betting Feed provides authenticated, sequence-aware access to Hyperscape’s duel lifecycle events and renderer health signals. This API is designed for betting market synchronization and should be used by trusted consumers only.

Base URL

Authentication

All endpoints require authentication via Bearer token or query parameter.

Query Parameter (EventSource Fallback)

Note: Query parameters appear in server logs. Use Bearer header when possible.

Token Configuration

Server (.env):
Security:
  • Tokens are compared using timing-safe timingSafeEqual on SHA-256 digests
  • Development bypass available via BETTING_FEED_SKIP_AUTH=true (NEVER in production)
  • Production fails closed when BETTING_FEED_ACCESS_TOKEN is unset

Endpoints

GET /api/internal/bet-sync/state

Bootstrap endpoint providing current state and replay buffer. Authentication: Required (Bearer header only, no query param) Response:
Rate Limit: 240 requests/minute per IP Example:
Use Cases:
  • Initial connection (get current state)
  • Reconnection after long disconnect (catch up via replay buffer)
  • Sequence gap recovery (re-bootstrap when gaps detected)

GET /api/internal/bet-sync/events

Server-Sent Events (SSE) feed for real-time duel lifecycle updates. Authentication: Required (Bearer header or ?streamToken= query param) Query Parameters:
  • since=<sequence> (optional) - Resume from specific sequence number
  • limit=<number> (optional) - Max frames in initial replay (default: 100, max: 2048)
Response: SSE stream
Heartbeat: Every 15 seconds (configurable via STREAMING_SSE_HEARTBEAT_MS) Rate Limit: 60 requests/minute per IP
Max Clients: 32 concurrent connections (configurable via BETTING_SSE_MAX_CLIENTS)
Example:
Replay Delivery Modes:
  • "bootstrap" - Client is behind or first connection (full buffer)
  • "incremental" - Client is caught up (frames since since)
  • "reset" - Client is ahead of server (server restarted, re-bootstrap needed)
Slow Client Eviction:
  • Clients with writableLength > STREAMING_SSE_MAX_PENDING_BYTES are disconnected
  • Default threshold: 1MB (configurable)
  • Prevents slow clients from blocking the feed

Data Types

BettingFeedPayload

StreamingCycleState

AgentSnapshot

RendererHealth

Degraded Reasons: Surface-Level (client/server not ready):
  • "socket_disconnected" - WebSocket connection lost
  • "world_not_ready" - 3D world not initialized
  • "terrain_not_ready" - Terrain system not loaded
  • "camera_target_unresolved" - Camera hasn’t locked to target
  • "initialization_failed" - World init error
  • "renderer_unavailable" - WebGPU not available
Streaming Guardrails (duel state invalid):
  • "agent1_invalid" - Agent 1 missing or invalid HP
  • "agent2_invalid" - Agent 2 missing or invalid HP
  • "arena_positions_invalid" - Positions overlapping or missing
Loading/Transition:
  • "loading_overlay_active" - Loading screen still visible
  • "initializing" - Waiting for duel data (IDLE phase)
  • "waiting_for_duel_data" - No streaming state yet

Configuration

Server Environment Variables

Rate Limits

Bootstrap Endpoint (/api/internal/bet-sync/state):
  • 240 requests/minute per IP
  • No concurrent connection limit (stateless)
SSE Events Endpoint (/api/internal/bet-sync/events):
  • 60 requests/minute per IP
  • Max 32 concurrent connections
  • Slow clients evicted at 1MB pending bytes

Integration Examples

TypeScript Client

Browser Client

Error Handling

HTTP Errors

401 Unauthorized:
  • Missing or invalid BETTING_FEED_ACCESS_TOKEN
  • Check token is set in server .env
  • Verify token matches exactly (no whitespace)
403 Forbidden:
  • CORS origin not allowed
  • Set INTERNAL_BET_SYNC_ALLOWED_ORIGIN to your domain
429 Too Many Requests:
  • Rate limit exceeded
  • Bootstrap: 240 req/min per IP
  • SSE: 60 req/min per IP
503 Service Unavailable:
  • Max concurrent SSE clients reached (32 default)
  • Wait for slot to open or increase BETTING_SSE_MAX_CLIENTS

SSE Errors

Connection Closed:
  • Slow client evicted (pending bytes > 1MB)
  • Network interruption
  • Server restart
Reconnection Strategy:

Best Practices

Idempotent Processing

Use phaseVersion for idempotent deduplication:

Renderer Health Checks

Always check renderer health before updating market state:

Sequence Continuity

Detect server restarts via sourceEpoch changes:

Gap Detection

Monitor for missing sequence numbers:

Monitoring

Health Checks

Server Health:
Renderer Health:

Metrics

SSE Client Count:
Replay Buffer Size:
Sequence Numbers:

Troubleshooting

Authentication Failures

Symptom: 401 Unauthorized Solutions:
  1. Verify BETTING_FEED_ACCESS_TOKEN is set in server .env
  2. Check token is passed correctly (Bearer header or ?streamToken=)
  3. Ensure token matches exactly (no extra whitespace)
  4. Check server logs for “Betting feed auth failed” warnings

Renderer Always Degraded

Symptom: rendererHealth.ready is always false Solutions:
  1. Check degradedReason for specific issue
  2. Verify streaming state is present
  3. Check agent snapshots have valid HP
  4. Verify arena positions are not overlapping
  5. Check loading overlay has dismissed
Debug:

SSE Connection Drops

Symptom: EventSource closes unexpectedly Solutions:
  1. Check server logs for “Slow client evicted” warnings
  2. Verify client is consuming frames fast enough
  3. Increase STREAMING_SSE_MAX_PENDING_BYTES if needed
  4. Implement reconnection logic with exponential backoff

Sequence Gaps

Symptom: Missing sequence numbers Solutions:
  1. Check sourceEpoch - if changed, server restarted
  2. Use bootstrap endpoint to get full replay buffer
  3. Implement gap detection and re-bootstrap logic
  4. Check network stability (SSE requires persistent connection)

Migration from Public Polling

Old Pattern (Deprecated)

Problems:
  • Polling delay (1 second minimum)
  • No renderer health signals
  • No sequence continuity
  • Higher server load
  • No replay buffer for reconnection
Benefits:
  • Real-time updates (no polling delay)
  • Renderer health signals
  • Sequence-aware (idempotent deduplication)
  • Replay buffer (reconnection support)
  • Lower server load

Security Considerations

Token Storage

Server:
  • Store in environment variables or secret manager
  • Never commit to git
  • Rotate periodically
  • Use strong random tokens: openssl rand -base64 32
Client:
  • Store in secure backend (never in browser localStorage)
  • Pass to frontend via secure session
  • Never expose in client-side JavaScript

Logging

Server:
  • Use redactStreamingSecretsFromUrl for all log output
  • Never log BETTING_FEED_ACCESS_TOKEN in access logs
  • Redact streamToken query params from logs
Example:

CORS

Restrict to Specific Origin:
Server Response:
Never Use Wildcard (*) for authenticated endpoints.

References

  • PR #1065: Internal bet sync feed and renderer health
  • Hyperbet Consumer PR: HyperscapeAI/hyperbet#28
  • Streaming Guardrails: packages/shared/src/utils/rendering/streamingGuardrails.ts
  • DuelBettingBridge: packages/server/src/systems/DuelScheduler/DuelBettingBridge.ts
  • Betting Feed Routes: packages/server/src/routes/streaming-betting-routes.ts
  • Integration Guide: docs/streaming-betting-integration.md