- AGENTS.md: build spec for Paperclip (constraints, conventions, 7 phases) - docs/phase-1..7: per-phase work, DoD, out-of-scope, risks - reference/: frozen decisions, stack+ports+budgets, content map, acceptance tests - agents/: persona and security policy drafts - Retires the React scaffold; history retained as record of the false start
3.7 KiB
3.7 KiB
Phase 3 — Public deployment
Goal: a live URL Sam can send someone. Early, deliberately — not at the end.
Depends on: Phase 2.
Read
AGENTS.md§4.2 first. This phase touches a shared host. The Caddy change is Sam's to apply, not the agent's.
Facts
| Item | Value |
|---|---|
| Public hostname | resume.lab.audasmedia.com.au |
| DNS | Wildcard *.lab.audasmedia.com.au already resolves publicly — no DNS work needed |
| TLS | Terminated by Caddy on .35 |
| App port | 8096 on .13 (verify free; 8095 belongs to offsite.lab) |
| Release path | /home/sam/deployment/resume/ |
Work
3.1 The release mechanism
deploy.sh
├─ build + test (fail fast)
├─ copy to releases/<timestamp>/
├─ flip `current` symlink (atomic)
├─ docker compose up -d
├─ curl http://127.0.0.1:8096/healthz → must be ready
└─ on failure: flip back to `previous`, exit non-zero
deploy.sh --rollbackrestores the previous release in one command.- No agent hand-edits files on a production host. Agents invoke the script or fix the script in git.
- Deploy is a script, not a model decision.
AGENTS.md§4.6.
3.2 Container
mem_limit=memswap_limit=120m.ports: ["127.0.0.1:8096:8096"]— never0.0.0.0.loggingcapped (max-size: 10m,max-file: 3).restart: unless-stopped.
3.3 Caddy — produce the diff, Sam applies it
Agent output is the exact block to add to .35's Caddyfile, plus the reload command. Do not apply it.
resume.lab.audasmedia.com.au {
encode zstd gzip
reverse_proxy 192.168.20.13:8096
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
X-Frame-Options "SAMEORIGIN"
Content-Security-Policy "default-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'"
}
@static path *.css *.js *.svg *.png *.jpg *.webp *.woff2
header @static Cache-Control "public, max-age=31536000, immutable"
}
Apply with caddy reload (not restart). Verify the other hostnames still resolve afterwards.
3.4 Repo visibility
Repo stays private through this phase. Sam decides on public visibility after the URL is verified.
Definition of Done
deploy.shanddeploy.sh --rollbackboth work; a deliberate bad build proves rollback.https://resume.lab.audasmedia.com.auserves the site.- Container bound to
127.0.0.1only; confirmed withss -ltn. memswap_limit == mem_limit; confirmed withdocker inspect.- Security headers present; CSP does not break the page.
- A1 still passes over the public URL with the AI layer stopped.
- Sam has applied the Caddy block and confirmed the other lab hostnames still work.
- Rollback rehearsed once and documented.
Out of scope
- Rate limiting for the AI endpoint (Phase 6).
- Monitoring, alerting, restore drills — add a minimal uptime check only.
- Any change to other sites, containers or the NixOS config.
Risks
| Risk | Mitigation |
|---|---|
| Caddy change breaks another site | Additive block only; caddy reload not restart; Sam verifies the other hostnames immediately after |
| Deploying a half-finished build | deploy.sh health gate + rollback; the site is designed to be useful at every stage |
| Port 8096 occupied | Verify before compose; pick the next free port and record it in reference/stack.md |
| Host memory pressure | 120 m cap; the app is static-serving and should idle well under it. Measure and record actual RSS |