Adds the afterRead hook factory for per-collection opt-in automatic VIEW
audit entry emission. Fire-and-forget design ensures failing sinks never
propagate to the user-facing read. Includes sentinel IP/UA fallbacks,
truncateIp /24 integration, shouldSkip predicate, and system actor
resolution. All 6 TDD test cases pass; both barrels updated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
bindAudit now wraps the inner sink/fan-out with TraceIdEnrichingAuditLog so
all sinks receive AuditEntry.correlationId auto-populated from the active
OTel span. bind-audit.test.ts assertions updated to check instanceof
TraceIdEnrichingAuditLog first, then .inner for the concrete sink type.
TraceIdEnrichingAuditLog exported from the package barrel.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Decorator implementing IAuditLog that auto-populates AuditEntry.correlationId
from the active OTel span via currentTraceId(). Caller-supplied correlationId
always wins (explicit > implicit). eraseSubject passes through unmodified.
Adds @opentelemetry/{api,sdk-trace-base,context-async-hooks} as devDeps for
test infrastructure.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reads the active OTel span context via trace.getActiveSpan(); returns the
32-char hex traceId or undefined when no span is active or traceId is the
all-zeros invalid value. Re-exported from both instrumentation/otel/index
and instrumentation/index barrels.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds auditProcedure (adminOnly middleware + defineErrorMiddleware([])) in
core-audit/src/integrations/api/procedures.ts. Adds createAuditRouter that
captures an IAuditLog and exposes a single eraseSubject mutation with zod
input validation. Non-admins receive FORBIDDEN. Barrel re-exports
pseudonymize, createAuditErasureHook, createAuditRouter, auditRouter,
AuditRouter, auditProcedure, AdminTrpcUser. Adds AUDIT_PSEUDONYM_SALT to
turbo.json globalEnv to clear lint warnings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds createAuditErasureHook in core-audit/src/hooks/. The factory returns
a CollectionAfterDeleteHook that calls auditLog.eraseSubject() when a
document is deleted. Defaults to "pseudonymize" mode; coerces numeric ids
to string; skips undefined/null/object ids. Barrel at hooks/index.ts.
6 unit tests cover all guard branches.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the Phase-2 stub with a real impl. Mode "delete" issues a bulk
payload.delete with overrideAccess:true to bypass the append-only rule.
Mode "pseudonymize" fetches up to 10_000 matching docs and patches each
actorId to the token produced by pseudonymize(). Adds 3 eraseSubject unit
tests to the existing payload-audit-log test file.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds `pseudonymize(actorId)` in core-audit — SHA-256 of salt+":"+actorId
truncated to 16 hex chars, prefixed "erased-". Salt from
AUDIT_PSEUDONYM_SALT env (fallback dev label). 6 unit tests: deterministic,
salt-change-differs, fallback-no-throw.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Prefix unused opts param with _opts in bindOtelInstrumentation (ESLint)
- Extend IPv6 regex to cover prefix::suffix form (e.g. 2001:0db8::1) so
the IPv6 scrub test passes correctly
- Delete orphaned sentry-pii-scrubber.test.ts files from apps/cms and
apps/web-tanstack that imported the deleted sentry/scrub module (broke
typecheck; the OTel-layer replacement tests are in pii-scrub-processor.test.ts)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
I3: bind-production.test.ts instrumentation orthogonality tests updated to use
bindOtelInstrumentation as primary name (bindSentryInstrumentation alias still
wired in mock setup for deprecation-alias coverage, not in assertions).
I4: as never cast in init-server-node.ts annotated with explanation of the
sdk-trace-base / sdk-node TypeScript version conflict that necessitates it.
I5: SentryLogRecordProcessor removed from @sentry/opentelemetry mock in
no-instrumentation.ts — that class does not exist in v10.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PII_KEY_SUBSTRINGS extended with all OTel HTTP semconv IP attribute keys
(http.client_ip, client.address, net.peer.ip, etc.) so they are key-redacted.
scrubValue() added to pii-scrub-processor.ts applies IPV4_REGEX / IPV6_REGEX
replacement for attribute values whose keys are not PII-keyed, and for log
record bodies that pass the substring check. Closes the gap left by deletion
of the old beforeSend value-level IP scrubbers. 5 new tests added.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All three apps' instrumentation.ts files now call initOtelServerNode directly
instead of initSentryServer/initSentryServerNode, closing the startup window
where @sentry/nextjs auto-instrumentation could send unscrubbed errors before
bindAll() fires. bindOtelInstrumentation no longer calls initOtelServerNode
(SDK init belongs at app boot, binding at request scope). Orphaned sentry/
init-server*.ts files deleted; their package.json subpath exports removed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The sentry-pii-scrubber.test.ts file tested the now-deleted sentry/scrub.ts
module. PII scrubbing is now validated in core-shared/otel/pii-scrub-processor.test.ts
at the correct layer.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>