|
6 | 6 | - Do not discard the terminal experience; prefer dual entry (standard landing + gated terminal) over a full UI rewrite. |
7 | 7 | - Create git commits and pull requests only when explicitly asked. |
8 | 8 | - Gate stays enabled in development; bypass only via `GATE_BYPASS_SECRET`, not an automatic dev-off flag. |
9 | | -- User fills API keys and other random secrets in `.env.development` locally (copy from `.env.example`); production via `.env` on GCP or Vercel platform env. |
| 9 | +- User fills API keys and other random secrets in `.env.development` locally (copy from `.env.example`); production via `.env` on GCP or Vercel platform env. Local `.env.development` API URLs should target `localhost:8080`, not production Cloud Run. |
10 | 10 | - Gate puzzles should be NATAS-style web challenges (login, hidden paths, Referer), not SSH/Bandit/Behemoth simulations. |
11 | 11 | - First time deploying to GCP; prefers clear, beginner-friendly infrastructure documentation and bootstrap guides. |
12 | 12 | - Portfolio-frontend chats must proactively use Cursor SDK + Team Kit skills/MCP (`.cursor/rules/cursor-sdk-team-kit.mdc`, alwaysApply). |
|
18 | 18 | - Multi-repo portfolio: `portfolio-backend` (Rust/Axum, SQLx/PostgreSQL) and `portfolio-frontend` (Next.js 16, React 19, terminal-interactive portfolio). |
19 | 19 | - Planned public routing: standard landing at `/`, terminal at `/terminal`, three-level gate at `/gate` (and `/gate/[level]`). |
20 | 20 | - Blog, social share, RSS, and other content routes stay shared/public; do not duplicate them across standard and terminal UIs. |
21 | | -- Gate puzzles (backend-validated, no answers in frontend bundle): L1 static login `yourblooo0`/`yourblooo0`; L2 requires L1 completion before `/s3cr3t/users.txt` reveals login `yourblooo1` + env password; L3 backend validates `Referer: {SITE_URL}/terminal` in `complete_level_3`; gate JWT pinned to HS256 with `iss`/`aud`. Terminal route `noindex`; terminal SSR verifies JWT via `/api/gate/status`. Gate/terminal is UX puzzle layer, not API auth perimeter (admin JWT separate). `GATE_BYPASS_SECRET` via Next.js `proxy.ts` (`X-Gate-Bypass`), not Rust handlers. Legacy env `GATE_L2_STUB_MD5`, `GATE_L3_SHELLCODE_HASH`, `GATE_L3_OFFSET` unused (archived shellcode design); only `GATE_L1_ANSWER`, `GATE_L2_ANSWER`, `GATE_TOKEN_SECRET` required. |
22 | | -- Backend API default port 8080; frontend align `NEXT_PUBLIC_API_URL` / `BACKEND_URL` with 8080 locally. Production (Vercel): both must be the HTTPS Cloud Run service base URL (no trailing slash); `NEXT_PUBLIC_*` changes require a Vercel redeploy; a wrong `*.run.app` URL yields a Google-edge 404 with zero Cloud Run requests. Most prod API traffic is Vercel SSR server-side, with silent empty fallbacks if the backend is unreachable. Local dev: backend `load_env_file()` reads `.env.development` when `ENVIRONMENT != production`; frontend `bun run dev` uses `--env-file=.env.development`; Docker Compose via `scripts/compose-dev.sh` or `--env-file .env.development`. CORS auto-merges localhost origins (ports 3000–3002) when `ENVIRONMENT != production`; gate vars documented in `.env.example`. |
| 21 | +- Gate puzzles (backend-validated, no answers in frontend bundle): L1 static login `yourblooo0`/`yourblooo0`; L2 requires L1 completion before `/s3cr3t/users.txt` reveals login `yourblooo1` + env password; L3 backend validates `Referer: {SITE_URL}/terminal` in `complete_level_3`; gate JWT pinned to HS256 with `iss`/`aud`. Terminal route `noindex`; terminal SSR verifies JWT via `/api/gate/status`. Gate/terminal is UX puzzle layer, not API auth perimeter (admin JWT separate). `GATE_BYPASS_SECRET` via Next.js `proxy.ts` (`X-Gate-Bypass`), not Rust handlers. Legacy env `GATE_L2_STUB_MD5`, `GATE_L3_SHELLCODE_HASH`, `GATE_L3_OFFSET` unused (archived shellcode design); only `GATE_L1_ANSWER`, `GATE_L2_ANSWER`, `GATE_TOKEN_SECRET` required. Rebuild/restart local Docker backend after gate answer or gate code changes — stale image keeps old L1 credentials. |
| 22 | +- Backend API default port 8080; frontend align `NEXT_PUBLIC_API_URL` / `BACKEND_URL` with 8080 locally. Production (Vercel): both must be the HTTPS Cloud Run service base URL (no trailing slash); `NEXT_PUBLIC_*` changes require a Vercel redeploy; a wrong `*.run.app` URL yields a Google-edge 404 with zero Cloud Run requests. Most prod API traffic is Vercel SSR server-side, with silent empty fallbacks if the backend is unreachable. `ROADMAP_EMAIL`/`ROADMAP_PASSWORD` are Cloud Run/GCP secrets only — not Vercel env. Local dev: backend `load_env_file()` reads `.env.development` when `ENVIRONMENT != production`; frontend `bun run dev` uses `--env-file=.env.development`; Docker Compose via `scripts/compose-dev.sh` or `--env-file .env.development`. CORS auto-merges localhost origins (ports 3000–3002) when `ENVIRONMENT != production`; gate vars documented in `.env.example`. |
23 | 23 | - Feature status SSOT: `portfolio-frontend/FEATURE_PLANNING.md` (+ `docs/dual-ui-gate.md` for gate ops); `ROADMAP.md` removed May 2026 after performance backlog moved to Feature #33. |
24 | 24 | - PWA is site-wide (`public/manifest.json`, `public/sw.js`, scope `/`, offline page); install prompt only after terminal onboarding tour completes. |
25 | | -- Observability stack (Loki, Grafana, Prometheus) runs on a GCE ops VM in production via `docker-compose.gcp-ops.yml`; same stack in local `docker-compose.yml` for dev. Redis health uses real PING via `REDIS_URL`; not yet used for app cache or gate sessions. Optional `METRICS_BEARER_TOKEN` protects `/metrics`; Prometheus prod scrape uses bearer auth. |
26 | | -- GCP production: backend on Cloud Run (asia-southeast2/Jakarta) with `max_instances=1` for in-memory gate sessions (Redis not used for gate yet), public `allUsers` `run.invoker` (Vercel frontend + public API; admin auth app-level), frontend on Vercel; `DATABASE_URL` must be the ops VM internal IP over Serverless VPC Access (not a subnet-CIDR host, not `localhost`); ops VM may use default VPC addressing or Terraform ops ranges—firewall must allow TCP 5432 from the VPC connector CIDR to the VM; unreachable Postgres at startup panics before the revision listens on 8080. Serverless VPC Access connector (`10.10.1.0/28`, not Direct VPC egress) with private-ranges-only egress to ops subnet `10.10.0.0/24` unless Cloud NAT for all-traffic; container command/args empty (image CMD `/app/portfolio-backend`, port 8080); Terraform (network, iam, artifact_registry, secrets, compute_ops, cloud_run); ops VM (e2-medium, no public IP, IAP SSH) hosts Postgres 16 + observability at `/mnt/data`; `REDIS_URL` optional on Cloud Run (health probe only, not in prod compose stack). CI via `deploy-gcp.yml` Workload Identity Federation. |
| 25 | +- Observability stack (Loki, Grafana, Prometheus) runs on a GCE ops VM in production via `docker-compose.gcp-ops.yml`; same stack in local `docker-compose.yml` for dev. Redis on ops VM (`6379`) backs distributed rate limiting and WebSocket presence when `REDIS_URL` is set; falls back to in-memory `tower_governor` + local presence counts when unset or unreachable. Optional `METRICS_BEARER_TOKEN` protects `/metrics`; Prometheus prod scrape uses bearer auth. |
| 26 | +- GCP production: backend on Cloud Run (asia-southeast2/Jakarta) with `max_instances=1` for in-memory gate sessions (Redis not used for gate yet), public `allUsers` `run.invoker` (Vercel frontend + public API; admin auth app-level), frontend on Vercel; `DATABASE_URL` must be the ops VM internal IP over Serverless VPC Access (not a subnet-CIDR host, not `localhost`); `REDIS_URL=redis://<ops_vm_internal_ip>:6379` from Terraform; ops VM may use default VPC addressing or Terraform ops ranges—firewall must allow TCP 5432 and 6379 from the VPC connector CIDR to the VM; unreachable Postgres at startup panics before the revision listens on 8080. Serverless VPC Access connector (`10.10.1.0/28`, not Direct VPC egress); Terraform egress `PRIVATE_RANGES_ONLY` — `ALL_TRAFFIC` through VPC without Cloud NAT breaks outbound APIs (roadmap.sh, GitHub, Resend, Gemini); container command/args empty (image CMD `/app/portfolio-backend`, port 8080); Terraform (network, iam, artifact_registry, secrets, compute_ops, cloud_run); ops VM (e2-medium, no public IP, IAP SSH) hosts Postgres 16 + observability at `/mnt/data`; `REDIS_URL` optional on Cloud Run (health probe only, not in prod compose stack). CI via `deploy-gcp.yml` Workload Identity Federation. |
27 | 27 | - Next.js 16 edge handler is `src/proxy.ts` (`export function proxy()`), not legacy `middleware.ts`. |
28 | | -- Frontend performance: `cacheComponents: true` (PPR) with Feature #33 docs at `docs/features/FEATURE_33_PERFORMANCE.md`; dual RUM (pino/Loki + Vercel Speed Insights). Pre-completion verification: backend `cargo fmt/check/clippy/test`; frontend `bun run lint` + `type-check`. Known Vitest hang: `background-manager.test.tsx` (~16 min). |
| 28 | +- Frontend performance: `cacheComponents: true` (PPR) with Feature #33 docs at `docs/features/FEATURE_33_PERFORMANCE.md`; `/roadmap` uses request-time fetch via `await headers()` under PPR; dual RUM (pino/Loki + Vercel Speed Insights). Production CSP must not use `strict-dynamic`/nonce without wiring nonce into Next.js scripts (`'unsafe-inline'` used); Vercel builds disable file logging (`LOG_TO_FILE`, `NEXT_PHASE=phase-production-build` — no `logs/server` mkdir); client logs POST plain JSON to `/api/logs` (in-memory crypto session fails across Vercel serverless instances). Pre-completion verification: backend `cargo fmt/check/clippy/test`; frontend `bun run lint` + `type-check`. Known Vitest hang: `background-manager.test.tsx` (~16 min). |
29 | 29 | - Backend performance & integrations: all API routes P95 <50ms SLO (`docs/performance/API_SLA.md`, `config/slo-rules.yml`, Grafana 50ms alerts, `scripts/latency-smoke.sh` in CI); roadmap/github in-memory cache with stale-while-revalidate; roadmap auth via `ROADMAP_EMAIL`/`ROADMAP_PASSWORD` → `POST …/v1-login` (Terraform secrets `roadmap-email`/`roadmap-password`; `ROADMAP_AUTH_TOKEN` removed); Swagger UI disabled in production. |
0 commit comments