docs(architecture): surface core-audit + DPA across architecture docs
Touches the deeper architecture surfaces the Phase 6 sweep skipped: - overview.md: split must-have (core-shared, core-cms, core-api) from optional (core-trpc, core-ui, core-realtime, core-events, core-audit); add core-audit to the Five tags optional list - dependency-flow.md: extend the bindAll diagram with resolveAudit; add auditLog row to the BindContext table; rename the TRACER/LOGGER/METRICS heading to include AUDIT (ADR-018); note the R52-style boundary rule for @repo/core-audit (consume via protocol) - vertical-feature-spec.md: target-state section now states 3 must-have + 5 optional cores; tag matrix includes the optional cores; bind- production signature destructure includes auditLog - di-explainer.html: §08 instrumentation gains an IAuditLog block + the Wiring path tree shows resolveAudit + auditLog in ctx - testing-strategy.md: RecordingAuditLog reference + reset() guidance
This commit is contained in:
@@ -94,12 +94,14 @@ Three layers work in tandem:
|
||||
|
||||
The two enforcement layers are independent but complementary. ESLint is stricter on per-import context (e.g., file-specific exemptions via `// @boundaries-ignore`), while Turborepo catches transitive issues that lint-time checking misses. Run `pnpm lint` and `pnpm turbo boundaries` in CI to catch all violations.
|
||||
|
||||
## TRACER / LOGGER / METRICS (ADR-014, ADR-017)
|
||||
## TRACER / LOGGER / METRICS / AUDIT (ADR-014, ADR-017, ADR-018)
|
||||
|
||||
The instrumentation layer is **per-feature container** but **app-wide instance**: each feature container binds `INSTRUMENTATION_SYMBOLS.ITracer`, `INSTRUMENTATION_SYMBOLS.ILogger`, and `INSTRUMENTATION_SYMBOLS.IMetrics` to the SAME instances, constructed once by the app's `bindAll()` dispatcher (Rule 0).
|
||||
|
||||
**Substrate:** OpenTelemetry SDK. Sentry is the exporter via `@sentry/opentelemetry`. PII scrubbing runs at the OTel processor layer (`PiiScrubSpanProcessor` + `PiiScrubLogRecordProcessor`) before the Sentry exporter sees the data. Feature code is never coupled to the OTel SDK or Sentry SDK directly (ADR-017).
|
||||
|
||||
**Audit is a parallel channel** with different durability and redaction contracts (ADR-018). Whereas OTel is sampled and pruned (best-effort observability), audit is append-only and retained for compliance. `auditLog` is bound by `resolveAudit` and added to `ctx` for feature binders that opt in; the binding is wrapped in `TraceIdEnrichingAuditLog` so every `AuditEntry` auto-correlates with the active OTel span via `correlationId`. See `docs/guides/audit-and-compliance.md` and the visual explainer at `docs/architecture/audit-and-compliance-explainer.html`.
|
||||
|
||||
```
|
||||
apps/web-next/src/server/bind-production.ts (bindAll)
|
||||
│
|
||||
@@ -118,9 +120,13 @@ apps/web-next/src/server/bind-production.ts (bindAll)
|
||||
│ server.ts → SocketIORealtimeBroadcaster + RealtimeHandlerRegistry (passed in from server.ts)
|
||||
│ page/test → InMemoryRealtimeBroadcaster + RealtimeHandlerRegistry (defaults)
|
||||
│ ↓
|
||||
├─ build ctx: BindProductionContext = { config, tracer, logger, metrics?, bus, queue, realtime, realtimeRegistry }
|
||||
├─ resolveAudit → IAuditLog (ADR-018)
|
||||
│ production → TraceIdEnrichingAuditLog( MultiSinkAuditLog([StdoutJsonAuditLog, PayloadAuditLog]) )
|
||||
│ dev-seed → TraceIdEnrichingAuditLog( StdoutJsonAuditLog ) — or Noop when core-audit is absent
|
||||
│ ↓
|
||||
├─ build ctx: BindProductionContext = { config, tracer, logger, metrics?, bus, queue, realtime, realtimeRegistry, auditLog }
|
||||
│ Required: tracer, logger, config (production only)
|
||||
│ Optional: metrics, bus, queue, realtime, realtimeRegistry (guard with ?. when used)
|
||||
│ Optional: metrics, bus, queue, realtime, realtimeRegistry, auditLog (guard with ?. when used)
|
||||
│ ↓
|
||||
├─ bindProductionBlog(ctx: BindProductionContext)
|
||||
│ │
|
||||
@@ -149,6 +155,7 @@ apps/web-next/src/server/bind-production.ts (bindAll)
|
||||
| `queue` | `IJobQueue?` | optional | Present when `core-shared/jobs` is wired |
|
||||
| `realtime` | `RealtimeBroadcasterProtocol?` | optional | `IRealtimeBroadcaster` at the aggregator |
|
||||
| `realtimeRegistry` | `RealtimeRegistryProtocol?` | optional | `IRealtimeHandlerRegistry` at the aggregator |
|
||||
| `auditLog` | `AuditLogProtocol?` | optional | `IAuditLog` at the aggregator; pre-wrapped in `TraceIdEnrichingAuditLog` so callers don't supply `correlationId` (ADR-018) |
|
||||
|
||||
Feature binders destructure `ctx` and use optional fields with `?.` or cast to the full interface when the feature unconditionally requires them (e.g. `bus as IEventBus` when a use case always needs the event bus).
|
||||
|
||||
@@ -156,4 +163,4 @@ Feature binders destructure `ctx` and use optional fields with `?.` or cast to t
|
||||
|
||||
**Why the shared container exists at all:** isolates Rule 0 resolution from feature containers. Feature containers don't need to know if Sentry is the exporter or not — they just receive an `ITracer` instance.
|
||||
|
||||
**Boundary rule:** feature packages MUST NOT import `@sentry/*` or `@opentelemetry/sdk-*` directly (R40 + R52, ESLint-enforced). The OTel bridge (`otel/sentry-bridge.ts`), browser init files (`sentry/init-client*.ts`), and app-level `instrumentation*.{ts,mjs}` / `next.config.{mjs}` / `vite.config.{ts}` entries are the only allowlisted paths.
|
||||
**Boundary rule:** feature packages MUST NOT import `@sentry/*` or `@opentelemetry/sdk-*` directly (R40 + R52, ESLint-enforced). The OTel bridge (`otel/sentry-bridge.ts`), browser init files (`sentry/init-client*.ts`), and app-level `instrumentation*.{ts,mjs}` / `next.config.{mjs}` / `vite.config.{ts}` entries are the only allowlisted paths. Likewise, features MUST NOT import `@repo/core-audit` directly — they consume `IAuditLog` via the `AuditLogProtocol` type re-exported from `@repo/core-shared/di/bind-protocols`.
|
||||
|
||||
Reference in New Issue
Block a user