Skip to content

Staging Environment

Nucleus currently runs in two environments:

  • Dev/localpnpm dev, mock auth, local D1 (.wrangler/)
  • Production*.nucleus.fast custom domains, real CF Access, prod D1 / KV / R2

There is no persistent staging environment where changes can be verified end-to-end (app + agents + pulse + mcp, all wired together) before hitting prod. This doc plans one.

What already exists (and why it’s not enough)

Section titled “What already exists (and why it’s not enough)”

See Preview Deployments. CI spins up a nucleus-preview-pr-{number} D1 database and a Worker version alias on pr-{number}.nucleus-app.workers.dev for every PR. Great for isolated smoke-tests, but:

  • Only the app worker — no admin, portal, pulse, mcp, agents, or orchestrator
  • R2 (UPLOADS) and Analytics Engine still point at production bindings
  • Ephemeral — destroyed on PR close, so nothing to demo from
  • *.workers.dev only — no custom domain, no CF Access

apps/app/wrangler.toml and apps/portal/wrangler.toml have [env.preview] sections pointing at nucleus-db-preview (1b38e91d-…). Twelve other workers — admin, pulse, mcp, orchestrator, and the 11 agents — have no preview env at all. No preview KVs, no preview R2, no preview DO namespace, no preview custom domain, no CF Access on *.workers.dev.

Net: preview is a scratch-DB hatch, not staging.

Section titled “Recommended approach: *.staging.nucleus.fast”

Add a Wrangler environment called staging to every worker, with its own subdomain, databases, bindings, and secrets.

  • app.staging.nucleus.fast
  • admin.staging.nucleus.fast
  • portal.staging.nucleus.fast
  • pulse.staging.nucleus.fast
  • mcp.staging.nucleus.fast
  • orchestrator.staging.nucleus.fast
  • *.agent.staging.nucleus.fast (11 agents)
  • docs.staging.nucleus.fast (Pages branch deploy)
  • staging.nucleus.fast (site — Pages branch deploy)
ResourceCountNotes
D1 databases2nucleus-db-staging, pulse-db-staging — apply migrations on first deploy
R2 buckets1nucleus-uploads-staging
KV namespaces7COOLDOWNS, TA_KV, RELEASE_STATE, PA_KV, CONTENT_STORE, DEV_AGENT_KV, MCP_SESSIONS (staging versions)
Durable Objects1DocumentCollabDO — new migration tag in staging env
CF Access app1Policy covering *.staging.nucleus.fast, same @dotcollective.com.au rule
DNS records~15One per subdomain pointing at the staging worker

Every worker gets an [env.staging] block:

  • apps/app/wrangler.toml — staging D1 ID, staging R2 bucket, DO migration tag, env vars (APP_URL=https://app.staging.nucleus.fast, CF_ACCESS_TEAM_DOMAIN=nucleusfast). Consider reducing or disabling crons in staging.
  • apps/admin/wrangler.toml — staging agent URLs (point at staging agents, not prod)
  • apps/portal/wrangler.toml — staging D1 + APP_URL
  • apps/pulse/worker/wrangler.toml — staging D1; swap hardcoded ENV = "production" for an env-specific var
  • apps/mcp/wrangler.jsonc — staging KV, staging NUCLEUS_APP service binding, staging NUCLEUS_API_URL
  • apps/orchestrator/wrangler.toml — staging KV, staging DO + Container class name
  • apps/agents/*/wrangler.toml — staging KVs, service bindings targeting staging app worker, staging agent URLs
  • Replace hardcoded https://app.nucleus.fast, https://mcp.nucleus.fast, https://pulse.nucleus.fast fallbacks with c.env.APP_URL / c.env.MCP_URL / etc. Grep for .nucleus.fast string literals in apps/*/worker/.
  • Anywhere ENV is compared (e.g. pulse’s hardcoded "production"), make it a vars.ENV value.

Every secret gets re-set with wrangler secret put --env staging <NAME>:

  • App (~15): NUCLEUS_SERVICE_TOKEN, CONNECTIONS_ENCRYPTION_KEY, XERO_CLIENT_SECRET, GOOGLE_CLIENT_SECRET, ANTHROPIC_API_KEY, CF_AI_API_TOKEN, CF_API_TOKEN, Slack / Twilio / Unsplash / TikTok / ReverseContact / Scrapin keys, MCP_BRIDGE_SECRET
  • Pulse (~8): Slack + Productive OAuth, TOKEN_ENCRYPTION_KEY, RESEND_API_KEY, DEV_BEARER_TOKEN
  • Admin: ADMIN_SECRET
  • Agents (per agent): ANTHROPIC_API_KEY, GITHUB_TOKEN, SLACK_BOT_TOKEN, NUCLEUS_SERVICE_TOKEN, plus agent-specific keys

OAuth choice point: staging can either (a) reuse prod OAuth apps with staging redirect URIs added, or (b) register separate staging OAuth apps per integration. (b) is cleaner isolation; (a) is ~80% less work. Recommend (a) for internal integrations (Slack, GitHub, Xero) and (b) only where the vendor requires it.

Add to root package.json:

deploy:staging → runs deploy:*:staging in parallel
deploy:app:staging → wrangler deploy --env staging (per workspace)

Add .github/workflows/deploy-staging.yml triggered on push to a staging branch (or on PR label). Mirror the existing deploy-app.yml patterns with --env staging.

Terminal window
cd apps/app
wrangler d1 migrations apply nucleus-db-staging --remote
wrangler d1 execute nucleus-db-staging --remote --file db/seed.sql

Optional: pipe a sanitised prod snapshot to staging for realistic testing.

  1. curl https://app.staging.nucleus.fast/api/health returns 200
  2. Log in through CF Access → frontend loads, /api/users/me returns the seeded admin user
  3. Pulse: pair a dev device against pulse.staging.nucleus.fast, confirm Slack status updates
  4. MCP: point Claude Desktop at mcp.staging.nucleus.fast, confirm list_companies returns
  5. Run at least one cron manually (wrangler trigger <worker> --env staging) and confirm it hits staging DB, not prod
SliceEffortWhat it unlocks
MVP — app + mcp + pulse only, staging D1, CF Access, DNS~1 dayEnd-to-end UI/API testing on a staging domain
Full parity — all 15 workers, KVs, R2, DO, OAuth redirects, staging agents, CI workflow~3–4 daysTrue pre-prod environment; agents schedule against staging DB
Hardened — sanitised prod data seeding, separate OAuth apps, alerts, staging-specific rate limits~1 week totalSafe for demoing / training / QA without touching prod data

Recommendation: do the MVP slice first. Agents are homogeneous — once one has an [env.staging] block, the rest are copy-paste.

  • apps/app/wrangler.toml — template for the [env.staging] block every worker needs
  • apps/app/worker/middleware/auth.ts — verify it doesn’t assume a prod-only CF Access audience
  • apps/pulse/worker/wrangler.toml — hardcoded ENV = "production" needs to become env-specific
  • apps/mcp/wrangler.jsonc — service binding NUCLEUS_APP must point at nucleus-app in the matching env
  • .github/workflows/deploy-app.yml (and siblings) — template for new deploy-staging.yml
  • Root package.json — add deploy:*:staging scripts