Operations

Deployment

Deploy the public site, documentation, workspace, API service, and secure runner to Cloudflare.

LegionCode uses separate Cloudflare deployments behind one public product origin.

Deployment shape

Public URLDeployment
legioncode.devLanding Pages project
legioncode.dev/cloud/Cloud private-alpha access page
legioncode.dev/docs/*Docs static project routed at the edge
legioncode.dev/docs/changelog/Changelog route in the docs project
legioncode.dev/agents/*Agents workspace routed through Pages
brain.legioncode.devAPI service Worker
api.legioncode.devSecure runner Worker and sandbox runtime

The agents app is built with a /agents/ base path. OAuth callbacks should return users to the same public agents path.

Required bindings

Production requires the configured Cloudflare bindings for the API service, secure runner, artifacts, sessions, database access, and edge routing. Keep bindings environment-specific and verify them before promoting a deployment.

Set secrets with Wrangler rather than committing them:

  • GitHub client secret, session secret, and GitHub token-encryption key;
  • BYOK credential-encryption key and version;
  • internal runtime-event secret;
  • any managed provider credentials enabled for the environment.

The landing Pages project requires a D1 binding named WAITLIST_DB for Cloud access requests. Apply apps/landing/migrations/0001_waitlist.sql before enabling the waitlist form.

For private alpha, set PRIVATE_ALPHA_ACCESS_MODE=allowlist in production and list approved GitHub logins in PRIVATE_ALPHA_GITHUB_LOGINS.

Rename cutover

Changing deployment names from the former product name to the legioncode-* targets is a coordinated infrastructure cutover. This repository change does not provision or rename Cloudflare resources. Before switching traffic:

  1. Inventory and record the current Worker UUIDs and versions, Durable Object namespace IDs and classes, container application IDs, service bindings, routes, domains, DNS records, Pages project IDs and domains, R2 bucket metadata, D1/KV/Hyperdrive IDs, and required secret names. Prefer an in-place Worker or Pages rename when the provider preserves those identities; fetch the resources after each rename and stop if any dependency identity is not preserved.
  2. R2 buckets do not have a rename operation. Create the legioncode-artifacts bucket only after recording the old bucket, copy objects without deleting the source, and verify every key, size, checksum, and custom metadata before directing reads to the new binding. Retain the old bucket for rollback.
  3. If a Worker, Pages project, container, route, or Durable Object namespace cannot preserve its identity in place, provision a destination only with an explicit migration plan. Migrate Durable Object namespaces and state explicitly, including RUN_ENGINE_RUNTIME, RUN_ADMISSION_LIMITER, AGENT_RUNTIME, and Sandbox; preserve the existing migration tags and record source and destination IDs. Never fall back to an empty namespace.
  4. Recreate or transfer secrets and encrypted credentials, service bindings, OAuth callbacks, CORS origins, and deployment variables. Verify every dependency against the recorded inventory before traffic moves. Preserve the existing D1, KV, and Hyperdrive IDs rather than creating empty replacements.
  5. Switch traffic in one coordinated window after health checks pass for the landing, docs, Web, Brain, and secure runtime paths. Keep rollback records for every binding and route change.

Before step 5, execute this explicit freeze and drain sequence. Launch flags do not stop in-flight executions, scheduled retention, or transcript settlement writes by themselves:

  1. GET the Brain Worker schedules, record the exact response, then PUT an explicit empty schedule array ([]). Verify the readback is empty and wait for trigger propagation. Restore the captured schedule only after the final R2 delta check.
  2. Set Brain LAUNCH_EMERGENCY_SHUTOFF_MODE=block_runs, then query canonical lifecycle state until active runs, pending captures, checkouts, and admission leases are drained. Do not use sampled logs or container health as drain proof.
  3. Set Secure API to LAUNCH_EMERGENCY_SHUTOFF_MODE=block_session_and_execute. Let authorized cancellation and event settlement finish. Include both canonical and legacy chat history POST routes in the writer drain; authenticated history GET and replay remain available.
  4. Set Secure API to block_all only after settlement is quiet. Verify both append routes return typed 503 responses while authorized cancellation and replay still work. The scheduled retention handler must return before any repository or R2 access when block_runs is active.
  5. While all writers remain frozen, list every R2 object through the paginated API, stream and record its byte hash, and copy while preserving HTTP and custom metadata. Re-list for a final delta before switching bindings. Keep the old bucket and stop on any identity, metadata, hash, or drain mismatch.

During the cutover, run exactly one migration worker against the shared Postgres database. The old and new migration targets must never run at the same time, even though the runner uses a Postgres advisory lock. The lock protects statements; it does not coordinate an operational deployment or state and artifact copying.

Deploy order

  1. Apply pending Postgres migrations from an isolated remote-development session. This entrypoint has no public fetch route:

    bash
    1cd apps/brain
    2corepack pnpm exec wrangler dev \
    3 --config wrangler.migrations.jsonc \
    4 --remote \
    5 --test-scheduled

    In a second terminal, invoke the local scheduled-test endpoint:

    bash
    1curl --fail-with-body "http://localhost:8787/__scheduled"

    Inspect the migration worker log for the exact applied and skipped migration IDs, then stop the remote-development session. The migration runner uses one PostgreSQL transaction plus an advisory lock; a failed statement is rolled back and must block deployment.

  2. Deploy the secure runner and verify its bindings.

  3. Deploy the API service and verify auth, database access, and service bindings.

  4. Build and deploy the agents app with all three required VITE_* endpoints.

  5. Build and deploy docs.

  6. Build and deploy landing last so public routes point at healthy upstreams.

Deploy builds fail fast

The agents deploy build requires VITE_BRAIN_BASE_URL, VITE_MUSCLE_BASE_URL, and VITE_MUSCLE_WS_URL. Do not replace that check with production defaults in source.

Verify

Check the landing page, Cloud page, waitlist submission, docs, changelog, agents shell, OAuth session endpoint, and runtime fingerprint endpoints. Then load one agents asset through legioncode.dev/agents/assets/... to verify prefix routing.

Confirm production CORS allows the public origin and that the API service sends authenticated users to https://legioncode.dev/agents.