docs/deployment: record security hardening pass + webapp + APNs
Backend CI / Test (push) Has been cancelled
Backend CI / Contract Tests (push) Has been cancelled
Backend CI / Build (push) Has been cancelled
Backend CI / Lint (push) Has been cancelled
Backend CI / Secret Scanning (push) Has been cancelled

Mark roadmap items done (network policies, Traefik middleware, CF Full
strict, CF IP UFW restriction, webapp deploy, APNs wired up, admin
URL-baking fix, admin probe bug). Update Chapter 4 (firewall rule
inventory now shows CF-only :443, no :80), Chapter 6 (request flow
walks through TLS on :443 and middleware hops), Chapter 13 (CF SSL
mode is Full strict, not Flexible; documents the origin cert
install), Chapter 7 (adds the web service section — proxy pattern,
3 replicas, PostHog build-args), and Appendix C (web manifests, CF
origin cert paths on disk, APNs .p8 path, updated network-policies
applied status).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-04-24 15:50:59 -05:00
parent ace03d2340
commit 7e77e3bbab
6 changed files with 198 additions and 124 deletions
+66 -4
View File
@@ -2,9 +2,10 @@
## Summary
Four workloads run in the `honeydue` namespace: **api** (Go REST API, 3
replicas), **admin** (Next.js panel, 1 replica), **worker** (Go background
jobs, 1 replica), and **redis** (cache + job queue, 1 replica, PVC-backed).
Five workloads run in the `honeydue` namespace: **api** (Go REST API, 3
replicas), **admin** (Next.js admin panel, 1 replica), **web** (Next.js
customer-facing app, 3 replicas), **worker** (Go background jobs, 1
replica), and **redis** (cache + job queue, 1 replica, PVC-backed).
This chapter deep-dives each: container image, resource limits, probes,
volumes, and why each knob is set the way it is.
@@ -14,10 +15,11 @@ volumes, and why each knob is set the way it is.
|---|---|---|---|---|
| `api` | `gitea.treytartt.com/admin/honeydue-api:<sha>` | 3 | 8000 | HTTP REST API |
| `admin` | `gitea.treytartt.com/admin/honeydue-admin:<sha>` | 1 | 3000 | Next.js admin panel |
| `web` | `gitea.treytartt.com/admin/honeydue-web:<sha>` | 3 | 3000 | Next.js customer-facing web client at `app.myhoneydue.com` |
| `worker` | `gitea.treytartt.com/admin/honeydue-worker:<sha>` | 1 | — | Background job processor |
| `redis` | `redis:7-alpine` | 1 | 6379 | Cache + Asynq queue |
All four are Kubernetes `Deployment` workloads (not StatefulSets, not
All five are Kubernetes `Deployment` workloads (not StatefulSets, not
DaemonSets). They share:
- ServiceAccount with `automountServiceAccountToken: false` (Chapter 5)
- `imagePullSecrets: [gitea-credentials]` (Chapter 11)
@@ -25,6 +27,66 @@ DaemonSets). They share:
- Individual env vars wired to `honeydue-secrets` keys
- Read-only root filesystem with `tmp` emptyDir mounted at `/tmp`
## Service — web (Next.js customer app)
### What it does
Lives at `https://app.myhoneydue.com`. Next.js 16 standalone build,
served by `node server.js` inside the container. Sibling repo:
`/Users/treyt/Desktop/code/honeyDue/honeyDueAPI-Web/`.
### Architecture: server-side proxy pattern
Unlike the admin panel (which makes CORS requests directly to
`api.myhoneydue.com`), the web app uses a proxy pattern:
```
Browser → https://app.myhoneydue.com/api/proxy/tasks/123/
→ Next.js route handler (src/app/api/proxy/[...path]/route.ts)
→ reads honeydue-token httpOnly cookie
→ attaches Authorization: Token <value>
→ https://api.myhoneydue.com/api/tasks/123/ (server-side fetch)
→ response flows back
```
**Consequences:**
- Browser never makes cross-origin requests. No CORS entry needed on
the Go API for `app.myhoneydue.com`.
- Auth tokens live in httpOnly cookies, not localStorage. XSS can't
exfiltrate them.
- The web pod needs outbound HTTPS to `api.myhoneydue.com` — covered
in the `allow-egress-from-web` NetworkPolicy (Chapter 5).
### Env vars
Build-time (baked into the client bundle by the Dockerfile `ARG`):
- `NEXT_PUBLIC_API_URL` — only used as a fallback; baked for safety
- `NEXT_PUBLIC_POSTHOG_KEY` — PostHog project API key
- `NEXT_PUBLIC_POSTHOG_HOST``https://analytics.88oakapps.com`
Runtime (ConfigMap):
- `API_URL=https://api.myhoneydue.com/api` — consumed by the
server-side proxy handlers
- `PORT=3000`, `HOSTNAME=0.0.0.0`
### Deployment spec highlights
- **3 replicas**, same as api — this is a production customer surface
- `topologySpreadConstraints` across `kubernetes.io/hostname`
evicting one node at most kills one pod
- `readOnlyRootFilesystem: true`; `emptyDir`s at `/app/.next/cache`
(Next.js build cache) and `/tmp`
- PDB `web-pdb` with `minAvailable: 2`
- runAsUser/runAsGroup `1001` (matches the `nextjs` user created in
the Dockerfile)
### Why same availability as api
The web client is now the primary user-facing surface. Users hitting
`app.myhoneydue.com/login` should never see a 502 because a single
node went down. 3 replicas × `minAvailable: 2` guarantees at least
two pods stay up through any voluntary disruption.
## Service 1 — api (Go REST API)
### What it does