From 70542c2dc26c35574d5cd4794a358bc83098e6ba Mon Sep 17 00:00:00 2001 From: Tyler Gillispie Date: Thu, 27 Aug 2026 16:44:18 -0700 Subject: [PATCH] docs(arch): update hypermedia architecture blueprint and AGENTS.md with SSE adapter, mid-stream resilience, and AI-optimized coding principles --- AGENTS.md | 59 +++++- docs/HYPERMEDIA_ARCHITECTURE_BLUEPRINT.md | 212 ++++++++++++++++++++++ 2 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 docs/HYPERMEDIA_ARCHITECTURE_BLUEPRINT.md diff --git a/AGENTS.md b/AGENTS.md index 5feeae6..7669340 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -65,7 +65,64 @@ Management (IAM) fabric and WebAuthn Passkey authority. `tasks/GUIDELINES.md`) by reference rather than copy-pasting operational rules into prompts. -## 4. History & Context Link +## 4. Hypermedia, Datastar & Vertical Feature Slicing Rules + +1. **Locality of Behavior (Vertical Slicing):** + - Co-locate all domain routes, queries, and UI view fragments in + `src/features//`. + - Name template files `fragments.tsx` or `views.tsx` to reinforce the + server-side hypermedia model. +2. **Right-Sized Transport Selection:** + - Standard point-to-point user actions MUST return standard `text/html` JSX + fragments. + - SSE streams (`text/event-stream`) are STRICTLY reserved for multi-user + broadcasts and live pub/sub updates. + - Abstract SSE protocol strings behind a typed Datastar adapter/SDK. Never + hand-roll raw SSE strings in route handlers. +3. **Hypermedia Error Invariant & Mid-Stream Resilience:** + - Never return raw JSON error payloads to browser/Datastar callers. + - All 4xx validation errors and 5xx server faults MUST return an HTML error + fragment targeting `#status-banner` or `.field-error`. + - If an error occurs mid-stream after an SSE connection is open (HTTP 200), + the handler must yield an error toast fragment before closing cleanly. +4. **Durable State vs. Transient Signals:** + - Database (PostgreSQL) and Cache (Valkey) are the SOLE authoritative sources + of state. + - Datastar `data-signals` must ONLY be used for transient presentation + toggles (e.g. dropdown open/close). Never cache domain data or store auth + secrets in client signals. +5. **Vanilla JS Subtree Protection:** + - Any DOM element manipulated by native browser APIs (WebAuthn passkeys, + clipboard copy feedback) MUST include `data-ignore` or `data-ignore-morph` + to prevent Datastar reconciliation conflicts. +6. **Payload & Rate Guardrails:** + - All interactive action endpoints must be bounded by rate-limiting and a + strict 16KB request body ceiling. + +## 5. AI-Optimized Code Organization & Engineering Principles + +1. **Bounded Files & Concise Functions:** + - Target small, focused functions (4–20 lines) and keep files bounded (under + 300–500 lines) so agents can read, reason, and edit full units in a single + turn without context fragmentation. +2. **Strict Single Responsibility Principle (SRP):** + - Every module must do exactly one thing well. Independent modules allow + agents to isolate and modify code without loading unrelated context. +3. **Flat Call Chains Over Clever DRY Abstractions:** + - Prefer a flat, shallow 1–2 hop call chain over deep, multi-level + abstraction hierarchies. Shallow boilerplate is vastly superior to a 5-file + hop for AI reasoning. +4. **Highly Distinctive, Searchable Naming:** + - Avoid generic identifiers (`Manager`, `DataHandler`, `process()`). Use + explicit, searchable domain names (`SessionPinRotator`, + `streamGuestDrawerTelemetry`, `SessionHandoffCard`). +5. **Deterministic Tooling Enforcement:** + - All code quality standards must be enforced by automated tooling + (`deno + fmt`, `deno task lint`, `deno task check`, `deno test`). Agents + must verify passing gates before completing tasks. + +## 6. History & Context Link This repository was cleanly extracted from `ed-droid`. diff --git a/docs/HYPERMEDIA_ARCHITECTURE_BLUEPRINT.md b/docs/HYPERMEDIA_ARCHITECTURE_BLUEPRINT.md new file mode 100644 index 0000000..5574111 --- /dev/null +++ b/docs/HYPERMEDIA_ARCHITECTURE_BLUEPRINT.md @@ -0,0 +1,212 @@ +# Hypermedia & Vertical Slicing Architecture Blueprint + +This document defines the architectural specification and plan of record for +Auth-Yes's transition to a server-driven hypermedia paradigm (Datastar + Deno +2 + Hono SSR JSX) and vertical feature slicing. + +--- + +## 1. System Architecture Overview + +```mermaid +graph TD + subgraph Client ["Browser Client (Zero-Build)"] + HTML["DOM (Morph Targets)"] + Ignored["Vanilla Subtree (data-ignore)
[WebAuthn Ceremony & Copy Feedback]"] + DS["Datastar Engine (~12kB Pinned)"] + Sig["data-signals (Transient UI Toggles ONLY)"] + end + + subgraph Security ["Ingress & Invariant Guards"] + RL["Rate Limiter & 16KB Payload Cap"] + CSRF["Origin / Sec-Fetch-Site Check"] + CN["Content Negotiator (HTML vs JSON vs Shell)"] + end + + subgraph Server ["Deno 2 + Hono SSR Engine"] + subgraph Slices ["Vertical Feature Slices (src/features/)"] + Sess["sessions/ (routes.ts, queries.ts, fragments.tsx)"] + Evt["events/ (routes.ts, queries.ts, fragments.tsx)"] + Adm["admin/ (routes.ts, queries.ts, fragments.tsx)"] + end + end + + subgraph Data ["Authoritative Data Layer (Zero Client Cache)"] + PG[(PostgreSQL 18 - Authoritative State)] + VK[(Valkey 8 - Pub/Sub & L1 Cache)] + end + + HTML -->|data-on-click / data-on-submit| DS + DS -->|Standard HTTP POST/PATCH with 16KB limit| RL + RL --> CSRF --> CN + CN -->|Browser Context| Slices + Slices --> PG & VK + Slices -->|Success 200: JSX View Fragment| HTML + Slices -->|Failure 400/500: JSX Error Toast Fragment| HTML + VK -.->|Targeted SSE Stream (Live Seats/Killswitch)| DS + DS -.->|Morphs DOM, preserves data-ignore| HTML +``` + +--- + +## 2. Core Architectural Standards + +#### Standard A: Dual-Transport & Right-Sized Delivery + +- **Mutations & User Interactions (`POST` / `PATCH`):** Return standard, + stateless `text/html` JSX fragments. Zero long-lived connection overhead. +- **Live Multi-User Broadcasts:** Targeted Server-Sent Events (SSE) bound to + Valkey Pub/Sub with `X-Accel-Buffering: no` for real-time seat counts and + remote session invalidation. +- **Abstracted Protocol Strings:** SSE event formatting is strictly encapsulated + in a typed adapter/SDK (`core/sse_adapter.ts`) rather than + string-concatenation in route handlers. +- **Headless Terminal & SDK Consumers:** Standard REST / ConnectRPC returning + JSON/shell strings via `Content-Negotiation`. + +### Standard B: Hypermedia Unhappy Path & Error Protocol + +- **Never return raw JSON errors to Datastar.** If a validation fails, a seat + limit is exceeded, or an internal error occurs: + - The server returns an HTTP 422/400/500 status with an **HTML error + fragment** targeting `#field-error-${name}` or `#status-banner`. + - Datastar morphs the error toast into place seamlessly without breaking the + hypermedia loop. +- **Mid-Stream Error Handling:** Because SSE streams open with `200 OK`, any + error occurring after connection initialization cannot return a 500 status. + The stream handler must yield an error toast fragment before cleanly closing + the connection. + +### Standard C: Signal Boundaries & The Durable State Invariant + +- **Authoritative Server Rule:** PostgreSQL and Valkey are the _only_ sources of + truth. Datastar `data-signals` are strictly transient (e.g. + `{ isDrawerOpen: false, activeTab: 'single' }`). +- **Zero Secrets in Signals:** Never place session tokens, passkeys, or + permission claims in client signals. + +### Standard D: Vanilla DOM Isolation (`data-ignore`) + +- Any DOM element touched by browser-native micro-helpers (WebAuthn passkey + ceremony indicators or clipboard feedback) MUST carry `data-ignore` or + `data-ignore-morph`. Datastar will bypass diffing on these nodes. + +### Standard E: Rate Limiting & Signal Payload Caps + +- All Datastar action endpoints are protected by `core/auth_guards.ts`: + - Max request body size capped at **16KB** (rejecting oversized signal + payloads). + - Sliding-window rate limiting on interactive triggers to prevent + button-mashing denial-of-service. + +--- + +## 3. Directory Taxonomy (Vertical Feature Slicing) + +``` +src/ +├── core/ <-- Core Invariants & Shared Infrastructure +│ ├── db.ts (PostgreSQL 18 pool & DDL) +│ ├── valkey.ts (Valkey 8 connection & Pub/Sub broker) +│ ├── sse_adapter.ts (Typed Datastar SSE streaming helper) +│ ├── auth_guards.ts (Rate limiter, 16KB payload cap, CSRF/Origin check) +│ ├── content_negotiation.ts (Detects Datastar HTML vs CLI/JSON clients) +│ └── spire_ffi.ts (Rust SPIFFE/mTLS FFI bindings) +│ +├── features/ <-- Self-Contained Domain Feature Slices +│ ├── sessions/ +│ │ ├── routes.ts (Hono router: /dashboard/sessions, /api/sessions/delegate) +│ │ ├── queries.ts (Session DB/Valkey queries) +│ │ ├── fragments.tsx (Hono JSX: SessionTable, SessionDeck, HandoffCard) +│ │ └── sessions.test.ts +│ │ +│ ├── events/ +│ │ ├── routes.ts (Hono router: /events, /join, /rotate-pin, /extend) +│ │ ├── queries.ts (Event passes, claimed seats, attendees queries) +│ │ ├── fragments.tsx (Hono JSX: EventCockpit, WorkshopDrawer, GuestDrawer) +│ │ ├── stream.ts (Targeted SSE stream handler for live seat counters) +│ │ └── events.test.ts +│ │ +│ ├── admin/ +│ │ ├── routes.ts (Hono router: /admin/users, /admin/apps, /admin/audit) +│ │ ├── queries.ts (Admin management SQL) +│ │ └── fragments.tsx (Hono JSX: AdminTables, RoleModals) +│ │ +│ └── auth/ +│ ├── routes.ts (Hono router: /login, /register, /recovery) +│ ├── webauthn.ts (Isolated ceremony micro-scripts with data-ignore) +│ └── fragments.tsx (Hono JSX: PasskeyLogin, Onboarding) +│ +├── shared/ <-- STRICTLY Domain-Agnostic UI Atoms +│ └── ui/ +│ ├── Layout.tsx (Global shell, strict CSP, loads /public/datastar-v1.x.js) +│ ├── Navbar.tsx (Top navigation bar) +│ ├── Toast.tsx (Target fragment for #status-banner error/success morphs) +│ ├── DrawerShell.tsx (Slide-over overlay, backdrop, and header) +│ ├── PillGroup.tsx (Selection button groups for hours, seats) +│ └── Accordion.tsx (Clean
wrapper without double-arrow regressions) +│ +└── tests/arch/ <-- Persistent Architectural & Network Test Harness + ├── transport_efficiency.test.ts (Asserts routine mutations return text/html in <2ms) + ├── sse_lifecycle.test.ts (Asserts Valkey SSE connection limits and drain cleanup) + ├── proxy_buffering.test.ts (Asserts Traefik X-Accel-Buffering: no bypass headers) + ├── error_fragment.test.ts (Asserts 4xx/5xx responses return valid JSX error fragments) + ├── xss_fuzzing.test.ts (Fuzzes fragment rendering with malicious script payloads) + └── content_negotiation.test.ts (Asserts CLI curl vs Datastar browser request isolation) +``` + +--- + +## 4. Persistent Architectural & Network Test Suite (`tests/arch/`) + +To guarantee efficiency and prevent regressions as the system evolves, we +maintain a dedicated **Architectural Test Harness**: + +1. **Transport Efficiency & Latency Tests:** + - Asserts that routine point-to-point actions (`/extend`, `/rotate-pin`, form + submissions) return standard `text/html` in $<2\text{ms}$ without opening + or leaking SSE stream connections. +2. **SSE Stream Lifecycle & Capacity Tests:** + - Simulates 100+ concurrent live cockpit clients listening to Valkey Pub/Sub; + asserts proper connection draining, explicit disconnect cleanup, and memory + ceiling stability. +3. **Reverse Proxy & Buffering Invariant Tests:** + - Validates that SSE stream endpoints emit `X-Accel-Buffering: no` and + `Cache-Control: no-cache` headers to prevent reverse proxies + (Traefik/Nginx) from choking or buffering streams. +4. **Dual-Mode Content Negotiation Tests:** + - Asserts that `curl -sSL ... /join/:slug?format=env` returns a valid shell + string, `Accept: application/json` returns pure JSON DTOs, and browser + requests return clean Datastar HTML fragments. +5. **XSS & Escape Fuzzing Harness:** + - Feeds script injection strings (``, + `">`) into event names and attendee usernames and + asserts the rendered JSX fragment outputs properly escaped entities + (`<script>`). + +--- + +## 5. AI-Optimized Code Organization & Engineering Principles + +1. **Bounded Files & Concise Functions:** + - Target small, focused functions (4–20 lines) and keep files bounded (under + 300–500 lines) so agents can read, reason, and edit full units in a single + turn without context fragmentation. +2. **Strict Single Responsibility Principle (SRP):** + - Every module must do exactly one thing well. Independent modules allow + agents to isolate and modify code without loading unrelated context. +3. **Flat Call Chains Over Clever DRY Abstractions:** + - Prefer a flat, shallow 1–2 hop call chain over deep, multi-level + abstraction hierarchies. Shallow boilerplate is vastly superior to a 5-file + hop for AI reasoning. +4. **Highly Distinctive, Searchable Naming:** + - Avoid generic identifiers (`Manager`, `DataHandler`, `process()`). Use + explicit, searchable domain names (`SessionPinRotator`, + `streamGuestDrawerTelemetry`, `SessionHandoffCard`). +5. **Deterministic Tooling Enforcement:** + - All code quality standards must be enforced by automated tooling + (`deno fmt`, `deno task lint`, `deno task check`, `deno test`). Agents must + verify passing gates before completing tasks. +6. **Payload & Rate Guardrails:** + - All interactive action endpoints must be bounded by rate-limiting and a + strict 16KB request body ceiling.