docs(AGENTS): Update frontend agent constraints and conventions

- Clarify SSR data shape contract and API path maintenance requirements. - Ensure API envelope shape consistency between Go and TypeScript. - Specify lease ID encoding and build output renaming for compatibility. - Emphasize OG metadata placeholder matching and discourage `useCallback` usage in new code.

cognitive committed Mar 4, 2026 at 04:29 UTC d1c751808e871bd48f125a15f221fdaec7e9182a
3 files changed +40 -8
AGENTS.md
+4 -8
@@ -31,11 +31,9 @@ Source of truth for architecture decisions: `docs/adr/README.md` and linked ADRs
31
32 3. **Reverse connection authorization must remain lease-token validated before bridge/forwarding.**
33 - Why: prevents unauthorized tunnel attachment (ADR-0003).
34 -4. **`/sdk/connect` must share the same policy source as `/sdk/register`.**
35 - - Why: ensures registration and reverse admission apply identical IP-ban + token checks in one enforcement pipeline.
34
37 -5. **Operator setup is not changed by this hardening.**
38 - - Why: anti-abuse changes are behavior-only and reuse existing flags/env/settings for policy management.
35 +4. **`/sdk/connect` must share the same policy source as `/sdk/register`.**
36 + - Why: registration and reverse admission must apply identical IP-ban + token checks in one enforcement pipeline.
37
38 ## Operational Truths (CI-Aligned, Minimal)
39
@@ -50,7 +48,7 @@ Source of truth for architecture decisions: `docs/adr/README.md` and linked ADRs
48 - Why: these are the enforced checks in `.github/workflows/ci.yml`.
49 - Note: `make tidy` is a local maintenance/pre-release step and is not currently part of the CI workflow.
50
53 -3. **Assume Go toolchain baseline from `go.mod` (currently 1.26.x).**
51 +3. **Assume Go toolchain baseline from `go.mod`.**
52 - Why: CI resolves Go from `go.mod`; avoid stale version assumptions.
53
54 4. **Use `Makefile` as build and verification authority; do not reference absent tooling (for example, no `justfile` in this repo).**
@@ -66,9 +64,7 @@ Source of truth for architecture decisions: `docs/adr/README.md` and linked ADRs
64
65 ## Go Conventions
66
69 -**Format:** `gofmt -w . && goimports -w .` before every commit.
70 -
71 -**Imports:** stdlib → external → internal (blank-line separated). Local prefix: `github.com/gosuda`.
67 +**Format:** `gofmt -w . && goimports -w .` before every commit. Imports: stdlib → external → internal (blank-line separated), local prefix `github.com/gosuda`.
68
69 **CGo:** always disabled — `CGO_ENABLED=0`. Pure Go only.
70
cmd/relay-server/frontend/AGENTS.md new
+35
@@ -0,0 +1,35 @@
1 +# Frontend AGENTS.md
2 +
3 +High-signal constraints for the relay-server frontend. Only items expensive to rediscover.
4 +
5 +## Frontend-Backend Contracts (Manually Synced)
6 +
7 +1. **SSR data shape is a 3-way contract.**
8 + Go `leaseRow` (`cmd/relay-server/utils.go`) ↔ TS `ServerData` (`src/hooks/useSSRData.ts`) ↔ `<script id="__SSR_DATA__">` injection (`cmd/relay-server/frontend.go`).
9 + - Why: no shared schema or codegen. Field drift silently breaks SSR hydration. The script tag ID `__SSR_DATA__` is hardcoded in all three locations.
10 +
11 +2. **API path constants require dual maintenance.**
12 + Go definitions in `types/api.go`, TS duplicates in `src/lib/apiPaths.ts`.
13 + - Why: no codegen. A path mismatch produces silent 404s on same-origin requests.
14 +
15 +3. **API envelope shape must match across Go and TS.**
16 + All responses use `{ ok, data?, error?: { code, message } }`. Go helpers (`utils.go`) and TS parser (`src/lib/apiClient.ts`) must agree. TS treats a missing boolean `ok` field as `invalid_envelope`.
17 + - Why: backend responses that skip the envelope (e.g., raw middleware errors) throw `invalid_envelope` instead of displaying the error.
18 +
19 +4. **Lease ID encoding uses base64url without padding.**
20 + Frontend encodes via `btoa` + character replacement (`src/lib/apiPaths.ts`). Backend two-pass decodes: `base64.URLEncoding` then `base64.RawURLEncoding` (`cmd/relay-server/utils.go`).
21 + - Why: standard base64 in either direction → 400 errors. `btoa` is browser-only — not portable to Node/SSR without polyfill.
22 +
23 +5. **Build output renames `index.html` to `portal.html`.**
24 + Vite plugin `rename-index` (`vite.config.ts`) performs this post-build. Go backend serves `portal.html`, not `index.html`. The rename is skipped when `VITEST` env is set.
25 + - Why: any tooling or script assuming `index.html` post-build will fail.
26 +
27 +6. **OG metadata placeholders must match between HTML and Go.**
28 + `index.html` (becomes `portal.html`) contains `[%OG_TITLE%]`, `[%OG_DESCRIPTION%]`, `[%OG_IMAGE_URL%]`. Server-side substitution happens in `cmd/relay-server/frontend.go`.
29 + - Why: renaming a placeholder in one place without the other → raw uninjected strings in responses.
30 +
31 +## Frontend Conventions
32 +
33 +1. **Do not use `useCallback` in new code.**
34 + React Compiler (`babel-plugin-react-compiler`, enabled in `vite.config.ts`) handles memoization automatically.
35 + - Why: manual `useCallback` is redundant with the compiler and adds noise. Existing usage in `useAdmin.ts` and `ServerListView.tsx` is legacy — remove when touching those files.
cmd/relay-server/frontend/CLAUDE.md new
+1
@@ -0,0 +1 @@
1 +AGENTS.md
\ No newline at end of file