Files
apf_portal/decisions/README.md
T
Julien Gautier b35bf2b3de docs: add ADR-0013 for the audit trail (dedicated append-only Postgres schema)
Pin the audit-trail architecture: a dedicated 'audit' schema in the same
Postgres instance as business data, with three Postgres roles enforcing
append-only at the database layer - audit_writer (INSERT only),
audit_reader (SELECT only), audit_archiver (DELETE only on rows past the
retention threshold). No role anywhere holds UPDATE or TRUNCATE; the BFF
verifies this at startup with a deliberate failing UPDATE probe.

The audit stream is decoupled from the application logs (different sink,
different access controls, different retention) but cross-referenced via
trace_id (ADR-0012) and actor_id_hash, which uses the same salt as the
app logs so an investigator joins the two streams without re-hashing.

Events captured in v1 cover the auth and session lifecycle: sign_in
(success/failure), sign_out, session.expired, session.revoked,
token.validation.failed, mfa.assertion.failed, authz.deny. Hooks for
admin actions and sensitive data access are designed-in but inert until
v1+ features call them - kept alive by tests so they don't drift.

Failure semantics are blocking - if the audit INSERT fails, the
in-flight operation fails with 503. Trade-off acknowledged: the audit DB
is part of the trust path. Mitigation is HA Postgres in prod, deferred
to the infrastructure ADR.

Retention defaults to 365 days, env-overridable, enforced by a daily
purge running under audit_archiver. The retention default is engineering
prudence, not legal advice - the org-side legal review of the actual
applicable retention regime is explicitly owed and noted in the ADR.

Cryptographic chaining and WORM storage are deferred unless a compliance
regime demands them.

decisions/README.md index updated. CLAUDE.md gains an explicit
'Audit trail' line pointing to ADR-0013.
2026-04-29 23:32:35 +02:00

5.1 KiB

Architectural Decision Records

This project records architecturally-significant decisions as ADRs in the MADR 4.0.0 format. References: adr.github.io.

Why ADRs

ADRs capture the why behind a decision — context, drivers, options considered, trade-offs accepted — at the moment the decision is made. They make architecture reviewable, onboarding faster, and prevent the same debate from being re-litigated later.

Conventions

  • Format: MADR 4.0.0. Start from template.md.
  • Filename: NNNN-kebab-case-title.md, e.g. 0007-adopt-tailwind-for-design-tokens.md.
  • Numbering: globally sequential 4-digit prefix. Numbers never reset, never get reused — even when an ADR is superseded or deprecated.
  • Layout: flat folder. ADRs are not nested into category subfolders; topical organization happens via tags.
  • Tags: every ADR carries a tags: array in the MADR frontmatter, drawn from the tag vocabulary below. An ADR may carry several tags. Propose new tags (or renames) in the same PR that needs them; never invent ad-hoc tags inline.
  • Status lifecycle: proposedaccepted → optionally deprecated or superseded by [ADR-NNNN](NNNN-other.md). Update the YAML frontmatter; never delete an ADR.
  • Index maintenance: every ADR addition or status change must update the Index below in the same commit.

When to write an ADR

Write one whenever a development decision is non-trivial: tool or library choice, framework pattern, security control, perf budget, a11y target, naming convention, deprecation, breaking change, or any choice that future contributors would benefit from understanding the why of.

Tag vocabulary

The vocabulary below is the source of truth. It is intentionally coarse — propose extensions only when an existing tag genuinely doesn't fit, and avoid overly narrow tags.

Tag Scope
frontend UI, Angular, components, design system, client-side state
backend API, BFF, server-side services
security AuthN, AuthZ, sessions, CSP, dependency scanning, secret management
performance Perf budgets, caching, bundle size, Lighthouse
accessibility WCAG, a11y testing, keyboard, ARIA, contrast
infrastructure CI/CD, hosting, deployment, runtime
observability Logs, metrics, traces, correlation IDs, monitoring
data Persistence, schemas, migrations, data flow
process Team conventions, workflows, repo policy

Status: starter vocabulary, to be refined as ADRs accumulate. Update this table whenever a tag is added, renamed, or retired.

Index

ADRs are listed in numerical order. To slice by topic, filter on the Tags column.

# Title Status Tags Date
0001 Use ADRs to record architectural decisions accepted process 2026-04-29
0002 Adopt Nx monorepo with the apps preset accepted infrastructure, frontend, backend 2026-04-29
0003 Workspace and app naming convention accepted process 2026-04-29
0004 Frontend stack — Angular (latest LTS), standalone, zoneless, Signals, CSR-only, Vitest accepted frontend 2026-04-29
0005 Backend stack — NestJS over Express, Fastify, Hono accepted backend 2026-04-29
0006 Persistence — PostgreSQL with Prisma accepted data, backend 2026-04-29
0007 Pre-commit hooks and Conventional Commits accepted process 2026-04-29
0008 Identity model — multi-tenant Entra ID for workforce, dual-audience design for future External ID accepted security, data 2026-04-29
0009 Authentication flow — OIDC Authorization Code + PKCE via MSAL Node, BFF session pattern accepted security, backend 2026-04-29
0010 Session management — opaque session IDs in cookies, payload in self-hosted Redis with AES-GCM at rest accepted security, backend, infrastructure 2026-04-29
0011 MFA enforcement — Entra ID Conditional Access baseline, BFF claim sanity-check, step-up hooks designed-in accepted security 2026-04-29
0012 Observability — Pino structured logs + OpenTelemetry tracing, W3C Trace Context propagation, stdout + collector accepted observability, backend, frontend 2026-04-29
0013 Audit trail — separated append-only Postgres schema, decoupled from app logs accepted security, observability, data 2026-04-29