Skip to main content
Hive realtime lives in apps/hive-realtime. It is a Bun WebSocket service used only by Hive; do not replace it with Supabase Realtime. The shared protocol and Yjs helpers live in @tuturuuu/realtime/hive so apps/hive, apps/hive-realtime, and apps/web use the same message schemas.

Required Environment

  • HIVE_REALTIME_TOKEN_SECRET: preferred shared HMAC secret used by apps/web to mint short-lived join tokens and by apps/hive-realtime to validate them. If this is not set, both services fall back to the existing platform Supabase service secret from the shared apps/web environment. This keeps Hive deployable with the same Supabase setup as apps/web, while still allowing operators to rotate Hive realtime signing independently later.
  • HIVE_REALTIME_URL: internal server URL used by apps/web and apps/hive when they need the service-side endpoint.
  • NEXT_PUBLIC_HIVE_REALTIME_URL: browser-facing WebSocket URL, normally wss://hive.tuturuuu.com/realtime in production.
  • HIVE_DATABASE_URL: Postgres URL for the dedicated Hive product database. Realtime uses this database to load compacted CRDT snapshots, append Yjs update bytes, and persist audit events. Supabase is not the Hive product store after backfill.
  • INTERNAL_WEB_API_ORIGIN: Docker-internal web gateway origin used by apps/hive API rewrites. In production Compose this should resolve to http://web-proxy:7803 so editor saves do not leave the Docker network and re-enter through the public web origin.
  • Supabase credentials already used by apps/web, especially NEXT_PUBLIC_SUPABASE_URL with SUPABASE_SECRET_KEY. SUPABASE_SERVER_URL may still override the server-side URL when the deployment needs a Docker-internal Supabase origin. These credentials are identity/session infrastructure only for Hive realtime token validation and web auth.
apps/hive shares the same Supabase setup as apps/web. Docker runtime env comes from .env.local with the same apps/web/.env.local fallback, and the production Hive image receives the web_env BuildKit secret during next build so prerendered auth routes can resolve NEXT_PUBLIC_SUPABASE_URL and related platform Supabase variables. When Hive is exposed as https://hive.tuturuuu.com through a Cloudflare tunnel to http://localhost:7814, keep browser-facing realtime URLs secure. Use NEXT_PUBLIC_HIVE_REALTIME_URL=wss://hive.tuturuuu.com/realtime or leave it unset so the client falls back to the same-origin /realtime route. Keep Docker-internal or host-local forwarding in HIVE_REALTIME_HTTP_URL or HIVE_REALTIME_URL; do not expose ws://... as the browser URL for an HTTPS page.

Local production-style servers (Turbo)

From the repo root, Turbo runs workspace dependency builds first, then starts each process (see turbo.json tasks @tuturuuu/hive#serve:hive and @tuturuuu/hive-realtime#serve:hive-realtime):
  • bun serve:hive — production Next server for Hive on port 7814 (requires a prior next build output; Turbo schedules build before serve:hive).
  • bun serve:hive-realtime — Bun WebSocket server (default port 7815 via PORT).

Blue/Green Deployment

docker-compose.web.prod.yml runs hive-blue, hive-green, and hive-realtime with the same production Supabase environment as apps/web. scripts/docker-web/blue-green.js promotes Hive with the same active color as web and waits for the target Hive color before proxy handoff. The generated nginx config routes:
  • hive.tuturuuu.com/ to the active hive-{color} satellite app on port 7814.
  • hive.tuturuuu.com/realtime to hive-realtime on port 7815.
The production proxy also listens on host port 7814, so a Cloudflare tunnel mapping hive.tuturuuu.com to localhost:7814 still goes through the blue/green proxy instead of bypassing it to a stale single Hive container. scripts/watch-blue-green-deploy.js watches the Hive Dockerfiles, package manifests, realtime source files, and docker-compose.web.prod.yml; changes to these files trigger the watcher container refresh path.

Protocol

Clients connect with a short-lived token from POST /api/v1/hive/servers/:serverId/realtime-token. Tokens include user ID, server ID, role, expiry, and event scopes. Accepted client messages are CRDT sync, awareness, compatibility, and health messages:
  • sync.hello
  • sync.update
  • sync.diff
  • sync.compacted
  • awareness.update
  • presence
  • server.status
  • error
  • world.event
  • world.event.applied
The durable shared world is a Yjs document per server. Clients send encoded Yjs deltas as base64 in sync.update, and the realtime service appends those bytes to hive_crdt_updates. State-vector sync lets a reconnecting client request only missing updates, while periodic compaction stores merged snapshots in hive_world_states.crdt_snapshot. revision is a compatibility display value backed by op_seq; it is not a write precondition for commutative CRDT world edits. Money, item ownership, warehouses, trades, LLM spend, and bankruptcy still go through the apps/web Hive APIs and Hive Postgres transactions. Realtime broadcasts only the committed visual projections for those authoritative writes. world.event and world.event.applied remain as temporary compatibility wrappers for older full-world edit flows. The service converts accepted payloads into CRDT updates before broadcasting them to the room. Do not store full world_data snapshots in hive_world_events for compatibility events. The current world snapshot belongs in hive_world_states; event rows should keep compact metadata and payloads so high-frequency editor saves do not double the amount of JSON written per operation. Awareness is ephemeral and TTL-based. It includes user ID, display name, avatar URL, role, color, active tool, selected entity, terrain cursor, camera position, world avatar position, viewport focus, and lastSeenAt. Do not persist awareness state into hive_world_states, hive_crdt_updates, or audit events. Operationally, the client should expect token refresh on reconnect, exponential backoff, heartbeat/TTL presence cleanup, actor echo suppression, offline edit queue replay, cursor/position throttling, and slow-client backpressure handling.