Drift detection for Phase 21 scheduled metadata refresh per D-18.
A "drift" is either:
- the freshly-fetched
entityIDdoes not match the persistedidp_entity_idon the connection (:entity_id_drift), or - the freshly-fetched signing-cert fingerprint set contains an
element NOT in the persisted
last_known_metadata_signing_certsMapSet (:new_signing_cert).
On drift, the wrapper auto-suspends the source and refuses to apply
this revision. Per D-32, the new cert still stages as :next via the
existing certificate-inventory path — drift detection ONLY pauses the
scheduled apply pending operator review (Phase 10/12 D-08 unchanged).
Why fingerprints, not PEMs: comparing PEM strings is whitespace-sensitive
(RESEARCH Pitfall 7). A metadata reformat that re-emits the same cert
with different line wrapping would re-fire :new_signing_cert. Compare
MapSets of SHA-256 fingerprints only.
Pure: no I/O, no Ecto. Caller passes already-fingerprint-extracted lists.
Summary
Functions
Compares a freshly-parsed metadata candidate against the stored
connection + source state.
Types
Functions
@spec diff(map(), map()) :: drift_result()
Compares a freshly-parsed metadata candidate against the stored
connection + source state.
candidate:
:idp_entity_id(string):certificate_fingerprints(list of SHA-256 hex strings)
source_state:
:idp_entity_id(string — from theConnection, not the source row):last_known_metadata_signing_certs(list of SHA-256 hex strings — fromMetadataSource)
Returns {:ok, :no_drift} or {:drift, %{reason: ...}}.