10 KiB
10 KiB
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
graph TD
subgraph Client ["Browser Client (Zero-Build)"]
HTML["DOM (Morph Targets)"]
Ignored["Vanilla Subtree (data-ignore)<br/>[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, statelesstext/htmlJSX fragments. Zero long-lived connection overhead. - Live Multi-User Broadcasts: Targeted Server-Sent Events (SSE) bound to
Valkey Pub/Sub with
X-Accel-Buffering: nofor 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.
- The server returns an HTTP 422/400/500 status with an HTML error
fragment targeting
- 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-signalsare 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-ignoreordata-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 <details> 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:
- Transport Efficiency & Latency Tests:
- Asserts that routine point-to-point actions (
/extend,/rotate-pin, form submissions) return standardtext/htmlin<2\text{ms}without opening or leaking SSE stream connections.
- Asserts that routine point-to-point actions (
- 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.
- Reverse Proxy & Buffering Invariant Tests:
- Validates that SSE stream endpoints emit
X-Accel-Buffering: noandCache-Control: no-cacheheaders to prevent reverse proxies (Traefik/Nginx) from choking or buffering streams.
- Validates that SSE stream endpoints emit
- Dual-Mode Content Negotiation Tests:
- Asserts that
curl -sSL ... /join/:slug?format=envreturns a valid shell string,Accept: application/jsonreturns pure JSON DTOs, and browser requests return clean Datastar HTML fragments.
- Asserts that
- XSS & Escape Fuzzing Harness:
- Feeds script injection strings (
<script>alert(1)</script>,"><img src=x onerror=...>) into event names and attendee usernames and asserts the rendered JSX fragment outputs properly escaped entities (<script>).
- Feeds script injection strings (
5. AI-Optimized Code Organization & Engineering Principles
- 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.
- 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.
- 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.
- Highly Distinctive, Searchable Naming:
- Avoid generic identifiers (
Manager,DataHandler,process()). Use explicit, searchable domain names (SessionPinRotator,streamGuestDrawerTelemetry,SessionHandoffCard).
- Avoid generic identifiers (
- 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.
- All code quality standards must be enforced by automated tooling
(
- Payload & Rate Guardrails:
- All interactive action endpoints must be bounded by rate-limiting and a strict 16KB request body ceiling.