From de83940599669a308626dd10b53612afe6ce3666 Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Wed, 29 Jul 2026 07:00:55 -0700 Subject: [PATCH] docs(selfhost): document ledger anchoring where operators actually look .env.selfhost.example covered providers and telemetry but never mentioned LOOPOVER_LEDGER_ANCHOR_*, despite anchoring being the one capability self-host uniquely can run -- the hosted deployment retired review execution, so its decision_ledger is empty and there is nothing there to anchor. Provisioning it on a live instance surfaced three stumbling blocks this file is the natural place to prevent: it is all-or-nothing (one half set means anchoring silently never runs), the PEM must be single-line with \n escapes or Docker's env_file parsing breaks it, and the Rekor shard rotates annually so the built-in default can only lag -- with no pointer toward the override that fixes it. Closes #9848 --- .env.selfhost.example | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.env.selfhost.example b/.env.selfhost.example index fec9164694..d93675dfc1 100644 --- a/.env.selfhost.example +++ b/.env.selfhost.example @@ -111,3 +111,40 @@ LOOPOVER_REVIEW_ENRICHMENT=false # The one way to disable it is the explicit air-gap flag below; see .env.example's Orb section for # full privacy detail. # ORB_AIR_GAP=true # air-gapped/OFFLINE deployments only: compute locally, never send + +# ============================================================================= +# 6. Decision-ledger anchoring — optional, and the one place self-host has that +# the hosted deployment does not +# ============================================================================= +# Every verdict this instance persists appends to a hash-chained ledger, which already makes +# tampering WITHIN the chain detectable. Anchoring additionally publishes a signed checkpoint of the +# chain's tip somewhere you do not control, so rewriting history back past a published anchor means +# forging a signature at an external log too -- not just editing your own database. +# +# This is genuinely self-host territory: the hosted deployment does not execute reviews, so its +# decision_ledger is empty and there is nothing there to anchor. Your instance's ledger is YOURS -- +# only your decisions, never other operators'. +# +# Generate both halves with the encodings the runtime expects: +# +# npm run ledger:anchor-keygen +# +# OPT-IN, but ALL-OR-NOTHING: setting only one half makes anchoring silently never run (preflight +# flags this, see src/selfhost/preflight.ts). Leave both unset to keep anchoring off. +# LOOPOVER_LEDGER_ANCHOR_KEYS= # public half: the JSON array, served by /v1/public/decision-ledger/anchor-key +# LOOPOVER_LEDGER_ANCHOR_PRIVATE_KEY= # SECRET, PKCS8 PEM. Must be ONE line, with the newlines + # written as literal backslash-n escapes: a real multi-line + # value breaks Docker's env_file parsing. +# +# Rekor shards ANNUALLY as log-.rekor.sigstore.dev, and the built-in default can only ever +# lag behind the live one. If anchors record `status: failed` with a message naming this endpoint, +# the shard has rotated -- point this at the current one and no code change is needed. +# LOOPOVER_LEDGER_ANCHOR_REKOR_SHARD_URL=https://log2025-1.rekor.sigstore.dev +# +# Second backend, independent of Rekor: a git commit in a repo you own, cross-mirrored by GH Archive +# and Software Heritage the moment it is pushed. Unset means this backend simply does not run. +# LOOPOVER_LEDGER_ANCHOR_GIT_OWNER= +# LOOPOVER_LEDGER_ANCHOR_GIT_REPO= # a dedicated, public, otherwise-empty repo is the intended shape +# LOOPOVER_LEDGER_ANCHOR_GIT_BRANCH=main +# LOOPOVER_LEDGER_ANCHOR_GIT_PATH=anchors.jsonl +# LOOPOVER_LEDGER_ANCHOR_GIT_INSTALLATION_ID=