3. Trust and attribution
The database ports deliberately do not authenticate. This crate is the
elsewhere that boundary points at: it is the only component here that
terminates a request, so it is the only one that can turn a credential into an
Audit.
Establishing the principal#
SV3.1 Every request MUST carry a verified credential. There MUST NOT be an unauthenticated mode, and there MUST NOT be a mode that checks a credential if present and proceeds when absent — that accepts an unauthenticated request whenever the caller omits the header, which is the same as not checking.
SV3.2 The credential is a PASETO v4.public token in
Authorization: Bearer. A missing, malformed, tampered or expired token is401.SV3.3 PASETO rather than JWT, and this MUST NOT be relaxed. A JWT names its own algorithm in a header the attacker also controls, which is the root of the
alg: noneand RS256→HS256 confusion families. Av4.publictoken is Ed25519 and cannot claim to be anything else; there is no negotiation to get wrong.SV3.4
v4.public, notv4.local.v4.localis symmetric: verifying and minting use the same key, so any instance able to check a credential is able to issue one, and a single read of the configuration impersonates any principal. This service holds only the public half and cannot mint a token at all.SV3.5 The verifying key is required at boot. Absent, empty, non-hex or wrong-length MUST all stop the process. There MUST be no configuration under which this service starts without a way to verify who is calling it.
An earlier revision accepted a principal from a trusted header when no key was configured. Two modes meant the safe one had to be chosen and the unsafe one was the default: a deployment that forgot the variable ran unauthenticated while looking configured, and the only signal was a log line at boot.
SV3.6 A trusted header MUST NOT be reintroduced as an alternative. Such a header is only as trustworthy as the guarantee that nothing else can reach the port — a guarantee that lives in network configuration this service cannot see, cannot verify, and cannot fail loudly about. A signature is a property of the request; a header is a property of an assumption about the topology.
What the store is told#
SV3.7 Every mutating call MUST pass an
Auditnaming the principal, and MUST record how it was established (actor_source), so a reader can weigh what the actor is worth. RestatesPR12.1–PR12.4, which are not[service]-marked: the store implements attribution, and this crate's obligation is to supply it truthfully.SV3.8 The
subclaim is the actor. A token without one, or with an empty one, MUST be refused rather than attributed to a placeholder. An audit trail namingunknownfor a request that carried a valid signature is worse than a refusal, because it looks like a record.SV3.9
X-Request-Id,X-Forwarded-Forand a caller-supplied reason MAY be recorded alongside, and MUST NOT be treated as identity. They are unauthenticated strings; their value is correlation, not attribution.
What this crate does not do#
SV3.10 No authorization. This crate verifies who is calling and never decides whether they may. It issues
401, never403.This MUST be stated wherever the trust boundary is documented, because the gap is invisible from outside: a deployment that authenticates every request and authorizes none looks secure and lets any valid token read any patient.
SV3.11 The listener's transport posture must be a decision, not a default. This service speaks plain HTTP;
O10.7governs the database link, not this listener. A deployment MUST either keep the bind loopback or terminate TLS in front of it — and a non-loopback bind MUST refuse to boot unless the deployment acknowledges, viaFHIR_LOCO_TLS_TERMINATED_UPSTREAM=true, that TLS terminates upstream. The refusal names this requirement and both ways out. PHI in the clear must be a choice someone made, not a config nobody read; the same shape asO10.7's database-side refusal andSV4.4's loopback default.Stated and enforced since 2026-08-07 (
auth::listener_posture, checked inbefore_runbefore anything is served). Until then this requirement recorded its own absence — "a gap in §10 rather than in this crate" (F-58) — but service obligations have lived in this specification since theSVrestatement (C0.16), so the obligation lands here, not in §10.
Part of the fhir-loco specification.