Operations

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

bash
1git clone https://github.com/Puneet-Pal-Singh/LegionCode.git
2cd LegionCode
3pnpm install

Run baseline checks before changing code:

bash
1pnpm lint
2pnpm check-types
3pnpm check:boundaries

Configure

Create apps/brain/.dev.vars from its example and provide GitHub OAuth, session, and credential-encryption secrets. A typical local configuration is:

dotenv
1GITHUB_REDIRECT_URI=http://localhost:8788/auth/github/callback
2FRONTEND_URL=http://localhost:5174
3MUSCLE_BASE_URL=http://localhost:8787
4BYOK_CREDENTIAL_ENCRYPTION_KEY=<64-character-hex-key>
5BYOK_CREDENTIAL_ENCRYPTION_KEY_VERSION=v1

Generate 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:

dotenv
1VITE_BRAIN_BASE_URL=http://localhost:8788
2VITE_MUSCLE_BASE_URL=http://localhost:8787
3VITE_MUSCLE_WS_URL=ws://localhost:8787

Chat 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:

VariableDefaultAllowed values
CHAT_IMAGE_AUTO_RESIZEtruetrue or false
CHAT_IMAGE_MAX_WIDTH20001–4096 pixels
CHAT_IMAGE_MAX_HEIGHT20001–4096 pixels
CHAT_IMAGE_MAX_BASE64_BYTES5242880128–5242880 bytes
CHAT_IMAGE_MAX_DECODE_PIXELS120000001–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:

bash
1pnpm --filter @legioncode/secure-agent-api dev # 8787
2pnpm --filter @legioncode/brain dev # 8788
3pnpm --filter @legioncode/web dev # 5174

Verify runtime fingerprints before testing a task:

bash
1curl http://localhost:8787/api/debug/runtime
2curl http://localhost:8788/api/debug/runtime

The public site and docs can be developed independently:

bash
1pnpm --filter @legioncode/landing dev
2pnpm --filter @legioncode/docs dev

Use the Cloudflare Pages local commands from deployment when you need to test edge functions, /agents/ routing, /docs/ routing, or the Cloud waitlist form.