You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: apps/loopover-ui/content/docs/self-hosting-configuration.mdx
+62Lines changed: 62 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -513,6 +513,68 @@ Lets LoopOver open a pull request that refreshes this repo's own `AGENTS.md`/`CL
513
513
]}
514
514
/>
515
515
516
+
## Decision-ledger anchoring (optional)
517
+
518
+
Your instance keeps a hash-chained ledger of every verdict it persists. That chain is **tamper-evident**
519
+
on its own — anyone can walk it and locate an edit. Anchoring makes it **tamper-proof against you**: a
520
+
scheduled job publishes a signed checkpoint of the chain's tip to places you do not control (a Sigstore
521
+
Rekor transparency log, and a git commit that GH Archive and Software Heritage mirror), so rewriting
522
+
history back past a published checkpoint would also require forging a signature or fabricating matching
523
+
evidence at an external mirror.
524
+
525
+
**This is a self-host capability, and only a self-host one.** Hosted review execution is retired, so
526
+
`api.loopover.ai` never decides anything and has no chain of its own to anchor — its ledger endpoints
527
+
answer for an empty chain and say so (`status: "empty_ledger"`). The chain that matters is the one *your*
528
+
instance wrote, so anchoring runs where the decisions were made.
529
+
530
+
Anchoring is **off until you provision a signing key**, and stays silent-but-visible until then: the public
531
+
anchors endpoint reports `status: "unconfigured"` rather than an empty list that could be mistaken for a
532
+
healthy idle instance.
533
+
534
+
### 1. Generate the keypair
535
+
536
+
```bash
537
+
npm run ledger:anchor-keygen
538
+
```
539
+
540
+
Prints both halves already in the encodings the runtime expects, with the key id derived from the public
541
+
half so the two cannot drift apart. It writes nothing to disk — the private key exists only in that output,
542
+
so run it on a machine you trust and paste straight into your secret store.
543
+
544
+
### 2. Provision both halves
545
+
546
+
| Variable | Secret? | What it is |
547
+
| --- | --- | --- |
548
+
|`LOOPOVER_LEDGER_ANCHOR_KEYS`| No — publish it | The JSON array of published public keys. Served verbatim by `/v1/public/decision-ledger/anchor-key`, which is how a third party verifies your anchors. |
549
+
|`LOOPOVER_LEDGER_ANCHOR_PRIVATE_KEY`|**Yes**| PKCS8 PEM. Treat it like any other credential: `wrangler secret put`, a compose secret, or a vault entry — never a committed file. |
550
+
551
+
Optional, for the git-commit backend (Rekor needs no credentials and uses its public shard by default):
552
+
553
+
| Variable | What it is |
554
+
| --- | --- |
555
+
|`LOOPOVER_LEDGER_ANCHOR_GIT_OWNER` / `_REPO`| The repo checkpoints are appended to. A dedicated, public, otherwise-empty repo is the intended shape. |
|`LOOPOVER_LEDGER_ANCHOR_GIT_INSTALLATION_ID`| The GitHub App installation used to commit. Unset means the git backend simply does not run; Rekor is unaffected. |
`status` distinguishes the states that used to be indistinguishable:
566
+
567
+
-`anchored` — checkpoints are publishing; the newest one is in `anchor`.
568
+
-`pending` — key provisioned and a ledger exists; the first checkpoint is due (hourly, or every 256 rows).
569
+
-`empty_ledger` — nothing has been decided yet. Expected on a fresh instance.
570
+
-`unconfigured` — no signing key. Anchoring is not running.
571
+
572
+
### Rotating the key
573
+
574
+
Keep the retired entry in `LOOPOVER_LEDGER_ANCHOR_KEYS` with its `notAfter` set to the rotation instant,
575
+
and append the new one. **A retired key is never removed, only closed** — anchors signed under it must stay
576
+
verifiable forever, and a verifier checking a two-year-old checkpoint needs the key it was signed with.
577
+
516
578
## Next steps
517
579
518
580
Configure the GitHub integration in [GitHub App and Orb](/docs/self-hosting-github-app), then add optional context through [AI providers](/docs/self-hosting-ai-providers), [REES](/docs/self-hosting-rees), or [RAG](/docs/self-hosting-rag). For the full gate-mode and per-repo settings reference — including the AI-review combine modes and a complete worked manifest — see [Tuning your reviews](/docs/tuning).
0 commit comments