MY PREVENTION PLAN / DESIGN EVIDENCE

Technical architecture draft

Draft v0.2 · 13 October 2026 · Agreed portfolio R1 stack. The local R1 application now exists; VPS setup remains separate. See ../verification.md for actual results.

Scope and authority

The full company-oriented POC remains the functional reference (FR01-FR14). Portfolio Demo R1 implements the selected subset and acceptance gates in the release plan. Its architecture below is the candidate's implementation choice, not CM/MC's architecture. Enterprise identity, real integrations, clinical governance and production operations remain separate company decisions.

Agreed stack

Concern Portfolio R1 choice
Application Next.js App Router and TypeScript; one application for the case page, role views and REST API
UI Tailwind CSS with accessible controls and responsive layouts
Localization next-intl; English default and Belgian Dutch (nl-BE) for all demo role views, guidance and errors; case and analysis material in English
Persistence PostgreSQL, parameterized queries through pg, versioned SQL migrations; no ORM required
Verification Vitest for domain rules and API integration against a separate PostgreSQL test database; Playwright for browser journeys
VPS runtime Node.js managed by systemd; Caddy reverse proxy with HTTPS; PostgreSQL installed directly on the VPS

Docker and Docker Compose are not part of this baseline. Supported dependency versions will be pinned during implementation after compatibility checks. Node.js is the server runtime for database-backed routes.

Components

Loading diagram. Scroll horizontally to see wide diagrams.

Diagram source
flowchart LR
  B[Browser: case page and guided demo] --> C[Caddy HTTPS]
  C --> N[Next.js application]
  N --> A[REST route handlers /api/v1]
  A --> S[Demo session and access policy]
  A --> D[Application services and transactions]
  D --> R[Pure versioned rules]
  D --> P[pg repository layer]
  P --> DB[(PostgreSQL)]
  D --> T[Decision trace and simulated outbox]
  T --> P

Route handlers validate requests and delegate to application services. Services own authorization and transaction boundaries; domain rules do not depend on Next.js, browser state or database clients. The interface displays server results. No client-side role selector or hidden button is an authorization boundary.

The full POC additionally describes simulated imports and delivery metrics. Their implementation remains Should/Could under R1, as recorded in BL04 and BL16. Missing coverage can be demonstrated using fixtures without a working importer.

Visitor sessions and isolation (BL01, BL18)

Create a server-issued opaque session for a visitor without account registration. An HttpOnly cookie identifies the session; use Secure cookies on HTTPS and an explicit SameSite policy. Store only a token hash server-side. Enforce origin/CSRF protection on browser mutations; a cookie alone is not sufficient mutation protection.

Each session owns an isolated demo dataset identified by demoSessionId, including seeded members, coaches, plans, activities, permissions and decisions. Every repository query and uniqueness constraint involving demo data must include this boundary. Role switching selects an allowed seeded identity inside that session and is checked server-side. A visitor cannot select an actor from another session.

Reset is a transaction that restores only the current visitor's fixed-clock dataset. Coordinate reset with in-flight mutations using a session generation/revision check so stale requests cannot recreate old state. Define session expiry and expired-data cleanup in an implementation ticket; do not leave public demo data unbounded.

Demo session creation, role switching, scenario selection and reset require explicit API contracts and tests before implementation. They are additions to the current OpenAPI draft, not existing endpoints. Do not put secrets or real member data in fixtures.

Data, rules and transactions

Use the domain model in document 09, extended with the session boundary. Keep accepted plan versions and review input snapshots immutable. Persist the decision trace with each review transaction. PostgreSQL constraints protect one unresolved support request per session/plan; idempotency records are scoped by session, actor and operation and reject reuse with a different payload.

Plan mutations retain the draft ETag/If-Match contract. Use a conditional revision update inside a transaction so stale acceptance returns the documented conflict without changing the active version. Authorization checks and protected mutations must coordinate on the same current permission state; coaching revocation blocks subsequent access and cancels unresolved requests. Use one checked-out pg client for each transaction and release it reliably.

The injected fixture clock is 11 October 2026 in Europe/Brussels, with the week starting 5 October. Runtime session expiry, operational logs and deployment timestamps use real time separately. Locale changes never change rule results or accepted plan data.

Review request sequence

  1. Resolve the visitor session, generation and allowed actor; enforce ownership or assigned-coach access.
  2. Read current plan state, version and relevant permissions.
  3. Normalize activity, reporting completeness and check-in for the fixed demo week.
  4. Apply the ordered pure review rules; absent data remains distinct from explicit zero.
  5. Persist the review snapshot, revision and decision atomically.
  6. Record any supported simulated outbox event with deduplication; never send real messages.
  7. Return the explanation and next action. A support request requires a separate explicit member action.

If simulated import or queued delivery is added, commit-time and delivery-time permission checks are mandatory for that feature. Its inclusion must not weaken R1 coaching controls.

Deployment boundary (BL20)

Local development and acceptance precede VPS setup and use a local PostgreSQL database plus an isolated local test database. The complete R1 Must journey, automated tests, database persistence and production-mode startup must pass locally before BL20 starts; see the local-first gate in handoff 19. No VPS connection is required for local execution. Local database installation/access must be established during setup; it is not assumed to exist. No containers are required.

The new VPS is configured separately from scratch after checking OS, existing services and access. Run Next.js under a dedicated unprivileged account using systemd, bind the application to loopback, and publish HTTPS through Caddy. PostgreSQL accepts local application connections only. Store environment secrets outside the repository with restricted access; use distinct application/migration/test credentials where appropriate. Do not read or publish the user's existing .env in review output.

The setup task covers SSH-key access, updates, firewall, supported Node.js, PostgreSQL, Caddy, the application service, domain/DNS, HTTPS, logs, restart behavior, database backups and a restore check. Preserve administrative access when changing SSH/firewall. No VPS credentials, OS or domain are presumed supplied.

The public URL must work independently of the developer's machine or BB Connect. Deployment is a separate task; no server changes occur while updating this architecture. This public synthetic-data demo is not a production health programme.

Verification and remaining documentation work

Vitest covers rule precedence, input validation, transaction outcomes, permission checks and session isolation using real test PostgreSQL for integration tests. Playwright covers Camille's role journey, missing-data explanation, duplicate support request, stale acceptance, revocation, locale switching and visitor-scoped reset. Manual review checks keyboard use, clarity of next actions and nl-BE wording.

Dated R1 extensions now update documents 09, 11/OpenAPI, 12, 14 and 15 for sessions/reset, localization and acceptance mappings. Separate entity tables use relational ownership keys with typed JSONB attributes; immutable evidence inserts are preserved. Executed results and remaining review limits are in ../verification.md.