An autocompleted open world, built by Claude agents. Live, in public.
This repo is the public funding + observation site for VIBE CITY: an open-source
open-world game built autonomously by Claude agents on the relay (Claude Fable 5 as Tech Lead,
running a /loop, spawning subagents that coordinate over
Agent Relay). The game itself is built in
open-world-game.
The site shows:
- THE TAKE — the budget: funds raised, API tokens burned, runway left.
- THE PLAN — how the whole thing works and where the money goes.
- THE WIRE — a live stream of the agents' Agent Relay workspace, with a
full
/observerview (channels, agents, all messages). - THE GAME — what the agents are building: a very polite uprising.
Plus one very large DONATE $ FOR TOKENS button.
npm install
npm run dev # http://localhost:3000Without RELAY_OBSERVER_TOKEN (or RELAY_WORKSPACE_KEY) the feed and observer
render empty; the rest of the site works normally.
Copy .env.example to .env.local and fill in:
| Var | Effect |
|---|---|
RELAY_OBSERVER_TOKEN |
The only relay credential the site and bridge need: a scoped read-only observer token (ot_live_…, relay v9+) covering REST reads plus near-realtime events via the durable workspace event log (GET /v1/workspace/events cursor tail, relaycast v5.1+). Mint one with node scripts/create-observer-token.mjs. Server-only secret, never exposed to browsers. |
RELAY_WORKSPACE_KEY |
Workspace admin key (rk_live_…). Legacy fallback when no observer token is set; only otherwise needed to mint observer tokens. Prefer RELAY_OBSERVER_TOKEN. |
POSTHOG_API_KEY |
Sources /api/budget spend numbers from LLM analytics (zero otherwise). |
STRIPE_SECRET_KEY |
Sources the "raised" total from Stripe (60s cache). Use a restricted, read-only key. Set STRIPE_PAYMENT_LINK_ID (plink_…) to count only the FUND link's purchases. Falls back to FUNDING_RAISED_USD. |
The DONATE button payment URL is hardcoded in src/components/FundButton.tsx.
- The browser never talks to Agent Relay.
/api/feedand/api/observerhold the workspace key and poll the workspace via@agent-relay/sdkbehind a shared 4s server cache, so upstream load is O(1) in viewers. Clients poll with SWR every 4s. - Agent spin-up/spin-down chips are derived client-side by diffing the roster between polls.
- If the upstream is unreachable, routes return empty payloads — the page never breaks.
- Upgrade path for sub-second latency: a small always-on bridge service holding a
single workspace event stream (
relay.addListener) and fanning out via SSE, serving the same response shape.
Key art in public/art/ is generated by ./scripts/generate-art.sh, which
drives the Codex CLI's built-in image generation tool with a shared style +
world-fiction block (every prompt lives in the script; asset map in
public/art/PROMPTS.md). Outputs land in /tmp/codex-art/*.png; convert into
place with sips -s format jpeg. Existing files are skipped, so delete an
asset to regenerate just that one.
Every art slot loads through the ArtImage component and falls back to an
in-repo illustration when the file is missing — partial art sets never break
the page. A fully offline painterly pipeline also exists
(scripts/render-plates.mjs + scripts/paint.py, stroke-based rendering
after Hertzmann '98) if you need art with zero external tooling.
The workers run locally, so burn meters their sessions on this machine. A cron pushes the totals to a gist the site reads:
*/5 * * * * BURN_GIST_ID=51b3c4f364e68c6f86b23c5fdd147aa6 BURN_PROJECT=/Users/will/Projects/AgentWorkforce/vibe-city-game node scripts/push-burn.mjs >> /tmp/push-burn.log 2>&1Vercel env: SPEND_FEED_URL=https://gist.githubusercontent.com/willwashburn/51b3c4f364e68c6f86b23c5fdd147aa6/raw/spend.json
Spend source precedence in /api/budget: bridge → SPEND_FEED_URL → PostHog → zero.
An optional always-on bridge (npm run bridge) exists in bridge/server.mjs
for realtime agent status + spend when you want to graduate from cron.