PROMPT FOR CODEX — paste everything below this line
You are deploying Chimti's new merged web app + API updates on the Otlu server
(Coolify). This prompt is complete and self-contained — follow it top to
bottom, in order. This is an INFRA task: do not improvise product/code changes.
If a precondition fails, STOP and report back instead of patching code.
Context (30 seconds)
- `chimti-web-app` is ONE repo replacing BOTH old frontends
(`chimti-user-app`, `chimti-admin-app`). Same repo builds two variants via
Docker build arg `VITE_APP_VARIANT=user|admin`. It is feature-complete:
registers + deep detail chains (clients→brand→locations→pricing, orders→items,
role editor, plans depth, AI consoles, help center), mobile card-list layout,
Capacitor dirs (ignore android/ ios/ for this deploy).
- Target domains (NEW): **https://app.chimti.ai** (user app) and
**https://admin.chimti.ai** (admin app).
- API stays at **https://chimti-api.otlu.io** — the web builds bake this base
URL at build time. Do NOT move the API in this task.
- `chimti-api` has additive changes that go live FIRST:
1) read-only billing routes (`/v1/invoices`, `/v1/plans`, `/v1/subscriptions`)
2) a background worker layer (`src/worker.js` + `src/workers/`, 21 jobs,
separate process off the same image; `npm run test:workers` → 25 green).
- Old apps at `chimti-user-app.otlu.io` / `chimti-admin-app.otlu.io` stay
deployed untouched as instant rollback. Do not delete them.
Step 0 — Preconditions (verify before touching Coolify)
Jagjeet has pushed the deploy branches. Confirm on a fresh checkout; if
anything is missing, STOP and tell him exactly what.
`chimti-api`:
- `src/routes/billing.js` exists and `src/app.js` registers it
- `src/worker.js`, `src/workers/` (7 files), `test/workers.test.js` exist
- `package.json` has `"worker": "node src/worker.js"` and `"test:workers"`
- Run: `node test/workers.test.js` → expect "all worker tests green — 25 passed"
`chimti-web-app`:
- Repo root has `Dockerfile`, `nginx.conf`, `.dockerignore`
- Sentinel files for the latest drops (A–D) exist:
`src/lib/brandCatalog.js`, `src/lib/accessControl.js`,
`src/pages/PermissionDetailsPage.jsx`, `src/pages/UserDetailsPage.jsx`,
`src/pages/HelpCenterPage.jsx`, `src/pages/AiOpsModulesPage.jsx`,
`src/pages/ProfilePage.jsx`, `src/pages/ShipmentsPage.jsx`
- `.env` in repo contains `VITE_BACKEND_API_BASE_URL=https://chimti-api.otlu.io`
- Run: `npm ci && npm test` → expect "all suites green" with render smoke
"47 routes (user variant)" and "34 routes (admin variant)"
Step 1 — Deploy chimti-api update
1. Coolify → existing `chimti-api` app → Redeploy (same Dockerfile, no config
change for this step).
2. Verify:
curl -s https://chimti-api.otlu.io/v1/health # JSON with "service":"chimti-api"
TOKEN="<accessToken from POST /v1/auth/login with a real internal account>"
curl -s -H "Authorization: Bearer $TOKEN" https://chimti-api.otlu.io/v1/plans | head -c 300
curl -s -H "Authorization: Bearer $TOKEN" https://chimti-api.otlu.io/v1/invoices | head -c 300
# expect {"items":[...]} from both (empty array is fine)
Step 2 — CORS for the new domains
In chimti-api's Coolify env, APPEND to `CORS_ORIGIN` (comma-separated, KEEP
every existing origin):
https://app.chimti.ai,https://admin.chimti.ai
Restart chimti-api, then verify preflight:
curl -s -o /dev/null -w '%{http_code}\n' -X OPTIONS https://chimti-api.otlu.io/v1/health \
-H 'Origin: https://app.chimti.ai' -H 'Access-Control-Request-Method: GET'
# expect 200/204, not 4xx
Step 3 — Worker process (new Coolify service)
1. New Coolify app `chimti-worker` from the SAME `chimti-api` repo (same
Dockerfile). No domain, no public port.
2. Start command override: `npm run worker` (image default CMD starts the API;
the worker MUST run `node src/worker.js`).
3. Env = copy ALL of chimti-api's env (needs `DATABASE_URL` etc.), PLUS:
WORKERS_ENABLED=1
WORKER_ALERT_EMAIL=me@jagjeetsinghsethi.com
WORKER_DIGEST_HOUR=8
WORKER_SNAPSHOT_HOUR=2
WORKER_DEMO_RESET_HOUR=4
(Optional kill switches `WORKER_<KEY>=0`, cadence overrides
`WORKER_<KEY>_INTERVAL_SECONDS` — keys listed in
`chimti-docs/architecture/workers-automation-plan.md`.)
4. Deploy; logs must show `chimti worker starting` and 21 × `job scheduled`.
First boot creates `worker_heartbeats` / `worker_snapshots` tables itself —
no migration step.
5. DB check:
SELECT name, last_run_at, last_ok_at, last_error FROM worker_heartbeats ORDER BY name;
-- within ~2 minutes most short-interval jobs should have last_ok_at set
Step 4 — DNS for chimti.ai
At the chimti.ai DNS provider add two records pointing at the Otlu server
(same IP as the *.otlu.io apps), TTL 300 during cutover:
app.chimti.ai A <server-ip> (or CNAME to the Coolify proxy hostname)
admin.chimti.ai A <server-ip>
Coolify/Traefik issues Let's Encrypt certs automatically once the apps exist.
Step 5 — Two web apps from chimti-web-app
Create TWO Coolify apps from `chimti-web-app`, Build Pack = Dockerfile (repo
root). nginx-static image; no runtime env needed.
- **`chimti-web-user`** — domain `app.chimti.ai`, build arg
`VITE_APP_VARIANT=user`
- **`chimti-web-admin`** — domain `admin.chimti.ai`, build arg
`VITE_APP_VARIANT=admin`
(`VITE_BACKEND_API_BASE_URL` defaults to `https://chimti-api.otlu.io` inside
the Dockerfile — do not set it.)
Deploy both; builds must end with vite `✓ built` and an nginx image.
Step 6 — Smoke checklist (tick every box)
User app — https://app.chimti.ai:
- [ ] Login page over valid HTTPS; Chimti wordmark + blue theme
- [ ] Brand login works; INTERNAL login rejected with variant-guard message
- [ ] Dashboard live numbers; dark mode persists on reload
- [ ] Orders → open order (7 tabs) → move status; assign pickup boy modal opens
- [ ] Customers → customer detail (5 tabs) → add internal note
- [ ] Locations → location detail shows Pricing Source (brand/store override)
- [ ] Preferences → item-level pricing editor loads and saves
- [ ] Staff: row-tap edit modal; Users: 4-step onboarding wizard opens
- [ ] Campaigns & Loyalty: create a test coupon → appears in list → pause it
- [ ] Processing queue: SLA strip renders (escalate only if a row is past ETA)
- [ ] Delivery tracking: route planner renders partners; Shipments: scan desk
accepts a live article tag (OK) and rejects a fake one (Unknown)
- [ ] Help Center loads; /ai-pos console renders with live signals
- [ ] Profile: change password with a test account (then change it back)
- [ ] Phone width: hamburger drawer + card-list registers, no sideways scroll
Admin app — https://admin.chimti.ai:
- [ ] Internal login works; brand login rejected
- [ ] Founder deck (MRR/ARR from /v1/subscriptions)
- [ ] Clients → client detail; Brands → brand detail (10 tabs) → store →
Services catalog editor saves
- [ ] Plans page: blueprint cards + feature matrix + register
- [ ] Permissions: role list → open a role → toggle one permission → Save →
re-open (persisted) → toggle back → Save
- [ ] Users → user detail (Overview/Access/Logs)
- [ ] Modules: list + module detail; Save works (PATCH /v1/platform-module-settings)
- [ ] Demo workspaces: Reset Demo Data confirm dialog (cancel it)
After 24h (bonus):
- [ ] `SELECT * FROM worker_snapshots ORDER BY day DESC LIMIT 5;` has yesterday's rows
- [ ] Founder digest email arrived at WORKER_ALERT_EMAIL ~08:00 server time
Step 7 — Old domains policy
Leave `chimti-user-app.otlu.io` / `chimti-admin-app.otlu.io` running untouched
(rollback). Only after Jagjeet confirms the new domains (a few days), add 301
redirects old → new and archive the old app repos.
Rollback
- Web: users go back to the old otlu.io apps (untouched), or drop the new DNS
records.
- API: redeploy previous chimti-api image from Coolify history. All changes are
additive — no schema rollback (worker tables are standalone).
- Worker: stop/disable `chimti-worker`; nothing depends on it.
Explicitly OUT of scope
- Moving the API to api.chimti.ai (separate later task: domain + CORS + new
web builds with new base URL).
- chimti-website / pre-launch page / customer app / field mobile app.
- Android/iOS store builds (Capacitor; run from Jagjeet's Mac).
- Backend backlog items the UI already labels honestly: POST /v1/shipments,
leads write APIs, access-control approval-requests API. Do not stub them.
Reference docs in the repos
- `chimti-web-app/docs/deploy.md` — build/variant summary
- `chimti-docs/deployment/codex-deploy-brief.md` — earlier brief (this prompt
supersedes it where they differ)
- `chimti-docs/architecture/workers-automation-plan.md` — all 21 jobs + env flags
- `chimti-docs/decisions/2026-08-01-chimti-web-merge.md` — decision record
(Drops A–D addenda = what's in this build)
Report back with: each step's status, the full smoke checklist ticked, the
worker_heartbeats query output, and anything you had to deviate on.