5.9 KiB
Deploy — prod backend on the DO droplet (lime / lilith-store-backend)
Target (probed 2026-06-29): lime = lilith-store-backend, Ubuntu 24.04, public 209.38.51.98 · wg 10.9.0.5 · VPC 10.20.0.2. Co-tenant: mac-sync-server (Bun). Postgres 16 + pgbouncer (socket/pooler, not TCP). Node 18 present — NestJS 11 needs Node 20+ (the one box change). 74G free. SSH alias lime (root, ~/.ssh/id_ed25519_1984).
Dev UI reaches prod over the WG mesh (10.9.0.5:3210) — no public TLS/DNS needed.
⚠️ These steps
sudo-write a SHARED prod host. They were blocked under auto mode (correctly). Run them in a non-auto session, or grant aBash(ssh lime *)permission rule, or run them yourself.
1. Node 20 on the droplet
ssh lime 'curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt-get install -y nodejs && node -v'
(mac-sync uses Bun, so a system Node bump is safe for it.)
2. Create the two DBs — on the DO Managed Postgres cluster
There is no local Postgres. The droplet's pgbouncer (:6432) fronts a DO Managed Postgres cluster: private-lilith-store-pg-do-user-28217120-0.l.db.ondigitalocean.com:25060 (holds the live quinn DB). So people + prospector are new databases on that managed cluster (additive — does NOT touch quinn):
- Via Terraform IaC (the DO infra is Terraform-managed in
uvlava/terraform/do). The DBs + dedicated users are already declared (pg_databases+= people/prospector;digitalocean_database_user.{people,prospector}). Just apply:cd ~/Code/@projects/uvlava/terraform/do TF_VAR_do_token=<your DO token> terraform apply # additive: +2 dbs, +2 users, 0 destroy terraform output -raw people_db_password terraform output -raw prospector_db_password terraform output -raw pg_host # private cluster host for the .env - Services connect directly to the managed endpoint over SSL (skip the shared pgbouncer to avoid touching live pooling):
*_DB_HOST=private-lilith-store-pg-...,*_DB_PORT=25060,*_DB_SSL=true. (Optionally add[databases]entries to/etc/pgbouncer/pgbouncer.ini+ reload to pool them, but that touches shared infra.)
3. Apply migrations
# prospector
for f in 0001_prospector 0002_drafts 0003_corrections; do
ssh lime "sudo -u postgres psql -d prospector" < migrations/$f.sql ; done
# people (from the cocottetech repo)
ssh lime "sudo -u postgres psql -d people" < <people-service>/migrations/0001_people.sql
4. Ship the built code
Build locally, rsync dist + manifests, install prod deps on the droplet:
npm run build && npm run build -w @prospector/mcp-prospector
rsync -az --delete dist package.json package-lock.json migrations lime:/opt/prospector/
ssh lime 'cd /opt/prospector && npm ci --omit=dev'
# people-service likewise to /opt/people-service
5. Env on the droplet (/opt/prospector/.env)
NODE_ENV=production
PROSPECTOR_API_PORT=3210
PROSPECTOR_DB_HOST=private-lilith-store-pg-do-user-28217120-0.l.db.ondigitalocean.com
PROSPECTOR_DB_PORT=25060 # DO managed cluster (direct, SSL)
PROSPECTOR_DB_SSL=true
PROSPECTOR_DB_NAME=prospector
PROSPECTOR_DB_USER=prospector
PROSPECTOR_DB_PASSWORD=<from doctl databases user create>
PROSPECTOR_SERVICE_TOKEN=<strong-token>
PEOPLE_BASE_URL=http://127.0.0.1:3061
PEOPLE_SERVICE_TOKEN=<people-token>
MACSYNC_BASE_URL=http://127.0.0.1:3201 # mac-sync runs on this same droplet
MACSYNC_SERVICE_TOKEN=<macsync-token>
MACSYNC_DEVICE_ID=<device>
MRNUMBER_BASE_URL=https://my.transquinnftw.com
MRNUMBER_SERVICE_TOKEN=<mr-token>
(people-service gets its own /opt/people-service/.env with PEOPLE_DB_* + PEOPLE_SERVICE_TOKEN.)
6. systemd units (/etc/systemd/system/{prospector,people-service}.service)
[Service]
WorkingDirectory=/opt/prospector
EnvironmentFile=/opt/prospector/.env
ExecStart=/usr/bin/node dist/main.js
Restart=always
User=root
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now people-service prospector → curl localhost:3061/health, curl localhost:3210/health.
7. Wire mac-sync → prospector webhook
In the @mac-sync server (same droplet): on a new inbound, fire-and-forget
POST http://127.0.0.1:3210/internal/inbound with Authorization: Bearer $PROSPECTOR_SERVICE_TOKEN, body {handle, channel:'imessage', text, occurredAt, hasCallSignal?}. Env-gated (PROSPECTOR_WEBHOOK_URL/token) so macsync runs standalone if unset. (Redo cleanly — the earlier agent left partial edits in @mac-sync.)
8. Point the dev UI at prod (over the mesh)
web/.env.local:
PROSPECTOR_API_URL=http://10.9.0.5:3210
PROSPECTOR_SERVICE_TOKEN=<the prod PROSPECTOR_SERVICE_TOKEN>
Restart npm run dev -w @prospector/web. The vite proxy injects the token; the panel now shows real prod decisions.
Verify (go-live)
/health both services → real inbound (or prospector_submit_inbound) → appears in prospector/activity → kill-switch flip persists → dev UI shows it over the mesh.
Post-migration notes (2026-06-29 unification)
- Run new migrations: for f in migrations/0006_bilingual.sql ; do ssh lime "sudo -u postgres psql -d prospector" < $f ; done
- Bilingual now in prospect_drafts (original/translated/detected_lang); Triage/Detail/Reports use dual when present (data from macsync inbound + future classifier trans).
- MCP (@packages/mcp-prospector) now exposes full tools (prospector_* + legacy mappings for cockpit parity): list, thread, draft, send, mr, pastebin, reports, markets, classify, submit, held, activity, etc. Use with PROSPECTOR_BASE_URL + TOKEN. Replaces LP mcp-prospector.
- UI fused: Triage = designs/main-view + inbox-ops + LP Stream; Reports = 4 reports + engine subs (Experiments/Patterns/Actions); Queue = queued-tasks + owed/backfill; etc. PWA install in Control.
- LP can now drop prospector (see MIGRATION-PLAN in session plan file for removal list + proxies during cutover).
- Rebuild/redeploy mcp + app after changes.