apps/api: NestJS API (REST + WebSocket), Prisma, Postgres. Optional Redis for pub/sub later.apps/web: Vue 3 app (Vite), Pinia, usespackages/sdkfor all network IO.packages/contracts: Zod schemas + TypeScript types; canonical contracts for REST + WS.packages/sdk: Typed REST client + WS client; validates usingpackages/contracts.
web (browser) ↕ HTTPS (REST) ↕ WS (ws) api (NestJS) ↕ SQL postgres
Optional (V2 or later): api ↔ redis pub/sub for multi-instance fanout.
- Browser ↔ API (REST): validate request DTOs in API; validate response payloads in SDK.
- Browser ↔ API (WS): validate inbound subscribe frames and outbound event frames with contracts.
- API ↔ DB: enforce tenant scoping in queries.
- Seed generator inserts events into DB (or event ingestion endpoint).
- REST
/eventsreturns paged events (cursor). - WS subscription pushes new events to clients subscribed to matching filters.
- Client appends events to a virtualized list; filters are route-driven.
- JWT in Authorization header for REST.
- WS uses JWT via query param or subprotocol header (record exact method in CONTRACTS.md and implement consistently).
- Tenant context is derived from JWT claims and/or explicit tenantId in route; server must verify membership.
- Canonical error shape defined in CONTRACTS.md and implemented in API + SDK.
- No ad-hoc error formats.
- Cursor semantics and resync rules are defined in CONTRACTS.md and ADR-0003.
- If client cannot keep up, server issues a
resync_requiredframe; client switches to REST to catch up.