Skip to main content

Add a New Standalone App

⚠️ This is the INFRA-MECHANICS appendix, not the full process. The canonical onboarding SOP — infrastructure and citizenship (spine, registry, governance, staffing, Definition of Done) — is app-onboarding.md. Start there. Known-stale: several workflows referenced below (create-hyperdrive.yml, scaffold-*.yml, setup-*-secrets.yml) no longer exist; use the scripts in packages/deploy/scripts/ directly. Librarian #1975 owns the refresh.
Use this runbook when onboarding a brand-new Factory app that is not one of the original 6 apps (wordis-bond, cypher-healing, prime-self, ijustus, the-calling, neighbor-aid). New apps use a standalone per-app workflow pattern rather than the bulk scripts (setup-all-apps.mjs, scaffold-all-apps.yml).

Rate Limiter ID Registry

Each app gets a unique Cloudflare rate limiter namespace ID. IDs are integers; never reuse one. Next available ID: 1022
⚠️ Rate-limit bindings go under wrangler unsafe.bindings with type: "ratelimit" — the top-level rate_limiters key is silently dropped (“unexpected field”) and binds nothing (the-calling#43).
Allocate separate ids per environment so a misconfigured staging worker cannot exhaust the prod rate-limit budget. Update this table every time a new app or environment is added.

Prerequisites

Before starting:
  • Neon project created for this app
  • Connection string stored as a GitHub Secret in Latimer-Woods-Tech/factory: {APP_UPPER}_CONNECTION_STRING
    • Example: app xico-city → secret MEXXICO_CITY_CONNECTION_STRING
  • CF_API_TOKEN set in Factory GitHub Secrets
  • GH_PAT (classic PAT with repo + read:packages) set in Factory GitHub Secrets
  • GitHub repo created: gh repo create Latimer-Woods-Tech/{app} --private

Step 1 — Add App to Factory Scripts

Edit three files in packages/deploy/scripts/:

1a. create-hyperdrive.mjs

Add an entry to the CONFIGS array:

1b. write-schema.mjs

Add a schema entry to the SCHEMAS object with the app’s canonical Drizzle ORM schema.

1c. add-app-deps.mjs

Add an entry specifying which @latimer-woods-tech/* packages the app uses beyond the defaults:

Step 2 — Update create-hyperdrive.yml

In .github/workflows/create-hyperdrive.yml, add:
  1. Env var in the Create Hyperdrive instances step:
  2. Store step (even though it will fail with 403 — the log still captures the UUID):

Step 3 — Create Standalone Workflow Files

Create two new files in .github/workflows/:

scaffold-{app}.yml

Copy scaffold-xpelevator.yml and replace all occurrences of xpelevator with {app}, then:
  • Set the correct rate limiter ID (from the registry above)
  • Leave --hyperdrive-id as a placeholder ("REPLACE_AFTER_STEP_4") until Step 4

setup-{app}-secrets.yml

Copy setup-xpelevator-secrets.yml and replace all occurrences of xpelevator with {app}.
  • The JWT secret references JWT_SECRET_{APP_UPPER} — add this to Factory GitHub Secrets before running
  • The Sentry DSN references SENTRY_DSN_{APP_UPPER} — add this to Factory GitHub Secrets before running

Step 4 — Commit, Push, Create Hyperdrive

Push (use the GCM workaround on Windows):
Then trigger the Hyperdrive creation workflow:

Step 5 — Extract Hyperdrive UUID from Logs

The “Store” step will fail with 403 (expected — see lessons-learned.md). Get the UUID from the successful “Create” step:
Then:
  1. Hard-code the UUID in scaffold-{app}.yml
  2. Store it as a Factory secret:
  3. Commit and push the updated scaffold workflow

Step 6 — Add Required Secrets to Factory

Before running the scaffold and secrets workflows, ensure these exist in Latimer-Woods-Tech/factory GitHub Secrets: Generate JWT secret:

Step 7 — Run Scaffold and Secrets Workflows


Step 8 — Update This Registry

Add the new app and its rate limiter ID to the registry table at the top of this file. Commit the update.

Checklist Summary

  • Rate limiter ID reserved (next available: 1012+)
  • Neon connection string in Factory Secrets as {APP_UPPER}_CONNECTION_STRING
  • GitHub repo created: Latimer-Woods-Tech/{app}
  • create-hyperdrive.mjs updated
  • write-schema.mjs updated with canonical Drizzle schema
  • add-app-deps.mjs updated with app-specific package deps
  • create-hyperdrive.yml updated with new env var + store step
  • scaffold-{app}.yml created
  • setup-{app}-secrets.yml created
  • All changes committed and pushed
  • create-hyperdrive.yml workflow triggered; UUID extracted from logs
  • UUID hard-coded in scaffold workflow + stored as HYPERDRIVE_{APP_UPPER} secret
  • JWT_SECRET_{APP_UPPER} added to Factory Secrets
  • SENTRY_DSN_{APP_UPPER} added to Factory Secrets
  • scaffold-{app}.yml workflow run successfully
  • setup-{app}-secrets.yml workflow run successfully
  • Rate limiter registry table updated in this file
  • Update github-secrets-and-tokens.md to include new app in the app list
  • Create .github/repo-contexts/{app}/CLAUDE.md with the repo’s standing orders (stack, hard constraints, surfaces, commit format). The CI validate job will fail until this file exists — see coh or humandesign for examples.