# Merkle & External Anchor Policy (FL4) **Audience:** Findings Ledger Guild · DevOps · Compliance **Applies to:** `src/Findings/StellaOps.Findings.Ledger` (Merkle worker, anchoring jobs) ## Anchoring cadence - **Batch size:** 1,000 events or **15 minutes**, whichever is first (`LedgerServiceOptions:Merkle.BatchSize/WindowDuration`). - **Tree:** flat Merkle over `merkle_leaf_hash` (see `schema-catalog.md` §1). Root hashed with SHA-256; no salt. - **Partitions:** per-tenant batching only; no cross-tenant mixing. - **Ordering:** leaves ordered by `(sequence_no, recorded_at)`. Any deviation is a failure. ## Anchor references - `ledger_merkle_roots.anchor_reference` formats: - `rekor::` when pushed to Rekor. - `airgap::` when sealed in offline bundle. - `none` (empty) for internal-only anchors. - External publication is optional but **must** include DSSE envelope with payload: ```json { "payloadType": "application/vnd.stella-ledger-anchor+json", "payload": { "tenant": "", "rootHash": "", "leafCount": 1000, "windowStart": "2025-12-02T00:00:00Z", "windowEnd": "2025-12-02T00:15:00Z", "policyHash": "", "schemaVersion": "ledger.event.v1" }, "signatures": [...] } ``` ## Determinism & recovery - Anchor worker enforces stable ordering; replay harness recomputes Merkle roots and fails when root mismatch (FL9 guard). - Root hash + DSSE signature are stored alongside export bundles for offline verification. - External anchors **never** include tenant-identifying data beyond tenant id already present in ledger tables. ## Air-gap posture - Rekor publication optional; when disabled, anchors are sealed inside offline bundles with `anchor_reference=airgap::`. - Anchor manifest is bundled in Offline Kit under `offline/ledger/anchors//.json`. - No outbound network calls when `ExternalAnchoring:Enabled=false`. ## Monitoring - Metrics: `ledger_merkle_anchor_duration_seconds`, `ledger_merkle_anchor_failures_total`, `ledger_backpressure_applied_total{reason="anchoring"}`, `ledger_quota_remaining{kind="ingest"}`. - Alerts: see `observability.md` (AnchorFailure + new Backpressure alert). ## Change control - Any change to batch size/window or hash recipe requires bumping `ledger.event` schema minor version and updating `schema-catalog.md`.