Static read-through audit of crates/dpp-node. No auth/authz logic lives in this crate (routes are mounted from other services); /health//metrics are its only own handlers. Four verified findings.
1. Invalid EU registry payloads are submitted anyway
Location: src/infra/registry/mapping.rs:157-163
Issue: envelope.payload.validate() failing only logs tracing::warn!("... failed B1 validation — sending anyway (pre-go-live)"), and the invalid payload is submitted to the EU registry regardless.
Failure scenario: Any malformed/incomplete registration payload (e.g. missing required Annex III fields) is sent as-is rather than being rejected before it leaves the node — the opposite of fail-closed for a regulatory submission.
2. Transfer notifications always omit the previous operator
Location: src/infra/registry/mapping.rs:299-320 (notify_transfer)
Issue: from_operator is always built with value/name/country hardcoded to empty strings ("would come from context"). The RegistrySyncPort::notify_transfer signature itself never receives a "from" operator — this is structural, not an edge case.
Failure scenario: Every EU-registry transfer notification this node ever sends permanently omits who the previous operator was.
3. Token-expiry check can panic on underflow
Location: src/infra/registry/token.rs:22-25 (CachedToken::is_expired)
Issue: self.expires_at - Duration::from_secs(30) on a std::time::Instant panics on underflow rather than saturating.
Failure scenario: If the EU registry's OAuth endpoint ever returns expires_in under ~30s while the node has been up for under ~20-30s (fresh restart racing a token refresh), this panics inside get_token() — on the hot path of every register/check_status/notify_transfer call.
4. Silent fallback on boot-time DB lookup failure
Location: src/main.rs:197-204
Issue: db.operator_repo.get(...).await.ok().flatten().map(|c| c.country).unwrap_or_default() discards any DB error with no log line — every other fallible boot step in this file logs on failure.
Failure scenario: A transient Postgres hiccup during boot silently sets operator_country to "", baked into every passport's facility/operator registry payload for the life of the process with zero signal anything went wrong.
Static read-through audit of
crates/dpp-node. No auth/authz logic lives in this crate (routes are mounted from other services);/health//metricsare its only own handlers. Four verified findings.1. Invalid EU registry payloads are submitted anyway
Location:
src/infra/registry/mapping.rs:157-163Issue:
envelope.payload.validate()failing only logstracing::warn!("... failed B1 validation — sending anyway (pre-go-live)"), and the invalid payload is submitted to the EU registry regardless.Failure scenario: Any malformed/incomplete registration payload (e.g. missing required Annex III fields) is sent as-is rather than being rejected before it leaves the node — the opposite of fail-closed for a regulatory submission.
2. Transfer notifications always omit the previous operator
Location:
src/infra/registry/mapping.rs:299-320(notify_transfer)Issue:
from_operatoris always built withvalue/name/countryhardcoded to empty strings ("would come from context"). TheRegistrySyncPort::notify_transfersignature itself never receives a "from" operator — this is structural, not an edge case.Failure scenario: Every EU-registry transfer notification this node ever sends permanently omits who the previous operator was.
3. Token-expiry check can panic on underflow
Location:
src/infra/registry/token.rs:22-25(CachedToken::is_expired)Issue:
self.expires_at - Duration::from_secs(30)on astd::time::Instantpanics on underflow rather than saturating.Failure scenario: If the EU registry's OAuth endpoint ever returns
expires_inunder ~30s while the node has been up for under ~20-30s (fresh restart racing a token refresh), this panics insideget_token()— on the hot path of everyregister/check_status/notify_transfercall.4. Silent fallback on boot-time DB lookup failure
Location:
src/main.rs:197-204Issue:
db.operator_repo.get(...).await.ok().flatten().map(|c| c.country).unwrap_or_default()discards any DB error with no log line — every other fallible boot step in this file logs on failure.Failure scenario: A transient Postgres hiccup during boot silently sets
operator_countryto"", baked into every passport's facility/operator registry payload for the life of the process with zero signal anything went wrong.