examples/secure-update-gateway/trust-policy.ron
// holger.bundle-trust-policy/v1 — the offline trust policy a verifier enforces.
//
// Trust is by certificate FINGERPRINT (sha256 of the cert DER). Replace the
// placeholder fingerprints below with your own:
// openssl x509 -in signer.crt -outform der | sha256sum
//
// The trust posture is STATED, never inferred. This sample runs
// `trust_mode: Some(PinnedSigners)`: a signer is trusted iff its fingerprint is
// listed below, and `trusted_roots` must stay empty because nothing would read it.
// For a CA chain instead, write `trust_mode: Some(RootedChain)` and put the
// hex-encoded DER of each root CA in `trusted_roots` (which must then be non-empty).
// A policy that states no `trust_mode` is REFUSED — holger-update-34 — because an
// unanswered trust question is not a permissive answer.
(
schema_version: "holger.bundle-trust-policy/v1",
trust_mode: Some(PinnedSigners),
trusted_roots: [],
// notBefore/notAfter on every signer certificate are enforced (the default).
// On a box whose real-time clock cannot be trusted — a flat RTC battery hands
// the verifier 1970 and every certificate looks "not yet valid" — set
// `IgnoreUnreliableClock` DELIBERATELY; it also gives up the only revocation
// an airgapped verifier has.
certificate_validity: Enforce,
approved_signers: [
(
fingerprint: "REPLACE_WITH_SECURITY_REVIEWER_FINGERPRINT",
subject: Some("CN=security-reviewer"),
roles: ["security-reviewer"],
),
(
fingerprint: "REPLACE_WITH_SYSTEM_OWNER_FINGERPRINT",
subject: Some("CN=system-owner"),
roles: ["system-owner"],
),
],
// Two DISTINCT approvers, both required roles, each satisfied by a distinct cert.
required_approval_roles: ["security-reviewer", "system-owner"],
minimum_distinct_approvers: 2,
// A distinct trusted release signer produces the final release signature.
trusted_release_signers: ["REPLACE_WITH_RELEASE_SIGNER_FINGERPRINT"],
release_signer_must_be_distinct: true,
// Freshness + required evidence.
maximum_bundle_age_secs: Some(31536000), // 365 days
require_expiry: true,
require_sbom: true,
require_vex: false,
require_scan: false,
// What a DOWNSTREAM box does once bundle metadata has expired: keep serving
// what is verified and accept no writes (`Some(ReadOnly)` — the owner's
// default, 2026-09-28, also what an unstated posture means), or stop serving
// (`Some(Refuse)`, an explicit per-estate choice). Stated here so the file
// says it.
on_expired_metadata: Some(ReadOnly),
// Optional target allow-lists (empty = any).
allowed_products: ["ExampleDrive"],
allowed_models: ["ACS880"],
)