spb/note-spboucher-ai
Public
1# Deployment — Note SPBoucher AI on MacLustr23Target: **M4M64c** (`~/apps/note-spboucher-ai`), public URL **https://note.spboucher.ai** through the MacLustr Tunnel (BHS64 Caddy → WireGuard → M4M64c:8480). Orchestrated by `mld` from the gateway M1M32; manifest `M1M32:~/dispatch/apps/note-spboucher-ai.json`.45## One-time preparation (done 2026-11-01)61. `brew install postgresql@17 pgvector` on M4M64c, `brew services start postgresql@17`, `listen_addresses = 'localhost'`.72. Role `note` + database `note_spboucher` with extensions `vector`, `pg_trgm`, `unaccent` (created as the Homebrew superuser; the app role cannot create extensions).83. Secrets file `M4M64c:~/apps/note-spboucher-ai/.env` (mode 600; template in `.env.example`; copy of record in `~/Desktop/Cluster/secrets/note-spboucher-ai/`). `mld` never overwrites it (`sync_excludes`).94. Restricted backup key `~/.ssh/note-backup_ed25519` on M4M64c authorised on M3U96b with `restrict,from="192.168.2.103,10.67.0.33"` (exception to the "no private keys on nodes" rule, limited to the offsite rsync; alias `m3u96b-backup`).105. DNS: `note.spboucher.ai A 51.161.112.61` (GoDaddy) — already present.1112## Deploy / update13```bash14# first deployment (laptop)15~/Desktop/cluster-skill/mld stage ~/Desktop/Cluster/projets/note-spboucher-ai note-spboucher-ai16~/Desktop/cluster-skill/mld deploy note-spboucher-ai # pinned to M4M64c; runs post_sync hooks: pnpm install, build, db:migrate, seed:ai17~/Desktop/cluster-skill/mld adopt note-spboucher-ai # makes the node folder a git clone (origin spbgit note-spboucher-ai.git)18# then REMOTE-FIRST: edit on the node, pnpm build, pm2 restart note-web note-worker, git commit/push on the node19ssh M4M64c 'cd ~/apps/note-spboucher-ai && pnpm build && pm2 restart note-web note-worker'20```21Owner password: `ssh -t M4M64c 'cd ~/apps/note-spboucher-ai && set -a && . ./.env && set +a && pnpm bootstrap'` (interactive, hidden input; stores only the Argon2id hash).2223## Health and rollback24- Local: `curl http://127.0.0.1:8480/api/health` on the node → `{ ok, db, pgvector }`; public: `curl https://note.spboucher.ai/api/health`.25- `mld status --live`, `mld heal --dry-run`, `mld logs note-spboucher-ai`, `pm2 logs note-web|note-worker`.26- Rollback code: on the node `git checkout <previous-commit> && pnpm build && pm2 restart note-web note-worker` (or `mld move`/`deploy --force` from a previous staging). Rollback schema: `psql $DATABASE_URL -f drizzle/<n>.down.sql` then delete the row from `drizzle.__drizzle_migrations` (see `docs/operations.md`). Data: restore a backup (`scripts/restore.sh`).27- Smoke test after deploy: `NOTE_URL=https://note.spboucher.ai NOTE_PASSWORD=… bash scripts/smoke.sh`.2829## Processes (PM2, via mld)30| Name | Command | Notes |31|---|---|---|32| `note-web` | `node node_modules/next/dist/bin/next start -p 8480 -H 0.0.0.0` | reads `.env`; max 3 GB |33| `note-worker` | `node node_modules/tsx/dist/cli.mjs src/worker/index.ts` | pg-boss queues; nightly backup 03:30 America/Toronto |3435`mld heal` (every 5 min on M1M32) restarts missing PM2 processes and repairs the Caddy route/wg1 peer.36