auth-yes/docs/HYPERMEDIA_ARCHITECTURE_BLUEPRINT.md

10 KiB
Raw Blame History

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, 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 <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:

  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 (<script>alert(1)</script>, "><img src=x onerror=...>) into event names and attendee usernames and asserts the rendered JSX fragment outputs properly escaped entities (&lt;script&gt;).

5. AI-Optimized Code Organization & Engineering Principles

  1. Bounded Files & Concise Functions:
    • Target small, focused functions (420 lines) and keep files bounded (under 300500 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 12 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.