spb/tools-spboucher-ai
Public
1# Phase 0 — Discovery: environment and existing infrastructure (2026-11-02)23## Summary4tools.spboucher.ai is deployed exactly like the other spboucher.ai applications: a Next.js + PostgreSQL app placed on a MacLustr node by the `mld` orchestrator, exposed through the MacLustr Tunnel (WireGuard → Caddy on the OVH gateway BHS64) with DNS at GoDaddy. No new tunnel, proxy or DNS mechanism was introduced.56## What was inspected (commands run from the operator laptop)7| Area | Command / source | Finding |8|---|---|---|9| Orchestrator | `mld nodes`, `mld status`, `M1M32:~/dispatch/apps/*.json` | 38 apps registered; manifests describe dir, PM2 processes, hooks, placement, `tunnel.domain`. Convention: `~/apps/<app>`, Homebrew Node + pnpm + PM2, PostgreSQL 17 local to the node, `.env` (mode 600) excluded from rsync. |10| Tunnel | `mld tunnel status` | Single gateway BHS64 (51.161.112.61, OVH Beauharnois); 17 WireGuard peers; Caddy with 38 routes `host → 10.67.0.x:port`, automatic Let's Encrypt. Routes are created by `mld deploy` from the manifest. |11| DNS | `dig tools.spboucher.ai`, `dig <random>.spboucher.ai` | `tools.spboucher.ai` already has an explicit A record → 51.161.112.61 (not a wildcard). No Caddy route existed; HTTPS failed at the TLS handshake (no content to preserve). Nameservers: GoDaddy (domaincontrol.com). |12| Registrar access | memory notes, `cluster-skill` docs | DNS changes are made in the GoDaddy web console; there is no API-token integration in the cluster tooling. Not needed here because the record exists. |13| Secrets | existing manifests, `~/Desktop/Cluster/secrets/` | Pattern: secrets live in `<app>/.env` on the node (600) plus an offline copy under `~/Desktop/Cluster/secrets/<app>/`; never in git. Some older manifests inline secrets in PM2 env — this project does **not** (hook requires `.env`). |14| Identity | note/chat/akos apps | Owner-only auth with argon2id passwords, TOTP and WebAuthn passkeys (simplewebauthn) — reused here. There is no shared SSO/IdP in the cluster. |15| Node candidates | `mld nodes` + SSH probes | **M2M32c** (Mac Studio, 12 cores, 32 GB, 417 GB free SSD): PostgreSQL 17.11 + pgvector 0.8.6 (Homebrew, localhost), Redis, Node 26, pnpm 12, PM2 7, wg1 peer 10.67.0.20, **zero PM2 processes** → chosen to avoid interfering with production nodes. Alternatives considered: M4M64c (hosts akos/chat/note), M3U96a (10 processes), M5M36 (no Postgres). |16| Remote-first rule | `MACLUSTR-PROCEDURE.md` | After first deploy, the node directory is a git clone (`mld adopt`), `origin` = spbgit (`gitsrv`). Edits happen on the node; laptop copy is a mirror. |17| Laptop toolchain | `node -v`, `pnpm -v`, `psql --version` | Node 25.9, pnpm 11.1, PostgreSQL 17.9 + pgvector 0.8.2 (dev DB), Playwright Chromium, pandoc/weasyprint. |1819## Conventions adopted20- Port **8540** (unused across the Caddy route table and on M2M32c).21- Dir `~/apps/tools-spboucher-ai`, PM2 processes `tools-web` (Next) and `tools-worker` (tsx).22- Database `tools_spboucher` on the node's local PostgreSQL; roles `tools_app` (read/write) and `tools_reader` (SELECT on public views only, `default_transaction_read_only`).23- Backups: encrypted `pg_dump` nightly to `~/backups/tools-spboucher-ai` with rsync to a second node (M3U96b), retention 14.24- Health endpoint `/api/health` used by `mld`.2526## Risk register27| Risk | Likelihood | Impact | Mitigation |28|---|---|---|---|29| Link checker flagged as a bot (403/429) on vendor sites | High | Low | 401/403/405/429 counted as *unverifiable*, not broken; weekly sweep; admin dashboard surfaces them. |30| Vendor renames/acquisitions make records stale | Medium | Medium | Source rescan job hashes official pages and flags changes; freshness policy per field. |31| Single residential uplink / node outage | Medium | Medium | `mld heal` restarts PM2 + routes; `mld move` relocates the app; backups off-node. |32| Secrets leakage via manifests | Low | High | Secrets only in node `.env`; manifest hook refuses to deploy without it; public role cannot read private tables. |33| Prompt injection through fetched pages (AI suggestions) | Medium | Medium | Fetched text wrapped as untrusted data; model output must be JSON and evidence quotes are verified against the page; nothing is written automatically. |34| Search quality regressions | Medium | Low | `pnpm eval:ranking` fixture (MRR threshold) in CI. |35| macOS TCC blocking PM2 file access | Low | Medium | App data stays inside the home directory (no external volumes). |36