Technology Notes
Short corrections of common category errors.
Non-normative
- Companion version
- 1.0
- Maps to COADF Core
- 2.2
- Status
- Current
- Last reviewed
Each note corrects a category error: two things that look alike and promise different things.
OpenTelemetry is not an audit trail
OpenTelemetry records and propagates execution context: which calls a request made, how long they took, where they failed. It is designed to be sampled, filtered and transformed on the way to a backend, and a trace that is not sampled is not exported; the Collector's filter processor drops matching telemetry by configuration. Its identity is one execution, carried in the W3C traceparent header. An audit trail answers a different question about a different unit: what happened to one business transaction, who acted and on what evidence, completely and without later edits.
- Never sample, filter or route audit entries through a telemetry pipeline.
- Correlate the two: record the audit
trace_idon spans as an attribute. Do not reuse a distributed trace identifier as the audit identity. - Classify what you correlate with. A span attribute leaves the audit store for exporters, vendors and a retention of its own: an opaque reference, never personal data or a secret.
- Expect one business transaction to span several distributed traces: the intake request, an asynchronous job, a decision made days later.
- OpenTelemetry: Sampling · official documentation · Checked on 2026-09-11
- OpenTelemetry: Transforming telemetry · official documentation · Checked on 2026-09-11
- W3C: Trace Context, the traceparent header · specification · W3C Recommendation, Level 1 · Checked on 2026-09-11
Kubernetes admission policies do not replace application-level validation
Admission control intercepts requests to the Kubernetes API server after authentication and authorisation, before the object is persisted. It sees Deployments, Pods and ConfigMaps, and requests to read them bypass it entirely. It never sees the HTTP requests a service handles, the messages it consumes or the replies a model returns. It judges requests as they arrive, which Kubernetes spells out for one admission plugin: when a LimitRange is added, the pods that already exist continue unchanged.
- Use admission for properties of workloads: images pinned by digest, revisions named, credentials absent where they must be absent.
- Keep boundary validation in the application, on every entry point it has.
- Audit existing objects separately, as Gatekeeper's audit does; admission only ever sees changes.
- Kubernetes: Admission control: what are they · official documentation · Kubernetes 1.37 · Checked on 2026-09-11
- Kubernetes: Limit Ranges: admission-time validation · official documentation · Kubernetes 1.37 · Checked on 2026-09-11
- Open Policy Agent Gatekeeper: Gatekeeper: audit · official documentation · Gatekeeper 3.23 · Checked on 2026-09-11
Event sourcing is not required for append-only evidence
Event sourcing makes an append-only log of events the system of record and derives state from it. That yields a complete history by construction, at a considerable price: every read model is a projection, and every change to an event's shape is a migration of history. An append-only audit trail beside ordinary state gives what COADF P-4 asks for (a history that is appended and never rewritten, reconstructable in one query) without changing how the rest of the system keeps its state. Per-operation privileges and refusal triggers are enough to build one in PostgreSQL.
- Choose event sourcing for the domain's own reasons, not in order to obtain an audit trail.
- An append-only table with an insert-only role, refusal triggers and idempotent writes realises append-only evidence without event sourcing, within the database's own trust boundary.
- Either way, the entry is written in the same transaction as the change it records.
- PostgreSQL Global Development Group: Privileges · official documentation · PostgreSQL 18 · Checked on 2026-09-11
Microservices are not required for probabilistic isolation
The probabilistic boundary is a property of code paths: no path lets model output become an authoritative object without an explicit, recorded step. A module boundary that the build enforces keeps that property inside one process, with import contracts in Python or architecture rules as unit tests in Java. A network boundary adds infrastructure enforcement, and with it contract versioning, partial failure, retries and context propagation across hops, which are new places for the property to be lost.
- Deploy the probabilistic component separately when scaling, isolating a vendor library or releasing independently calls for it, not because a principle does.
- Enforce the module boundary with an architecture test in either case.
- When the boundary is a network, validate again on the receiving side.
- import-linter: Contract types · official documentation · import-linter 2.15 · Checked on 2026-09-11
- ArchUnit: ArchUnit user guide · official documentation · ArchUnit 1.5 · Checked on 2026-09-11
Static typing does not replace runtime validation at external boundaries
A type system checks the code against itself. At an external boundary the data comes from outside the code: a model, a client, a queue. TypeScript's type annotations are erased before the program runs and JSON.parse is typed as returning any; the Python runtime does not enforce type annotations; and a Java type describes what a deserialiser was asked to produce, not what the sender sent. The type is a promise the boundary has to keep, at run time, with a schema.
- Derive the static type from the runtime schema wherever the language allows it.
- Validate on every receiving side, not only at the web entry point.
- Keep casts out of boundary code: a cast is where the type system stops checking.
- TypeScript: The Basics: erased types · official documentation · Checked on 2026-09-11
- TypeScript: lib.es5.d.ts: JSON.parse · project repository · Checked on 2026-09-11
- Python Software Foundation: typing: support for type hints · language documentation · Python 3.14 · Checked on 2026-09-11
A green CI pipeline does not prove a control is architecturally sufficient
A green pipeline proves that the checks it ran passed. It says nothing about checks that were skipped and reported as successful, selections that matched no test, fences placed on the wrong path, or properties nobody wrote a check for. COADF's conformance model states the same rule from the other side: a control is reported as enforced only when its evidence ran in the identified run and passed.
- Record which checks ran, and how many assertions executed, for every run.
- Prove each fence's teeth on its real path, and prove it again when the path changes.
- Review what is not checked as seriously as what is.
- GitHub: Control jobs with conditions · official documentation · Checked on 2026-09-11
