Local development
Install the monorepo, configure local services, and run the workspace development stack.
Use a local source checkout when contributing to LegionCode or testing deployment changes. Hosted product users do not need the monorepo.
Prerequisites
- Node.js 20 LTS
- pnpm 9
- Git
- a Cloudflare account for remote bindings and sandbox work
- a Postgres database reachable through Cloudflare Hyperdrive
- a GitHub OAuth application for authentication testing
Install
1git clone https://github.com/Puneet-Pal-Singh/LegionCode.git2cd LegionCode3pnpm installRun baseline checks before changing code:
1pnpm lint2pnpm check-types3pnpm check:boundariesConfigure
Create apps/brain/.dev.vars from its example and provide GitHub OAuth, session, and credential-encryption secrets. A typical local configuration is:
1GITHUB_REDIRECT_URI=http://localhost:8788/auth/github/callback2FRONTEND_URL=http://localhost:51743MUSCLE_BASE_URL=http://localhost:87874BYOK_CREDENTIAL_ENCRYPTION_KEY=<64-character-hex-key>5BYOK_CREDENTIAL_ENCRYPTION_KEY_VERSION=v1Generate encryption material with openssl rand -hex 32. Never commit .dev.vars.
Configure the Web endpoints in a local .env file when defaults do not match:
1VITE_BRAIN_BASE_URL=http://localhost:87882VITE_MUSCLE_BASE_URL=http://localhost:87873VITE_MUSCLE_WS_URL=ws://localhost:8787Chat image settings
Brain fully decodes PNG, JPEG, WebP, and GIF uploads before storing them. The original bytes remain in authenticated R2 storage; provider requests receive validated images resized to fit these optional Brain variables:
| Variable | Default | Allowed values |
|---|---|---|
CHAT_IMAGE_AUTO_RESIZE | true | true or false |
CHAT_IMAGE_MAX_WIDTH | 2000 | 1–4096 pixels |
CHAT_IMAGE_MAX_HEIGHT | 2000 | 1–4096 pixels |
CHAT_IMAGE_MAX_BASE64_BYTES | 5242880 | 128–5242880 bytes |
CHAT_IMAGE_MAX_DECODE_PIXELS | 12000000 | 1–12000000 pixels |
The decoding ceiling is checked before allocating pixels. Upload limits still apply: four images per message, 5 MiB per original, and 10 MiB per message. Disabling resizing rejects images above the configured dimensions or encoded size. Invalid configuration fails validation. Resizing can convert an image to PNG or JPEG; GIFs requiring resizing use the decoded first frame.
Image-bearing conversations require image input support in the server's model catalog, including later text-only follow-ups and revisions. Unknown support returns an explicit error so operators can correct discovery metadata.
These defaults and the PNG/JPEG resizing sequence adapt OpenCode's image settings and image processing.
Rename cutover safety
The legioncode-* Wrangler names are the repository's forward-only deployment
targets. Local configuration does not rename or migrate existing Cloudflare
resources. Before using a renamed target for a real environment, record Worker,
Durable Object, container, Pages, route, domain, DNS, bucket, database, and
secret identities. Prefer verified in-place Worker or Pages renames that retain
those identities; stop if the provider cannot prove that dependencies remain
attached. R2 requires a new legioncode-artifacts bucket and a copy-and-verify
operation with the old bucket retained. Preserve existing D1/KV/Hyperdrive IDs;
only provision replacement resources when an explicit state migration plan
proves that identity and data are preserved. Explicitly migrate Durable Object
namespaces and state rather than using an empty namespace, and recreate or
transfer secrets, OAuth callbacks, CORS origins, and encrypted credentials.
Before switching traffic, suspend the Brain schedule with an explicit schedules
API PUT body of [] and capture its prior value. Set Brain
LAUNCH_EMERGENCY_SHUTOFF_MODE=block_runs, drain canonical active runs and
leases, then set Secure API to block_session_and_execute until internal
execution and transcript settlement are quiet. Include canonical and legacy
chat history POST routes as writers. Use block_all only for the final rename
and R2 copy window; verify both append routes return typed 503 responses while
authorized cancellation and replay remain available. Restore the exact captured
schedule only after paginated R2 listing, streamed byte hashes, HTTP/custom
metadata verification, and a final delta check pass. Keep the old bucket and
stop on any identity or drain mismatch. Switch traffic only after all upstream
health checks pass together.
Run the Postgres migration worker from one target at a time. Do not run old and new migration workers concurrently: the advisory lock serializes SQL statements, but it does not coordinate deployment cutover, Durable Object state, or artifact migration. The existing migration tags remain immutable.
After pulling this rename, stop any workers started with the previous local
configuration before restarting. If ignored local files exist, update their
worker names and cross service bindings to match the tracked configs: Brain
uses legioncode-brain and SECURE_API=legioncode-api; the secure runtime uses
legioncode-api and BRAIN=legioncode-brain. Preserve private database URLs,
credentials, resource IDs, and other options. Update the branding in any
private .dev.vars system prompt and update local staging smoke environment
values for the new worker URLs and legioncode_session cookie; do not copy
private values into the repository. Restart both workers so an old-name
process cannot be reused by port detection.
Run and verify
Start each service in its own terminal:
1pnpm --filter @legioncode/secure-agent-api dev # 87872pnpm --filter @legioncode/brain dev # 87883pnpm --filter @legioncode/web dev # 5174Verify runtime fingerprints before testing a task:
1curl http://localhost:8787/api/debug/runtime2curl http://localhost:8788/api/debug/runtimeThe public site and docs can be developed independently:
1pnpm --filter @legioncode/landing dev2pnpm --filter @legioncode/docs devUse the Cloudflare Pages local commands from deployment when you need to test edge functions, /agents/ routing, /docs/ routing, or the Cloud waitlist form.