Development setup
This page is for working on the hosted product's public parts: the server, Nova, the web app, the edge Worker, the contracts and the specs. To build games with the engine, you do not need any of this: start with Quickstart.
What works from a public clone
| Works | Does not work without the private parts |
|---|---|
The server and the web app at http://localhost:3000 | Worker runs: the sandbox snapshot needs the private bundling CLI (npm run snapshot:pack stops with one sentence) |
| Accounts, the feed, the shared-game page, the play page | npm run starter:upload, npm run android:backfill, npm run web:backfill (they export games) |
| Chatting with Nova (with an Anthropic API key) | The iOS and Android apps and the App Clip |
The edge Worker under wrangler dev: the proxy, live progress, the meter | Generated phone contracts (npm run contracts writes the web files only) |
npm run typecheck, npm run lint, npm test, npm run build | |
npm run contracts, npm run parity, npm run db:migrate |
Nova answers and starts runs locally, but without a sandbox snapshot a run fails at its sandbox step, and Nova treats it as a failure on the platform's side (she retries once, then gives up). See What is open and what is private.
Prerequisites
- Node 24 (
enginesin the rootpackage.json) and npm. - A Postgres database. The server uses
@neondatabase/serverless, so a Neon database or branch fits best. Use a database of your own, never one a deployment uses. - An Anthropic API key, for Nova.
- Optional: a Vercel Blob token (chat pictures and uploads), and a Cloudflare account for
wrangler.
The server
npm install
cp .env.example .env.local # then fill it in (below)
npm run db:migrate # applies db/migrations to DATABASE_URL
npm run dev # next dev on :3000: the web app, the API, Nova at /eve/v1/*, the workflows
npm run dev sets NODE_OPTIONS=--network-family-autoselection-attempt-timeout=2000, which gives each address of a host two seconds to connect instead of Node's default, so database calls do not time out on slow networks.
The smallest .env.local for the web app and Nova:
| Variable | Value |
|---|---|
DATABASE_URL | Your database's connection string |
APP_SESSION_SECRET | Any long random string |
INTERNAL_API_SECRET | Any long random string |
ANTHROPIC_API_KEY | Your key; Nova needs it |
PUBLIC_BASE_URL | http://localhost:3000 (.env.example has the hosted value) |
CRESCENT_EDGE_URL, REALTIME_SECRET | Empty, or a local edge (below) |
CRESCENT_REQUIRE_SIGN_IN | off, unless you set up Google sign-in with GOOGLE_WEB_CLIENT_ID |
BLOB_READ_WRITE_TOKEN | Optional: picture uploads in the chat |
CRESCENT_APP_URL defaults to http://localhost:3000; set it when next dev runs on another port. Every other variable in .env.example is for real runs or the hosted product, and each has a comment. Deploy lists them by step.
Locally the web app runs games on the other loopback name: open the app on http://localhost:3000 and games load from http://127.0.0.1:3000, or the other way round (src/web/play.ts).
The edge Worker
cd edge
npm install
npm run typecheck
printf 'REALTIME_SECRET=any-local-test-secret\n' > .dev.vars # git-ignored, never commit
npx wrangler dev --local --var ORIGIN_URL:http://localhost:3000 # proxies to next dev, serves /rt/* on :8787
To send the server's events through it, set CRESCENT_EDGE_URL=http://localhost:8787 and the same REALTIME_SECRET in .env.local. A sandbox runs on the provider's network and cannot reach a Worker on your machine, so live events from a real run need a deployed edge.
To try the meter against a stand-in for Anthropic, also pass --var ANTHROPIC_UPSTREAM:http://localhost:<port> and put ANTHROPIC_API_KEY and INTERNAL_API_SECRET in .dev.vars.
After editing edge/wrangler.jsonc, run npx wrangler types without a .dev.vars present, and commit the regenerated worker-configuration.d.ts.
Contracts, specs and docs
npm run contracts -- --check # generated web files are current
npm run parity # what each app is missing
npm --prefix docs-site run start -- --port 3100 # this site
Troubleshooting
| Symptom | Cause |
|---|---|
| Every edge page answers 503 | ORIGIN_URL is unset or still the placeholder |
| The edge answers 508 | ORIGIN_URL points at the Worker itself |
/rt/* answers 503 | REALTIME_SECRET is not set on the edge |
| The app site association file answers 502 | The origin returned something that is not JSON, often a deployment-protection page |
| A run fails at once with "No Claude credential" | No ANTHROPIC_API_KEY where the workflow runs |
| A run fails with "DAYTONA_SNAPSHOT is not configured" | No sandbox snapshot; expected in a public clone |
Making a game answers sign_in_required | Sign-in is on: set CRESCENT_REQUIRE_SIGN_IN=off locally, or sign in with Google |