Sub-issue of #9719. Decision-independent — worth doing under any of that issue's options.
Problem
Anchoring has nine env vars, all documented in src/env.d.ts and all genuinely read:
| Var |
Gates |
LOOPOVER_LEDGER_ANCHOR_KEYS |
published public key list; picks the current signing key |
LOOPOVER_LEDGER_ANCHOR_PRIVATE_KEY |
the P-256 PKCS8 signing secret |
LOOPOVER_LEDGER_ANCHOR_REKOR_SHARD_URL |
Rekor shard (defaults to the public one) |
LOOPOVER_LEDGER_ANCHOR_GIT_OWNER / _REPO / _BRANCH / _PATH |
the git anchor target |
LOOPOVER_LEDGER_ANCHOR_GIT_INSTALLATION_ID |
installation used to mint the write token |
LOOPOVER_LEDGER_ANCHOR_REPORT_TOKEN |
bearer for POST /v1/decision-ledger/anchor-attempts |
None of them appear in src/selfhost/preflight.ts or src/selfhost/config-lint.ts. An operator gets zero boot-time guidance.
This matters more than a normal missing-config warning, because the failure is completely silent: runScheduledLedgerAnchor logs ledger_anchor_skipped_unconfigured and returns (src/review/ledger-anchor-scheduler.ts:113-121). The job still fires every ~2 minutes, does two queries, and no-ops. Nothing surfaces. A self-host operator who believes they have anchoring enabled has no way to discover they do not, short of reading logs they have no reason to read.
Note a self-host container does run the anchor scheduler today — src/index.ts:145 pushes anchor-decision-ledger unconditionally, src/server.ts:1322-1339 drives worker.scheduled, and self-host drains via processJob directly so the hosted ack-and-drop never applies. It also has a populated decision_ledger. So a self-host is one keypair away from real anchoring, and nothing tells the operator that.
Scope
Add preflight/config-lint coverage that distinguishes three states:
- Not configured at all — no keys, no private key. Informational, not a warning: anchoring is opt-in.
- Partially configured — this is the real target. A published key with no private half, a private key with no published key, exactly-zero or more-than-one key with
notAfter: null (currentAnchorKey fails closed on ambiguity, ledger-anchor.ts:203-206), or git owner/repo set without _GIT_INSTALLATION_ID. Each of these silently disables anchoring today while looking configured.
- Fully configured — confirm, so a successful setup is visibly successful.
Mirror the existing preflight output shape rather than inventing one.
Out of scope
The architecture question of where anchors are published (#9719 proper) and provisioning the keypair itself.
Sub-issue of #9719. Decision-independent — worth doing under any of that issue's options.
Problem
Anchoring has nine env vars, all documented in
src/env.d.tsand all genuinely read:LOOPOVER_LEDGER_ANCHOR_KEYSLOOPOVER_LEDGER_ANCHOR_PRIVATE_KEYLOOPOVER_LEDGER_ANCHOR_REKOR_SHARD_URLLOOPOVER_LEDGER_ANCHOR_GIT_OWNER/_REPO/_BRANCH/_PATHLOOPOVER_LEDGER_ANCHOR_GIT_INSTALLATION_IDLOOPOVER_LEDGER_ANCHOR_REPORT_TOKENPOST /v1/decision-ledger/anchor-attemptsNone of them appear in
src/selfhost/preflight.tsorsrc/selfhost/config-lint.ts. An operator gets zero boot-time guidance.This matters more than a normal missing-config warning, because the failure is completely silent:
runScheduledLedgerAnchorlogsledger_anchor_skipped_unconfiguredand returns (src/review/ledger-anchor-scheduler.ts:113-121). The job still fires every ~2 minutes, does two queries, and no-ops. Nothing surfaces. A self-host operator who believes they have anchoring enabled has no way to discover they do not, short of reading logs they have no reason to read.Note a self-host container does run the anchor scheduler today —
src/index.ts:145pushesanchor-decision-ledgerunconditionally,src/server.ts:1322-1339drivesworker.scheduled, and self-host drains viaprocessJobdirectly so the hosted ack-and-drop never applies. It also has a populateddecision_ledger. So a self-host is one keypair away from real anchoring, and nothing tells the operator that.Scope
Add preflight/config-lint coverage that distinguishes three states:
notAfter: null(currentAnchorKeyfails closed on ambiguity,ledger-anchor.ts:203-206), or git owner/repo set without_GIT_INSTALLATION_ID. Each of these silently disables anchoring today while looking configured.Mirror the existing preflight output shape rather than inventing one.
Out of scope
The architecture question of where anchors are published (#9719 proper) and provisioning the keypair itself.