# CannaManage — now LIVE & public, auth blocker fixed (2026-06-22) ## TL;DR `https://cannamanage.plate-software.de` is **live in production** over HTTPS. The 9-day systemic missing-token auth blocker (flagged 2026-06-13 / 2026-06-18) is **fixed and verified end-to-end through the public chain**. Push→deploy→tunnel now mirrors inspectflow. End of alpha. ## What shipped (2 commits on main) - **a686957** `feat(deploy): public hosting + fix systemic auth-token bug` - **83b46c8** `harden(deploy): db internal-only + robust container-loopback frontend verify` ### 1. The auth fix (the important one) Root cause (unchanged from prior notes): the frontend used a **static `next.config.mjs` rewrites()** to proxy `/api/backend/*` → backend, which cannot inject the user's bearer token, so every authenticated server call hit the API unauthenticated and the UI silently fell back to mock data. Fix — replaced the static rewrite with a **server-side catch-all Route Handler**: - `cannamanage-frontend/src/app/api/backend/[...path]/route.ts` (new) — reads the NextAuth session via `auth()`, injects `Authorization: Bearer `, streams request/response bodies (`duplex: "half"`) for uploads/downloads, strips hop-by-hop headers, GET/POST/PUT/PATCH/DELETE. - `src/lib/auth.ts` — `session()` callback now exposes `session.accessToken`. - `src/types/next-auth.d.ts` — added `accessToken?: string` to `Session`. - `next.config.mjs` — removed the static `rewrites()`. **Why `auth()` not `getToken()`**: `getToken()` autodetects the `__Secure-` cookie prefix, which breaks across the public-HTTPS → internal-HTTP proxy boundary. `auth()` resolves the session reliably behind the frp/Apache chain. **Accepted tradeoff**: `accessToken` is now readable at `/api/auth/session` (client-visible). Fine for alpha; revisit if we want it httpOnly-only. ### 2. Public hosting wiring (mirrors inspectflow) Chain: browser →HTTPS→ IONOS Apache (82.165.206.45, LE TLS) →ProxyPass→ VPS frps (85.214.154.199:**30010**) →frp tunnel→ TrueNAS frpc → frontend:3000. The frontend proxies `/api/backend/*` to `backend:8080` server-side, so **only the frontend port is tunnelled** (no Caddy needed, unlike inspectflow). - `docker-compose.truenas.yml` — public `NEXTAUTH_URL`/`AUTH_URL`, `AUTH_TRUST_HOST`, rotated prod secrets, `BACKEND_URL=http://backend:8080`, **db internal-only** (`ports: !override []`), backend remapped 8081→8080 (host 8080 taken by searxng). - `.gitea/workflows/deploy.yml` — injects `AUTH_SECRET`/`JWT_SECRET`/`DB_PASSWORD` from Gitea repo secrets; new **"reconcile DB role password"** step (ALTER USER — POSTGRES_PASSWORD only applies on first volume init, the persistent `cannamanage_pgdata` volume kept the old password); **hardened frontend verify** (probes container loopback via bundled `node`, not host wget — fixes a transient false-failure when polling mid-recreate). - frpc.toml on TrueNAS: added `cannamanage` proxy localPort 3000 → remotePort 30010. - IONOS: Let's Encrypt cert via acme.sh + `:443` vhost ProxyPass → VPS:30010. ## Port map (NO collision with inspectflow — explicitly verified) | layer | inspectflow | cannamanage | |---|---|---| | VPS frps remotePort | 30009 | **30010** | | TrueNAS host publish | 8090→80 (caddy) | 3000 (frontend), 8081→8080 (backend) | | db / internal | internal-only | **internal-only** (5432/tcp, no host bind) | ## Verified live (end-to-end, public HTTPS) - `GET /` → 307 → `/login` (correct NextAuth redirect), valid LE TLS. - Login `admin@test.de` → 302; session = role ADMIN, accessToken present. - **`GET /api/backend/members` → HTTP 200** through the full public chain. ✅ - db: no host listener on :5432 after hardened deploy. ✅ - Deploy run #59 (deploy.yml) = success with hardened verify. ✅ ## Flags / follow-ups for Work Lumen 1. **PII / GDPR**: this is a German cannabis-club app (KCanG) now public. Members table is empty today, but before real data lands we need: backups (deploy/backup.sh exists — wire a cron), retention enforcement, and a documented restore drill. 2. **Secret rotation**: prod secrets live only in Gitea repo secrets + the running containers. They are NOT in git (verified). Document them in a vault; rotating `JWT_SECRET` invalidates all tokens, `AUTH_SECRET` all sessions, `DB_PASSWORD` needs the ALTER USER reconcile (already automated in deploy.yml). 3. **accessToken client-exposure** tradeoff (see above) — revisit post-alpha. 4. **ci.yml is red** (separate workflow): backend `mvn test` + frontend `pnpm lint` fail in CI but pass locally; local type-check shows only TEST-file errors (missing vitest globals / stale assertions). deploy.yml does NOT depend on ci.yml (tests are a local-only gate per its header — nested-DinD can't volume-mount). Worth fixing ci.yml separately so the badge is honest. 5. **HTTP→apex redirect cosmetic quirk**: `http://cannamanage…` 301s to apex `plate-software.de` (Apache `*:80` vs `plate-software.de:80` vhost ordering) — same as inspectflow, harmless since the site is HTTPS, but tidy it if it bugs you. 6. acme.sh default CA was ZeroSSL and stalled (retryafter=86400). Switched the box default to **Let's Encrypt** (`--set-default-ca --server letsencrypt`) — matches inspectflow. Auto-renew cron inherits this now. ## Test creds (alpha) `admin@test.de` / `Test1234!` (hash was reset during smoke testing).