Runbook: A3 resolved (key located+confirmed, path kept private), A4 corrected (driver = pacta_provider CLI), A3b opened (key has no second copy)

The append driver was never lost: pacta's committed provider/ CLI
(check / log-append / log-publish) produced leaves 8-11, signing heads
with the verified-dalek-serial dogfood backend (self_inclusion:
verified). Only the per-run orchestration was session work — A4 is now
rehearsal + private documentation, not reconstruction. A3: private key
located laptop-side (0600, gitignored state dir; public half
byte-matches provider.ed25519.pub); exact path deliberately excluded
from this public file. A3b: the key has NO second copy anywhere —
encrypted SD backup procedure added as a Phase-A blocker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mrwulf 2026-07-12 18:46:36 +02:00
parent 7d58fe52c7
commit 301c7e9006

View file

@ -44,8 +44,8 @@ Agent Appendix at the end. Every step ends in a mechanical check.
| pacta | github.com/saymrwulf/proof-aware-crypto-tooling-agent | `3d81d53` (change-frozen during paper processing) | | pacta | github.com/saymrwulf/proof-aware-crypto-tooling-agent | `3d81d53` (change-frozen during paper processing) |
| Forgejo mirrors | `https://zkdefi.org/saymrwulf/<repo>.git` (anonymously readable) | pull-synced by server cron nightly 03:00 UTC (`/home/admin/cloud/bin/reconcile-mirrors.py`, log `.reconcile.log`); verify per step A5 | | Forgejo mirrors | `https://zkdefi.org/saymrwulf/<repo>.git` (anonymously readable) | pull-synced by server cron nightly 03:00 UTC (`/home/admin/cloud/bin/reconcile-mirrors.py`, log `.reconcile.log`); verify per step A5 |
| log public key | `lean-transparency-log/provider.ed25519.pub` (PEM) | fingerprint `874c8a00…a56a` in `log-metadata.json` | | log public key | `lean-transparency-log/provider.ed25519.pub` (PEM) | fingerprint `874c8a00…a56a` in `log-metadata.json` |
| log PRIVATE key | **location not yet confirmed**, but verified NOT on the droplet (the server only serves; no key material under `~/cloud/ltl/`) — so it is laptop-side or on removable media | see step A3 — Phase B is blocked until confirmed | | log PRIVATE key | **RESOLVED 2026-07-12**: laptop-side, mode 0600, inside a gitignored state dir of the pacta working tree (exact path in operator-private notes, deliberately not in this public file); public half byte-matches `provider.ed25519.pub`. NOT on the droplet. **No second copy exists** — see step A3b | A3 done; A3b (backup) open |
| producer driver | **does not exist in version control** | see step A4 — Phase B is blocked until persisted | | producer driver | **RESOLVED 2026-07-12**: it exists and is committed — pacta's `provider/` CLI (`python3 -m pacta_provider`: `check` → signed attestation; `log-append` → leaf + signed STH + receipt; `log-publish` → public face). Heads are signed with `signing_backend: verified-dalek-serial` (the dogfooded verified signer), `self_inclusion: verified`. Only the per-run orchestration was session work | see step A4 (rehearsal, not reconstruction) |
| server deployment | private repo `PersonalCloudServer` (github, `master`) — since `a186bac` includes the ltl vhost/service/reconstruct.py, md5-verified == droplet | see its `DEPLOY.md` § "The LTL service" | | server deployment | private repo `PersonalCloudServer` (github, `master`) — since `a186bac` includes the ltl vhost/service/reconstruct.py, md5-verified == droplet | see its `DEPLOY.md` § "The LTL service" |
--- ---
@ -91,33 +91,41 @@ openssl pkey -in <CANDIDATE_PRIVATE_KEY> -pubout \
operator's private notes (never in git). Do not copy the key anywhere, operator's private notes (never in git). Do not copy the key anywhere,
do not print it, do not change its permissions. do not print it, do not change its permissions.
### A4. Reconstruct and persist the append driver (blocker, found 2026-07-12) ### A4. Rehearse and document the append invocation (revised 2026-07-12)
Finding: the script that appended leaves 811 was session work and was Correction to this runbook's first version: the producer driver is NOT
never committed anywhere. Phase B must not be improvised. Before the lost session work — it is the committed `pacta_provider` CLI in pacta's
IACR decision arrives, a driver script must be written, committed (to `provider/` tree (`check` emits the signed attestation; `log-append`
pacta `tools/`, in a commit that touches nothing else — this does not appends the leaf and signs the new head via `make_signed_tree_head`,
alter deployed behavior or any paper claim), and rehearsed. It must do using the verified-dalek-serial dogfood signer; `log-publish` exports
exactly, in order, against a THROWAWAY copy of the log repo: the public face that `lean-transparency-log` and the droplet's
1. build the attestation JSON for subject `ltl-accumulator-verified @ `published/` carry; `serve` never touches keys). Leaves 811 were
2da0a79` with the same schema as `entries/000008.json` produced exactly this way. What was never persisted is only the
(`pacta.attestation` builders + certificate list from the button's per-run orchestration (the loop + flags).
Phase-3 cone output);
2. compute the leaf bytes (`pacta.transparency.leaf_bytes_for_attestation`), To do before the IACR decision arrives:
append as `entries/000012.json`; 1. Write down, in operator-private notes, the exact `pacta_provider
3. recompute the root over all 13 leaves (`merkle_root`), build the new check` / `log-append` / `log-publish` invocation for the subject
head via `pacta.transparency.make_signed_tree_head(log_id, `ltl-accumulator-verified @ 2da0a79` (flags per the leaves-811
tree_size=13, root_hash, timestamp, private_key_path, pattern; key/pub paths from A3's notes).
public_key_path)`; 2. Rehearse it against a THROWAWAY copy of the log state.
4. update `latest-sth.json`, append to `sth-history.jsonl`, write the
inclusion receipt under `receipts/`;
5. NEVER touch existing files under `entries/`.
**Check (rehearsal, throwaway copy only):** `pacta witness-audit` on **Check (rehearsal, throwaway copy only):** `pacta witness-audit` on
the throwaway copy exits 0 — every prefix root recomputed, every the throwaway export exits 0 — every prefix root recomputed, every
historical STH + signature verified, including the new one. The historical STH + signature verified, including the new one. The
throwaway copy is then DELETED (its head was signed with the real key throwaway copy is then DELETED (its head was signed with the real key
over a rehearsal tree — it must never be published or retained; if over a rehearsal tree — it must never be published or retained; if
retention is wanted for study, rehearse with a throwaway KEY instead). retention is wanted for study, rehearse with a throwaway KEY instead).
### A3b. Back up the signing key (opened 2026-07-12 — the key has NO second copy)
The laptop file is the only copy in existence; a disk failure would
freeze the log at its current size forever (still verifiable, never
extendable). Operator-only: create an ENCRYPTED backup (e.g.
`openssl enc -aes-256-cbc -pbkdf2` or `age`) of the key file onto the
SD card under the standing naming convention, passphrase memorized or
stored separately — never plaintext, never in any git repo, never on
the droplet. **Check:** decrypt the backup to a temp file, run A3's
openssl pubkey diff against it (prints KEY CONFIRMED), shred the temp
file.
### A5. Confirm the Forgejo mirrors (no SSH needed) ### A5. Confirm the Forgejo mirrors (no SSH needed)
The mirrors are anonymously readable. For each repo, both commands must The mirrors are anonymously readable. For each repo, both commands must
print the same hash: print the same hash: