Files
ai-resume/reference/stack.md
Sam Rolfe df864cc29e deploy: health gate and shape assertion follow the LAN publish address
Once the port is published on 192.168.20.13 the host no longer listens on
127.0.0.1:8096, so wait_healthy(), do_status() and the rollback path would all
get connection-refused and every deploy would roll back. They now probe
$PUBLISH_IP (RESUME_PUBLISH_IP, default 192.168.20.13). assert_shape() asserts
the published host IP is that address instead of loopback; the mem_limit ==
memswap_limit checks are unchanged. check_port stays address-agnostic on
`sport = :8096`.

Records the amendment in reference/stack.md (ports table + the bind rule) and
docs/phase-3-deploy.md (§3.2 bullet, flow diagram, DoD line), citing Sam's
decision of 2026-10-26, interaction 54aeb1a2. AGENTS.md is Sam's to change and
is untouched.
2026-10-26 22:40:22 +11:00

83 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Stack, hosts, ports, budgets
## Hosts (measured 2026-10-17)
| Host | Name | Role | RAM total / free | Swap used | CPUs |
|---|---|---|---|---|---|
| `.13` | `nixos-desktop` | **Runtime** — app, Qdrant, Prefect worker | 15 GiB / **2.3 GiB** | **8.1 GiB** | 12 |
| `.35` | `sam-Ubuntu1` | **Public edge** — Caddy terminates 443; also Gitea, Immich, Nextcloud, Penpot, Wiki.js | 14 GiB / 4.1 GiB | 4.0 GiB | 2 |
| `.27` | `sam-4screen-desktop` | **Build / dev** — sources, Obsidian vault, agent work | — | — | — |
`.13` is memory-bound, not CPU-bound. The budget is real: exceeding it destabilises services Sam relies on.
## Ports
| Service | Host | Port | State |
|---|---|---|---|
| Caddy | `.35` | 443 (+ LAN 8000) | existing — **Sam applies any change** |
| **Go app** | `.13` | **8096** | new — verify free (`8095` = offsite.lab); published on `192.168.20.13:8096`, **never** `0.0.0.0` |
| Qdrant REST | `.13` | 6333 | new, `127.0.0.1` only |
| Qdrant gRPC | `.13` | 6334 | new, `127.0.0.1` only |
| Prefect server | `.13` | existing | reuse — do not deploy a second |
| OmniRoute | `.13` | existing | reuse — the only model gateway |
| Postgres | `.13` | 5432 | reuse if needed |
| Whisper / Piper | `.13` | existing | reuse (Phase 7 voice only) |
| LangGraph service | `.13` | 8096+1 or existing | Phase 5 — see note below |
**Known occupied:** 3000, 3020, 5000, 5002, 5432, 8000, 8001, 8080, 8082, 8090, 8091, 8092, 8095, 8230, 8231, 8500, 8501, 8788–8792, 3081–3084, 1883.
## Memory budget
| Component | Cap |
|---|---|
| Go app | 120 m |
| Qdrant | 400 m |
| Prefect worker | 150 m |
| Guardrail (if local) | 300 m |
| **Total ceiling** | **1.0 GiB / 4 containers** |
### Qdrant memory, measured (2026-10-26, v1.20.0, 400 m cap, 1024 dims)
| Chunks | Settled RSS | Peak during ingest |
|---|---|---|
| 500 | 128 MiB | 149 MiB |
| 5,000 | 138 MiB | 305 MiB |
| 20,000 | 151 MiB | 292 MiB |
Within the cap at all three sizes, but only with the memory-frugal collection
settings in `corpus/qdrant.py`: HNSW graph and payloads on disk
(`hnsw_config.on_disk`, `on_disk_payload`), `m=8`, `ef_construct=64`,
`memmap_threshold=5000`, `indexing_threshold=20000`. With Qdrant's defaults the
container is OOM-killed between 5,000 and 20,000 chunks. The pipeline is the only
code that creates the collection, so those settings cannot drift.
Ceiling: 20,000 chunks at 1024 dimensions. Beyond that, set
`RESUME_QDRANT_VECTORS_ON_DISK=1`, reduce the dimension, or raise the cap — in
that order. Detail: `docs/phase-4-notes.md` §7.
Every service sets `mem_limit` **and** `memswap_limit` to the same value so it cannot consume host swap. Bind services to `127.0.0.1`; Caddy on `.35` is the only public listener.
**One documented amendment (Go app, port 8096):** the app publishes on the LAN address `192.168.20.13:8096`, not `127.0.0.1`. Caddy runs on `.35` and proxies to `192.168.20.13:8096`, so a loopback-only publish is unreachable from the edge and the public URL returns 502. This is the same way `8095` (offsite), `8092` (photo-filter), `3909` (garage-ui) and `4200` (prefect) are published on `.13`. The port is LAN-only — never `0.0.0.0` — and Caddy on `.35` is still the only public listener, so `AGENTS.md` §4.2 holds in substance. Decision: Sam, 2026-10-26, interaction `54aeb1a2`. `deploy/deploy.sh` follows the same address (`RESUME_PUBLISH_IP`, default `192.168.20.13`) in its health gate and shape assertion.
## Paths
| Item | Path |
|---|---|
| Repo working copy | `/home/sam/paseo/projects/resume_ai_system_paperclip_build` |
| Release root | `/home/sam/deployment/resume/` |
| Live symlink | `/home/sam/deployment/resume/current` |
| Secrets | `~/.config/environment.d/10-secrets.conf` |
| Planning reference | `/home/sam/paseo/projects/resume_ai_system/plan/` (read-only) |
## Public entry
`resume.lab.audasmedia.com.au` — wildcard DNS already resolves to the public edge on `.35`. **No DNS work is required.** Caddy block is specified in Phase 3 §3.3.
## Open items
| Item | Owner |
|---|---|
| Embedding provider (Gemini free tier vs OpenRouter vs local) | Sam — Phase 4 shipped provider-agnostic (`corpus/embeddings.py`); the `omniroute` adapter is ready, `local-hashing` is a placeholder, so the decision is still Sam's before Phase 5 answers questions |
| Repo public/private after launch | Sam — decide after Phase 3 |
| Phone number on the contact page | Sam |