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