Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

server/lib/src/lib.rs

//! The holger server crate: turns a RON config into a running artifact server.
//!
//! Config is a directed graph of three node kinds — [`Repository`] (a backend
//! plus its upstreams), [`ExposedEndpoint`] (a gRPC + optional HTTP/OCI gateway),
//! and [`StorageEndpoint`] (disk or a znippy archive). [`wire_holger`] resolves
//! that graph in passes: index every node by `ron_name`, check the forward names
//! (repo→storage, repo→exposed) and reverse links (exposed→repos), then build a
//! per-endpoint [`FastRoutes`] table. Any repo with configured upstreams is
//! wrapped in a `ProxyBackend` at this stage so a 404 in the local archive falls
//! through to the upstream pull-through automatically.
//!
//! Two entry modes share the same wired graph: call [`Holger::fetch`]/[`Holger::put`]
//! in-process, or [`Holger::start`] to spin up the tonic gRPC services (and HTTP
//! gateways) fronted by shared auth ([`auth`]) and one append-only audit sink
//! ([`audit`]). Boot is deliberately fault-tolerant — a single bad archive is
//! disabled and logged (see [`Holger::instantiate_backends`]) rather than sinking
//! the whole server; only an all-repos failure hard-fails.
//!
//! Gotcha: the wiring uses raw `*const` pointers between nodes, so the graph is
//! valid only while the owning `Vec`s stay put — never mutate or reallocate
//! `repositories`/`exposed_endpoints`/`storage_endpoints` after `wire_holger`.
//! Related seams: `ron_url`/`ron_http_url` parse as bare `SocketAddr`s, so any
//! scheme'd URL must go through `bare_socket_addr` first (see [`dev_pair_ron`]).

// holger-server-lib/src/lib.rs
//
// Two modes of operation:
//   1. Rust function bindings — use Holger struct directly as a library
//   2. gRPC server — configure exposed_endpoints in RON, call holger.start()

use serde::{Deserialize, Serialize};

use std::{fs::File, io::BufReader, path::Path};

#[allow(unused_imports)]
use anyhow::{Context as _, Result};
#[cfg(test)]
use ron::de::from_reader;

use crate::exposed::fast_routes::FastRoutes;
use crate::exposed::ExposedEndpoint;
pub use crate::repository::Repository;
/// holger's frozen error-code registry, re-exported so every surface (the CLI
/// included) tags failures from ONE source instead of taking its own dep.
pub use holger_errcode as errcode;
use std::collections::HashMap;
use std::sync::Arc;
pub use traits::RepositoryBackendTrait;

/// **The repositories paid signups made** (lane M4): what the metered box's `add_principal`
/// declares and every build's boot serves (`Holger::declare_sold_repositories`).
pub mod sold {
    use std::collections::BTreeMap;
    use std::path::Path;

    /// Under the data root: name → [`SoldRepository`]. Also every repository an admin made
    /// at run time (`holger.account.v1.CreateRepository`, lane P4): one record of what was
    /// made after the config was written, which the twin carries and a recharge brings back.
    pub const SOLD_REPOS_REL: &str = "fleet/sold-repositories.json";

    /// One repository made at run time: the estate's repository it is shaped like, the signup
    /// it was made for (empty for one an admin made), and — lane P4 — its owner and whether its
    /// reads need a credential. The two new fields are left out when empty, so a record written
    /// before them reads, and writes back, byte for byte.
    #[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
    pub struct SoldRepository {
        pub template: String,
        pub signup: String,
        /// The principal (subject) it was made for. Empty: the estate's, or a sale whose buyer
        /// has not registered yet.
        #[serde(default, skip_serializing_if = "String::is_empty")]
        pub owner: String,
        /// Its reads need a credential of this server.
        #[serde(default, skip_serializing_if = "std::ops::Not::not")]
        pub private: bool,
        /// **The people its owner granted** (lane P1), by subject: `read` or `write` (read and
        /// write). Left out when empty.
        #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
        pub grants: BTreeMap<String, String>,
    }

    /// The word a grant is kept under: `write` (read and write) or `read`.
    pub fn access_word(write: bool) -> &'static str {
        if write {
            "write"
        } else {
            "read"
        }
    }

    /// **What the read gate keeps of `s`** (lane P1): its owner ([`gate_owner`]) and its grants.
    pub fn gate_entry(data_root: &Path, s: &SoldRepository) -> crate::auth::SoldEntry {
        crate::auth::SoldEntry {
            owner: gate_owner(data_root, &s.owner),
            grants: s
                .grants
                .iter()
                .map(|(who, a)| (who.clone(), a == "write"))
                .collect(),
        }
    }

    /// Pure: a name a repository may be made under — lower-case letters, digits, `-`, `_` and
    /// `.`, starting with a letter or digit, at most 64 long.
    pub fn repository_name_ok(name: &str) -> bool {
        let b = name.as_bytes();
        !b.is_empty()
            && b.len() <= 64
            && b[0].is_ascii_alphanumeric()
            && b.iter().all(|c| {
                c.is_ascii_lowercase() || c.is_ascii_digit() || matches!(c, b'-' | b'_' | b'.')
            })
            && !name.contains("..")
    }

    /// The repositories made at run time under `data_root` (none when the file is absent or
    /// cannot be read).
    pub fn read(data_root: &Path) -> BTreeMap<String, SoldRepository> {
        std::fs::read(data_root.join(SOLD_REPOS_REL))
            .ok()
            .and_then(|b| serde_json::from_slice(&b).ok())
            .unwrap_or_default()
    }

    /// **Whose a repository a paid order makes here is** (lane P1): on an estate the shop sold to
    /// a buyer (its first-login invite taken in, [`crate::auth::FIRST_ADMIN_MARKER_REL`]), the
    /// buyer's subject once they registered, the estate's owners (`@holger-owners`) before;
    /// elsewhere nobody's yet (empty: the estate's admins alone).
    pub fn buyer_of(data_root: &Path) -> String {
        let Ok(bytes) = std::fs::read(data_root.join(crate::auth::FIRST_ADMIN_MARKER_REL)) else {
            return String::new();
        };
        let used_by = serde_json::from_slice::<serde_json::Value>(&bytes)
            .ok()
            .and_then(|v| v.get("used_by").and_then(|u| u.as_str()).map(str::to_owned))
            .filter(|u| !u.trim().is_empty());
        used_by.unwrap_or_else(|| format!("@{}", crate::auth::ESTATE_OWNERS))
    }

    /// **Who the read gate admits to a repository owned by `owner`** (lane P1): its owner, and
    /// on an estate the shop sold to a buyer the people the buyer granted (their invitees, in
    /// [`crate::auth::ESTATE_OWNERS`]) — as one `,`-separated matcher list
    /// ([`crate::auth::owner_names`]). The estate's admins are admitted by the gate itself.
    pub fn gate_owner(data_root: &Path, owner: &str) -> String {
        let group = format!("@{}", crate::auth::ESTATE_OWNERS);
        let sold_estate = data_root.join(crate::auth::FIRST_ADMIN_MARKER_REL).exists();
        let owner = owner.trim();
        match (owner.is_empty(), sold_estate && owner != group) {
            (_, false) => owner.to_owned(),
            (true, true) => group,
            (false, true) => format!("{owner},{group}"),
        }
    }

    /// **The buyer registered** (lane P1): every repository waiting for the estate's owners
    /// (`@holger-owners`) is the buyer's own from now on, in the record the twin carries.
    /// Answers the names bound.
    pub fn bind_buyer(data_root: &Path, subject: &str) -> Result<Vec<String>, String> {
        let group = format!("@{}", crate::auth::ESTATE_OWNERS);
        update(data_root, |sold| {
            let mut bound = Vec::new();
            for (name, s) in sold.iter_mut() {
                if s.owner == group {
                    s.owner = subject.to_owned();
                    bound.push(name.clone());
                }
            }
            Ok(bound)
        })
    }

    /// One writer at a time (the pull channel's `add_principal` and an admin's
    /// `CreateRepository` both write the record).
    static WRITE: std::sync::Mutex<()> = std::sync::Mutex::new(());

    /// **Change the record under one lock**: read it, let `f` change it (an `Err` writes
    /// nothing), write it whole. Answers what `f` answered.
    pub fn update<T>(
        data_root: &Path,
        f: impl FnOnce(&mut BTreeMap<String, SoldRepository>) -> Result<T, String>,
    ) -> Result<T, String> {
        let _one = crate::lock::LockOrRecover::lock_or_recover(&WRITE);
        let mut sold = read(data_root);
        let out = f(&mut sold)?;
        let bytes = serde_json::to_vec_pretty(&sold).map_err(|e| e.to_string())?;
        let path = data_root.join(SOLD_REPOS_REL);
        std::fs::create_dir_all(path.parent().unwrap_or(data_root))
            .and_then(|_| std::fs::write(&path, bytes))
            .map_err(|e| format!("{}: {e}", path.display()))?;
        Ok(out)
    }

    #[cfg(test)]
    mod p1_tests {
        use super::*;

        /// ★ Lane P1: before the buyer registers, a paid order's repository waits for the
        /// estate's owners; their registration binds it to their subject in the record; the gate
        /// admits the owner and, on a sold estate, the people the buyer granted.
        #[test]
        fn the_buyer_is_bound_as_owner_when_they_register() {
            let d = tempfile::tempdir().unwrap();
            assert_eq!(
                buyer_of(d.path()),
                "",
                "an estate the shop sold nobody: nobody's"
            );
            assert_eq!(
                gate_owner(d.path(), "passkey:p4"),
                "passkey:p4",
                "an admin's own, elsewhere: the owner alone"
            );
            let marker = d.path().join(crate::auth::FIRST_ADMIN_MARKER_REL);
            std::fs::create_dir_all(marker.parent().unwrap()).unwrap();
            std::fs::write(&marker, r#"{"sha256":"x","at":1,"seeded":true}"#).unwrap();
            assert_eq!(buyer_of(d.path()), "@holger-owners");
            update(d.path(), |s| {
                s.insert(
                    "acme-artifacts".into(),
                    SoldRepository {
                        template: "crates".into(),
                        signup: "s-1".into(),
                        owner: buyer_of(d.path()),
                        private: true,
                        ..Default::default()
                    },
                );
                s.insert(
                    "p4-own".into(),
                    SoldRepository {
                        template: "crates".into(),
                        signup: String::new(),
                        owner: "passkey:admin".into(),
                        private: true,
                        ..Default::default()
                    },
                );
                Ok(())
            })
            .unwrap();
            assert_eq!(
                bind_buyer(d.path(), "passkey:buyer").unwrap(),
                vec!["acme-artifacts".to_string()]
            );
            let r = read(d.path());
            assert_eq!(
                (
                    r["acme-artifacts"].owner.as_str(),
                    r["p4-own"].owner.as_str()
                ),
                ("passkey:buyer", "passkey:admin")
            );
            std::fs::write(
                &marker,
                r#"{"sha256":"x","at":1,"seeded":true,"used_by":"passkey:buyer"}"#,
            )
            .unwrap();
            assert_eq!(
                buyer_of(d.path()),
                "passkey:buyer",
                "a later order is the buyer's at once"
            );
            assert_eq!(
                gate_owner(d.path(), "passkey:buyer"),
                "passkey:buyer,@holger-owners",
                "the buyer and the people they granted"
            );
            assert_eq!(gate_owner(d.path(), ""), "@holger-owners");
        }
    }
}
pub use crate::storage::StorageEndpoint;

/// Attestations — signed statements bound to an artifact DIGEST. The evidence
/// plane a gated promotion reads: a gate runs outside holger, signs a DSSE
/// envelope over an in-toto Statement naming specific bytes, and holger verifies
/// and files it. Sits beside `sbom` (which stores documents by coordinate) and
/// `security` (which turns them into verdicts).
#[cfg(feature = "admission")]
pub mod admission;
/// Without `admission` every request is admitted; the doors keep their ticket seam.
#[cfg(not(feature = "admission"))]
#[path = "admission_off.rs"]
pub mod admission;
#[cfg(feature = "admission")]
pub mod admission_grpc;
#[cfg(feature = "intake")]
pub mod attest;
pub mod audit;
pub mod auth;
/// **How much storage there is** — `AdminService.Capacity`: one `statvfs(3)`
/// over the data root plus the archive bytes of every repository that owns
/// storage, and the ONE mapping from a backend to its proto `RepositoryInfo`.
pub mod capacity;
/// The estate's clock on a box: what every expiry check reads, following the
/// front's signed time (edda `estate-clock`).
pub mod clock;
pub mod exposed;
pub mod group;
pub mod grpc;
/// The package-handler catalog behind `ron_handler` — which handler READS a
/// repository's container, as distinct from `ron_repo_type`, which names the
/// container itself.
pub mod handlers;
pub mod keyfile;
pub mod listen;
#[cfg(feature = "pkcs11")]
pub mod pkcs11_signer;
pub mod refusals;

/// **The job queue** (`jobs` feature) — promote, replicate, upload and gate as
/// durable jobs on the shared `jera` ledger, dequeued by a gatling worker pool,
/// so an RPC returns a `JobId` instead of holding the caller for the whole run.
/// Stinky 01 §1.3 measured what it replaces. Off ⇒ every operation runs inline
/// exactly as before and no queue exists.
#[cfg(feature = "jobs")]
pub mod jobs;
#[cfg(all(feature = "jobs", feature = "intake"))]
pub mod pull_intake;
/// Without `jobs` there is no pull-through, but the image layout, the registry client and
/// the provenance material are shared with the scanned pushes (lane HOL-OCI-EVIDENCE,
/// [`promote::oci_push`]).
#[cfg(all(feature = "intake", not(feature = "jobs")))]
pub mod pull_intake {
    pub mod http;
    pub mod oci;
    pub mod provenance;
}
/// **Holger's verbs as the ONE monetize's pulled orders reach them** (metered only).
#[cfg(feature = "metered")]
pub mod fleet_box;
/// Late keys: values fed after the start, applied live (A7 row 8).
pub mod late;
/// **A repository made at run time serves at once** (lane M4b) — see the module.
pub mod live_routes;
/// **The three host services a meter guest is lent** — the usage source, the
/// audit sink and the clock. `holger_plugin_host::NoServices` refuses all three
/// by name; this is what answers them. Gated with the guest kind itself: a build
/// without `meter-guests` has nothing to lend them to.
#[cfg(feature = "meter-guests")]
pub mod meter_services;
/// **The paid state** — the signed entitlement a repository may be capped by.
/// Compiled ONLY by the metered build; the open build does not contain it.
#[cfg(feature = "metered")]
pub mod metered;
/// **Usage** — what a repository is consuming, in holger's own units. In EVERY
/// build: counting is not money, and a box that cannot say how much it holds is
/// worse for everyone.
pub mod metering;
/// **The estate keys the ONE box fed this box again, kept** (MAIL-RELAY + NIGHT-PUBREPO, metered).
#[cfg(feature = "metered")]
pub mod refeed;
/// **A sold Holger's resend sign-in link**, minted and mailed here (A16, metered + passkeys).
#[cfg(all(feature = "metered", feature = "auth-passkey"))]
pub mod sign_in_again;
/// The pull channel's crate, for the CLI's enrolment (metered only).
#[cfg(feature = "metered")]
pub use holger_fleet;
/// Offline vulnerability intelligence (S7): the `intel:` block and the seam the
/// OSV scan reads the local advisory database through. See `intel/`.
pub mod intel;
pub mod metrics;
/// The mirror CHANGE MAP: what an on-prem mirror already has from each upstream
/// repository, and the diff that turns a fresh upstream listing into "fetch only
/// these". Self-contained — no gRPC, no CLI; a caller wires it.
pub mod mirror;
/// The mirror's DECLARATION (`mirror:`) and its CYCLE — the caller
/// [`mirror`] was written for and did not have. Config, transport, loop; no
/// second engine.
pub mod mirror_cycle;
/// The intel crate itself, for the CLI's `scan` verb under `supply-chain`.
#[cfg(feature = "import-door")]
pub use holger_intel;
/// The airgap export as one door: `holger export` and `BundleService.Export` (lane B3).
#[cfg(feature = "intake")]
pub mod export_door;
/// The export's decision profile: what crosses to the airgap side (lane HOL-NCSC).
#[cfg(feature = "intake")]
pub mod export_profile;
/// The box's import doors (lane W4): intel and update / airgap bundles over
/// gRPC, HTTP and the `intel.inbox` / `bundle.inbox` drop directories, one body
/// of code with the CLI verbs. See `import_door.rs`.
#[cfg(feature = "bundle-import")]
pub mod import_door;
pub mod object;
#[cfg(feature = "intake")]
pub mod promote;
// Archive-less deb/rpm/helm/maven3/go repositories as views of the intake chain's
// AVAILABLE set (lane HOL-MVP-FORMATS).
#[cfg(feature = "intake")]
pub mod chain_view;
// The passkey ceremony door. Gated on `auth-passkey`, which only the `full` tier
// pins: the lean tiers are a headless registry core reached by CI runners and
// agents, and a WebAuthn ceremony needs a browser. Gating it here is also what
// keeps the p256/ciborium closure out of `mini` and `embedded` (ship-small).
/// `boundary: OneWay | Fetch`, `domain:` and `trust:` (lane HOL-CDS): one-way by default.
pub mod boundary;
/// Content digests on the engine: blake3 forked over gatling for the digests
/// holger chooses itself (W12).
pub mod declaration;
pub mod digest;
/// `network: Offline`: the boot refusal and the outbound-dial guard.
pub mod egress;
/// The upstream's push of each new signed bundle into the diode's folder (`export:`).
pub mod export_push;
/// A follower's last word and the one rule that judges it (twins + paired downstreams).
pub mod follow_point;
/// **Holger as an identity host** (IDENTITY-HOST): the product part of edda's identity core.
#[cfg(feature = "auth-passkey")]
pub mod identity_host;
/// **MIGRATION** — the ONE-TIME move of an incumbent Nexus / Artifactory
/// registry into this holger, behind `MigrationService` so a UI can drive it.
/// The classifier and the byte mover are shared verbatim with the agent CLI
/// (`traits::migrate` + `holger_agent_lib::migrate`); what lives here is the
/// sink that writes into this server own repositories and the job ledger that
/// lets `Start` return while the work runs for hours.
///
/// ONLINE ONLY. Migrating from Nexus is outbound by definition, so the
/// zero-egress airgap tiers do not carry the code at all.
#[cfg(feature = "migration")]
pub mod migration;
/// **Holger's data, off-site** (lane M5): the main's estate and artifacts, sealed for the
/// operator, shipped to an off-site store on a jera job; restorable into a fresh box.
pub mod offsite;
#[cfg(feature = "auth-passkey")]
pub mod passkey;
pub mod peers;
#[cfg(feature = "intake")]
pub mod properties;
pub mod proxy;
#[cfg(feature = "admission")]
pub mod pull_through;
/// Without `admission` the proxies walk inline; the seam stays.
#[cfg(not(feature = "admission"))]
#[path = "pull_through_off.rs"]
pub mod pull_through;
/// Emptying a repository on the main and applying it on the twin (`purge.rs`).
pub mod purge;
pub mod raw;
/// `/readyz` made real (lane B4): storage, the data root, the scan sandbox.
pub mod ready;
pub mod replication;
/// The monotone time floor and the signed offline time token (lane HOL-NCSC).
#[cfg(feature = "bundle-import")]
pub mod time_floor;
// The estate over the wire (twin / recharge): manifest + fetch, pure half.
pub mod estate_wire;
// The twin follows its primary over the network (artifacts via the mirror, estate via the wire).
/// The repositories a person has here (`holger.account.v1`, lane P4).
pub mod account;
/// The data set grows WITHOUT a restart — holger's door over edda `appliance-grow`
/// (gunnar's phase machine): the write hold, the held backends, the releases,
/// the twin roster, `holger.grow.v1.GrowService`.
pub mod grow;
/// Repository tokens for package clients (`holger.token.v1`, lane P3).
#[cfg(feature = "auth-passkey")]
pub mod repo_token;
/// Where more room for a repository is bought (`holger.room.v1`).
pub mod room;
/// This box's generic settings (`/-/settings/<name>`, owner 2026-10-26): bytes under names the
/// clients choose; holger knows none of them.
pub mod settings;
pub mod twin_follow;
// The twin over mutual TLS 1.3: the replication issuer, door, certify and dial (nordisk-twin,
// `estate`); its names, config block and pure rules in every build.
/// The artifact flow, drawn from the RON with the estate and the counters on it
/// (`AdminService.FlowSvg`, `GET /-/flow.svg`, `GET /-/flow.json`; lane HOL-SVG-RON).
pub mod flow;
/// What is open on this Holger: the `access` block, public repositories, the read gate's
/// posture at boot and its audit (HCON-5).
pub mod posture;
/// The Security page, as data: `AdminService.SecuritySummary` (lane HOL-SEC).
pub mod security_view;
/// The estate board, as data: `AdminService.Topology` and `GET /-/estate.json` (lane E3; H2).
pub mod topology;
/// An estate box is WireGuard-only: its estate doors bind, and its estate links dial, on
/// wg0's overlay only (lane E1).
pub mod tunnel_addr;
pub mod twin_tls;
/// The board's upstreams: proxy members and mirror sources, with their evidence (lane A13).
pub mod upstreams;

/// **The plugin trust root's retired environment fallback** (lane E4): REFUSED
/// since stinky SB #22 — the root is `ron_plugin_trust_roots` only.
pub const PLUGIN_TRUST_ROOT_ENV: &str = "HOLGER_PLUGIN_TRUST_ROOT";

/// **Every security policy the environment once carried**, and where it lives
/// now (stinky SB #22, refused by [`Holger::refuse_env_policy`]).
/// The promotion gate keys keep the frozen `holger-promote-2` they were already
/// refused with; the rest are `holger-server-13`.
pub const RETIRED_ENV_POLICY: [(&str, &str, &holger_errcode::ErrCode); 5] = [
    (
        "HOLGER_PROMOTE_ENFORCE",
        "the `promotion:` block: a stage with `gated: true` / `gates: [\"osv\"]`",
        &holger_errcode::PROMOTE_POLICY_BLOCKED,
    ),
    (
        "HOLGER_PROMOTE_ADVISORY_DB",
        "`promotion:` `advisory_db`",
        &holger_errcode::PROMOTE_POLICY_BLOCKED,
    ),
    (
        "HOLGER_PROMOTE_DENY_LICENSES",
        "`promotion:` `deny_licenses`",
        &holger_errcode::PROMOTE_POLICY_BLOCKED,
    ),
    (
        "HOLGER_PROMOTE_REQUIRE_PROPERTY",
        "`promotion:` `require_property: Some(\"key=value\")`",
        &holger_errcode::SERVER_ENV_POLICY_REFUSED,
    ),
    (
        PLUGIN_TRUST_ROOT_ENV,
        "`ron_plugin_trust_roots`",
        &holger_errcode::SERVER_ENV_POLICY_REFUSED,
    ),
];

/// The retired serve-time quarantine property (W6). Setting it refuses the boot
/// with `holger-intake-14`: the intake chain's QUARANTINED state is the one hold.
pub const RETIRED_QUARANTINE_ENV: &str = "HOLGER_QUARANTINE_PROPERTY";
/// The place of the estate's own name in a shared medium's declaration
/// ([`Holger::resolve_estate`]; holger-recipe `estate_ron::ESTATE_PLACEHOLDER`).
pub const ESTATE_PLACEHOLDER: &str = "{estate}";

/// The admin view (`AdminService.AdminSnapshot`): every disk and volume, the twin, the
/// growth, the clock, plugins/monetize, audit and the installer's log door — an admin's read.
pub mod admin_view;
/// How long this box's paired certificates have left: the grades its health words carry
/// (lane E2, the renewal's alarm).
#[cfg(feature = "estate")]
pub mod cert_health;
// The recharge lane: a dead main filled from its twin, the four refusals first.
/// The CVE lookup as an index: `(ecosystem, package)` → advisories, built once (T10).
#[cfg(feature = "scanners")]
pub mod advisory_index;
/// **The evidence public-key door** (lane HOL-OCI-EVIDENCE): `/-/evidence-key`, the public
/// half of `attestation.signer_cert`, for `cosign verify-attestation --key <url>`.
#[cfg(feature = "intake")]
pub mod evidence_key;
/// Untrusted work as jobs behind the Heimdall job API (lane HOL-SANDBOX-TRAIT): the
/// server's runner, its pinned key, and the programs `holger-scan-worker` runs.
#[cfg(feature = "intake")]
pub mod job_runner;
/// The one mutex-poison rule: recover, log once, clear the poison.
pub mod lock;
/// The allocator's share of the server's memory: glibc's arenas capped (lane HOL-RSS).
pub mod memory;
#[cfg(feature = "estate")]
pub mod recharge_lane;
/// **OCI 1.1 referrers** of Holger's own evidence (lane HOL-REFERRERS): the scan
/// evidence and SBOM statements about an image, in the shapes cosign verifies.
#[cfg(feature = "intake")]
pub mod referrers;
/// **The repository-metadata signing key** (lane HOL-SIGNED-APT-DNF): the operator-made
/// OpenPGP key the deb and rpm chain views sign `InRelease`, `Release.gpg` and
/// `repomd.xml.asc` with, and its public half at `/-/repo-key.asc`.
pub mod repo_sign;
pub mod repository;
pub mod retention;
/// The scan sandbox in the server (lane SANDBOX-SCAN): every scan that unpacks,
/// parses or runs an engine over an artifact runs in the scan worker's container.
#[cfg(feature = "scanners")]
pub mod sandbox;
/// **Signed repository indexes** (lane HOL-SIGN-2): the signer of a deb or rpm repository's
/// `InRelease`, `Release.gpg` and `repomd.xml.asc`, for the chain views and, wrapped around
/// the backend, for an archive-backed repository.
pub mod signed_index;
/// **The sandbox's medium alone** (lane HOL-CDS-5, `sandbox-media`): a one-way downstream
/// appliance scans nothing, yet verifies every bundle in a job (`job_runner`), so it lays crun
/// and the worker's rootfs from its medium at boot, and starts clamd as the main does.
#[cfg(all(feature = "sandbox-media", not(feature = "scanners")))]
pub mod sandbox {
    pub mod clamd;
    pub mod media;
}
/// ★ **The front's report, kept by main** (lane HOL-FLOW-FRONT): its doors, the names it serves
/// and where each goes, with a proof of its own; the flow unfolds the front to it.
pub mod front_report;
#[cfg(feature = "intake")]
pub mod sbom;
/// The scanner framework: one `Scanner` trait, typed evidence, the evidence store.
pub mod scanner;
pub mod search;
#[cfg(feature = "scanners")]
pub mod security;
/// **The estate's Sigstore trust root** (lane HOL-OCI-EVIDENCE): shipped signed in the medium,
/// seeded at boot, refreshed only by a signed bundle import.
#[cfg(feature = "intake")]
pub mod sigstore_trust;
/// Tier 1's RON: `sources:`, `steps:` and `rules:` (Holger G0).
pub mod sources;
pub mod storage;
#[cfg(feature = "sync")]
pub mod sync;
/// **The twin's role** (plan §3 W9): the declared `role:` key, the refusal a
/// follower answers a write with, the loopback rewrite that keeps a twin off the
/// public interface, and the `twin ≥ primary` store invariant.
pub mod twin;
/// Upstream-registry federation (the `full` tier's harbour clients). Gated behind
/// `federation`: the `mini`/`embedded` tiers do NOT compile it and do NOT link
/// Skidbladnir or any registry-client dependency.
#[cfg(feature = "federation")]
pub mod upstream;
pub mod visitors;
// The `rust-remote` pull-through cache upstream — a SYNC `ureq` HTTP client, so an
// outbound-egress path. Gated behind `online` (like reqwest) so the zero-egress
// airgap tiers (`embedded`/`mini`) link NO HTTP client at all.
#[cfg(feature = "online")]
pub mod upstream_rust;

pub use crate::object::LocalHolger;
#[cfg(feature = "fetch")]
pub use crate::object::RemoteHolger;

/// The property store a build without `supply-chain` does not have: never
/// constructed, only named so the retention planner has one type in both builds.
#[cfg(not(feature = "intake"))]
struct NoPropertyStore;
#[cfg(not(feature = "intake"))]
impl NoPropertyStore {
    fn matches(&self, _repo: &str, _id: &traits::ArtifactId, _key: &str, _value: &str) -> bool {
        false
    }
}
pub use traits::HolgerObject;
// Re-export the core artifact types + the backend trait so in-process consumers
// (e.g. nornir's embedded-holger viz pane) can enumerate a repository's
// backend directly — `holger.repositories[i].backend_repository` is an
// `Arc<dyn RepositoryBackendTrait>`, so the trait must be reachable to call
// `list`/`fetch` on it without depending on `holger-traits` separately.
pub use traits::{ArtifactEntry, ArtifactId};

// ========================= VERSION IDENTITY =========================

/// Short git commit hash of the build tree, stamped by `build.rs` (pure-Rust,
/// no shelling — reads `.git/HEAD` directly). `"unknown"` when built without git
/// metadata (e.g. a source tarball).
pub const GIT_HASH: &str = env!("HOLGER_GIT_HASH");

/// The single-source holger version identity: `holger v<CARGO_PKG_VERSION>
/// (<git-hash>)`. Every version surface — the server bins' `--version`/`-V`, the
/// gRPC + in-process `health.version`, and any About/identity readout — prints
/// THIS exact string so they can never drift. Built at compile time (`concat!`).
pub const VERSION_LINE: &str = concat!(
    "holger v",
    env!("CARGO_PKG_VERSION"),
    " (",
    env!("HOLGER_GIT_HASH"),
    ")"
);

/// The `env_logger` filter every holger binary installs when `RUST_LOG` is unset.
///
/// `env_logger`'s own default is `error`, and that is a security bug in a server
/// whose entire boot-time posture report is `log::warn!`: `warn_unpinned_providers`,
/// `warn_unenforced_mtls_ca`, `warn_unauthenticatable_read_gate` and the
/// `auth_allow_unbound_oidc` acknowledgement all say "a control you think is on is
/// off", and under `error` an operator sees none of them — the binary boots clean
/// and silent with the control disabled. These lines are few and fire once at boot,
/// so there is no volume argument for hiding them.
///
/// `RUST_LOG` still wins wherever it is set, so this only moves the floor for a
/// deployment that never configured logging — which is exactly the deployment least
/// likely to notice the missing warning any other way.
///
/// # Why `mirror_cycle` is named at `info`
///
/// MEASURED on `oden`, 2026-09-20. The on-prem mirror rebooted, its first connect
/// failed because the network was one second behind the service, and the journal
/// then said **nothing for eighty minutes** while `systemctl` reported `active`
/// and `NRestarts: 0`. It looked exactly like a mirror that had died. It had not:
/// it completed a cycle every 300 s the whole time (the change map's mtime lands
/// on boot + n×300 s to the second) — but a completed cycle is `log::info!`, the
/// unit pins no `RUST_LOG`, and under a bare `warn` floor SUCCESS IS INVISIBLE.
/// Silence therefore meant both "mirroring fine" and "not mirroring at all", and
/// an operator could not tell which without reading Arrow files off the disk.
///
/// One directive fixes that, and the volume argument does not apply: a mirror
/// emits one line per declared pair per `ron_every_secs` — two lines every five
/// minutes on the box this was measured on. `RUST_LOG` still wins everywhere.
///
/// # Why the clock is named at `info`
///
/// MEASURED on the mock gate, 2026-10-22 (holger 3bb889e5): a box's fed clock sync
/// that fails logs at `warn` (its first sync runs before the tunnel to the front is
/// up, so it always fails once), and the sync that follows the tunnel and succeeds
/// logs at `info` — under the bare `warn` floor the failure was the box's LAST clock
/// line forever, the front's log said it signed that box's time a second later, and
/// the gate read a healthy clock as a failed one. A sync is one line per trigger
/// (boot, tunnel up, a skew refusal, every 6 h), so naming it costs nothing.
pub const DEFAULT_LOG_FILTER: &str = "warn,holger_server_lib::mirror_cycle=info,holger::clock=info";

/// The log target of a box's clock syncs (named at `info` in [`DEFAULT_LOG_FILTER`]).
pub const CLOCK_LOG_TARGET: &str = "holger::clock";

#[cfg(test)]
mod default_log_filter_tests {
    use super::DEFAULT_LOG_FILTER;

    /// RED if somebody trims the filter back to a bare `warn`: that is the exact
    /// state in which a healthy mirror and a dead one both print nothing. (The
    /// CLI's `boot_security_warnings_are_visible_with_no_rust_log_set` proves the
    /// floor still admits `warn`; this proves the mirror is still named.)
    #[test]
    fn the_default_floor_admits_warn_and_names_the_mirror_at_info() {
        assert!(
            DEFAULT_LOG_FILTER.starts_with("warn"),
            "the global floor must still be warn — every boot-time SECURITY line is one: \
             {DEFAULT_LOG_FILTER}"
        );
        assert!(
            DEFAULT_LOG_FILTER.contains("holger_server_lib::mirror_cycle=info"),
            "a completed mirror cycle logs at info; without this directive a deployment that \
             never set RUST_LOG cannot tell a mirroring mirror from a stopped one: \
             {DEFAULT_LOG_FILTER}"
        );
        assert!(
            DEFAULT_LOG_FILTER.contains(&format!("{}=info", super::CLOCK_LOG_TARGET)),
            "a clock sync that succeeds logs at info; without this directive a box whose first \
             sync failed before its tunnel was up says it failed for ever: {DEFAULT_LOG_FILTER}"
        );
    }
}

/// Lowercase-hex SHA-256 of `data` — the content id the retention executor
/// verifies before a backend unlinks (must match the backend's own recompute).
/// SHA-256 (not blake3) so it agrees with the loose-file backends' native
/// checksum path.
fn sha256_hex(data: &[u8]) -> String {
    // LAW #5 dedup: the shared content-hash primitive lives once in edda's
    // `nornir-hash` leaf; every backend consumes it so all recomputes agree.
    nornir_hash::sha256_hex(data)
}

/// `namespace/name@version` (or `name@version` when unscoped) — the artifact
/// label written into the GC audit record.
fn retention_artifact_label(id: &ArtifactId) -> String {
    match &id.namespace {
        Some(ns) => format!("{ns}/{}@{}", id.name, id.version),
        None => format!("{}@{}", id.name, id.version),
    }
}

// ========================= WIRING ENGINE =========================

/// Build the `ProxyBackend` that serves `repo`, or `None` when `repo` declares no
/// upstreams (and is therefore served by its own backend directly).
///
/// Extracted from `wire_holger` so it can be tested. What needs testing is not the
/// proxy — that has its own tests — but the ATTACHMENT: each upstream is paired
/// here with the origin-digest seam that lets the proxy ask what the registry
/// itself published for a coordinate. Both of the proxy's digest gates degrade
/// silently to the old, ungated behaviour when that seam is absent, which is the
/// right thing for an upstream that cannot be asked and indistinguishable from
/// correct when the wiring breaks. So the wiring gets a test of its own.
pub(crate) fn build_proxy_for(repo: &Repository) -> Option<crate::proxy::ProxyBackend> {
    let backend_arc = repo.backend_repository.as_ref()?;
    if repo.wired_upstreams.is_empty() {
        return None;
    }
    // An upstream with no seam is `plain` and is proxied exactly as before —
    // silently turning every existing pull-through repo into a pass-through would
    // be a product change nobody asked for.
    let upstreams: Vec<crate::proxy::ProxyUpstream> = repo
        .wired_upstreams
        .iter()
        .filter_map(|&ptr| {
            if ptr.is_null() {
                return None;
            }
            let upstream: &Repository = unsafe { &*ptr };
            let backend = upstream.backend_repository.clone()?;
            // The estate board's upstream row (lane A13): declared here, its
            // outcomes recorded by the walk under the same backend name.
            let address = match (&upstream.ron_base_url, upstream.ron_repo_type.as_str()) {
                (Some(u), _) => u.clone(),
                (None, _) => String::new(),
            };
            crate::upstreams::register_proxy(
                backend.name(),
                &upstream.ron_repo_type,
                &address,
                &repo.ron_name,
                crate::clock::now_unix_ms(),
            );
            Some(match &upstream.upstream_attestation {
                Some(attestation) => {
                    crate::proxy::ProxyUpstream::with_attestation(backend, attestation.clone())
                }
                None => crate::proxy::ProxyUpstream::plain(backend),
            })
        })
        .collect();
    let mut proxy = crate::proxy::ProxyBackend::with_upstreams(backend_arc.clone(), upstreams);
    // Optional negative cache: when the repo declares `ron_negative_cache_secs`, a
    // coordinate/path that misses primary + every upstream is remembered as a miss
    // for that TTL (0 / unset ⇒ disabled, unchanged pull-through).
    if let Some(secs) = repo.ron_negative_cache_secs {
        proxy = proxy.with_negative_cache_secs(secs);
    }
    Some(proxy)
}

/// **A paid signup's repository, shaped like `template`** (lane M4): a copy of the template's
/// declaration (its config only, never its wired backend) under `name`, with its own bytes
/// beside the template's. The boot (`Holger::declare_sold_repositories`) and the run-time
/// path (`live_routes::LiveRoutes::serve`) make it the same way.
pub(crate) fn sold_copy(
    template: &Repository,
    name: &str,
) -> std::result::Result<Repository, String> {
    let mut r = serde_json::to_value(template)
        .and_then(serde_json::from_value::<Repository>)
        .map_err(|e| e.to_string())?;
    let beside = |p: &Option<String>, file: &str| {
        p.as_deref().map(|p| {
            std::path::Path::new(p)
                .parent()
                .unwrap_or(std::path::Path::new("."))
                .join(file)
                .display()
                .to_string()
        })
    };
    r.ron_data_dir = beside(&template.ron_data_dir, name);
    r.ron_writable_archive = beside(&template.ron_writable_archive, &format!("{name}.znippy"));
    r.ron_archive_path = None;
    r.ron_name = name.to_string();
    Ok(r)
}

/// **One repository's backend in the growth's hold** (`grow::Held`), able to build itself
/// again from the repository's own config. The boot (`wire_holger`) and the run-time path
/// (`live_routes::LiveRoutes::serve`) wrap the same way.
pub(crate) fn held_backend(
    repo: &Repository,
    inner: Arc<dyn RepositoryBackendTrait>,
    hold: &Arc<crate::grow::Hold>,
    catalog: &Arc<handlers::HandlerCatalog>,
) -> Arc<dyn RepositoryBackendTrait> {
    let rebuild = ron::to_string(repo).ok().map(|spec| {
        let catalog = Arc::clone(catalog);
        Box::new(move || {
            let mut again: Repository = ron::from_str(&spec)?;
            again.backend_from_config_with(&catalog)?;
            again.backend_repository.take().ok_or_else(|| {
                anyhow::anyhow!(
                    "repository `{}` built no backend from its config",
                    again.ron_name
                )
            })
        }) as crate::grow::Rebuild
    });
    let held = crate::grow::Held::wrap(inner, repo.ron_name.clone(), Arc::clone(hold), rebuild);
    hold.register(Arc::new(crate::grow::HeldRelease(Arc::clone(&held))));
    held
}

/// A config's `role:` key as written (`role: Primary`, `role: Twin(..)`), so the
/// refusal can name it (stinky SB #36) — RON spells no `Some(..)` around it.
fn declared_role<'de, D: serde::Deserializer<'de>>(
    d: D,
) -> std::result::Result<Option<twin::NodeRole>, D::Error> {
    <twin::NodeRole as serde::Deserialize>::deserialize(d).map(Some)
}

pub fn wire_holger(holger: &mut Holger) -> Result<()> {
    // ── THE TWIN'S DOORS ─────────────────────────────────────────────────────
    //
    // FIRST, before anything is indexed or wired, because a door that got as far
    // as being wired with a public address is a door somebody will later bind.
    // A twin keeps every port its config declares and moves every PUBLIC door
    // (`ron_http_url`) onto loopback (`twin::NodeRole::effective_bind`); its
    // gRPC CONTROL door stays on the configured (utility) address, as gunnar's
    // twin keeps :50051 — writes refused there by name, certify and recharge
    // answered (`twin::NodeRole::effective_control_bind`, owner 2026-09-27).
    //
    // Done HERE and not at each bind site on purpose: a door added to the config
    // — or a second listener added to this struct — is loopbacked without anyone
    // remembering to do it, and the RON FILE stays byte-identical to the
    // primary's, which is what `[estate-twin-drift]` requires.
    // THE ROLE COMES ONLY FROM THE PAIRING (stinky SB #36). The installer's door
    // elects it (the weakest box is the twin) and the box records it once
    // (`twin::elect`, `holger-role.ron`). An environment variable or a config
    // `role:` key that names one is REFUSED by name (`holger-twin-21`) — read, it
    // is a second declaration of what the box is; ignored, an operator believes a
    // setting holds. `[estate-twin-drift]` keeps the twin's RON byte-identical to
    // the primary's, so the config never could carry it.
    crate::twin::refuse_declared_role_in_env(holger.ron_role.as_ref())
        .map_err(|e| anyhow::anyhow!("{e}"))?;
    let data_root = holger.data_root();
    let effective = crate::twin::effective_role(&data_root).map_err(|e| anyhow::anyhow!("{e}"))?;
    if effective.is_twin() {
        log::warn!(
            "this box runs as `twin`: the role the pairing elected, recorded in {}",
            crate::twin::role_record_path(&data_root).display()
        );
    }
    holger.role = effective;

    // THE REPLICATION DOOR's address is read off the CONFIGURED gRPC door, before
    // a twin's doors move onto loopback: the twin keeps this one where the config
    // put it (a recharge dials it), and a primary later elected live keeps it too.
    holger.replication_bind = match &holger.replication_door {
        None => None,
        Some(d) => Some(match &d.ron_bind {
            Some(b) => b.clone(),
            None => {
                let first = holger.exposed_endpoints.first().ok_or_else(|| {
                    anyhow::anyhow!("replication_door: no ron_bind and no exposed endpoint to sit beside; declare replication_door.ron_bind")
                })?;
                twin_tls::default_bind(&first.ron_url)
                    .map_err(|e| anyhow::anyhow!("replication_door: {e}"))?
            }
        }),
    };

    if holger.role.is_twin() {
        for exp in &mut holger.exposed_endpoints {
            exp.ron_url = holger.role.effective_control_bind(&exp.ron_url);
            if let Some(http) = &exp.ron_http_url {
                exp.ron_http_url = Some(holger.role.effective_bind(http));
            }
        }
        log::warn!(
            "this server is a TWIN of `{}`: its public doors are bound on {}, its gRPC control \
             door where the config says, and every write is refused by name ({})",
            holger.role.primary().unwrap_or("?"),
            twin::TWIN_LOOPBACK_HOST,
            holger_errcode::TWIN_REFUSES_WRITES.code
        );
    }

    // ── AN ESTATE BOX IS WIREGUARD-ONLY (lane E1, Gunnar's pattern) ─────────
    // Every estate door — the gRPC control plane, the HTTP door the front's console
    // and route reach, the replication door the twin follows — binds on wg0's
    // overlay address: `0.0.0.0` moves there, loopback (a twin's public doors) stays,
    // and a door declared on any other address REFUSES TO START. The door names only
    // tunnel hosts, so the twin's credential never points off the tunnel.
    if let Some(estate) = crate::tunnel_addr::estate() {
        let refuse = |e: String| anyhow::anyhow!("{e}. REFUSING TO START.");
        for exp in &mut holger.exposed_endpoints {
            exp.ron_url = estate.bind("the gRPC door", &exp.ron_url).map_err(refuse)?;
            if let Some(http) = &exp.ron_http_url {
                exp.ron_http_url = Some(estate.bind("the HTTP door", http).map_err(refuse)?);
            }
        }
        if let Some(b) = &holger.replication_bind {
            holger.replication_bind = Some(estate.bind("the replication door", b).map_err(refuse)?);
        }
        if let Some(d) = holger.replication_door.as_mut() {
            d.ron_advertise = crate::tunnel_addr::tunnel_hosts(
                Some(&estate),
                std::mem::take(&mut d.ron_advertise),
            );
        }
        log::warn!("this box is an ESTATE box: WireGuard-only — its estate doors answer on the tunnel ({}) only", estate.overlay.map(|o| o.to_string()).unwrap_or_default());
    }

    let mut repo_map = HashMap::new();
    // Endpoints are NAMES a repository's `ron_in`/`ron_out` must resolve to; no
    // pointer to them is kept (nothing ever read one — stinky 25).
    let mut exposed_names = std::collections::HashSet::new();
    let mut storage_names = std::collections::HashSet::new();

    for repo in &*holger.repositories {
        repo_map.insert(repo.ron_name.clone(), repo as *const Repository);
    }
    for exp in &*holger.exposed_endpoints {
        exposed_names.insert(exp.ron_name.clone());
    }
    for st in &*holger.storage_endpoints {
        storage_names.insert(st.ron_name.clone());
    }

    // Wire repository IO references
    for repo in &mut holger.repositories {
        for name in &repo.ron_upstreams {
            if let Some(ptr) = repo_map.get(name) {
                repo.wired_upstreams.push(*ptr);
            } else {
                return Err(anyhow::anyhow!("Missing upstream repo: {}", name));
            }
        }

        // Members of a virtual `group` repo (resolved in declaration order).
        for name in &repo.ron_members {
            if let Some(ptr) = repo_map.get(name) {
                repo.wired_members.push(*ptr);
            } else {
                return Err(anyhow::anyhow!(
                    "Missing member repo '{}' for group '{}'",
                    name,
                    repo.ron_name
                ));
            }
        }

        if let Some(io) = &repo.ron_in {
            if !storage_names.contains(&io.ron_storage_endpoint) {
                return Err(anyhow::anyhow!(
                    "Missing storage endpoint: {}",
                    io.ron_storage_endpoint
                ));
            }
            if !exposed_names.contains(&io.ron_exposed_endpoint) {
                return Err(anyhow::anyhow!(
                    "Missing exposed endpoint: {}",
                    io.ron_exposed_endpoint
                ));
            }
        }

        if let Some(io) = &repo.ron_out {
            if !storage_names.contains(&io.ron_storage_endpoint) {
                return Err(anyhow::anyhow!(
                    "Missing storage endpoint: {}",
                    io.ron_storage_endpoint
                ));
            }
            if !exposed_names.contains(&io.ron_exposed_endpoint) {
                return Err(anyhow::anyhow!(
                    "Missing exposed endpoint: {}",
                    io.ron_exposed_endpoint
                ));
            }
        }
    }

    // Wire reverse links (endpoint → repos)
    for exp in &mut holger.exposed_endpoints {
        for repo in &holger.repositories {
            if let Some(io) = &repo.ron_out {
                if io.ron_exposed_endpoint == exp.ron_name {
                    exp.wired_out_repositories.push(repo as *const Repository);
                }
            }
        }
    }

    // The effective serving backend for one repo: its raw backend, or — when it
    // declares upstreams — a `ProxyBackend` wrapping it so 404s fall through. A
    // repo with no materialized backend (e.g. a `group`, or one disabled on init)
    // yields `None`. Shared by the direct route and by group-member resolution, so
    // a group over a caching-proxy member sees the SAME pull-through behaviour.
    // ── THE GROWTH'S HOLD ON EVERY REPOSITORY (every build) ──────────────────
    //
    // Each repository's backend is wrapped ONCE, here, FIRST — under the paid
    // limit below and before any route, proxy, mirror, replication pair or job
    // takes a clone — in `grow::Held`: every write that exists counts in and out
    // of the growth's hold (so the drain sees a mirror pull or a twin's follow
    // as surely as an upload), and every read is refused while the set is off.
    // It is also how the archive the repository serves from is CLOSED before the
    // unmount and opened again after: the wrapper is the only holder of the
    // inner backend, and it can build it again from the repository's own config
    // through the same `backend_from_config_with` the boot used.
    let catalog = Arc::new(holger.handler_catalog()?);
    {
        let hold = holger.grow_hold();
        for repo in &mut holger.repositories {
            if repo.ron_repo_type == "group" {
                continue;
            }
            if let Some(inner) = repo.backend_repository.take() {
                repo.backend_repository = Some(held_backend(repo, inner, &hold, &catalog));
            }
        }
    }

    // ── THE PAID LIMIT ON EVERY PROGRAMMATIC WRITE (metered build only) ──────
    //
    // Each repository's backend is wrapped ONCE, here, before any route, proxy,
    // mirror, replication pair or job can take a clone of it — so every `put`
    // that never passes a door is asked the same `metered::Quota` the doors ask.
    // A twin is not wrapped (see `metered::QuotaGuard`); nothing is wrapped
    // when no monetize key is configured.
    #[cfg(feature = "metered")]
    if !holger.role.is_twin() {
        if let Some(quota) = holger.quota()? {
            let root = holger.data_root();
            for repo in &mut holger.repositories {
                if repo.ron_repo_type == "group" {
                    continue;
                }
                if let Some(inner) = repo.backend_repository.take() {
                    repo.backend_repository = Some(crate::metered::QuotaGuard::wrap(
                        inner,
                        repo.ron_name.clone(),
                        Arc::clone(&quota),
                        root.clone(),
                    ));
                }
            }
        }
    }

    fn effective_backend(repo: &Repository) -> Option<Arc<dyn RepositoryBackendTrait>> {
        match build_proxy_for(repo) {
            Some(proxy) => Some(Arc::new(proxy)),
            None => repo.backend_repository.clone(),
        }
    }

    // Build route tables for each exposed endpoint, wrapping repos that have
    // configured upstreams in a ProxyBackend so 404s fall through automatically.
    for exp in &mut holger.exposed_endpoints {
        let mut routes: Vec<(String, Arc<dyn RepositoryBackendTrait>)> = Vec::new();

        for &repo_ptr in &exp.wired_out_repositories {
            if repo_ptr.is_null() {
                continue;
            }
            let repo: &Repository = unsafe { &*repo_ptr };

            // A virtual `group` repo owns no `backend_repository`; it aggregates
            // its members' effective backends behind a single name (first hit wins).
            if repo.ron_repo_type == "group" {
                let members: Vec<Arc<dyn RepositoryBackendTrait>> = repo
                    .wired_members
                    .iter()
                    .filter_map(|&ptr| {
                        if ptr.is_null() {
                            return None;
                        }
                        let member: &Repository = unsafe { &*ptr };
                        effective_backend(member)
                    })
                    .collect();
                routes.push((
                    repo.ron_name.clone(),
                    Arc::new(crate::group::GroupBackend::new(
                        repo.ron_name.clone(),
                        members,
                    )),
                ));
                continue;
            }

            let Some(backend) = effective_backend(repo) else {
                continue;
            };
            routes.push((repo.ron_name.clone(), backend));
        }

        exp.aggregated_routes = if routes.is_empty() {
            None
        } else {
            Some(FastRoutes::new(routes))
        };
    }

    // ── A REPOSITORY MADE AT RUN TIME SERVES AT ONCE (lane M4b) ──────────────
    // What `LiveRoutes::serve` needs to build one more backend the way the loops
    // above did and swap it into its door's table (shared with every clone).
    let live = crate::live_routes::LiveRoutes {
        doors: holger
            .exposed_endpoints
            .iter()
            .filter_map(|ep| {
                ep.aggregated_routes
                    .as_ref()
                    .map(|t| (ep.ron_name.clone(), t.clone()))
            })
            .collect(),
        specs: holger
            .repositories
            .iter()
            .filter(|r| r.ron_repo_type != "group")
            .filter_map(|r| {
                ron::to_string(r)
                    .ok()
                    .map(|spec| (r.ron_name.clone(), spec))
            })
            .collect(),
        catalog: Arc::clone(&catalog),
        hold: holger.grow_hold(),
        read_gate: holger.read_gate(),
        #[cfg(feature = "metered")]
        quota: if holger.role.is_twin() {
            None
        } else {
            holger.quota()?.map(|q| (q, holger.data_root()))
        },
        every: Default::default(),
        made: Default::default(),
    };
    let _ = holger.live_routes_cell.set(Arc::new(live));

    // ── THE METER PLANE ──────────────────────────────────────────────────────
    //
    // Built HERE and not in `instantiate_backends`, because the usage source a
    // meter guest reads through counts through the route table, and the route
    // table does not exist until the loop above has run. A meter that could not
    // be given the routes would be a meter loaded with `NoServices` — which is
    // exactly the inert plane this replaces.
    //
    // A module that fails its grant or its verification is an `Err` and stops
    // the boot: a metering plane that quietly holds fewer meters than the
    // operator configured is a box that stopped counting without saying so.
    #[cfg(feature = "meter-guests")]
    holger.build_meter_plane()?;

    // ── THE MIRROR PLANE ─────────────────────────────────────────────────────
    //
    // Validated HERE and not at parse time, because half of what makes a mirror
    // declaration wrong is a cross-reference: `ron_to` has to name a repository
    // that this very config declares AND that came out of `instantiate_backends`
    // writable. Both are true only after the loop above.
    //
    // A refusal stops the boot. A mirror that could not be validated would be a
    // server that reports a configured upstream on `/-/flow` and takes nothing
    // from it — the same green-looking dead appliance the serving marker exists
    // to catch.
    if let Some(mirror) = &holger.mirror {
        let repos = holger.mirror_repo_posture();
        mirror.validate(&repos)?;
    }

    Ok(())
}

// ========================= ROOT HOLGER =========================

/// Server security posture. `Standard` keeps the existing availability-oriented
/// semantics (open reads, best-effort audit). `Critical` is opt-in and
/// **fail-closed**: startup refuses any configuration that cannot enforce the
/// profile (see [`Holger::validate_profile`]).
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum SecurityProfile {
    #[default]
    Standard,
    Critical,
}

/// Whether the node accepts network mutations at all. `ReadWrite` is the existing
/// behavior; `ReadOnly` rejects every network PUT/POST/PATCH/DELETE and every
/// mutating gRPC call **before the request body is collected**, at the auth choke
/// points, in addition to the existing write-gate.
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum OperationMode {
    #[default]
    ReadWrite,
    ReadOnly,
}

/// The root config node. **Unknown keys are a boot error** — see
/// [`read_ron_config`] for the reasoning; in short, a key holger does not
/// understand is a key holger is not honouring, and the worst instance of that is
/// a security control that reads as ON in the file and is OFF in the process.
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Holger {
    /// Property keys only an ADMIN may set over the wire, on top of the one the
    /// server derives from live state (the promotion engine's required property).
    ///
    /// `None` (the default) ⇒ [`grpc::DEFAULT_RESERVED_PROPERTY_KEYS`], which is
    /// `keep` — the documented retention pin. Declare this ONLY if this deployment
    /// renamed the pin (`retention --protect-property <other>=…`), because the
    /// retention policy is built in another process and the server cannot derive
    /// the real key. `Some([])` deliberately reserves nothing.
    ///
    /// The `retention` verb refuses a `--protect-property` key that is not in this
    /// set: a pin any Writer can set is not a pin.
    #[serde(default)]
    pub reserved_properties: Option<Vec<String>>,
    pub repositories: Vec<Repository>,
    pub exposed_endpoints: Vec<ExposedEndpoint>,
    pub storage_endpoints: Vec<StorageEndpoint>,

    /// Optional auth configuration. Absent or empty methods = open access.
    #[serde(default)]
    pub auth: Option<auth::AuthConfig>,

    /// **What is open on this Holger** (HCON-5): `access: (reads: Gated | Open, front: Open |
    /// Gated)`. Absent: the build decides ([`posture::BUILD_READS`]: an estate build gates
    /// reads, the default and downstream builds read openly). A repository's
    /// `visibility: Public` is read with no sign-in even when reads are gated.
    #[serde(default)]
    pub access: Option<posture::Access>,

    /// Audit-log configuration. Defaults to ON (the hash-chained log) so a config that
    /// says nothing still gets an audit trail; `mode: Off` refuses the start in every
    /// profile (`holger-audit-13`, lane HOL-NCSC).
    #[serde(default)]
    pub audit: AuditSettings,

    /// Security posture. Defaults to `Standard` so every existing config is
    /// unchanged; `Critical` is opt-in and fail-closed.
    #[serde(default)]
    pub security_profile: SecurityProfile,

    /// Read/write posture. Defaults to `ReadWrite`; `ReadOnly` blocks every
    /// network mutation at the choke points before the body is collected.
    #[serde(default)]
    pub operation_mode: OperationMode,

    /// Network posture. Omitted, it is `Auto` (Holger v2.0 B2): the airgap unless the
    /// config names an upstream ([`Holger::resolved_network`]). `Offline` is the secure
    /// side of an airgap: the boot refuses a config that needs the network, and every
    /// outbound dial is refused before a socket is opened ([`egress`]).
    #[serde(default)]
    pub network: egress::NetworkMode,

    /// Directory of `.wasm` package handlers to load at startup, in addition to
    /// the ones linked in. A module registers under the handler name its own
    /// manifest declares, and any repository may then name it with
    /// [`Repository::ron_handler`] — so a new handler is a config string plus a
    /// file here, with no holger rebuild.
    ///
    /// **Omitted ⇒ only the linked handlers**, and the plugin loader never runs.
    /// A directory that is configured but missing, unreadable, or a file is a
    /// boot error rather than an empty catalog: "no plugins" and "I could not
    /// look" would otherwise be the same answer, and the second one surfaces in
    /// production as a 404 over bytes that exist.
    #[serde(default)]
    pub ron_plugin_dir: Option<String>,

    /// **What each module in [`Holger::ron_plugin_dir`] may do.**
    ///
    /// ```ron
    /// ron_plugin_grants: [
    ///     ( module: "allocation.wasm", grants: ["usage.read", "store.list"] ),
    /// ],
    /// ```
    ///
    /// From the config FILE, never from an environment variable: the appliance
    /// has no shell, so an env var is a decision nobody can make and nobody can
    /// read back.
    ///
    /// **A module with no entry gets the EMPTY grant**, so a module dropped into
    /// the directory without an operator decision can do nothing at all; if its
    /// manifest declares anything, that is a refusal by name at boot. An unknown
    /// capability name is a boot error listing the vocabulary, rather than a
    /// typo that silently grants less.
    #[serde(default)]
    pub ron_plugin_grants: Vec<PluginGrant>,

    /// **The roots every loaded module must chain to** (lane E4, 2026-10-19): DER
    /// certificates, the house's being `korp-root-ca.der` (dwarves `trust/`), the
    /// root the forge's plugin-signing certificate is issued under. Each module in
    /// [`Holger::ron_plugin_dir`] then needs its detached `.p7s` (CMS over the
    /// module's BLAKE3), verified BEFORE it is compiled; a missing signature is
    /// `holger-plugin-6`, a bad or a stranger's one `holger-plugin-7`.
    ///
    /// Empty = none (the `HOLGER_PLUGIN_TRUST_ROOT` fallback is refused, stinky
    /// SB #22), and then a build that loads
    /// METER guests refuses its plugin directory by name (`holger-plugin-13`): a
    /// meter counts what a customer pays for and is never loaded unverified. A
    /// handler-only build without a root loads its handlers as before.
    #[serde(default)]
    pub ron_plugin_trust_roots: Vec<String>,

    /// The handler half of the module census, recorded while
    /// [`Holger::instantiate_backends`] builds the catalog, so `ServerProfile`
    /// can report it without compiling every module a second time.
    ///
    /// Not configuration: `#[serde(skip)]`, and a RON file that names it is
    /// naming something holger overwrites at boot.
    #[serde(skip)]
    pub handler_census: Vec<(String, String)>,

    /// **The meter plane this boot has**, built once by [`wire_holger`] as soon
    /// as the route tables exist — because that is the earliest moment the usage
    /// source can be handed the routes it counts through.
    ///
    /// `None` until then, and `None` is not "no meters": it is "the plane has
    /// not been built yet". [`Holger::module_census`] tells the two apart, and a
    /// caller that never wired the config still gets a census (built with
    /// `NoServices`, which is all a census needs).
    ///
    /// Not configuration: `#[serde(skip)]`.
    #[cfg(feature = "meter-guests")]
    #[serde(skip)]
    pub meter_plane: Option<Arc<holger_plugin_host::MeterCatalog>>,

    /// The one audit sink this config has, once something has asked for it. See
    /// [`Holger::build_audit_log`] for why it is memoized rather than rebuilt.
    ///
    /// Not configuration: `#[serde(skip)]`.
    #[serde(skip)]
    audit_sink: std::sync::OnceLock<Arc<dyn audit::AuditLog>>,
    /// **The ONE job runner of this process** (lane HOL-SANDBOX-TRAIT,
    /// [`Holger::jobs`]): made on first use, its per-boot key pinned then; the import
    /// door and the scan worker share it. Not configuration: `#[serde(skip)]`.
    #[cfg(feature = "intake")]
    #[serde(skip)]
    jobs_cell: std::sync::OnceLock<Arc<job_runner::Jobs>>,
    /// The SIEM exporters [`Holger::start_siem_export`] started.
    /// Not configuration: `#[serde(skip)]`.
    #[serde(skip)]
    siem_exporters: std::sync::OnceLock<audit::siem::ExporterHandles>,
    /// The ONE paid-limit check of this process (`metered::Quota`), made on first
    /// use, so the HTTP door and every repository's `QuotaGuard` share its
    /// readings — two caches would each miss the other's admissions.
    /// Not configuration: `#[serde(skip)]`.
    #[cfg(feature = "metered")]
    #[serde(skip)]
    quota_cell: std::sync::OnceLock<Option<Arc<crate::metered::Quota>>>,
    /// **The ONE paid-state store of this process**, made on first use, so a key the
    /// installer feeds after the start reaches every door that holds it (A7 row 8).
    /// Not configuration: `#[serde(skip)]`.
    #[cfg(feature = "metered")]
    #[serde(skip)]
    entitlement_cell: std::sync::OnceLock<Option<Arc<crate::metered::EntitlementStore>>>,
    /// **The growth's monetize go-ahead policy**, kept so a late key is applied in place.
    /// Not configuration: `#[serde(skip)]`.
    #[cfg(feature = "metered")]
    #[serde(skip)]
    grow_policy_cell: std::sync::OnceLock<Arc<appliance_grow::MonetizePolicy>>,
    /// **The monetize key is AWAITED** (A7 row 8): an estate box whose config and kept feed
    /// name none builds its paid-state store keyless, and the front's later offer gives it
    /// the key live ([`Holger::live_keys`]). Set by holger-server's boot, never by the RON.
    #[cfg(feature = "metered")]
    #[serde(skip)]
    pub monetize_key_awaited: bool,
    /// **The growth's ONE write hold** (`crate::grow::Hold`), made on first
    /// use: `wire_holger` wraps every repository in it and `start` hands the
    /// SAME one to the gRPC gates and the HTTP door.
    /// Not configuration: `#[serde(skip)]`.
    #[serde(skip)]
    grow_hold_cell: std::sync::OnceLock<Arc<crate::grow::Hold>>,
    /// **The read gate every door shares** ([`Holger::read_gate`]). Not configuration.
    #[serde(skip)]
    read_gate_cell: std::sync::OnceLock<Arc<crate::auth::ReadGate>>,
    /// **What serves a repository made at run time** ([`crate::live_routes::LiveRoutes`]),
    /// set by `wire_holger` once the route tables exist. Not configuration: `#[serde(skip)]`.
    #[serde(skip)]
    live_routes_cell: std::sync::OnceLock<Arc<crate::live_routes::LiveRoutes>>,
    /// **The repositories paid orders made, and whose they are** (lane P1,
    /// [`crate::auth::SoldOwners`]): read from `fleet/sold-repositories.json` once, then the ONE
    /// table every read gate of this process holds. Not configuration: `#[serde(skip)]`.
    #[serde(skip)]
    sold_owners_cell: std::sync::OnceLock<crate::auth::SoldOwners>,
    /// **The shop's first-login invite**, as the installer fed it (lane P1): the passkey store
    /// takes it in once, when it opens ([`passkey::PasskeyDoor::seed_first_admin`]). Not
    /// configuration: `#[serde(skip)]`.
    #[cfg(feature = "auth-passkey")]
    #[serde(skip)]
    pub first_admin_invite: Option<passkey::FirstAdminInvite>,
    /// **The site the ONE box fed** (RA5-1: `identity_host::site_from_feed` over the installer's
    /// feed, set by holger-server's boot): a shop-laid box's sign-up is what its estate declares,
    /// never guessed from its host. `None`: the box's own (`identity_host::site_of`). Not
    /// configuration: `#[serde(skip)]`.
    #[cfg(feature = "auth-passkey")]
    #[serde(skip)]
    pub estate_site: Option<nornir_identity::Site>,
    /// **The growth plane** (`crate::grow::GrowPlane`), made once per process.
    /// Not configuration: `#[serde(skip)]`.
    #[cfg(feature = "estate")]
    #[serde(skip)]
    grow_plane_cell: std::sync::OnceLock<Arc<crate::grow::GrowPlane>>,

    /// **monetize's Ed25519 public key**, 32 bytes as 64 lowercase hex — the key a
    /// signed `EntitlementFact` must verify against.
    ///
    /// **Omitted ⇒ no plan is enforced**: `EntitlementService.Set` answers
    /// `holger-metered-4` rather than storing a fact it cannot vouch for. The field
    /// is read only by the `metered` build; an open build parses and ignores it, so
    /// one config file serves both and a deployment that later buys the metered
    /// binary does not have to be re-written.
    ///
    /// It is a PUBLIC key: it goes in the config file, never in the environment.
    /// The environment is where `marketplace`'s client SECRET lives, and the
    /// difference between the two is the whole reason they are wired differently.
    #[serde(default)]
    pub monetize_pubkey: Option<String>,

    /// **The estate's pay page** (lane M4): where a repository refused for its plan is paid
    /// (`{tenant}` the repository). A box of a sold estate is fed it by its front (the shop's
    /// `refusal_link`); unset, a paid refusal names none.
    #[serde(default)]
    pub pay_page: Option<String>,

    /// **Retired (A34-1, 2026-10-26): parsed, and changes nothing.** It used to
    /// let an `Oidc` method with no resolvable JWKS boot on nornir-auth-oidc's
    /// `/userinfo` fall-through, which checked neither `iss` nor `aud`. That path
    /// is gone (nornir-auth-oidc 0.2 verifies a JWT or refuses it), so such a
    /// method authenticates nobody and [`auth::refuse_unbound_oidc`] refuses it
    /// whatever this says. Kept so an old config still loads (the config is
    /// strict); `true` is warned about at every boot.
    #[serde(default)]
    pub auth_allow_unbound_oidc: bool,

    /// The **promotion stage ladder** (`.nornir/promote-sync-attest-design.md` §4,
    /// `.nornir/promotion-design.md` §1) — the rungs an artifact is promoted along,
    /// and the repository that holds it at each rung.
    ///
    /// **Omitted ⇒ no stage model**, which is exactly what holger did before this
    /// field existed: [`promote::PromotionEngine::seal`] judges no hop, writes no
    /// lineage, and a promotion between any two repository names is permitted. That
    /// is the unchanged default and every existing config keeps it.
    ///
    /// Present ⇒ every promotion is resolved onto the ladder BEFORE anything is
    /// written, so a backwards hop or a skipped rung leaves the store untouched, and
    /// every successful hop is appended to the lineage under
    /// `<data_root>/holger-prod-store/lineage/<ladder_name>`.
    ///
    /// Declaring it and getting it wrong is a **boot error**, not a runtime surprise
    /// — see [`PromotionSettings::ladder`].
    #[serde(default)]
    #[cfg(feature = "intake")]
    pub promotion: Option<PromotionSettings>,

    /// **Tier 1: where packages come from** ([`sources`], Holger G0). Each entry is an
    /// `upload` door into the steps, a `crates` pull-through or an upstream `holger`
    /// mirror, and lowers onto the shapes above (`repositories`, `mirror`) at load.
    #[serde(default)]
    pub sources: Vec<sources::Source>,

    /// **Tier 1: the steps** ([`sources::Steps`]), the new spelling of `promotion:`
    /// with its `intake:` block. Lowered into [`Holger::promotion`] at load; naming
    /// both is refused by name.
    #[serde(default)]
    #[cfg(feature = "intake")]
    pub steps: Option<sources::Steps>,

    /// **Tier 1: the global floor** ([`sources::Rules`]). Holds for every source; a
    /// source's `gates` may only tighten it. Absent with `steps:` ⇒ the security
    /// gateway's default floor ([`sources::Rules::default`]).
    #[serde(default)]
    #[cfg(feature = "intake")]
    pub rules: Option<sources::Rules>,

    /// The effective policy of every upload source, built by [`Holger::lower_sources`].
    /// Not configuration.
    #[serde(skip)]
    #[cfg(feature = "intake")]
    pub source_policies: Option<sources::SourcePolicies>,

    /// The `steps:` block as the RON wrote it, kept by [`Holger::lower_sources`] for the flow
    /// drawing (lane HOL-SVG-RON): the lowering adds the floor's scanners to what the RON
    /// names, and the drawing names what the RON names. Not configuration.
    #[serde(skip)]
    #[cfg(feature = "intake")]
    pub steps_declared: Option<sources::Steps>,

    /// The **evidence plane** (`attestation:` block) — the trust roots and the
    /// accepted predicate types for [`attest::AttestStore`].
    ///
    /// Absent ⇒ no attestation store is mounted, which is where every existing
    /// deployment is: MEASURED at `542e296`, nothing in the shipped tree ever
    /// constructed one, so `attest.rs` was 1 458 lines with no caller.
    #[serde(default)]
    #[cfg(feature = "intake")]
    pub attestation: Option<AttestationSettings>,

    /// **AI model files** (`models:` block). GGUF, Safetensors and ONNX are
    /// checked and described by the intake's model scanner; the pickle family is
    /// blocked. `allow_pickle` names the repositories where a pickle-family file
    /// is a warning instead (`models: (allow_pickle: ["ml-research"])`); each
    /// must be a declared repository, or the boot is refused
    /// (`holger-model-8`). Absent ⇒ blocked everywhere.
    #[serde(default)]
    #[cfg(feature = "scanners")]
    pub models: scanner::model::ModelSettings,

    /// **The mirror** (`mirror:` block) — the upstreams this holger continuously
    /// mirrors, the repositories it takes from each, and what it does about a
    /// coordinate whose sameness it cannot prove. See
    /// [`mirror_cycle::MirrorSettings`].
    ///
    /// **Absent ⇒ no mirror**, which is where every config written before this
    /// key existed is: [`mirror`] shipped as a library with no configuration and
    /// no caller, so an older file described a holger that mirrored nothing and
    /// still describes exactly that. Nothing is listed, nothing is fetched,
    /// nothing is written; not one byte of prior behaviour moves.
    ///
    /// Present and wrong is a **boot error**, not a runtime surprise — see
    /// [`mirror_cycle::MirrorSettings::validate`], which [`wire_holger`] calls.
    #[serde(default)]
    pub mirror: Option<mirror_cycle::MirrorSettings>,

    /// **`boundary: OneWay | Fetch`** (lane HOL-CDS, [`boundary::Boundary`]): which way
    /// packages may come in. Omitted is `OneWay` on a downstream. A downstream build checks
    /// it at every start ([`Holger::check_boundary`]); any other build checks it when the
    /// file names it.
    #[serde(default)]
    pub boundary: Option<boundary::Boundary>,

    /// **`domain: "<label>"`**: the security domain this Holger serves, shown in the
    /// console's top bar, the CLI's output and every response's `X-Holger-Domain`.
    #[serde(default)]
    pub domain: Option<String>,

    /// **`trust: (first_import: Admin)`** ([`boundary::TrustSettings`]): the first import
    /// of a vendor's product is an admin's action on this side (NCSC gap 2).
    #[serde(default)]
    pub trust: boundary::TrustSettings,

    /// **`export: (push_to: Some("<dir>"), every: "<interval>")`** (the upstream's diode
    /// send side, [`export_push::ExportPushSettings`]): each new signed bundle of the
    /// AVAILABLE set is written there, atomically. On a one-way downstream it is an export
    /// door and refuses the start.
    #[serde(default)]
    pub export: Option<export_push::ExportPushSettings>,

    /// The file this config was read from and its text (for the line a refusal names).
    /// Not configuration: `#[serde(skip)]`.
    #[serde(skip)]
    pub ron_source: Option<(String, String)>,

    /// **The artifact-flow drawing** (`flow:` block, [`flow::FlowSettings`]): ON by
    /// default; `flow: (enabled: false)` makes `/-/flow*` answer 404 and
    /// `AdminService.FlowSvg` NotFound.
    #[serde(default)]
    pub flow: flow::FlowSettings,

    /// **Pull-through INTO the intake chain** (`pull_through:` block,
    /// [`pull_intake`]): remote sources (an OCI registry such as `cgr.dev`, a
    /// PEP 503/691 index such as PyPI or Chainguard Libraries for Python) whose
    /// packages are fetched on a client's request, INGESTED into the chain's
    /// `incoming` as `pull-through:<source>`, scanned and policy-checked under
    /// the floor plus the source's gates, and served only once AVAILABLE.
    /// Needs `promotion.intake` and the job queue.
    #[cfg(all(feature = "jobs", feature = "intake"))]
    #[serde(default)]
    pub pull_through: Option<pull_intake::PullThroughSettings>,

    /// **The fetch sources** (lane cds4, plan row HOL-CDS-4): each source a fetch job may reach,
    /// by its declared hosts (`job_runner::fetch::FetchSource`). On a box whose policy requires a
    /// scan, a `plaintext: true` or `internal: true` source is refused by name unless it also
    /// says `signed_provenance: true`; either one is a warning on the Security page.
    #[cfg(all(feature = "fetch", feature = "intake"))]
    #[serde(default)]
    pub fetch_sources: Vec<job_runner::fetch::FetchSource>,

    /// **The sync protocol** (`sync:` block, Holger v2.0 H4, [`sync::SyncConfig`]): this
    /// Holger's name and key, the downstreams whose acks it takes (upstream role) and
    /// the upstreams whose signed manifests it applies (downstream role). Needs
    /// `promotion.intake`.
    #[cfg(feature = "sync")]
    #[serde(default)]
    pub sync: Option<sync::SyncConfig>,

    /// **Offline vulnerability intelligence** (`intel:` block) — where the local
    /// advisory database lives, whether the network is refused (the default),
    /// and when a stale database warns and blocks. See [`intel::IntelConfig`].
    ///
    /// Absent ⇒ the OSV scan reads `promotion.advisory_db` as before, and every
    /// existing config means what it meant. Always compiled, so an mvp box's
    /// `intel import` reads the same file a full server does.
    #[serde(default)]
    pub intel: Option<intel::IntelConfig>,

    /// **Download admission and the queued pull-through** ([`admission`]).
    /// Absent ⇒ `PassThrough`: every request planned `Full`, no share, nothing
    /// refused, proxies walk their upstreams inline — exactly as before.
    #[serde(default)]
    #[cfg(feature = "admission")]
    pub admission: Option<admission::AdmissionConfig>,

    /// **A `role:` key — REFUSED by name** (stinky SB #36, `holger-twin-21`).
    ///
    /// Kept as a known key only so that a config carrying one is told WHY it is
    /// refused rather than "unknown field". A box's role comes only from the
    /// pairing's election, recorded in `holger-role.ron` ([`twin::elect`]); the
    /// twin's config stays byte-identical to the primary's, which is what
    /// `[estate-twin-drift]` demands.
    #[serde(
        default,
        rename = "role",
        skip_serializing_if = "Option::is_none",
        deserialize_with = "declared_role"
    )]
    pub ron_role: Option<twin::NodeRole>,

    /// **What this box IS, this boot**: the pairing's election recorded on the
    /// box ([`twin::effective_role`]), `Primary` when none was — set by
    /// [`wire_holger`], never read from the config (stinky SB #36).
    #[serde(skip)]
    pub role: twin::NodeRole,

    /// **Recharge this box from a twin before it serves** (owner plan 2b,
    /// 2026-09-27): `host:port` of the twin (or paired downstream) that holds
    /// what the dead main held. At the first start the estate (config and side
    /// stores) and every repository are pulled from it ONCE
    /// ([`twin_follow::recharge`]); a marker (`holger-recharged`) keeps it once.
    /// Absent ⇒ nothing is pulled.
    #[serde(default)]
    pub recharge_from: Option<String>,

    /// **The replication door** (stinky 14 §P3): a second gRPC listener at the
    /// gRPC port + 1, mutual TLS 1.3 under this box's OWN issuer, serving a
    /// twin (or a recharging main) the repositories and `holger.estate.v1`
    /// read-only — see [`twin_tls`]. Absent ⇒ no door, no issuer, the box as
    /// before. A twin keeps this door on its CONFIGURED address: it is the one
    /// door a twin must keep open, because a recharge dials it.
    #[serde(default)]
    pub replication_door: Option<twin_tls::ReplicationDoorConfig>,

    /// Where the replication door binds, resolved by [`wire_holger`] BEFORE a
    /// twin's doors are rewritten onto loopback. Not configuration.
    #[serde(skip)]
    pub replication_bind: Option<String>,

    /// **This process's follower for THIS box** — the posture its doors answer
    /// with, captured by [`Holger::start`], so a follow started later (a twin at
    /// boot: [`twin_follow::spawn`]) reports to the SAME posture `TwinStatus`
    /// and `/-/twin` read. Not configuration.
    #[serde(skip)]
    pub live_follower: std::sync::OnceLock<twin_follow::Follower>,

    /// The job queue [`Holger::start`] opened (`<data_root>/jobs`), kept so the
    /// HTTP door's pull-through and the mirror's failed assets reach the SAME
    /// ledger the gRPC door uses. Not configuration.
    #[cfg(feature = "jobs")]
    #[serde(skip)]
    pub job_queue_cell: std::sync::OnceLock<Arc<jobs::JobQueue>>,

    /// The ONE door admission of this process (both transports share it).
    /// Not configuration.
    #[serde(skip)]
    #[cfg(feature = "admission")]
    pub door_admission: std::sync::OnceLock<Arc<admission::DoorAdmission>>,

    /// The ONE intake chain of this process, built by [`Holger::start`] from
    /// `promotion.intake` and shared by both doors. Not configuration.
    /// Shared with every chain view (`chain_view`, lane HOL-MVP-FORMATS): a view built before
    /// the chain reads it from here once it is set.
    #[serde(skip)]
    #[cfg(feature = "intake")]
    pub intake_cell: chain_view::ChainSlot,

    /// The ONE intake scan pipeline (`promotion.intake.scan`), shared by the
    /// SCANNED transition, the `osv` gate's stored-evidence read (W5) and the
    /// evidence doors. Filled when the chain is built. Not configuration.
    #[serde(skip)]
    #[cfg(feature = "intake")]
    pub scan_pipeline_cell: promote::gate::EvidenceCell,

    /// The ONE scan worker (lane SANDBOX-SCAN), shared by the intake chain, its
    /// scan pipeline and the `osv` gate's licence read. Filled when the chain is
    /// built. Not configuration.
    #[serde(skip)]
    #[cfg(feature = "intake")]
    pub scan_worker_cell: promote::gate::WorkerCell,

    /// The ONE import door of this process (lane W4, [`import_door`]), built by
    /// [`Holger::start`] and shared by both transports and the inbox jobs. Not
    /// configuration.
    #[serde(skip)]
    #[cfg(feature = "intake")]
    pub import_door_cell: std::sync::OnceLock<Arc<import_door::ImportDoor>>,

    /// **The box's repository signing key** (lane HOL-SIGNED-APT-DNF), read once from the
    /// data root's key store ([`repo_sign::secret_path`]): the deb and rpm views sign with
    /// it and `/-/repo-key.asc` serves its public half. Not configuration: the operator
    /// makes it on the box (`holger repo-key create`).
    #[serde(skip)]
    pub repo_key_cell: std::sync::OnceLock<Option<Arc<repo_sign::RepoKey>>>,
}

/// The declared evidence plane (the `attestation:` block of the RON config).
///
/// Unknown keys are refused for the same reason as everywhere else in this
/// file: a typo'd key would read as configured and run as absent.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
#[cfg(feature = "intake")]
pub struct AttestationSettings {
    /// Paths to DER-encoded root certificates a statement's signer must chain
    /// to. At least one is required; see [`Holger::attestation_store`].
    pub trust_roots: Vec<String>,
    /// in-toto predicate types the store accepts. Empty ⇒ the store's default
    /// set ([`attest::AttestPolicy::default`]).
    #[serde(default)]
    pub accept_predicates: Vec<String>,

    /// PKCS#8 private key (DER) holger signs its own evidence with. With
    /// [`signer_cert`](Self::signer_cert) this enables the promotion VSA.
    ///
    /// Absent ⇒ holger emits no evidence and SAYS SO in the audit record, rather
    /// than promoting silently. holger mints no key: `nornir --seal` signs with
    /// `ZnippySealer::dev()`, which mints an in-memory CA and discards the root,
    /// so a signature produced that way chains to a CA nobody has. This key is
    /// the operator's.
    #[serde(default)]
    pub signer_key: Option<String>,

    /// The X.509 certificate (DER) carrying `signer_key`'s public key. It must
    /// chain to one of [`trust_roots`](Self::trust_roots), because holger's own
    /// VSA is written through [`attest::AttestStore::accept`] like any other
    /// evidence: if it does not verify, it is not filed.
    #[serde(default)]
    pub signer_cert: Option<String>,

    /// `"ed25519"` (default) or `"ecdsa-p256"`.
    #[serde(default)]
    pub signer_alg: Option<String>,

    /// **The signer's key in a hardware token** instead of `signer_key` (lane HOL-NCSC, NCSC
    /// P3): `Some(Pkcs11(module: "/usr/lib/softhsm/libsofthsm2.so", token_label: "holger",
    /// key_label: "scan", pin_file: "/etc/holger/hsm.pin"))`. The key never leaves the token;
    /// `signer_cert` is its certificate. Needs a build with the `pkcs11` feature.
    #[serde(default)]
    pub signer_hsm: Option<HsmSigner>,

    /// Certificates (DER paths) of OTHER signers whose evidence this holger
    /// accepts: on the secure side of an airgap, the online holger's evidence
    /// signer. Chaining to a root proves a key belongs to the organisation;
    /// enrolment names the one key that speaks for the scanners. This holger's
    /// own [`signer_cert`](Self::signer_cert) is always enrolled.
    #[serde(default)]
    pub enrolled_signers: Vec<String>,
}

/// **A signer whose key lives in a hardware token** ([`AttestationSettings::signer_hsm`]).
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub enum HsmSigner {
    /// A PKCS#11 module (an HSM, a smart card, SoftHSM2): the token with `token_label`, the
    /// private key with `key_label`, the user PIN in `pin_file` (a key file: 0600).
    Pkcs11 {
        module: String,
        token_label: String,
        key_label: String,
        pin_file: String,
    },
}

/// The declared promotion ladder (the `promotion:` block of the RON config).
///
/// Unknown keys are refused here for the same reason as on [`Holger`]: a typo'd
/// `allow_skipping` would read as "one rung at a time" in the file while the
/// process ran with the default — and the default is the *safe* one, so the typo
/// would be invisible until the day somebody hotfixed straight to release and it
/// worked.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
#[cfg(feature = "intake")]
pub struct PromotionSettings {
    /// The ladder's name — the lineage namespace. One ecosystem's ladder per name;
    /// `stage_history` and `current_stage` are answered within it.
    pub ladder_name: String,

    /// The rungs, **bottom (rehearsal) first**. A stage names itself and the
    /// repository that holds artifacts while they are at it, which is what
    /// `seal(from_repo, to_repo, …)` already takes — so the ladder is a lookup over
    /// the mechanism that exists, not a second one.
    ///
    /// `gated: true` on a rung means arriving there runs every gate this build
    /// carries; `gates: ["osv", …]` names them.
    pub stages: Vec<promote::Stage>,

    /// **The advisory feed** — a path to an OSV/GHSA/RUSTSEC JSON snapshot.
    ///
    /// This is `HOLGER_PROMOTE_ADVISORY_DB` moved from the environment into the
    /// config file. holger ships as an appliance with no OS and no shell, so an
    /// environment variable was a configuration surface the operator could not
    /// reach. Absent means an empty CVE set; the licence and provenance policy
    /// still applies. A path that cannot be read or parsed is a BOOT ERROR, not
    /// a silently empty gate.
    #[serde(default)]
    pub advisory_db: Option<String>,

    /// SPDX identifiers the licence policy denies. Was
    /// `HOLGER_PROMOTE_DENY_LICENSES`. A denied id never passes, also when it is
    /// on `allow_licenses` too.
    #[serde(default)]
    pub deny_licenses: Vec<String>,

    /// SPDX identifiers the licence policy allows. Empty (the default) allows
    /// every id that is not denied; non-empty blocks every id outside it. An
    /// `OR` passes when one branch is allowed, an `AND` needs every branch
    /// (`scanner::license::LicensePolicy`).
    #[serde(default)]
    pub allow_licenses: Vec<String>,

    /// **What a package whose licence is unknown gets** — `Hold` (the default),
    /// `Block` or `Pass`. The licence is read from the package's own metadata
    /// by the licence scanner (`scanner::license`); a package that states none,
    /// none this build can read, or one that is not SPDX, is unknown. `Hold` refuses the hop as "held for
    /// review", so an unknown licence never passes silently (B2, 2026-10-19).
    #[serde(default)]
    #[cfg(feature = "scanners")]
    pub unknown_license: security::UnknownLicense,

    /// **The gates that run on INGEST** — on a PUT, before the bytes reach the
    /// backend.
    ///
    /// Promotion is not the first boundary an artifact crosses; upload is. With
    /// this empty (the default, and what holger has always done) a vulnerable
    /// artifact is stored and served from the DEV repository, and the gate's job
    /// becomes stopping it from moving rather than stopping it from arriving.
    ///
    /// Same catalog, same HOL-GUN-007: a name this build does not carry is a
    /// boot error listing what is accepted.
    #[serde(default)]
    pub ingest_gates: Vec<String>,

    /// The in-toto predicate type the `attested` gate requires, when that gate
    /// is named by a rung. Absent means the build carries no `attested` gate, so
    /// a rung naming it is refused at boot by name rather than skipped.
    #[serde(default)]
    pub require_attestation: Option<String>,

    /// **A custom property the SOURCE coordinate must carry to be promoted** —
    /// `"key=value"`, or a bare `"key"` for any value (`"signed=true"`). Was
    /// `HOLGER_PROMOTE_REQUIRE_PROPERTY`; an appliance has no shell to set an
    /// environment variable in, and that variable is now refused by name.
    /// Absent ⇒ no requirement. A value that names no key is a boot error.
    #[serde(default)]
    pub require_property: Option<String>,

    /// **What this estate demands of provenance at the promotion boundary** —
    /// `Require`, `Warn` or `Off`. Plan §3 W17.
    ///
    /// Absent ⇒ [`promote::ProvenancePolicy::Warn`], and the direction of that
    /// default is load-bearing in BOTH directions: `Require` would make an
    /// upgraded binary refuse every promotion on a config that did not change,
    /// and `Off` would make an estate that never signed anything print the same
    /// as one that considered the question and declined. `Warn` refuses nothing
    /// and says, by frozen code, exactly what `Require` would have refused. The
    /// full argument is on [`promote::ProvenancePolicy`].
    ///
    /// The gate this key steers is registered on EVERY row, whatever the policy,
    /// so `gates: ["provenance"]` is a configuration an operator can write on any
    /// shipped holger — which it was not before 2026-09-21.
    #[serde(default)]
    pub provenance: promote::ProvenancePolicy,

    /// Permit a forward hop that jumps rungs (the hotfix-straight-to-release shop).
    /// Default `false` — one rung at a time, which is the whole point of having a
    /// ladder.
    #[serde(default)]
    pub allow_skipping: bool,

    /// **The intake chain** ([`promote::intake`]): which repositories are
    /// incoming, quarantine, approved and production, and the policy for each
    /// state. Present ⇒ the ladder above must be exactly those four repositories
    /// in that order, every upload into `incoming` is INGESTED through the chain,
    /// nothing is written into the other three by a door, and production serves
    /// only AVAILABLE artifacts, from the sealed store. Absent ⇒ no intake chain.
    #[serde(default)]
    pub intake: Option<promote::intake::IntakeSettings>,
}

#[cfg(feature = "intake")]
impl PromotionSettings {
    /// The licence policy these settings state: the one the promotion gate and
    /// the intake's licence slot both apply.
    #[cfg(feature = "scanners")]
    pub fn license_policy(&self) -> scanner::license::LicensePolicy {
        scanner::license::LicensePolicy::new(
            self.allow_licenses.clone(),
            self.deny_licenses.clone(),
            self.unknown_license,
        )
    }

    /// Resolve the declaration into a [`promote::StageLadder`], refusing a ladder
    /// that cannot mean what it says.
    ///
    /// `known_repos` is the set of repository names the config declares. It is
    /// passed in rather than read off `self` so this is pure over its arguments and
    /// testable without building a whole `Holger`.
    ///
    /// **Four refusals, each because the alternative fails OPEN:**
    ///
    /// 1. **No rungs.** `promotion: Some(…)` with an empty `stages` asked for a
    ///    stage model and named none. An empty [`promote::StageLadder`] is the
    ///    "judge nothing" ladder (`stages.rs:196`), so accepting this would install
    ///    a ladder that permits every hop under a config that reads as though hops
    ///    were controlled. Declaring nothing and declaring an empty something must
    ///    not be the same thing.
    /// 2. **A repeated stage name.** [`StageLadder::position`] is a `find`, so the
    ///    second rung of a duplicated name is unreachable and every hop is judged
    ///    against the first. Silently.
    /// 3. **A repeated repository.** [`StageLadder::stage_of_repo`] is also a
    ///    `find`. Two rungs on one repository means `seal` resolves that repository
    ///    to whichever rung comes first, so the other rung can never be hopped to
    ///    *or from*, and a promotion that looks like it crossed a boundary did not.
    /// 4. **A repository the config does not declare.** This one would otherwise be
    ///    a *runtime* refusal — `check_repo_hop` answers `UnknownStage` — which
    ///    turns a typo in the ladder into a promotion that fails in production with
    ///    a message about stages rather than at boot with a message about the typo.
    ///    Worse, the typo can fail open: if the MISspelled name is the `to_repo` of
    ///    a hop nobody takes, nothing ever notices.
    ///
    /// A one-rung ladder is allowed and is not a mistake: it is a repository that
    /// artifacts may be promoted *from* and never *to*, and `check_hop` refuses
    /// every hop on it by `NotForward`, which is a coherent thing to want.
    pub fn ladder(
        &self,
        known_repos: &std::collections::BTreeSet<&str>,
    ) -> Result<promote::StageLadder> {
        if self.stages.is_empty() {
            anyhow::bail!(
                "config `promotion`: the ladder '{}' declares no stages. A ladder with no rungs \
                 judges nothing, which is what OMITTING the whole `promotion` block means — so \
                 declaring an empty one would read as 'promotion is controlled' and run as \
                 'promotion is unconditional'. Name the rungs, or remove the block.",
                self.ladder_name
            );
        }

        let mut seen_names: std::collections::BTreeSet<&str> = std::collections::BTreeSet::new();
        let mut seen_repos: std::collections::BTreeSet<&str> = std::collections::BTreeSet::new();
        for stage in &self.stages {
            if !seen_names.insert(stage.name.as_str()) {
                anyhow::bail!(
                    "config `promotion`: stage name '{}' appears more than once on ladder '{}'. \
                     A rung is found by name, so the second one is unreachable and every hop \
                     would be judged against the first.",
                    stage.name,
                    self.ladder_name
                );
            }
            if !seen_repos.insert(stage.repo.as_str()) {
                anyhow::bail!(
                    "config `promotion`: repository '{}' is on ladder '{}' at more than one \
                     stage. A promotion names repositories, so that repository resolves to one \
                     rung and the other can never be hopped to or from.",
                    stage.repo,
                    self.ladder_name
                );
            }
            if !known_repos.contains(stage.repo.as_str()) {
                anyhow::bail!(
                    "config `promotion`: stage '{}' on ladder '{}' names repository '{}', which \
                     this config does not declare (declared: {}). Caught here rather than at \
                     promote time, where it would surface as 'no such stage' instead of naming \
                     the typo.",
                    stage.name,
                    self.ladder_name,
                    stage.repo,
                    known_repos.iter().copied().collect::<Vec<_>>().join(", ")
                );
            }
        }

        if let Some(intake) = &self.intake {
            let repos: Vec<&str> = self.stages.iter().map(|s| s.repo.as_str()).collect();
            intake.validate(&repos, self.allow_skipping)?;
        }

        let ladder = promote::StageLadder::new(self.stages.clone());
        Ok(if self.allow_skipping {
            ladder.allow_skipping()
        } else {
            ladder
        })
    }
}

/// Audit-log configuration. On by default; the default backend is the
/// append-only JSON-lines log ([`audit::FileAuditLog`]).
///
/// Unknown keys are refused here for the same reason as on [`Holger`]: a typo'd
/// `enabled` would read as "audit on" in the file while the process installed the
/// default — and under `Critical` the audit sink is a *requirement*, so a typo
/// would turn a fail-closed gate into a pass.
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct AuditSettings {
    /// Directory for the append-only audit file. Relative paths
    /// resolve against the server's working directory.
    #[serde(default = "default_audit_dir")]
    pub dir: String,
    /// Master switch. `false` installs a no-op audit log (audit disabled).
    ///
    /// This is the LEGACY spelling of [`AuditMode::Off`] and is kept because
    /// deployed RON files say it. `enabled: false` still means off, whatever
    /// `mode` says — see [`AuditSettings::effective_mode`], which is the only
    /// place the two are combined.
    #[serde(default = "default_true")]
    pub enabled: bool,

    /// **How much** to record: `Everything` (the default) or `Off`. Under
    /// `SecurityProfile::Critical` this must not be `Off`.
    #[serde(default)]
    pub mode: audit::AuditMode,

    /// Records between two checkpoint lines of the hash chain
    /// ([`audit::chain`]); `0` writes none. Absent = 256. A checkpoint is
    /// signed when `attestation.signer_key`/`signer_cert` are configured.
    #[serde(default)]
    pub checkpoint_every: Option<u64>,

    /// **SIEM export** ([`audit::siem`]): JSON lines, syslog, CEF, OTLP. Absent,
    /// or a sink without `enabled: true`, exports nothing.
    #[serde(default)]
    pub siem: Option<audit::siem::SiemSettings>,
}

fn default_audit_dir() -> String {
    "holger-audit".to_string()
}

impl AuditSettings {
    /// The mode this config actually asks for, combining the modern `mode` key
    /// with the legacy `enabled` switch.
    ///
    /// `enabled: false` wins: a deployed file that says "audit off" must keep
    /// meaning it after an upgrade that introduced a `mode` key defaulting to
    /// `Everything`. The other direction — `enabled: true, mode: Off` — is simply
    /// `Off`; `enabled` cannot turn auditing ON beyond what `mode` asks for.
    ///
    /// One function rather than two call sites reading two fields, because the
    /// combination is exactly the sort of thing that ends up spelled differently
    /// in the server and in the CLI and then disagrees.
    pub fn effective_mode(&self) -> audit::AuditMode {
        if !self.enabled {
            audit::AuditMode::Off
        } else {
            self.mode
        }
    }
}

/// **The built-in meter's name** (`allow-meter`, H2 #2): the open allocation guest,
/// linked. It is the name in the module census and the name an operator's grant line uses.
#[cfg(feature = "allow-meter")]
pub const BUILTIN_METER: &str = "allocation";

/// One line of [`Holger::ron_plugin_grants`]: a module file name, and the
/// capabilities an operator decided it may have.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct PluginGrant {
    /// The module's file name as it sits in the plugin directory
    /// (`allocation.wasm`), or the name a LINKED guest registers under. Not the
    /// name inside its manifest: the operator grants the file they put there.
    pub module: String,
    /// Capability names, exactly as `Capability::name()` spells them.
    #[serde(default)]
    pub grants: Vec<String>,
}

/// Anchor a configured directory against the installer's data root. Absolute
/// stays absolute; relative joins `data_root` when one is supplied, else is left
/// CWD-relative. Split out from [`Holger::resolved_audit_dir`] so the policy is
/// testable without mutating process env.
///
/// It is ONE policy, not the audit log's private habit — every server-side
/// directory a RON file names goes through it, so "where does holger keep this"
/// has one answer per install and not one answer per config key. It was named
/// `anchor_audit_dir` while the audit log was its only caller;
/// [`Holger::resolved_ron_plugin_dir`] is the second, and the rename is the
/// admission that the first name was describing the caller rather than the rule.
fn anchor_under_data_root(
    configured: &str,
    data_root: Option<std::ffi::OsString>,
) -> std::path::PathBuf {
    let configured = std::path::Path::new(configured);
    if configured.is_absolute() {
        return configured.to_path_buf();
    }
    match data_root {
        Some(root) if !root.is_empty() => std::path::PathBuf::from(root).join(configured),
        _ => configured.to_path_buf(),
    }
}

#[cfg(test)]
mod audit_dir_anchor_tests {
    use super::anchor_under_data_root;
    use std::ffi::OsString;
    use std::path::PathBuf;

    #[test]
    fn relative_default_anchors_under_data_root() {
        assert_eq!(
            anchor_under_data_root("holger-audit", Some(OsString::from("/home/me/.holger"))),
            PathBuf::from("/home/me/.holger/holger-audit")
        );
    }

    #[test]
    fn absolute_config_wins_over_data_root() {
        assert_eq!(
            anchor_under_data_root("/srv/audit", Some(OsString::from("/home/me/.holger"))),
            PathBuf::from("/srv/audit")
        );
    }

    #[test]
    fn without_data_root_stays_cwd_relative() {
        assert_eq!(
            anchor_under_data_root("holger-audit", None),
            PathBuf::from("holger-audit")
        );
        assert_eq!(
            anchor_under_data_root("holger-audit", Some(OsString::new())),
            PathBuf::from("holger-audit")
        );
    }

    /// The `.wasm` plugin directory gets THE SAME home, and an absent one is still
    /// absent.
    ///
    /// `ron_plugin_dir` had no declared home at all: the configured string went
    /// into `catalog.load_dir(dir)` raw, so a relative `plugins` pointed at
    /// whatever CWD the process was launched in — the host's when holger is
    /// embedded, the unit's when installed, something else after a reinstall. The
    /// question "does a module survive a reinstall" was answered by the launcher
    /// rather than by the config. It is anchored now, by the one helper.
    ///
    /// RED-when-broken: hand `load_dir` the raw string again and the first case
    /// here stops joining the data root.
    #[test]
    fn the_plugin_dir_is_anchored_like_the_audit_dir() {
        let plugin_dir_of = |configured: Option<&str>, data_root: Option<&str>| {
            // `resolved_ron_plugin_dir` reads `$HOLGER_DATA`; the POLICY it applies
            // is this helper, so the policy is asserted without mutating process
            // env (the same split `anchor_under_data_root` exists for).
            configured
                .map(str::trim)
                .filter(|d| !d.is_empty())
                .map(|d| anchor_under_data_root(d, data_root.map(OsString::from)))
        };
        assert_eq!(
            plugin_dir_of(Some("plugins"), Some("/home/me/.holger")),
            Some(PathBuf::from("/home/me/.holger/plugins"))
        );
        assert_eq!(
            plugin_dir_of(Some("/srv/holger-plugins"), Some("/home/me/.holger")),
            Some(PathBuf::from("/srv/holger-plugins"))
        );
        assert_eq!(
            plugin_dir_of(Some("plugins"), None),
            Some(PathBuf::from("plugins"))
        );
        // Absent / blank ⇒ no directory, and the loader never runs.
        assert_eq!(plugin_dir_of(None, Some("/home/me/.holger")), None);
        assert_eq!(plugin_dir_of(Some("   "), Some("/home/me/.holger")), None);
    }
}

#[cfg(test)]
mod critical_audit_tests {
    use super::*;

    fn holger_from(ron: &str) -> Holger {
        let tmp = tempfile::tempdir().unwrap();
        let p = tmp.path().join("h.ron");
        std::fs::write(&p, ron).unwrap();
        read_ron_config(&p).unwrap()
    }

    #[test]
    fn critical_audit_disabled_build_fails_closed() {
        let h = holger_from(
            r#"(repositories:[],exposed_endpoints:[],storage_endpoints:[],audit:(dir:"a",enabled:false),security_profile:Critical)"#,
        );
        assert!(
            h.build_audit_log().is_err(),
            "critical profile must refuse to build an audit log when audit is disabled"
        );
    }

    #[test]
    fn critical_unwritable_audit_dir_build_fails_closed() {
        // A file where the audit dir should be — create_dir_all under it fails.
        let tmp = tempfile::tempdir().unwrap();
        let blocker = tmp.path().join("not-a-dir");
        std::fs::write(&blocker, b"x").unwrap();
        let dir = blocker.join("audit");
        let ron = format!(
            r#"(repositories:[],exposed_endpoints:[],storage_endpoints:[],audit:(dir:"{}",enabled:true),security_profile:Critical)"#,
            dir.to_string_lossy()
        );
        let p = tmp.path().join("h.ron");
        std::fs::write(&p, &ron).unwrap();
        let h = read_ron_config(&p).unwrap();
        assert!(
            h.build_audit_log().is_err(),
            "critical profile must fail closed when the audit sink cannot be opened"
        );
    }

    #[test]
    fn standard_audit_disabled_is_refused_never_a_noop() {
        // HOL-NCSC: no profile runs without its audit log (`holger-audit-13`).
        let h = holger_from(
            r#"(repositories:[],exposed_endpoints:[],storage_endpoints:[],audit:(dir:"a",enabled:false))"#,
        );
        let e = format!(
            "{:#}",
            h.build_audit_log()
                .err()
                .expect("audit off refuses the start")
        );
        assert!(e.contains("[holger-audit-13]"), "{e}");
    }

    /// ★ NCSC (lane HOL-AUDIT): an audit dir that cannot be opened refuses the
    /// start in the STANDARD profile too — never a silent no-op logger.
    #[test]
    fn standard_unopenable_audit_dir_refuses_instead_of_a_noop_logger() {
        let tmp = tempfile::tempdir().unwrap();
        let blocker = tmp.path().join("not-a-dir");
        std::fs::write(&blocker, b"x").unwrap();
        let ron = format!(
            r#"(repositories:[],exposed_endpoints:[],storage_endpoints:[],audit:(dir:"{}",enabled:true))"#,
            blocker.join("audit").to_string_lossy()
        );
        let p = tmp.path().join("h.ron");
        std::fs::write(&p, &ron).unwrap();
        let h = read_ron_config(&p).unwrap();
        let e = h
            .build_audit_log()
            .err()
            .expect("the standard profile must refuse, not fall back to a no-op log")
            .to_string();
        assert!(e.contains("[holger-audit-12]"), "{e}");
    }

    fn tampered_audit_dir() -> (tempfile::TempDir, std::path::PathBuf) {
        let tmp = tempfile::tempdir().unwrap();
        let dir = tmp.path().join("audit");
        let log = audit::FileAuditLog::new(&dir)
            .unwrap()
            .with_checkpoint_every(0);
        for i in 0..3u64 {
            audit::AuditLog::record(
                &log,
                audit::AuditEvent::new(
                    "rita",
                    audit::AuditAction::Download,
                    "pypi",
                    format!("x-{i}.whl"),
                    "10.0.0.1:1",
                    200,
                    7,
                ),
            )
            .unwrap();
        }
        let path = dir.join(audit::AUDIT_FILE);
        let text = std::fs::read_to_string(&path).unwrap().replacen(
            "\"ident\":\"rita\"",
            "\"ident\":\"mallory\"",
            1,
        );
        std::fs::write(&path, text).unwrap();
        (tmp, dir)
    }

    /// ★ NCSC (lane HOL-AUDIT): the chain is verified at startup. A tampered log
    /// refuses a critical boot by name; a standard boot records the break (so it
    /// reaches the SIEM) and serves.
    #[test]
    fn a_tampered_chain_is_detected_at_startup() {
        let (_t, dir) = tampered_audit_dir();
        let ron = |extra: &str| {
            format!(
                r#"(repositories:[],exposed_endpoints:[],storage_endpoints:[],audit:(dir:"{}",enabled:true){extra})"#,
                dir.to_string_lossy()
            )
        };
        let critical = holger_from(&ron(",security_profile:Critical"));
        let e = critical
            .build_audit_log()
            .err()
            .expect("a critical boot over a broken chain is refused")
            .to_string();
        assert!(
            e.contains("[holger-audit-4]") && e.contains("line 1"),
            "{e}"
        );

        let standard = holger_from(&ron(""));
        let log = standard
            .build_audit_log()
            .expect("the standard profile boots");
        let rows = log.query_source().unwrap().events().unwrap();
        let last = rows.last().unwrap();
        assert!(
            last.detail.contains("audit chain BROKEN at startup")
                && last.detail.contains("[holger-audit-4]"),
            "{:?}",
            last.detail
        );
    }

    // ── the three modes, as an operator writes them in RON ───────────────────

    fn audit_ron(extra: &str) -> Holger {
        holger_from(&format!(
            r#"(repositories:[],exposed_endpoints:[],storage_endpoints:[],audit:(dir:"a"{extra}))"#
        ))
    }

    /// The RON key and its three values, spelled exactly as a config file spells
    /// them. This is the contract with every deployed `holger.ron`.
    #[test]
    fn the_ron_mode_key_accepts_off_and_everything() {
        use audit::AuditMode;
        for (ron, want) in [
            (",mode:Off", AuditMode::Off),
            (",mode:Everything", AuditMode::Everything),
        ] {
            let h = audit_ron(ron);
            assert_eq!(h.audit.mode, want, "RON `{ron}`");
            assert_eq!(h.audit.effective_mode(), want);
        }
    }

    /// A config written before the modes existed keeps the behaviour it had. A
    /// default that recorded LESS would silently downgrade every deployment's
    /// evidence on upgrade, which is the one direction this must not fail in.
    #[test]
    fn a_config_that_names_no_mode_still_records_everything() {
        let h = audit_ron("");
        assert_eq!(h.audit.effective_mode(), audit::AuditMode::Everything);
    }

    /// `enabled: false` is the legacy spelling of `mode: Off` and must keep
    /// meaning it, even against a `mode` that says otherwise — an upgrade must
    /// not switch auditing back ON for a deployment that turned it off.
    #[test]
    fn the_legacy_enabled_switch_still_means_off_and_wins() {
        use audit::AuditMode;
        assert_eq!(
            audit_ron(",enabled:false").audit.effective_mode(),
            AuditMode::Off
        );
        assert_eq!(
            audit_ron(",enabled:false,mode:Everything")
                .audit
                .effective_mode(),
            AuditMode::Off,
            "`enabled: false` is not overridden by a mode key"
        );
        assert_eq!(
            audit_ron(",enabled:true,mode:Off").audit.effective_mode(),
            AuditMode::Off,
            "and `enabled` cannot turn auditing on beyond what `mode` asks for"
        );
    }

    /// A misspelled mode REFUSES the config rather than running a different
    /// amount of auditing than the file asks for — the same reasoning behind
    /// `deny_unknown_fields` on this struct.
    #[test]
    fn a_misspelled_mode_refuses_the_config() {
        let tmp = tempfile::tempdir().unwrap();
        let p = tmp.path().join("h.ron");
        std::fs::write(
            &p,
            r#"(repositories:[],exposed_endpoints:[],storage_endpoints:[],audit:(dir:"a",mode:Everyting))"#,
        )
        .unwrap();
        assert!(
            read_ron_config(&p).is_err(),
            "an unknown mode is a refusal, not a default"
        );
    }

    /// `mode: Off` must trip the critical gate exactly as `enabled: false` does.
    /// A fail-closed check that only knew the old key would let a critical node
    /// boot unaudited through the new one.
    #[test]
    fn critical_refuses_mode_off_the_same_way_it_refuses_enabled_false() {
        let h = holger_from(
            r#"(repositories:[],exposed_endpoints:[],storage_endpoints:[],audit:(dir:"a",mode:Off),security_profile:Critical)"#,
        );
        let err = h
            .validate_profile()
            .expect_err("critical must refuse an unaudited node");
        assert!(
            format!("{err}").contains("audit"),
            "the refusal names audit: {err}"
        );
        assert!(
            h.build_audit_log().is_err(),
            "and the sink refuses to build too"
        );
    }
}

fn default_true() -> bool {
    true
}

impl Default for AuditSettings {
    fn default() -> Self {
        Self {
            dir: default_audit_dir(),
            enabled: true,
            // The default must be the old behaviour: an existing RON file that
            // names no mode gets the complete, per-call, chained log it got
            // before this key existed. A default that recorded LESS would be a
            // silent downgrade of every deployment's evidence on upgrade.
            mode: audit::AuditMode::Everything,
            checkpoint_every: None,
            siem: None,
        }
    }
}

// ========================= LOAD RON CONFIG =========================

/// Read the RON config graph from disk.
///
/// **A key holger does not recognise is a boot error**, not a shrug: [`Holger`]
/// and [`AuditSettings`] are `deny_unknown_fields`. Serde's default is to skip an
/// unknown key silently, and the failure mode that buys is the worst one a config
/// file has — `security_profil: Critical` parses, the process runs `Standard`, and
/// every fail-closed gate in [`Holger::validate_profile`] is simply never reached.
/// Nothing in the logs, nothing in the audit trail, nothing at the door: a node the
/// operator believes is Critical serves like a lab box. The same shape hides a
/// typo'd `operation_mode` (a ReadOnly node accepting writes), a typo'd
/// `require_auth_for_reads`, or a typo'd `auth_allow_unbound_oidc`.
///
/// The cost of the strict reading is a config with a stale key failing to boot.
/// That risk was weighed and taken: no field has ever been REMOVED from `Holger`
/// (checked over the struct's whole history), so there is no such config in the
/// wild; the config is read once, at boot, in front of an operator, not in a hot
/// path; and RON names the offending key and its position in the error, so the fix
/// is mechanical. If a field is ever retired, retire it by keeping the key and
/// ignoring it — `#[serde(default)] #[deprecated]` — rather than by deleting it,
/// which is what `deny_unknown_fields` turns from a convention into a rule.
/// **Is `r` a read-only view of the intake chain's AVAILABLE set** (lane HOL-MVP-FORMATS):
/// a deb, rpm, helm, maven3 or go repository with no archive and no handler of its own.
/// ONE predicate (SEC-REVIEW 2026-11-01 P10) for the wiring that makes the view, the chain's
/// list of views, and the side doors that exempt it: a handler-backed repository is no view,
/// so where a scan is required it is a side door.
#[cfg(feature = "intake")]
pub(crate) fn is_chain_view(r: &Repository) -> bool {
    r.ron_archive_path.is_none()
        && r.ron_writable_archive.is_none()
        && r.ron_handler
            .as_deref()
            .map(str::trim)
            .is_none_or(str::is_empty)
        && chain_view::view_format(&r.ron_repo_type).is_some()
}

pub fn read_ron_config<P: AsRef<Path>>(path: P) -> Result<Holger> {
    let path = path.as_ref();
    let file = File::open(path)?;
    let reader = BufReader::new(file);
    let holger: Holger = sources::ron_options().from_reader(reader).map_err(|e| {
        anyhow::anyhow!(
            "config {}: {e}\n\
             (holger reads its TOP-LEVEL config strictly — an unrecognised key there is \
             refused rather than skipped, because a mistyped key is a setting holger is not \
             honouring while the file says it is. Fix the key named above, or delete it.)",
            path.display()
        )
    })?;
    let mut holger = holger;
    holger.ron_source = std::fs::read_to_string(path)
        .ok()
        .map(|t| (path.display().to_string(), t));
    if let Some(d) = &holger.domain {
        boundary::validate_domain(d)
            .map_err(|e| anyhow::anyhow!("config {}: {e}", path.display()))?;
    }
    holger
        .lower_sources()
        .map_err(|e| anyhow::anyhow!("config {}: {e:#}", path.display()))?;
    holger
        .apply_access()
        .map_err(|e| anyhow::anyhow!("config {}: {e}", path.display()))?;
    Ok(holger)
}

/// **How far the strictness goes, and where it stops.**
///
/// `#[serde(deny_unknown_fields)]` is on [`Holger`] and [`AuditSettings`], so a
/// typo in `security_profile`, `operation_mode`, `reserved_properties` or inside
/// the `audit` block is a boot error rather than a silently ignored line.
///
/// It is NOT on the nested node types — `Repository`, `ExposedEndpoint`,
/// `StorageEndpoint`. The `auth` block IS strict since 2026-10-23 (Holger G0):
/// `AuthConfig` and its methods live in the shared `nornir-auth-policy` leaf, which
/// denies unknown fields from 0.1.5, so `require_auth_for_read` (for
/// `require_auth_for_reads`) stops the boot by name. The tier 1 blocks (`sources:`,
/// `steps:`, `rules:` and every source's `gates`, [`sources`]) are strict too.
///
/// A CONSEQUENCE worth stating: with the top level strict, an OLDER holger reading
/// a config written for a NEWER one now fails to boot rather than ignoring the
/// fields it does not know. That is the intended direction — a binary that cannot
/// honour a setting should say so — but it means config and binary must be rolled
/// together. The corollary rule for this file: retire a field by keeping the key
/// and ignoring it, never by deleting it.
///
/// Autogenerate the RON config for the **dev release pair** that nornir embeds:
///
/// * `/cache`    — a writable-znippy primary with a `crates-io` (`rust-remote`)
///   upstream → a transparent caching crates.io mirror (write-through fills it).
/// * `/sparring` — a writable-znippy registry → the release rehearsal target;
///   `seal` freezes it into a static drift snapshot.
///
/// nornir writes this to a file and launches `holger start --config <file>` as a
/// subprocess (no library linking, no version coupling). cargo speaks the HTTP
/// SPARSE protocol, so it talks to `http_addr` (the FastRoutes gateway), e.g.
/// `sparse+http://<http_addr>/cache/index/`; `grpc_url` is the control plane.
/// Archives live under `data_dir`.
/// Render the **dev release pair** config — the registry nornir embeds:
///
/// * `/cache`    — a writable-znippy primary with a `crates-io` (`rust-remote`)
///   upstream → a transparent caching crates.io mirror (write-through fills it).
/// * `/sparring` — a writable-znippy registry → the release rehearsal target;
///   `seal` freezes it into a static drift snapshot.
///
/// nornir writes this to a file and launches `holger start --config <file>` as a
/// subprocess (no library linking, no version coupling). cargo speaks the HTTP
/// SPARSE protocol, so it talks to `http_addr` (the FastRoutes gateway), e.g.
/// `sparse+http://<http_addr>/cache/index/`; `grpc_url` is the control plane.
/// Archives live under `data_dir`.
///
/// nornir launches `holger start --config <that file>`, which makes the output a
/// shipped contract (pinned byte for byte by `dev_pair_ron_is_byte_identical`).
pub fn dev_pair_ron(data_dir: &Path, grpc_url: &str, http_addr: &str) -> String {
    let d = data_dir.display();
    let grpc = bare_socket_addr(grpc_url);
    let http = bare_socket_addr(http_addr);
    let mut stores = String::new();
    // The crates.io pull-through (`rust-remote`) exists only with `online`. Without it
    // (the small default build, 2026-10-22) the pair is two local registries, so the
    // first start still boots instead of refusing an unknown repository type.
    let online = cfg!(feature = "online");
    let mut repos = String::new();
    if online {
        repos.push_str(
            "        ( ron_name: \"crates-io\", ron_repo_type: \"rust-remote\", ron_base_url: Some(\"https://index.crates.io\"), ron_upstreams: [], ron_in: None, ron_out: None ),\n",
        );
    }
    let upstreams = if online { "[\"crates-io\"]" } else { "[]" };
    for n in ["cache", "sparring"] {
        stores.push_str(&format!(
            "        ( ron_name: \"{n}-store\", ron_storage_type: \"znippy\", ron_path: \"{d}/{n}/\" ),\n"
        ));
        repos.push_str(&format!(
            "        ( ron_name: \"{n}\", ron_repo_type: \"rust\", ron_writable_archive: Some(\"{d}/{n}.znippy\"), ron_base_url: Some(\"http://{http}\"), ron_upstreams: {upstreams}, ron_in: None, ron_out: Some(( ron_storage_endpoint: \"{n}-store\", ron_exposed_endpoint: \"dev\" )) ),\n"
        ));
    }
    format!(
        "(\n    exposed_endpoints: [\n        ( ron_name: \"dev\", ron_url: \"{grpc}\", ron_http_url: Some(\"{http}\") ),\n    ],\n    storage_endpoints: [\n{stores}    ],\n    repositories: [\n{repos}    ],\n)\n"
    )
}

/// Reduce a URL-ish string to a bare `host:port`: strip any `scheme://` prefix and
/// any trailing `/path`. `start` parses `ron_url` / `ron_http_url` as bare
/// `SocketAddr`s.
pub(crate) fn bare_socket_addr(url: &str) -> &str {
    let no_scheme = url.split_once("://").map_or(url, |(_, rest)| rest);
    no_scheme.split(['/', '?', '#']).next().unwrap_or(no_scheme)
}

#[cfg(test)]
mod dev_pair_tests {
    /// Without `online` there is no `rust-remote` type, so the pair has no crates.io
    /// upstream: two local registries that boot.
    #[cfg(not(feature = "online"))]
    #[test]
    fn dev_pair_ron_without_online_names_no_remote() {
        let r = crate::dev_pair_ron(std::path::Path::new("/d"), "127.0.0.1:1", "127.0.0.1:2");
        assert!(
            !r.contains("rust-remote") && !r.contains("crates-io"),
            "{r}"
        );
        assert!(
            r.contains("ron_name: \"cache\"") && r.contains("ron_name: \"sparring\""),
            "{r}"
        );
    }

    #[cfg(feature = "online")]
    #[test]
    fn dev_pair_ron_is_byte_identical() {
        let dir = std::path::PathBuf::from("/var/lib/holger-dev");
        let rendered = crate::dev_pair_ron(&dir, "https://127.0.0.1:18443", "127.0.0.1:18444");
        let expected = r#"(
    exposed_endpoints: [
        ( ron_name: "dev", ron_url: "127.0.0.1:18443", ron_http_url: Some("127.0.0.1:18444") ),
    ],
    storage_endpoints: [
        ( ron_name: "cache-store", ron_storage_type: "znippy", ron_path: "/var/lib/holger-dev/cache/" ),
        ( ron_name: "sparring-store", ron_storage_type: "znippy", ron_path: "/var/lib/holger-dev/sparring/" ),
    ],
    repositories: [
        ( ron_name: "crates-io", ron_repo_type: "rust-remote", ron_base_url: Some("https://index.crates.io"), ron_upstreams: [], ron_in: None, ron_out: None ),
        ( ron_name: "cache", ron_repo_type: "rust", ron_writable_archive: Some("/var/lib/holger-dev/cache.znippy"), ron_base_url: Some("http://127.0.0.1:18444"), ron_upstreams: ["crates-io"], ron_in: None, ron_out: Some(( ron_storage_endpoint: "cache-store", ron_exposed_endpoint: "dev" )) ),
        ( ron_name: "sparring", ron_repo_type: "rust", ron_writable_archive: Some("/var/lib/holger-dev/sparring.znippy"), ron_base_url: Some("http://127.0.0.1:18444"), ron_upstreams: ["crates-io"], ron_in: None, ron_out: Some(( ron_storage_endpoint: "sparring-store", ron_exposed_endpoint: "dev" )) ),
    ],
)
"#;
        assert_eq!(rendered, expected, "the dev-pair RON is a shipped contract");
    }
}

impl Holger {
    // ─── Rust API (direct function bindings) ─────────────────────────

    /// Fetch an artifact by ID from a named repository
    pub fn fetch(
        &self,
        repository: &str,
        id: &traits::ArtifactId,
    ) -> Result<Option<traits::Bytes>> {
        let repo = self.get_repo(repository)?;
        repo.fetch(id)
    }

    /// Store an artifact (write-enabled repos only)
    pub fn put(&self, repository: &str, id: &traits::ArtifactId, data: &[u8]) -> Result<()> {
        let repo = self.get_repo(repository)?;
        if !repo.is_writable() {
            anyhow::bail!("Repository '{}' is read-only", repository);
        }
        repo.put(id, data)
    }

    /// List all configured repository names
    pub fn list_repositories(&self) -> Vec<&str> {
        self.repositories
            .iter()
            .map(|r| r.ron_name.as_str())
            .collect()
    }

    /// Cross-repo artifact SEARCH (parity §H, `.nornir/search-design.md`) — the
    /// in-process entry the CLI's `search` verb drives. Collects every configured
    /// repository's live backend and runs the pure [`search::search_repos`]
    /// engine against `query`. Repos with no materialized backend (a virtual
    /// `group`, or one disabled on init) are skipped — their members are searched
    /// directly. Read-only: it never mutates a backend.
    pub fn search_all(&self, query: &search::SearchQuery) -> search::SearchResults {
        let repos = self.backend_pairs();
        #[cfg(feature = "intake")]
        let props = self.property_store();
        #[cfg(feature = "intake")]
        let lookup: Option<&dyn search::PropertyLookup> = Some(&*props);
        #[cfg(not(feature = "intake"))]
        let lookup: Option<&dyn search::PropertyLookup> = None;
        search::search_repos_with_properties(&repos, query, lookup)
    }

    /// Collect this store's `(repo_name → live backend)` pairs — the raw material
    /// both `search_all` and the replication adapters aggregate over. Repos with
    /// no materialized backend (a virtual `group`, or one disabled on init) are
    /// skipped.
    fn backend_pairs(&self) -> Vec<(String, Arc<dyn RepositoryBackendTrait>)> {
        self.repositories
            .iter()
            .filter_map(|r| {
                r.backend_repository
                    .as_ref()
                    .map(|b| (r.ron_name.clone(), b.clone()))
            })
            .collect()
    }

    /// `(name, writable)` for every repository this config declares — what
    /// [`mirror_cycle::MirrorSettings::validate`] checks a `ron_to` against.
    ///
    /// Writability is read off the BACKEND, never off the RON: a repo can
    /// declare a `ron_data_dir` and still come out of `instantiate_backends`
    /// read-only (a sealed archive, a tier that linked no writer). The mirror
    /// has to refuse on what the process actually holds, not on what the file
    /// intended.
    pub fn mirror_repo_posture(&self) -> Vec<(String, bool)> {
        self.repositories
            .iter()
            .map(|r| {
                (
                    r.ron_name.clone(),
                    r.backend_repository
                        .as_ref()
                        .map(|b| b.is_writable())
                        .unwrap_or(false),
                )
            })
            .collect()
    }

    /// **The per-repository read scope**, derived from the repositories that
    /// declared [`ron_require_auth`](crate::repository::Repository::ron_require_auth).
    ///
    /// Derived from the config each time it is asked for, because configuration
    /// cannot change under a running server — the same reasoning the flow graph
    /// is built once on. Empty for every config that has not opted in, which
    /// makes [`crate::auth::ReadGate::applies`] exactly
    /// `AuthConfig::read_gate_enabled()` and the whole path a no-op.
    ///
    /// **ONE gate per server** (lane P4): built on the first call — the wiring's, after every
    /// repository of this start is declared — and the SAME `Arc` from then on, so a repository
    /// gated at run time ([`crate::auth::ReadGate::gate_from_now`]) is gated on every door.
    pub fn read_gate(&self) -> Arc<crate::auth::ReadGate> {
        Arc::clone(self.read_gate_cell.get_or_init(|| {
            Arc::new(
                crate::auth::ReadGate::from_repos(
                    self.repositories
                        .iter()
                        .filter(|r| r.ron_require_auth)
                        .map(|r| r.ron_name.clone()),
                )
                .with_floors(
                    self.repositories
                        .iter()
                        .filter_map(|r| r.ron_require_role.map(|f| (r.ron_name.clone(), f))),
                )
                // HCON-5: the public repositories and the front's word, one gate for every door.
                .with_access(
                    self.repositories
                        .iter()
                        .filter(|r| r.ron_visibility == posture::Visibility::Public)
                        .map(|r| r.ron_name.clone()),
                    self.access.unwrap_or_default().front == posture::Front::Open,
                )
                // Lane P1: every repository made at run time is its owner's (owner-only).
                .with_sold(self.sold_owners()),
            )
        }))
    }

    /// **The repositories paid orders made, and whose they are** (lane P1): the ONE table
    /// every read gate of this process holds, read once from `fleet/sold-repositories.json`.
    /// Each is private: read and written by its owner and the estate's admins only.
    pub fn sold_owners(&self) -> crate::auth::SoldOwners {
        self.sold_owners_cell
            .get_or_init(|| {
                let path = self.data_root().join(crate::sold::SOLD_REPOS_REL);
                let sold: std::collections::BTreeMap<String, crate::sold::SoldRepository> =
                    std::fs::read(&path)
                        .ok()
                        .and_then(|b| serde_json::from_slice(&b).ok())
                        .unwrap_or_default();
                let root = self.data_root();
                crate::auth::SoldOwners::of_entries(sold.into_iter().map(|(name, s)| {
                    let e = crate::sold::gate_entry(&root, &s);
                    (name, e)
                }))
            })
            .clone()
    }

    /// The backend behind one repository name, or `None`.
    pub fn backend_named(&self, name: &str) -> Option<Arc<dyn RepositoryBackendTrait>> {
        self.repositories
            .iter()
            .find(|r| r.ron_name == name)
            .and_then(|r| r.backend_repository.clone())
    }

    /// Replicate this store's artifacts INTO another holger store (parity §I,
    /// `.nornir/replication-design.md`) — the in-process entry the CLI's
    /// `replicate --from --to` verb drives. Reads only from `self` (the source)
    /// and writes only into `dest` (the destination); content-id idempotent, so a
    /// re-run over an unchanged source is a no-op.
    ///
    /// Repos are paired by NAME: a source repo `foo` replicates into the
    /// destination repo `foo`. A source repo with no same-named writable
    /// destination is reported (its artifacts fail closed), never silently
    /// dropped. `only` restricts the run to a single source repo name; `None`
    /// replicates every repo that has a destination match. See
    /// [`replication::replicate_pairs`].
    pub fn replicate_into(
        &self,
        dest: &Holger,
        only: Option<&str>,
        opts: &replication::ReplicationOptions,
    ) -> replication::ReplicationReport {
        let sources = self.backend_pairs();
        let dests = dest.backend_pairs();
        let mut pairs = Vec::new();
        for (sname, sback) in &sources {
            if let Some(want) = only {
                if sname != want {
                    continue;
                }
            }
            match dests.iter().find(|(dname, _)| dname == sname) {
                Some((dname, dback)) => pairs.push(replication::RepoPair {
                    source_name: sname.clone(),
                    source: sback.clone(),
                    dest_name: dname.clone(),
                    dest: dback.clone(),
                }),
                None => log::warn!(
                    "replicate: source repo '{sname}' has no same-named destination repo — skipped"
                ),
            }
        }
        replication::replicate_pairs(&pairs, opts)
    }

    /// The configured audit dir, anchored. An ABSOLUTE `audit.dir` is honoured
    /// verbatim (explicit config always wins). A RELATIVE one — including the
    /// `holger-audit` default — resolves against `$HOLGER_DATA` when set, and
    /// only against the process CWD when it is not.
    ///
    /// `$HOLGER_DATA` is the data root the installer already establishes
    /// (`~/.holger` user scope, `/home/holger/.holger` system scope — see
    /// `holger-server install`, which also sets `WorkingDirectory=`). An
    /// installed server therefore resolves identically either way. What this
    /// fixes is the EMBEDDED/uninstalled case: holger running in-process inside
    /// dwarves or nornir inherits *their* CWD, so a CWD-relative default
    /// scattered a fresh audit log, job ledger, PROD store, SBOM and property
    /// store into whatever directory the host happened to start in.
    pub fn resolved_audit_dir(&self) -> std::path::PathBuf {
        anchor_under_data_root(&self.audit.dir, std::env::var_os("HOLGER_DATA"))
    }

    /// The resolved `ron_plugin_dir` — the `.wasm` package handlers' HOME, anchored
    /// the same way the audit dir is.
    ///
    /// `None` ⇒ no plugin directory is configured and the loader never runs.
    ///
    /// It had NO declared home before: the configured string was handed to
    /// `catalog.load_dir(dir)` RAW, so a relative `plugins` resolved against
    /// whatever the process's CWD happened to be. For an installed server that is
    /// `WorkingDirectory=` and looks fine; for holger running in-process inside
    /// dwarves or nornir it is the HOST's CWD, and for a reinstall it is wherever
    /// the new unit starts. So "does a module survive a reinstall" had no answer
    /// from the config — it depended on the directory the process was started in,
    /// which is exactly the drift `resolved_audit_dir` was written to end for the
    /// audit log, the job ledger, the PROD store, the SBOM and the property store.
    /// A loaded `.wasm` is code holger executes; where it comes from is not a
    /// question that should be answered by an accident of the launcher.
    ///
    /// Absolute stays absolute (an operator who names `/srv/holger-plugins` means
    /// it); relative joins `$HOLGER_DATA` when the installer has set one, and only
    /// falls back to CWD-relative when it has not — identical policy, one helper.
    pub fn resolved_ron_plugin_dir(&self) -> Option<std::path::PathBuf> {
        let dir = self
            .ron_plugin_dir
            .as_deref()
            .map(str::trim)
            .filter(|d| !d.is_empty())?;
        Some(anchor_under_data_root(dir, std::env::var_os("HOLGER_DATA")))
    }

    /// The server-side operational data root — the parent of the resolved audit
    /// dir (the promote PROD store + jera job ledger already live under it), or
    /// `.` when no meaningful audit dir is set. One derivation so the gRPC
    /// endpoint, the HTTP gateway, and the CLI all agree on where server state is.
    pub fn data_root(&self) -> std::path::PathBuf {
        self.resolved_audit_dir()
            .parent()
            .filter(|p| !p.as_os_str().is_empty())
            .map(|p| p.to_path_buf())
            .unwrap_or_else(|| std::path::PathBuf::from("."))
    }

    /// Where the pairing records live ([`crate::peers`]): the SAME data root the
    /// gRPC endpoint and the HTTP gateway use, so the `pair` verb, the serving
    /// doors and the mirror's pin check can never read two different places.
    /// **Every place the running config keeps repository bytes** (lane HOL-312-HARDEN): each
    /// repository's storage places and each storage endpoint's `ron_path` — history for
    /// [`crate::twin::held_state_with`].
    pub fn history_stores(&self) -> Vec<std::path::PathBuf> {
        let mut out: Vec<std::path::PathBuf> = self
            .repositories
            .iter()
            .flat_map(|r| r.storage_places())
            .map(|p| p.path)
            .collect();
        out.extend(
            self.storage_endpoints
                .iter()
                .map(|e| std::path::PathBuf::from(&e.ron_path)),
        );
        out
    }

    /// **Where every repository keeps its bytes**, by name — the `disk_bytes`
    /// meter's places (`repository::Repository::storage_places`).
    pub fn storage_places(&self) -> crate::metering::StoragePlaces {
        self.repositories
            .iter()
            .map(|r| (r.ron_name.clone(), r.storage_places()))
            .collect()
    }

    /// **Each repository's upstreams**, by name: its `upstreams` and its
    /// remote-cache upstream repository. Non-empty = what it holds is a CACHE
    /// (the admin view's cache line).
    /// **Start the disk speed test** over the data root (the admin view's
    /// speed figure), once per process, on its own thread. The CLI calls it on
    /// an appliance's PID 1 or when `HOLGER_DISK_SPEED` is set.
    pub fn start_disk_speed_test(&self) -> bool {
        crate::admin_view::start_disk_speed(self.data_root())
    }

    pub fn upstreams(&self) -> std::collections::BTreeMap<String, Vec<String>> {
        self.repositories
            .iter()
            .map(|r| {
                let mut u = r.ron_upstreams.clone();
                u.extend(r.ron_upstream_repo.iter().cloned());
                (r.ron_name.clone(), u)
            })
            .collect()
    }

    /// **The paid-state store**, metered build only; `None` when no
    /// `monetize_pubkey` is configured. A configured key that is not a 32-byte
    /// Ed25519 key is a BOOT ERROR, not a warning: the alternative is a metered
    /// server that looks configured, answers `holger-metered-4` on every push,
    /// and is discovered by monetize rather than by the operator. The gRPC
    /// `EntitlementService` and both write doors' [`metered::Quota`] open the
    /// SAME directory through this one function.
    #[cfg(feature = "metered")]
    pub fn entitlement_store(
        &self,
    ) -> anyhow::Result<Option<Arc<crate::metered::EntitlementStore>>> {
        if let Some(s) = self.entitlement_cell.get() {
            return Ok(s.clone());
        }
        let made = self.make_entitlement_store()?;
        Ok(self.entitlement_cell.get_or_init(|| made).clone())
    }

    #[cfg(feature = "metered")]
    fn make_entitlement_store(
        &self,
    ) -> anyhow::Result<Option<Arc<crate::metered::EntitlementStore>>> {
        let Some(hex_key) = self
            .monetize_pubkey
            .as_deref()
            .map(str::trim)
            .filter(|s| !s.is_empty())
        else {
            // An estate box whose front offers the key after the start: keyless until then.
            if self.monetize_key_awaited {
                return Ok(Some(Arc::new(crate::metered::EntitlementStore::awaiting(
                    crate::metered::store_dir(&self.data_root()),
                    self.repository_roster(),
                ))));
            }
            return Ok(None);
        };
        let raw = hex::decode(hex_key).map_err(|e| {
            anyhow::anyhow!(errcode::METERED_NOT_CONFIGURED.tag(format!(
                "`monetize_pubkey` is not hex ({e}) — it is monetize's 32-byte Ed25519 \
                 public key as 64 lowercase hex characters"
            )))
        })?;
        let bytes: [u8; 32] = raw.as_slice().try_into().map_err(|_| {
            anyhow::anyhow!(errcode::METERED_NOT_CONFIGURED.tag(format!(
                "`monetize_pubkey` decodes to {} byte(s); an Ed25519 public key is 32",
                raw.len()
            )))
        })?;
        let store = crate::metered::EntitlementStore::new(
            crate::metered::store_dir(&self.data_root()),
            self.repository_roster(),
            &bytes,
        )
        .map_err(|_| {
            anyhow::anyhow!(errcode::METERED_NOT_CONFIGURED
                .tag("`monetize_pubkey` is 32 bytes but not a valid Ed25519 public key"))
        })?;
        Ok(Some(Arc::new(store)))
    }

    /// **The artifact flow's door** (lane HOL-SVG-RON): the shape read from this RON and the
    /// paid repositories' disk caps. `None` when the RON says `flow: (enabled: false)`.
    pub fn flow_door(&self) -> Option<crate::flow::Door> {
        if !self.flow.enabled {
            return None;
        }
        #[cfg(feature = "metered")]
        let quota: Option<crate::flow::QuotaOf> = self.quota().ok().flatten().map(|q| {
            Arc::new(move |repo: &str| {
                q.store()
                    .get(repo)
                    .ok()
                    .flatten()
                    .and_then(|f| f.caps.get(crate::metering::METER_DISK_BYTES).copied())
                    .filter(|c| *c > 0)
            }) as crate::flow::QuotaOf
        });
        #[cfg(not(feature = "metered"))]
        let quota: Option<crate::flow::QuotaOf> = None;
        Some(crate::flow::Door {
            shape: Arc::new(crate::flow::FlowGraph::from_config(self)),
            quota,
        })
    }

    /// **The write doors' paid-limit check** (`metered::Quota`), metered build
    /// only; `None` when nothing is paid for (no monetize key). ONE per process
    /// (`quota_cell`): the HTTP door and every `QuotaGuard` share its readings.
    #[cfg(feature = "metered")]
    pub fn quota(&self) -> anyhow::Result<Option<Arc<crate::metered::Quota>>> {
        if let Some(q) = self.quota_cell.get() {
            return Ok(q.clone());
        }
        let made = self.entitlement_store()?.map(|store| {
            Arc::new(
                crate::metered::Quota::new(store, Arc::new(self.storage_places()))
                    .with_pay_page(self.pay_page.clone()),
            )
        });
        Ok(self.quota_cell.get_or_init(|| made).clone())
    }

    /// **What takes a late monetize key and pay page live** (A7 row 8): the paid-state
    /// store, the paid limit, the growth's go-ahead policy and the meter plugin's key slot
    /// this process holds. Asked after [`Holger::start`]: every one is made by then.
    #[cfg(feature = "metered")]
    pub fn live_keys(&self) -> crate::late::LiveKeys {
        crate::late::LiveKeys {
            store: self.entitlement_cell.get().cloned().flatten(),
            quota: self.quota_cell.get().cloned().flatten(),
            grow: self.grow_policy_cell.get().cloned(),
            meters: Some(self.data_root().join("meters")),
        }
    }

    pub fn peers_root(&self) -> std::path::PathBuf {
        self.data_root()
    }

    /// **The growth's write hold** — ONE per process, see `grow_hold_cell`.
    pub fn grow_hold(&self) -> Arc<crate::grow::Hold> {
        Arc::clone(
            self.grow_hold_cell
                .get_or_init(|| Arc::new(crate::grow::Hold::new())),
        )
    }

    /// **The growth plane** — the machine over holger's world, made once:
    /// the hold every repository was wrapped with, the twin roster under the
    /// data root, this build's volume layer (`grow-runtime`: korp's md set) and
    /// who may say go (monetize's key on a metered box that has one).
    /// `None` in a build without `estate` (H2's estate cut): there is no plane to make.
    #[cfg(not(feature = "estate"))]
    pub fn grow_plane(
        &self,
        _posture: &Arc<crate::twin::TwinPosture>,
    ) -> Option<Arc<crate::grow::GrowPlane>> {
        None
    }

    #[cfg(feature = "estate")]
    pub fn grow_plane(
        &self,
        posture: &Arc<crate::twin::TwinPosture>,
    ) -> Option<Arc<crate::grow::GrowPlane>> {
        Some(Arc::clone(self.grow_plane_cell.get_or_init(|| {
            let root = self.data_root();
            let roster = Arc::new(crate::grow::TwinRoster::new(Some(&root)));
            let world = crate::grow::HolgerWorld::new(
                self.grow_hold(),
                roster,
                crate::grow::build_volumes(),
                Arc::clone(posture),
                root,
            );
            let key = self
                .monetize_pubkey
                .as_deref()
                .map(str::trim)
                .and_then(|h| hex::decode(h).ok())
                .and_then(|b| <[u8; 32]>::try_from(b.as_slice()).ok());
            #[cfg(feature = "metered")]
            let policy: Arc<dyn appliance_grow::GoAheadPolicy> = self
                .grow_policy_cell
                .get_or_init(|| crate::grow::monetize_policy(key))
                .clone();
            #[cfg(not(feature = "metered"))]
            let policy = crate::grow::go_ahead_policy(key);
            crate::grow::GrowPlane::new(world, policy)
        })))
    }

    /// The property keys this config reserves to ADMIN, resolved.
    ///
    /// `reserved_properties` when declared (an explicit list REPLACES the default,
    /// including an empty one), else [`grpc::DEFAULT_RESERVED_PROPERTY_KEYS`].
    ///
    /// This is the ONE resolution, read by both halves that have to agree: the
    /// server installs it as the gRPC admin floor, and the `retention` verb
    /// refuses a `--protect-property` key that is not in it. A pin an ordinary
    /// Writer can set over the wire is not a pin, and the two halves live in
    /// different processes, so they must not each carry their own idea of the
    /// default.
    #[cfg(feature = "intake")]
    pub fn reserved_property_keys(&self) -> Vec<String> {
        match &self.reserved_properties {
            Some(keys) => keys.clone(),
            None => grpc::DEFAULT_RESERVED_PROPERTY_KEYS
                .iter()
                .map(|k| k.to_string())
                .collect(),
        }
    }

    /// The hosted-SBOM store (`<data_root>/holger-sboms`). Derived once from the
    /// config so every surface — the `SbomService` gRPC RPCs, the `GET /-/sbom`
    /// HTTP door, and the `holger-server sbom` CLI verb — reads and writes the
    /// same on-disk store. See [`sbom::SbomStore`], `.nornir/sbom-hosting-design.md`.
    #[cfg(feature = "intake")]
    pub fn sbom_store(&self) -> sbom::SharedSbomStore {
        Arc::new(sbom::SbomStore::with_roster(
            self.data_root().join("holger-sboms"),
            self.repository_roster(),
        ))
    }

    /// The set of repository names this server serves, EXACTLY as configured.
    ///
    /// The metadata stores (`sbom_store`, `property_store`) are closed to this
    /// set. Both are addressed by a repository name that arrives off the wire and
    /// is authorized as a raw string, so without a roster a caller writes metadata
    /// for repositories that do not exist — unbounded directories nobody reaps,
    /// and a property search axis seeded with coordinates that were never served.
    #[allow(dead_code)]
    fn repository_roster(&self) -> Arc<std::collections::BTreeSet<String>> {
        Arc::new(
            self.repositories
                .iter()
                .map(|r| r.ron_name.clone())
                .collect(),
        )
    }

    /// The custom-property store (`<data_root>/holger-props`). Derived once from
    /// the config so every surface — the gRPC `Search` property axis, the
    /// `GET`/`PUT /-/properties` HTTP door, and the `holger-server properties` CLI
    /// verb — reads and writes the same on-disk store. See
    /// [`properties::PropertyStore`], `.nornir/properties-design.md`.
    #[cfg(feature = "intake")]
    pub fn property_store(&self) -> properties::SharedPropertyStore {
        Arc::new(properties::PropertyStore::with_roster(
            self.data_root().join("holger-props"),
            self.repository_roster(),
        ))
    }

    /// The **passkey door** over this box's identity core (`<data_root>/holger-passkeys`), or
    /// `None` when no `Passkey` auth method is configured: the whole ceremony surface then
    /// answers 404, so a console can tell "not configured" from "configured and refusing".
    #[cfg(feature = "auth-passkey")]
    pub fn passkey_door(&self) -> anyhow::Result<Option<passkey::SharedPasskeyDoor>> {
        let Some(auth) = self.auth.as_ref() else {
            return Ok(None);
        };
        let resolved = self.serving_auth();
        let Some(door) = passkey::PasskeyDoor::open_at(
            &self.data_root(),
            &resolved,
            self.build_audit_log()?,
            self.estate_site,
        )
        .map_err(|e| anyhow::anyhow!("passkey door: {e}"))?
        else {
            return Ok(None);
        };
        door.attach_gate(self.read_gate());
        // The shop's first-login invite (lane P1), taken in once, on the primary: a twin's
        // people are its primary's, replicated.
        if let Some(first) = self
            .first_admin_invite
            .as_ref()
            .filter(|_| !self.role.is_twin())
        {
            match door.seed_first_admin(first, auth) {
                Ok(said) => log::warn!("passkey: the shop's first-login invite: {said}"),
                Err((_, why)) => {
                    log::error!("passkey: the shop's first-login invite was NOT taken in: {why}")
                }
            }
        }
        Ok(Some(door))
    }

    /// **Sign-up follows the site the feed declares, live** (RA5-1): the identity core this
    /// process serves takes the site a later feed declares (`identity_host::follow_site`). A
    /// box with no core (no passkey method) follows nothing.
    pub fn follow_estate_site(&self, feeds: &dyn late::Feeds) {
        #[cfg(feature = "auth-passkey")]
        if let Some(core) = identity_host::live(&self.serving_auth()) {
            identity_host::follow_site(feeds, core);
        }
        #[cfg(not(feature = "auth-passkey"))]
        let _ = feeds;
    }

    /// The auth config with any `Passkey` method's `session_key_file` resolved to
    /// its default (`<data_root>/holger-passkeys/session.key`) when the operator
    /// left it unset. See the call site in [`Holger::start`] for why this must
    /// happen before anything reads the config.
    /// ★ **The auth config every door serves with** (lane HOL-NCSC): the file's, with the
    /// passkey session key resolved ([`Holger::start`] says why), and **configured
    /// authentication implies RBAC**: methods with no role policy (no `roles`, `group_roles`,
    /// `repo_roles`, `duties` or `default_role`) serve with `default_role: Reader`. Before, such
    /// a config was authentication-only and every signed-in identity could do everything.
    pub fn serving_auth(&self) -> auth::AuthConfig {
        let mut cfg = self.auth_config_with_resolved_passkey_key();
        if !cfg.methods.is_empty() && !cfg.rbac_enabled() {
            cfg.default_role = Some(auth::Role::Reader);
        }
        cfg
    }

    fn auth_config_with_resolved_passkey_key(&self) -> auth::AuthConfig {
        let mut cfg = self.auth.clone().unwrap_or_default();
        // P3: a box with passkey sign-in and no Admin group gets the people's groups.
        let _ = auth::passkey_groups(&mut cfg);
        let default_key = self.data_root().join("holger-passkeys").join("session.key");
        for method in &mut cfg.methods {
            if let auth::AuthMethodConfig::Passkey {
                session_key_file, ..
            } = method
            {
                if session_key_file
                    .as_deref()
                    .map(str::trim)
                    .unwrap_or("")
                    .is_empty()
                {
                    *session_key_file = Some(default_key.to_string_lossy().into_owned());
                }
            }
        }
        cfg
    }

    /// The **promotion engine** (`<data_root>/holger-prod-store`), gates and all.
    /// Derived once from the config so every surface — the gRPC
    /// `PromotionService.PromoteArtifact` RPC and the `holger-server promote` CLI
    /// verb — seals into the SAME immutable PROD store under the SAME policy.
    ///
    /// The gates come from the RON `promotion` block, so a local promotion cannot
    /// be a way to walk around the boundary the served path enforces.
    ///
    /// ## What changed, and why
    ///
    /// Until 2026-09-20 the gate was `promote::PolicyGate::from_env()`, reading
    /// `HOLGER_PROMOTE_ENFORCE`, `HOLGER_PROMOTE_ADVISORY_DB` and
    /// `HOLGER_PROMOTE_DENY_LICENSES`. On an appliance with no OS and no shell
    /// those are a configuration surface the operator cannot reach, and unset
    /// meant `Ok(None)`, which meant every promotion passed unscanned and looked
    /// in the log exactly like one that had been scanned. All three are now keys
    /// on `promotion:`.
    ///
    /// **HOL-GUN-007**: a rung that names a gate this build does not carry is an
    /// `Err` here, so serve refuses to start and the CLI refuses to promote. The
    /// alternative is a rung that reads as guarded and runs unguarded. The same
    /// is true of an advisory feed that cannot be read.
    ///
    /// * **Property requirement** (`promotion.require_property: Some("key=value")`,
    ///   or a bare `key`) — OFF unless set. The old `HOLGER_PROMOTE_REQUIRE_PROPERTY`
    ///   environment variable is refused by name.
    /// * **The served set** — the repositories this config declares. A promotion to
    ///   any other name is `PromoteError::UnknownRepository`, always.
    ///
    /// No job ledger is attached here: that is serve-side observability which the
    /// server layers on with [`promote::PromotionEngine::with_job_ledger`].
    /// **The evidence plane** — the [`attest::AttestStore`] under
    /// `<data_root>/holger-attestations`, with its trust roots read from the
    /// `attestation:` block.
    ///
    /// ## Why this function had to be written
    ///
    /// MEASURED at `542e296`: `server/lib/src/attest.rs` is 1 458 lines
    /// implementing DSSE + in-toto verification keyed by artifact digest,
    /// delegating the cryptography wholly to `znippy_common::sign::dsse_verify`,
    /// and **nothing in the shipped tree constructed an `AttestStore`**. There
    /// was no door, no constructor and no consumer. A verification plane nobody
    /// can reach verifies nothing.
    ///
    /// `None` when the config declares no `attestation:` block, which is the
    /// state every existing deployment is in; the store is then absent rather
    /// than empty, and a gate that needs evidence says so.
    ///
    /// ⚠ Trust roots are REQUIRED when the block is present. A store with no
    /// roots would accept a self-signed statement, and the point of the plane is
    /// that the statement chains to something the operator chose.
    #[cfg(feature = "intake")]
    pub fn attestation_store(&self) -> Result<Option<Arc<attest::AttestStore>>> {
        let Some(cfg) = &self.attestation else {
            return Ok(None);
        };
        if cfg.trust_roots.is_empty() {
            anyhow::bail!(
                "config `attestation`: no `trust_roots`. An attestation store with no roots \
                 accepts a statement signed by anything, including the uploader, which is not \
                 evidence. Name at least one DER-encoded root certificate path, or remove the \
                 block."
            );
        }
        let roots: Vec<std::path::PathBuf> = cfg
            .trust_roots
            .iter()
            .map(std::path::PathBuf::from)
            .collect();
        // A declared root that cannot be read is a NAMED boot refusal carrying the
        // frozen `holger-attest-10`, never a quietly shorter trust set. See
        // `attest::AttestTrust::from_der_root_files`.
        let mut trust = attest::AttestTrust::from_der_root_files(&roots).map_err(|e| {
            anyhow::anyhow!(
                "config `attestation.trust_roots`: {}. An unreadable root refuses the boot \
                 rather than becoming a store that trusts nothing and reports every statement \
                 as unverifiable.",
                e.tagged()
            )
        })?;
        // Enrolment: this holger's own signer (its evidence is filed through
        // `accept` like anybody's, so an unenrolled own key could sign nothing),
        // and every signer the block names.
        for c in cfg.signer_cert.iter().chain(cfg.enrolled_signers.iter()) {
            let der = std::fs::read(c)
                .map(|b| attest::der_of(&b))
                .with_context(|| {
                    format!("config `attestation`: reading the signer certificate '{c}'")
                })?;
            trust.enrol_signer_der(&der);
        }
        let mut policy = attest::AttestPolicy::none();
        for pt in &cfg.accept_predicates {
            policy = policy.accept(pt);
        }
        if cfg.accept_predicates.is_empty() {
            policy = attest::AttestPolicy::default();
        }
        Ok(Some(Arc::new(
            attest::AttestStore::new(self.data_root().join("holger-attestations"))
                .with_trust(trust)
                .with_policy(policy),
        )))
    }

    /// holger's own signing identity for the evidence a promotion emits.
    ///
    /// `None` when the `attestation:` block declares no key, which means a
    /// promotion writes no verification summary. That absence is RECORDED in the
    /// audit detail by the promote handler; it is not the same as a promotion
    /// whose evidence was written.
    ///
    /// Declaring one half of the pair is an error: a key with no certificate
    /// cannot produce a verifiable envelope, and failing at the first promotion
    /// instead of at boot is the wrong time to find out.
    #[cfg(feature = "intake")]
    pub fn attestation_signer(&self) -> Result<Option<Arc<attest::AttestSigner>>> {
        let Some(cfg) = &self.attestation else {
            return Ok(None);
        };
        // HOL-NCSC (NCSC P3): the key in a hardware token.
        if let Some(hsm) = &cfg.signer_hsm {
            if cfg.signer_key.is_some() {
                anyhow::bail!(
                    "{}",
                    holger_errcode::KEY_HSM_REFUSED
                        .tag("config `attestation`: name `signer_key` or `signer_hsm`, not both")
                );
            }
            let Some(c) = &cfg.signer_cert else {
                anyhow::bail!("{}", holger_errcode::KEY_HSM_REFUSED.tag("config `attestation.signer_hsm` needs `signer_cert`, the certificate of the token's key"));
            };
            let HsmSigner::Pkcs11 {
                module,
                token_label,
                key_label,
                pin_file,
            } = hsm;
            #[cfg(feature = "pkcs11")]
            {
                let cert = std::fs::read(c)
                    .with_context(|| format!("config `attestation.signer_cert`: reading '{c}'"))?;
                let alg = znippy_common::sign::SigAlg::from_name(
                    cfg.signer_alg.as_deref().unwrap_or("ed25519"),
                )
                .map_err(|e| anyhow::anyhow!("config `attestation.signer_alg`: {e:#}"))?;
                let signer = pkcs11_signer::Pkcs11Signer::open(
                    module,
                    token_label,
                    key_label,
                    std::path::Path::new(pin_file),
                    alg,
                    cert.clone(),
                )
                .map_err(|e| anyhow::anyhow!("config `attestation.signer_hsm`: {e}"))?;
                return Ok(Some(Arc::new(attest::AttestSigner::from_signer(
                    Box::new(signer),
                    cert,
                ))));
            }
            #[cfg(not(feature = "pkcs11"))]
            {
                let _ = (c, module, token_label, key_label, pin_file);
                anyhow::bail!("{}", holger_errcode::KEY_HSM_REFUSED.tag("config `attestation.signer_hsm`: this build has no PKCS#11 signer; build with the `pkcs11` feature"));
            }
        }
        match (&cfg.signer_key, &cfg.signer_cert) {
            (None, None) => Ok(None),
            (Some(k), Some(c)) => {
                let key = keyfile::read_private(
                    std::path::Path::new(k),
                    "config `attestation.signer_key`",
                )
                .with_context(|| format!("config `attestation.signer_key`: reading '{k}'"))?;
                let cert = std::fs::read(c)
                    .with_context(|| format!("config `attestation.signer_cert`: reading '{c}'"))?;
                let alg = cfg.signer_alg.as_deref().unwrap_or("ed25519");
                let signer = attest::AttestSigner::from_pkcs8(alg, &key, &cert)
                    .map_err(|e| anyhow::anyhow!("config `attestation.signer_*`: {e}"))?;
                Ok(Some(Arc::new(signer)))
            }
            (Some(_), None) => anyhow::bail!(
                "config `attestation`: `signer_key` is set and `signer_cert` is not. A DSSE \
                 envelope carries the signer's certificate fingerprint as its keyid and a \
                 verifier needs the certificate to check it, so a key alone can produce nothing \
                 verifiable."
            ),
            (None, Some(_)) => anyhow::bail!(
                "config `attestation`: `signer_cert` is set and `signer_key` is not. There is \
                 nothing to sign with."
            ),
        }
    }

    /// **The box's repository signing key** (lane HOL-SIGNED-APT-DNF): `None` when the
    /// operator made none (`holger repo-key create`); an error when the key store holds one
    /// that cannot be used, which refuses the boot of a box with deb or rpm views.
    pub fn repo_key(&self) -> Result<Option<Arc<repo_sign::RepoKey>>> {
        if let Some(k) = self.repo_key_cell.get() {
            return Ok(k.clone());
        }
        let k = repo_sign::load(&self.data_root())?.map(Arc::new);
        if let Some(k) = &k {
            log::info!(
                "repository signing key {} ({}): the deb and rpm views sign their metadata; \
                 its public half is at {}",
                k.fingerprint_hex(),
                k.user_id(),
                repo_sign::KEY_PATH
            );
        }
        let _ = self.repo_key_cell.set(k.clone());
        Ok(k)
    }

    /// **The evidence key the public door serves** (lane HOL-OCI-EVIDENCE): the public half
    /// of `attestation.signer_cert`, when the block declares a signer (key AND certificate).
    /// `None` without one; a certificate that does not read is said in the log and serves
    /// nothing (the signer refuses the boot over it elsewhere).
    #[cfg(feature = "intake")]
    pub fn evidence_key(&self) -> Option<Arc<evidence_key::EvidenceKey>> {
        let cfg = self.attestation.as_ref()?;
        let (Some(_), Some(cert)) = (&cfg.signer_key, &cfg.signer_cert) else {
            return None;
        };
        let der = std::fs::read(cert)
            .map_err(|e| log::warn!("evidence key door: reading '{cert}': {e}"))
            .ok()?;
        evidence_key::EvidenceKey::from_cert_der(&der)
            .map_err(|e| log::warn!("evidence key door: '{cert}': {e}"))
            .ok()
            .map(Arc::new)
    }

    /// **The gates THIS BUILD carries**, built from the `promotion:` block and
    /// from the meter guests this boot loaded.
    ///
    /// One catalog, two boundaries: the promotion hop and the ingest PUT. They
    /// must be built from the same declaration, or a gate could mean one thing
    /// on upload and another on promotion.
    ///
    /// How many attestation trust roots this config DECLARES.
    ///
    /// Zero means the evidence plane cannot verify anything — no `attestation:`
    /// block, or a block whose `trust_roots` is empty. The provenance gate needs
    /// this number and not the store, because the number is knowable before the
    /// store is built and the distinction it draws (*could nothing ever have been
    /// verified?*) is a property of the declaration, not of the disk.
    #[cfg(feature = "intake")]
    pub fn declared_trust_roots(&self) -> usize {
        self.attestation
            .as_ref()
            .map(|a| a.trust_roots.len())
            .unwrap_or(0)
    }

    /// Does this config scan at intake (`promotion.intake.scan`)? Then the
    /// gates read the stored evidence back before scanning again (W5).
    #[cfg(feature = "scanners")]
    fn reads_stored_evidence(&self) -> bool {
        self.promotion
            .as_ref()
            .and_then(|p| p.intake.as_ref())
            .is_some_and(|i| i.scan.is_some())
    }

    /// Empty when there is no `promotion:` block **and** no meter is loaded.
    #[cfg(feature = "intake")]
    pub fn gate_catalog(&self) -> Result<promote::GateCatalog> {
        #[allow(unused_mut)]
        let mut cat = match &self.promotion {
            // No `promotion:` block ⇒ no ladder, so nothing resolves a gate set
            // and the catalog is never consulted. It is still built with the
            // provenance gate in it, so `available()` tells the truth about what
            // this BUILD carries rather than about what this FILE configured.
            None => promote::GateCatalog::new().with(std::sync::Arc::new(
                promote::gate::ProvenanceGate::new(
                    promote::ProvenancePolicy::default(),
                    self.declared_trust_roots(),
                ),
            )),
            #[cfg(not(feature = "scanners"))]
            Some(settings) => promote::gate::catalog_from_config(
                settings.require_attestation.clone(),
                settings.provenance,
                self.declared_trust_roots(),
            ),
            #[cfg(feature = "scanners")]
            Some(settings) => {
                let advisory_db = Self::promotion_advisory_db(settings)?;
                // W5: with intake scanning on, the `osv` gate reads the stored
                // evidence first (the SAME pipeline the SCANNED transition writes).
                let osv_with_evidence = self.reads_stored_evidence().then(|| {
                    promote::gate::OsvGate::new(promote::gate::osv_scanner_for(
                        settings.license_policy(),
                        advisory_db.clone(),
                    ))
                    .with_evidence(self.scan_pipeline_cell.clone())
                    .with_worker(self.scan_worker_cell.clone())
                });
                let mut cat = promote::gate::catalog_from_config(
                    advisory_db,
                    settings.license_policy(),
                    settings.require_attestation.clone(),
                    settings.provenance,
                    self.declared_trust_roots(),
                );
                // A gate that is standing and verifying nothing must not print the
                // same line as a gate that is standing and verifying. See
                // `ProvenanceGate::posture`.
                log::info!(
                    "{}",
                    promote::gate::ProvenanceGate::new(
                        settings.provenance,
                        self.declared_trust_roots()
                    )
                    .posture()
                );
                if let Some(g) = osv_with_evidence {
                    cat = cat.with(Arc::new(g));
                }
                cat
            }
        };
        // S7: with an `intel:` block the OSV gate reads the local advisory
        // database (offline by default), re-read per check so an import goes
        // live without a restart, its staleness folded into the verdict. It
        // replaces the `osv` gate by name, so the arm above stays as it was.
        #[cfg(feature = "scanners")]
        if let (Some(settings), Some(intel)) = (&self.promotion, &self.intel) {
            if settings
                .advisory_db
                .as_deref()
                .is_some_and(|p| !p.trim().is_empty())
            {
                anyhow::bail!(
                    "config names two advisory sources: `promotion.advisory_db` and the \
                     `intel:` database. Keep one; the intel database is the signed one."
                );
            }
            let source = intel::scan::IntelSource::from_config(intel)?;
            log::info!(
                "intel: the OSV gate reads the local advisory database at {} ({})",
                intel.db_dir,
                if source.net().is_offline() {
                    "offline: network refused"
                } else {
                    "online, local fallback"
                }
            );
            let scanner = promote::gate::osv_scanner_for(
                settings.license_policy(),
                security::AdvisoryDb::default(),
            );
            let mut gate = promote::gate::OsvGate::new(scanner).with_intel(Arc::new(source));
            if self.reads_stored_evidence() {
                gate = gate
                    .with_evidence(self.scan_pipeline_cell.clone())
                    .with_worker(self.scan_worker_cell.clone());
            }
            cat = cat.with(Arc::new(gate));
        }
        // Every loaded meter guest, under the name its own manifest declares, so
        // a rung may name it explicitly — and `available()` lists it, so an
        // operator who mistypes the name is told what was there. The meter gates
        // also run whether or not they are named; see [`Holger::meter_gates`].
        #[cfg(feature = "meter-guests")]
        for g in self.meter_gates() {
            cat = cat.with(g);
        }
        Ok(cat)
    }

    /// **One `WasmGate` per meter guest this boot loaded.**
    ///
    /// Empty until [`Holger::build_meter_plane`] has run (that is, until
    /// [`wire_holger`] has built the route tables), and the whole method is
    /// absent from a build without `meter-guests`.
    ///
    /// # Why these run without being named in the config
    ///
    /// A meter guest reaches the plugin directory only if an operator put it
    /// there, and it loads only if the same operator gave it a line in the grant
    /// table. That is already two explicit decisions. Making a THIRD one — the
    /// gate name in a `gates:` list — the condition for the guest's verdict to
    /// count would mean a box that loads a metering module, reports it in the
    /// census, and then ignores what it says. A loaded meter that is never
    /// consulted is a meter that is not standing.
    ///
    /// The verdict is applied exactly as `meter_gate` translates it: `Allow` and
    /// `AllowWithNotice` pass (a notice is NOT a refusal; data is never held
    /// hostage), `Refuse` rejects, and an `Err` — a trap, exhausted fuel, a
    /// missed deadline, undecodable bytes — is recorded undecided and REFUSES
    /// the hop. A meter that cannot answer is never a meter that waves
    /// everything through.
    #[cfg(all(feature = "meter-guests", feature = "intake"))]
    pub fn meter_gates(&self) -> Vec<Arc<dyn promote::Gate>> {
        let Some(plane) = &self.meter_plane else {
            return Vec::new();
        };
        plane
            .meters()
            .iter()
            .map(|m| {
                Arc::new(promote::meter_gate::WasmGate::new(Arc::clone(m)))
                    as Arc<dyn promote::Gate>
            })
            .collect()
    }

    /// Append the meter gates to a resolved set, skipping any the configuration
    /// already named so a guest named on a rung is not run twice.
    #[cfg(feature = "intake")]
    pub fn with_meter_gates(&self, set: promote::GateSet) -> promote::GateSet {
        #[cfg(not(feature = "meter-guests"))]
        {
            set
        }
        #[cfg(feature = "meter-guests")]
        {
            let already: Vec<String> = set.names().iter().map(|s| s.to_string()).collect();
            let mut set = set;
            for g in self.meter_gates() {
                if !already.iter().any(|n| n == g.name()) {
                    set = set.with(g);
                }
            }
            set
        }
    }

    /// The union of every exposed endpoint's route table.
    ///
    /// The usage source counts through ONE table; a repository served on two
    /// doors is one repository and must be counted once. First door wins, which
    /// is the same first-hit rule `FastRoutes::lookup` already applies.
    #[cfg(feature = "meter-guests")]
    fn served_routes(&self) -> FastRoutes {
        let mut seen: Vec<(String, Arc<dyn traits::RepositoryBackendTrait>)> = Vec::new();
        for ep in &self.exposed_endpoints {
            let Some(table) = &ep.aggregated_routes else {
                continue;
            };
            for (name, backend) in table.all_repos() {
                if !seen.iter().any(|(n, _)| *n == name) {
                    seen.push((name, backend));
                }
            }
        }
        FastRoutes::new(seen)
    }

    /// **Build the meter plane and lend it the three host services.**
    ///
    /// The usage source is [`crate::metering::count`] over the union of the
    /// route tables; the audit sink is this server's ONE append-only log; the
    /// clock is holger's wall clock, said to be exactly that. See
    /// [`crate::meter_services`] for why none of the three needs a capability
    /// check of its own — the grant table is the authority, and a second copy of
    /// the rule is a second place for the rule to be wrong.
    ///
    /// Called by [`wire_holger`]. It compiles every module, so it is called once.
    #[cfg(feature = "meter-guests")]
    pub fn build_meter_plane(&mut self) -> Result<()> {
        // The store a meter is lent. Its OWN directory under the data root, and
        // never a repository's: a meter that could write where it measures could
        // move its own number. The open guest is granted `store.list` only, and
        // the grant is what bounds this — not this line.
        let store: holger_plugin_host::SharedStore = Arc::new(holger_plugin_host::DirStore::new(
            self.data_root().join("meters"),
        ));
        // The commercial meter plugin's verifying key, fed at load from the box's seed (metered).
        #[cfg(feature = "metered")]
        match crate::fleet_box::meter_key_bytes(self.monetize_pubkey.as_deref()) {
            Some(bytes) => store.put(crate::fleet_box::METER_KEY_SLOT, &bytes).map_err(|e| anyhow::anyhow!("meter plane: the monetize key slot: {e}"))?,
            None => log::warn!("meter plane: no monetize_pubkey handed to this box, so the meter plugin is fed no key and refuses every fact"),
        }
        let services: Arc<dyn holger_plugin_host::MeterHostServices> = Arc::new(
            crate::meter_services::HolgerMeterServices::new(
                self.served_routes(),
                self.build_audit_log()?,
            )
            // The storage meters' filesystem: the same root the door measures,
            // so "how much storage is there" has one answer on this box.
            .with_data_root(self.data_root())
            .with_storage_places(self.storage_places()),
        );
        let catalog = self.meter_catalog(store, services)?;
        if !catalog.is_empty() {
            log::info!(
                "meter plane: {} guest(s) wired to the usage source, the audit sink and the \
                 clock; granted {}",
                catalog.meters().len(),
                catalog
                    .granted_capabilities()
                    .iter()
                    .map(|c| c.name())
                    .collect::<Vec<_>>()
                    .join(", "),
            );
        }
        self.meter_plane = Some(Arc::new(catalog));
        Ok(())
    }

    /// **The gates that run on a PUT**, before the bytes reach the backend.
    ///
    /// Empty ⇒ ingest is ungated, which is what holger has always done. Not the
    /// same as "every ingest gate allowed": the [`nornir_gate::Decision`] an
    /// empty set produces says "none configured".
    ///
    /// A loaded meter guest is appended whether or not the config names it — the
    /// upload boundary is the one a quota has to be enforced at, because a cap
    /// enforced after the upload is a cap that already cost the disk. See
    /// [`Holger::meter_gates`] for why installation is the decision.
    #[cfg(feature = "intake")]
    pub fn ingest_gates(&self) -> Result<promote::GateSet> {
        let named = match &self.promotion {
            Some(settings) if !settings.ingest_gates.is_empty() => {
                self.gate_catalog()?.resolve(&settings.ingest_gates)?
            }
            _ => promote::GateSet::empty(),
        };
        let set = self.with_meter_gates(named);
        if !set.is_empty() {
            log::info!("INGEST gates ENABLED: {}", set.names().join(", "));
        }
        Ok(set)
    }

    /// **The intake chain** this config declares (`promotion.intake`), built once
    /// over `audit` and kept: both doors and the CLI drive the same one. `None`
    /// when the config declares no intake chain.
    ///
    /// Refuses to build when the incoming repository cannot take a write or
    /// cannot map an upload path to a coordinate: the chain tracks every upload
    /// by its coordinate, and an upload it cannot name is one it cannot gate.
    /// The advisory snapshot `promotion.advisory_db` names, read once per caller;
    /// empty when none is named. Shared by the OSV gate and the intake's OSV scanner.
    #[cfg(feature = "scanners")]
    fn promotion_advisory_db(settings: &PromotionSettings) -> Result<security::AdvisoryDb> {
        Ok(match &settings.advisory_db {
            Some(p) if !p.trim().is_empty() => security::advisory_db_from_osv_file(p.trim())
                .with_context(|| {
                    format!(
                        "config `promotion.advisory_db`: '{p}' could not be read as an \
                         OSV/GHSA/RUSTSEC snapshot. A feed that is named and unreadable \
                         refuses the boot rather than becoming an empty gate, because an \
                         empty gate passes everything and says nothing."
                    )
                })?,
            _ => security::AdvisoryDb::default(),
        })
    }

    /// The scanners `promotion.intake.scan` configures, as the pipeline the
    /// SCANNED transition runs: modgunn's OSV scanner over the promotion advisory
    /// snapshot, the wheel SBOM generator, the model scanner (pickle family
    /// blocked except where `models.allow_pickle` says), the licence scanner over
    /// the promotion licence policy, the required kinds as named, an
    /// append-only evidence store under `<data_root>/holger-scan-evidence` that
    /// audits into `audit`, and signed attestations when an attestation signer is
    /// configured, and the provenance scanner when `scan.provenance` is given. The
    /// other kinds stand empty (`error: not configured`).
    #[cfg(feature = "scanners")]
    fn intake_scan_pipeline(
        &self,
        settings: &PromotionSettings,
        scan: &promote::intake::IntakeScanSettings,
        audit: Arc<dyn audit::AuditLog>,
        worker: &Arc<sandbox::ScanWorker>,
    ) -> Result<scanner::ScanPipeline> {
        // S7: with an `intel:` block the vulnerability slot reads the local
        // advisory database (offline, staleness folded in) through S2's trait.
        let vuln: Arc<dyn scanner::Scanner> = match &self.intel {
            Some(intel) => Arc::new(crate::intel::scanner::IntelVulnScanner::new(Arc::new(
                crate::intel::scan::IntelSource::from_config(intel)?,
            ))),
            None => Arc::new(scanner::osv::OsvVulnScanner::new(
                Self::promotion_advisory_db(settings)?,
            )),
        };
        // S10: the model slot; the pickle family is blocked except where
        // `models.allow_pickle` names a declared repository.
        scanner::model::validate_allow_pickle(
            &self.models.allow_pickle,
            self.repositories.iter().map(|r| r.ron_name.as_str()),
        )?;
        // SANDBOX-SCAN: every scanner that reads the artifact's bytes runs in the
        // scan worker (the wheel SBOM, the model and licence scanners, the file
        // inspection and YARA); the vulnerability slot matches the worker's
        // coordinates (an image's packages) against the local advisory DB here;
        // ClamAV streams the bytes to clamd's socket.
        let mut set = scanner::ScannerSet::new().with(Arc::new(sandbox::VulnFromWorker::new(
            vuln,
            Arc::clone(worker),
        )));
        for s in sandbox::WorkerScanner::all(worker) {
            set = set.with(s);
        }
        if let Some(intake) = &settings.intake {
            if let Some(c) = &intake.inspect.clamav {
                let clam = holger_inspect::malware::ClamAv::from_config(c);
                if c.required {
                    set = set.require(scanner::ScannerKind::Malware);
                }
                match clam.engine() {
                    Ok(_) => set = set.with(Arc::new(sandbox::ClamStream::new(Arc::new(clam)))),
                    Err(why) => log::warn!("intake scanners: clamav not configured: {why}"),
                }
            }
            if intake.inspect.yara.as_ref().is_some_and(|y| y.required) {
                set = set.require(scanner::ScannerKind::Malware);
            }
        }
        let mut provenance = None;
        // Lane G3a: the provenance scanner, over the `rules` floor and each
        // source's gate, reading the material under `<data_root>/holger-provenance`.
        if let Some(p) = &scan.provenance {
            let bundle_policy = settings
                .intake
                .as_ref()
                .and_then(|i| i.trust_policy.as_deref());
            let trust_json = scanner::provenance::ProvenanceScanner::load_trust_root_json(
                &p.rules,
                bundle_policy,
            )?;
            let trust =
                scanner::provenance::ProvenanceScanner::load_trust_root(&p.rules, bundle_policy)?;
            let inbox = match p.inbox.as_deref().filter(|s| !s.trim().is_empty()) {
                Some(dir) => std::path::PathBuf::from(dir),
                None => self.data_root().join("holger-provenance"),
            };
            provenance = Some(Arc::new(
                scanner::provenance::ProvenanceScanner::new(
                    p.clone(),
                    trust,
                    Arc::new(scanner::provenance::ProvenanceInbox::new(inbox)),
                )?
                // The material is parsed in the scan worker, never here.
                .with_remote(Arc::new(
                    sandbox::RemoteProvenance::new(Arc::clone(worker), trust_json)
                        .with_apk_keys(scanner::provenance::ProvenanceScanner::load_apk_keys(
                            &p.rules,
                            bundle_policy,
                        )?)
                        .with_pgp_keys(scanner::provenance::ProvenanceScanner::load_pgp_keys(
                            &p.rules,
                            bundle_policy,
                        )?),
                )),
            ));
        }
        for kind in &scan.required {
            set = set.require(*kind);
        }
        let store = self.scan_evidence_store(audit);
        let mut pipeline = scanner::ScanPipeline::new(set, store);
        if let Some(p) = provenance {
            pipeline = pipeline.with_provenance(p);
        }
        if let (Some(store), Some(signer)) = (self.attestation_store()?, self.attestation_signer()?)
        {
            pipeline = pipeline.with_attestation(store, signer);
        }
        let configured: Vec<&str> = scanner::ScannerKind::ALL
            .iter()
            .filter(|k| pipeline.scanners().is_configured(**k))
            .map(|k| k.as_str())
            .collect();
        let required: Vec<&str> = scan.required.iter().map(|k| k.as_str()).collect();
        log::info!(
            "intake SCANNED runs scanners: configured {configured:?}, required {required:?}"
        );
        Ok(pipeline)
    }

    /// ★ **The job runner of this server** (lane HOL-SANDBOX-TRAIT): the runner
    /// `promotion.intake.rules.sandbox` and the `promotion.intake.sandbox` block
    /// declare, made once per process and shared by every caller; `None` without an
    /// intake chain. `sandbox: off` in a release build refuses (`holger-sandbox-6`).
    #[cfg(feature = "intake")]
    pub fn jobs(&self) -> Result<Option<Arc<job_runner::Jobs>>> {

The file goes on; the console shows all of it.