mirror of
https://github.com/saymrwulf/proof-aware-crypto-tooling-agent.git
synced 2026-09-04 20:03:40 +00:00
cockpit: estate map as a fifth view, with RUNTIME as a first-class dimension
Operator questions answered in code: (1) the estate map now lives IN the cockpit too — /estate serves the interactive map (lanes, typed edges, loops, clickable dossiers) to the same human audience the cockpit serves; ESTATE.md remains the canonical committed version (renders on GitHub, reachable by agents via llms.txt, needs no running process), and a name-level sync test guards drift between the two renderings — the published_assets lesson applied preemptively. (2) 'What is RUNNING?' is now answered everywhere: an operations strip on the map (ALWAYS ON: caddy + the LTL read-only container + Forgejo with its single 03:00 mirror cron, all droplet; ON-DEMAND: append/publish/sign ceremonies, cockpit, MCP, operator machine only; NOT RUNNING: warden — prototype, no deployed instance, no funds watched; everything else: static files or external parties), a per-entity Runtime line in every dossier, and a verified What-is-running table in ESTATE.md (container commands, :ro mounts and read_only:true on the LTL service, and the crontab checked on the droplet 2026-07-20, not recalled). Suite 128 -> 130 (estate route + drift guard). Local-only as before: nothing deployed, live estate untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
dcb48f453e
commit
1acbaa1a76
5 changed files with 471 additions and 2 deletions
20
ESTATE.md
20
ESTATE.md
|
|
@ -9,7 +9,7 @@ machinery hub and the only repo that changes freely.
|
|||
|
||||
State snapshot (2026-07-19): log **13 leaves**, root `3488a2d0…`, key
|
||||
fingerprint `874c8a00…`, paper **v0.9 camera-ready (23 pp)**, five
|
||||
attested components, pacta suite 118 green.
|
||||
attested components, pacta suite 130 green.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
|
|
@ -133,6 +133,24 @@ deployed verifier; see the corpus KNOWN-GAPS ledger).
|
|||
| **Operational log state** | `provider/state/transparency-log-main` — the true accumulator. Appends happen here; the mirror is its projection. |
|
||||
| **Evidence archive (offline)** | review kits and stamped artifacts (`_timestamp_hash8` convention); never in git. |
|
||||
|
||||
## What is running (operations, verified 2026-07-20)
|
||||
|
||||
| Entity | Runtime | Where | Starts / stops |
|
||||
|---|---|---|---|
|
||||
| caddy (TLS, static blog) | **always on** | droplet container | `docker compose`, restart-unless-stopped |
|
||||
| LTL web service (ltl.zkdefi.org) | **always on** | droplet container | `pacta_provider serve` with read-only mounts and `read_only: true` — it *cannot* write, sign, or append |
|
||||
| Forgejo mirror | **always on** | droplet container | plus exactly one cron: 03:00 daily mirror reconcile |
|
||||
| provider write side (check / append / publish / sign) | **on demand** | operator machine | runs only during an append ceremony, minutes at a time; the signing key is offline otherwise |
|
||||
| warden (the financial agent) | **not running** | nowhere | implemented prototype: a wallet directory plus CLI/MCP/cockpit processes that exist only while explicitly started; no deployed instance, no funds watched |
|
||||
| custody cockpit | **on demand** | operator machine, localhost | `pacta wallet cockpit`, stops with Ctrl-C; read-only |
|
||||
| everything else (repos, paper, book, mirror, SD) | **no process** | — | static files; consumers and reviewers are external and episodic |
|
||||
|
||||
The human-facing interactive rendering of this whole map, runtime
|
||||
dimension included, is the cockpit's **Estate map** view
|
||||
(`pacta wallet cockpit` → `/estate`); this file remains the canonical
|
||||
committed version, and `tests/test_walletui.py` guards name-level drift
|
||||
between the two.
|
||||
|
||||
## Edge glossary
|
||||
|
||||
| Edge | Meaning |
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ separate, explicitly reviewed milestone.
|
|||
| **Posture** (`/`) | custody latch state, ledger head with full hash-chain re-verification, the pinned quorum members (backend, component, tier, source commit, binary hash), spending policy verbatim, incident/refusal counts | `Wallet.posture()` / `Wallet.verify_ledger()` |
|
||||
| **Signature queue** (`/queue`) | parked airgap signing requests (outbox) and whether the device has answered (inbox) — observed, never operated | airgap outbox/inbox listing |
|
||||
| **Incidents & refusals** (`/incidents`) | incident records and signed refusal receipts, verbatim, newest first | `incidents/*.json`, `receipts/*.json` |
|
||||
| **Estate map** (`/estate`) | the whole endeavour — every repo, service, mirror, loop — with RUNTIME on every entity (always-on / on-demand / not-running / static) | rendering of ESTATE.md's model (drift-guarded by test) |
|
||||
| **Receipt inspector** (`/inspect`) | paste an attestation + transparency receipt + log public key; the verdict, per-signature results, and diagnostics come verbatim from the deployed verifier | `pacta.transparency.verify_receipt` |
|
||||
|
||||
Every panel also states what it does **not** prove (e.g. the quorum
|
||||
|
|
|
|||
411
src/pacta/estateview.py
Normal file
411
src/pacta/estateview.py
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
"""estateview - the estate map as a cockpit view.
|
||||
|
||||
The same model as ESTATE.md (the canonical committed version), rendered
|
||||
interactively for humans, with RUNTIME as a first-class dimension: every
|
||||
entity dossier states whether anything is actually running, where, and
|
||||
when it starts and stops. A sync test guards name-level drift between
|
||||
this page and ESTATE.md.
|
||||
"""
|
||||
|
||||
ESTATE_HTML = r'''<title>LTL estate map — repos, services, loops</title>
|
||||
<style>
|
||||
:root{
|
||||
--ground:#f8f9fa; --panel:#ffffff; --ink:#1c2430; --ink2:#5a6675;
|
||||
--line:#dde2e9; --src:#8a93a0; --sub:#1e7f4f; --mach:#3b4d8f;
|
||||
--pub:#6d4a8f; --cons:#a86a10; --loop1:#c25e00; --loop2:#7a2ea0;
|
||||
--held:#2b3442; --ok:#e2f2e9; --warn:#fdf0da;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;background:var(--ground);color:var(--ink);
|
||||
font:14px/1.45 system-ui,sans-serif}
|
||||
code,.mono{font-family:ui-monospace,Menlo,Consolas,monospace}
|
||||
header{padding:1.1rem 1.4rem .4rem}
|
||||
h1{font-size:1.25rem;margin:0;letter-spacing:-.01em}
|
||||
.sub{color:var(--ink2);font-size:.86rem;margin-top:.15rem}
|
||||
.facts{display:flex;flex-wrap:wrap;gap:.45rem;padding:.6rem 1.4rem .2rem}
|
||||
.fact{background:var(--panel);border:1px solid var(--line);border-radius:5px;
|
||||
padding:.18rem .55rem;font-size:.78rem;color:var(--ink2)}
|
||||
.fact b{color:var(--ink);font-weight:600}
|
||||
.legend{display:flex;flex-wrap:wrap;gap:.9rem;padding:.45rem 1.4rem .6rem;
|
||||
font-size:.76rem;color:var(--ink2);align-items:center}
|
||||
.lg{display:flex;align-items:center;gap:.34rem}
|
||||
.lg svg{display:block}
|
||||
.wrap{display:grid;grid-template-columns:minmax(0,1fr) 330px;gap:0;
|
||||
align-items:start}
|
||||
@media(max-width:1080px){.wrap{grid-template-columns:1fr}}
|
||||
.boardScroll{overflow-x:auto;padding:0 0 1.2rem 1.4rem}
|
||||
.board{position:relative;width:1310px;padding-right:1rem}
|
||||
.lanes{display:grid;grid-template-columns:170px 226px 300px 260px 226px;
|
||||
gap:34px;position:relative;z-index:2}
|
||||
.lane>.laneTitle{font-size:.68rem;letter-spacing:.09em;text-transform:uppercase;
|
||||
font-weight:700;margin:0 0 .55rem;padding-bottom:.3rem;
|
||||
border-bottom:2px solid var(--lc,var(--line))}
|
||||
.lane{--lc:var(--line)}
|
||||
.lane.src{--lc:var(--src)} .lane.sub{--lc:var(--sub)}
|
||||
.lane.mach{--lc:var(--mach)} .lane.pub{--lc:var(--pub)}
|
||||
.lane.cons{--lc:var(--cons)}
|
||||
.laneTitle{color:var(--lc)}
|
||||
.col{display:flex;flex-direction:column;gap:.55rem}
|
||||
.node{background:var(--panel);border:1px solid var(--line);
|
||||
border-left:3px solid var(--lc);border-radius:6px;
|
||||
padding:.5rem .6rem;cursor:pointer;position:relative}
|
||||
.node:hover,.node:focus-visible{border-color:var(--lc);outline:none;
|
||||
box-shadow:0 1px 4px rgba(28,36,48,.12)}
|
||||
.node.pinned{box-shadow:0 0 0 2px var(--lc)}
|
||||
.node.dim{opacity:.28}
|
||||
.node h3{margin:0;font-size:.8rem;font-weight:600}
|
||||
.node h3.mono{font-size:.76rem}
|
||||
.node .role{color:var(--ink2);font-size:.72rem;margin-top:.1rem}
|
||||
.chips{display:flex;flex-wrap:wrap;gap:.25rem;margin-top:.3rem}
|
||||
.chip{font-size:.62rem;font-weight:600;border-radius:8px;padding:.05rem .4rem}
|
||||
.chip.ok{background:var(--ok);color:var(--sub)}
|
||||
.chip.warn{background:var(--warn);color:var(--cons)}
|
||||
.chip.gen{background:#efe9f5;color:var(--pub)}
|
||||
.chip.frz{background:#e9edf3;color:#41506b}
|
||||
.chip.l1{background:#fbe9dc;color:var(--loop1)}
|
||||
.chip.l2{background:#f1e4f7;color:var(--loop2)}
|
||||
.group{border:1px dashed var(--lc);border-radius:8px;padding:.55rem .55rem .6rem}
|
||||
.group>.gTitle{font-size:.72rem;font-weight:700;margin:0 0 .45rem;
|
||||
display:flex;align-items:baseline;gap:.4rem}
|
||||
.group>.gTitle .mono{font-size:.7rem}
|
||||
.group .col{gap:.4rem}
|
||||
.group .node{border-left-width:2px;padding:.4rem .5rem}
|
||||
.held{background:var(--held);border-color:var(--held)}
|
||||
.held>.gTitle{color:#e8ecf2}
|
||||
.held .node{background:#39445a;border-color:#4c5872;color:#e8ecf2}
|
||||
.held .node .role{color:#aab4c6}
|
||||
svg.edges{position:absolute;inset:0;z-index:1;pointer-events:none;
|
||||
overflow:visible}
|
||||
.e{fill:none;stroke:#b6bec9;stroke-width:1.3}
|
||||
.e.attest{stroke:var(--sub)} .e.publish{stroke:var(--mach)}
|
||||
.e.serve{stroke:var(--pub)} .e.consume{stroke:var(--cons)}
|
||||
.e.tmpl{stroke:var(--mach);stroke-dasharray:5 4}
|
||||
.e.sync,.e.pros{stroke-dasharray:5 4}
|
||||
.e.pros{stroke:var(--cons)}
|
||||
.e.loop1{stroke:var(--loop1);stroke-width:2.4}
|
||||
.e.loop2{stroke:var(--loop2);stroke-width:2.4;stroke-dasharray:8 5}
|
||||
.e.hot{stroke-width:3}
|
||||
.e.faded{opacity:.12}
|
||||
.eLabel{font-size:9.5px;fill:var(--ink2)}
|
||||
.eLabel.loop1{fill:var(--loop1);font-weight:700}
|
||||
.eLabel.loop2{fill:var(--loop2);font-weight:700}
|
||||
aside{position:sticky;top:0;padding:1rem 1.4rem 1rem .4rem;max-height:100vh;
|
||||
overflow-y:auto}
|
||||
@media(max-width:1080px){aside{position:static;padding:0 1.4rem 2rem}}
|
||||
.panel{background:var(--panel);border:1px solid var(--line);border-radius:8px;
|
||||
padding:.9rem 1rem}
|
||||
.panel h2{margin:0;font-size:.95rem}
|
||||
.panel .laneTag{font-size:.65rem;letter-spacing:.08em;text-transform:uppercase;
|
||||
font-weight:700;margin-bottom:.3rem}
|
||||
.panel ul{margin:.55rem 0 0;padding-left:1.1rem}
|
||||
.panel li{margin:.28rem 0;font-size:.82rem}
|
||||
.panel .hint{color:var(--ink2);font-size:.8rem}
|
||||
.mut{display:inline-block;margin-top:.55rem;font-size:.68rem;font-weight:700;
|
||||
border-radius:8px;padding:.1rem .5rem}
|
||||
.mut.free{background:var(--ok);color:var(--sub)}
|
||||
.mut.frozen{background:#e9edf3;color:#41506b}
|
||||
.mut.generated{background:#efe9f5;color:var(--pub)}
|
||||
.mut.operator{background:#e8ddc9;color:#6b4d10}
|
||||
.mut.external{background:var(--warn);color:var(--cons)}
|
||||
footer{padding:.4rem 1.4rem 1.6rem;color:var(--ink2);font-size:.75rem;
|
||||
max-width:62rem}
|
||||
@media(prefers-reduced-motion:no-preference){
|
||||
.node,.e{transition:opacity .15s,box-shadow .15s,stroke-width .15s}}
|
||||
</style>
|
||||
|
||||
<header>
|
||||
<h1>LTL estate map</h1>
|
||||
<div class="sub">Every persisting entity of the Lean Transparency Log endeavour, arranged as five lanes of custody — click any card for its dossier. The two colored routes are the loops that make this estate hard to hold in one head.</div>
|
||||
</header>
|
||||
<div class="facts">
|
||||
<span class="fact">log <b>13 leaves</b></span>
|
||||
<span class="fact">root <b class="mono">3488a2d0…</b></span>
|
||||
<span class="fact">key <b class="mono">874c8a00…</b></span>
|
||||
<span class="fact">paper <b>v0.9 · 23 pp · camera-ready</b></span>
|
||||
<span class="fact">attested components <b>5</b></span>
|
||||
<span class="fact">pacta suite <b>130 green</b></span>
|
||||
<span class="fact">state as of <b>2026-07-20</b></span>
|
||||
</div>
|
||||
<div class="facts" style="padding-top:.15rem">
|
||||
<span class="fact" style="border-color:#1e7f4f"><b style="color:#1e7f4f">ALWAYS ON</b> droplet: caddy (TLS, static blog) · LTL web service (read-only container) · Forgejo (+ 03:00 mirror cron)</span>
|
||||
<span class="fact" style="border-color:#3b4d8f"><b style="color:#3b4d8f">ON-DEMAND</b> operator machine: append/publish/sign ceremonies · cockpit · MCP — exist only while invoked</span>
|
||||
<span class="fact" style="border-color:#a86a10"><b style="color:#a86a10">NOT RUNNING</b> warden: implemented prototype, no deployed instance, no funds watched</span>
|
||||
<span class="fact"><b>everything else</b>: static files or external parties — no process at all</span>
|
||||
</div>
|
||||
<div class="legend" id="legend">
|
||||
<span class="lg"><svg width="26" height="8"><line x1="0" y1="4" x2="26" y2="4" stroke="#b6bec9" stroke-width="1.3"/></svg>extract / feed</span>
|
||||
<span class="lg"><svg width="26" height="8"><line x1="0" y1="4" x2="26" y2="4" stroke="#1e7f4f" stroke-width="1.3"/></svg>attest</span>
|
||||
<span class="lg"><svg width="26" height="8"><line x1="0" y1="4" x2="26" y2="4" stroke="#3b4d8f" stroke-width="1.3"/></svg>append / publish</span>
|
||||
<span class="lg"><svg width="26" height="8"><line x1="0" y1="4" x2="26" y2="4" stroke="#3b4d8f" stroke-width="1.3" stroke-dasharray="5 4"/></svg>template (CI-pinned)</span>
|
||||
<span class="lg"><svg width="26" height="8"><line x1="0" y1="4" x2="26" y2="4" stroke="#6d4a8f" stroke-width="1.3"/></svg>serve / deploy</span>
|
||||
<span class="lg"><svg width="26" height="8"><line x1="0" y1="4" x2="26" y2="4" stroke="#a86a10" stroke-width="1.3"/></svg>consume</span>
|
||||
<span class="lg"><svg width="26" height="8"><line x1="0" y1="4" x2="26" y2="4" stroke="#c25e00" stroke-width="2.4"/></svg>Loop 1 — dogfood signer</span>
|
||||
<span class="lg"><svg width="26" height="8"><line x1="0" y1="4" x2="26" y2="4" stroke="#7a2ea0" stroke-width="2.4" stroke-dasharray="8 5"/></svg>Loop 2 — self-attestation</span>
|
||||
</div>
|
||||
|
||||
<div class="wrap">
|
||||
<div class="boardScroll"><div class="board" id="board">
|
||||
<svg class="edges" id="edgeSvg"></svg>
|
||||
<div class="lanes">
|
||||
|
||||
<div class="lane src"><div class="laneTitle">Upstream inputs · frozen</div>
|
||||
<div class="col">
|
||||
<div class="node" id="srcDalek"><h3 class="mono">curve25519-dalek-source</h3><div class="role">upstream Rust, pinned clone</div></div>
|
||||
<div class="node" id="srcAnza"><h3 class="mono">anza-cryptography-source</h3><div class="role">Solana fork, pinned clone</div></div>
|
||||
<div class="node" id="srcRisc0"><h3 class="mono">risc0-…-dalek-source</h3><div class="role">RISC Zero fork, pinned clone</div></div>
|
||||
<div class="node" id="srcBet"><h3 class="mono">betrusted-…-dalek-source</h3><div class="role">Betrusted fork (+ xous-core, litex-boards context)</div></div>
|
||||
<div class="node" id="srcPasta"><h3 class="mono">pasta_curves-source</h3><div class="role">Pasta curves, pinned clone</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lane sub"><div class="laneTitle">Verified subjects</div>
|
||||
<div class="col">
|
||||
<div class="node" id="dalek"><h3 class="mono">dalek-ed25519-verified</h3><div class="role">16 certs · leaf 8 (gen 3)</div>
|
||||
<div class="chips"><span class="chip ok">attested</span><span class="chip l1">signer source</span></div></div>
|
||||
<div class="node" id="anza"><h3 class="mono">anza-ed25519-verified</h3><div class="role">16 certs · leaf 9</div>
|
||||
<div class="chips"><span class="chip ok">attested</span></div></div>
|
||||
<div class="node" id="risc0"><h3 class="mono">risc0-ed25519-verified</h3><div class="role">16 certs · leaf 10</div>
|
||||
<div class="chips"><span class="chip ok">attested</span></div></div>
|
||||
<div class="node" id="bet"><h3 class="mono">betrusted-ed25519-verified</h3><div class="role">16 certs · leaf 11</div>
|
||||
<div class="chips"><span class="chip ok">attested</span></div></div>
|
||||
<div class="node" id="pasta"><h3 class="mono">pasta-pallas-verified</h3><div class="role">field layer proven · curve layer pending</div>
|
||||
<div class="chips"><span class="chip warn">not attested</span></div></div>
|
||||
<div class="node" id="corpus"><h3 class="mono">ltl-accumulator-verified</h3><div class="role">61 certs · proofs about the log's own accumulator model</div>
|
||||
<div class="chips"><span class="chip ok">attested · entry 13</span><span class="chip frz">frozen 172a1d0</span><span class="chip l2">loop 2</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lane mach"><div class="laneTitle">Machinery & operator-held</div>
|
||||
<div class="col">
|
||||
<div class="group"><div class="gTitle"><span class="mono">proof-aware-crypto-tooling-agent</span><span style="color:var(--ink2);font-weight:400">(pacta)</span></div>
|
||||
<div class="col">
|
||||
<div class="node" id="provider"><h3>Provider service</h3><div class="role">check → append → publish · site & API code · publish templates</div>
|
||||
<div class="chips"><span class="chip ok">templates CI-pinned</span></div></div>
|
||||
<div class="node" id="signer"><h3>Dogfood signer</h3><div class="role">verified-dalek-serial binary — signs every head</div>
|
||||
<div class="chips"><span class="chip l1">loop 1</span></div></div>
|
||||
<div class="node" id="conslib"><h3>Consumer library</h3><div class="role">verify · pin store · receipts · R0–R5 risk model</div></div>
|
||||
<div class="node" id="wardenCode"><h3>warden (code)</h3><div class="role">quorum-custody wallet · MCP · custody card</div></div>
|
||||
<div class="node" id="paper"><h3>Paper</h3><div class="role">ltl.tex — v0.9 camera-ready · archives v0.1 / v0.2</div></div>
|
||||
<div class="node" id="course"><h3>Course + llms.txt</h3><div class="role">14 notebooks · agent-readable index</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group held"><div class="gTitle">Operator-held · never in git</div>
|
||||
<div class="col">
|
||||
<div class="node" id="key"><h3>Signing key</h3><div class="role">offline · sole copy + encrypted SD backup</div></div>
|
||||
<div class="node" id="opstate"><h3>Operational log state</h3><div class="role">transparency-log-main — the true accumulator</div></div>
|
||||
<div class="node" id="sd"><h3>Evidence archive (offline)</h3><div class="role">review kits · stamped artifacts</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lane pub"><div class="laneTitle">Published faces</div>
|
||||
<div class="col">
|
||||
<div class="node" id="mirror"><h3 class="mono">lean-transparency-log</h3><div class="role">public mirror — leaves, heads, receipts, fail-closed verify.py + selftest</div>
|
||||
<div class="chips"><span class="chip gen">generated by publish</span></div></div>
|
||||
<div class="node" id="site"><h3 class="mono">ltl.zkdefi.org</h3><div class="role">homepage from live leaves · /v1 API · /paper (+v0.2, v0.1) · key endpoint</div></div>
|
||||
<div class="node" id="forgejo"><h3>Forgejo mirror</h3><div class="role">droplet · nightly 03:00 · full saymrwulf account</div>
|
||||
<div class="chips"><span class="chip gen">disaster copy</span></div></div>
|
||||
<div class="node" id="pcs"><h3>Infra as code (private)</h3><div class="role">droplet configuration in a private repo — unnamed so this map stays shareable</div></div>
|
||||
<div class="node" id="book"><h3 class="mono">verifying-crypto-with-lean</h3><div class="role">undergraduate book — educational face, no LTL coupling</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lane cons"><div class="laneTitle">Consumers</div>
|
||||
<div class="col">
|
||||
<div class="node" id="cloner"><h3>Offline cloner</h3><div class="role">git clone → verify.py --all (fails closed) → own witness view</div></div>
|
||||
<div class="node" id="wardenRun"><h3>warden (runtime)</h3><div class="role">internal consumer — quorum of 4 attested fork verifiers</div></div>
|
||||
<div class="node" id="agents"><h3>Agents</h3><div class="role">MCP tools · custody card with embedded inclusion proofs</div></div>
|
||||
<div class="node" id="swiss"><h3 class="mono">swisspost-evoting-go-poc</h3><div class="role">prospective — dalek family-level match only, no receipt code</div>
|
||||
<div class="chips"><span class="chip warn">prospective</span></div></div>
|
||||
<div class="node" id="reviewers"><h3>External reviewers</h3><div class="role">GPT-5.6 + Claude — adversarial consumers of paper, corpus, log</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div></div>
|
||||
|
||||
<aside><div class="panel" id="panel">
|
||||
<div class="laneTag" style="color:var(--ink2)">Dossier</div>
|
||||
<h2>Click any card</h2>
|
||||
<p class="hint">Hovering highlights a card's edges; clicking pins its dossier here. The board scrolls sideways if your window is narrow.</p>
|
||||
</div></aside>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
Canonical committed version: <span class="mono">ESTATE.md</span> in the pacta repo (prose + Mermaid + the What-is-running table). This page is its human-facing rendering, served read-only by the cockpit. Excluded by design: unrelated saymrwulf repos, and the operator-private detail beneath the three dark cards.
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
const RUNTIME = {
|
||||
srcDalek:"frozen clone — nothing runs", srcAnza:"frozen clone — nothing runs",
|
||||
srcRisc0:"frozen clone — nothing runs", srcBet:"frozen clone — nothing runs",
|
||||
srcPasta:"frozen clone — nothing runs",
|
||||
dalek:"static repo — proofs replay on demand", anza:"static repo — proofs replay on demand",
|
||||
risc0:"static repo — proofs replay on demand", bet:"static repo — proofs replay on demand",
|
||||
pasta:"static repo — open work, run manually", corpus:"frozen repo — replay on demand",
|
||||
provider:"SPLIT: the write side (check/append/publish) runs ON DEMAND on the operator machine, only during a ceremony; the read-only web face runs ALWAYS ON in the droplet container",
|
||||
signer:"on demand — invoked only while signing during a ceremony; key offline otherwise",
|
||||
conslib:"library — runs inside whichever consumer invokes it",
|
||||
wardenCode:"NOT RUNNING — implemented prototype; no deployed instance",
|
||||
paper:"static files, served by the always-on site",
|
||||
course:"static; regenerated manually",
|
||||
key:"OFFLINE — touched only during signing ceremonies, with per-command operator grant",
|
||||
opstate:"dormant files between ceremonies — no process",
|
||||
sd:"offline medium",
|
||||
mirror:"static git repo — no process; consumers run verify.py themselves",
|
||||
site:"ALWAYS ON — droplet container (pacta_provider serve), read-only mounts, read_only:true, no key material",
|
||||
forgejo:"ALWAYS ON — droplet container; one cron: 03:00 mirror reconcile",
|
||||
pcs:"config files — deployed by hand",
|
||||
book:"static repo",
|
||||
cloner:"external, episodic",
|
||||
wardenRun:"NOT RUNNING — starts only when the operator or an agent launches MCP or the cockpit, stops with them",
|
||||
agents:"external, episodic", swiss:"external, dormant since 2026-07-07",
|
||||
reviewers:"external, per review round",
|
||||
};
|
||||
const DOSSIER = {
|
||||
srcDalek:{lane:"Upstream inputs",mut:"frozen",facts:["Pinned clone of upstream curve25519-dalek + ed25519-dalek (one implementation: curve crate + signature crate).","Input to Aeneas/Charon extraction; never modified here.","Excluded from all maintenance passes by standing order."]},
|
||||
srcAnza:{lane:"Upstream inputs",mut:"frozen",facts:["Pinned clone of the Solana/Anza cryptography fork.","Input to extraction; never modified."]},
|
||||
srcRisc0:{lane:"Upstream inputs",mut:"frozen",facts:["Pinned clone of the RISC Zero dalek fork.","Input to extraction; never modified."]},
|
||||
srcBet:{lane:"Upstream inputs",mut:"frozen",facts:["Pinned clone of the Betrusted dalek fork.","xous-core and litex-boards sit alongside as platform context.","Input to extraction; never modified."]},
|
||||
srcPasta:{lane:"Upstream inputs",mut:"frozen",facts:["Pinned clone of the Pasta curves crate.","Feeds pasta-pallas-verified; never modified."]},
|
||||
dalek:{lane:"Verified subjects",mut:"frozen",facts:["16 reviewed certificates: field, group law, scalars, signature apex (T1–T4).","Attested in all three log generations; current leaf 8.","LOOP 1 anchor: the dogfood signer binary is built from this source — the log's heads are signed by code whose proofs are inside the log.","Attestation pins a commit; the branch only moves for docs."]},
|
||||
anza:{lane:"Verified subjects",mut:"frozen",facts:["16 reviewed certificates; current leaf 9.","Same proof pyramid as dalek, rebuilt for the fork's code structure."]},
|
||||
risc0:{lane:"Verified subjects",mut:"frozen",facts:["16 reviewed certificates; current leaf 10.","Differs from Betrusted's corpus by 27 changed proof lines (the paper's portability datum)."]},
|
||||
bet:{lane:"Verified subjects",mut:"frozen",facts:["16 reviewed certificates; current leaf 11."]},
|
||||
pasta:{lane:"Verified subjects",mut:"free",facts:["Field layer proven from own extraction; curve layer (group law + scalar mul) is the one open verification task in the estate.","NOT attested — the log carries only the four Ed25519 forks + the corpus."]},
|
||||
corpus:{lane:"Verified subjects",mut:"frozen",facts:["61 certificates over one boundary axiom (LTLAcc.sha256); 222-constant environment inventory; 15-gap honest ledger.","Mechanizes the archived report's §6: extractors, consistency binding, per-step pin safety.","LOOP 2 anchor: attested INTO the log as entry 13 — the log carries kernel-checked proofs about its own accumulator model.","Frozen at 172a1d0 (the attested commit); doc-only commits may move the branch.","Docs carry numbering notes: paper references are v0.2 numbering."]},
|
||||
provider:{lane:"pacta · machinery",mut:"free",facts:["pacta_provider: attestation check → log-append → log-publish; webdocs homepage + /v1 API code.","Holds the publish TEMPLATES for the mirror's verify.py / selftest / README — since 2026-07-19 pinned by CI (test_published_assets) after the audit caught a stale fail-open template.","Deploys to the droplet as the ltl container."]},
|
||||
signer:{lane:"pacta · machinery",mut:"free",facts:["verified-dalek-serial: the Ed25519 binary built from the attested dalek source.","Signs every tree head; before signing, the provider re-checks inclusion of the signer's own leaf.","LOOP 1: signature vouches for the tree; the tree contains the attestation of the signer's source (leaf 8). Execution provenance is reported, not proven — stated in the paper."]},
|
||||
conslib:{lane:"pacta · machinery",mut:"free",facts:["src/pacta: receipt verification, pin store (rollback rejection, fork evidence), R0–R5 risk model, claim cards.","The deployed iterative verifiers the paper differential-tests against the recursive model live here."]},
|
||||
wardenCode:{lane:"pacta · machinery",mut:"free",facts:["WALLET.md: custody capsule, hash-chained ledger, inbound quorum boundary, outbound signing firewall.","Inbound: accepts log-derived statements only when independently attested verifier backends agree.","Agent surfaces: MCP over stdio, custody card, posture challenge, refusal receipts."]},
|
||||
paper:{lane:"pacta · machinery",mut:"free",facts:["paper/ltl.tex — v0.9 camera-ready, 23 pages: trust decomposition, accountability games with explicit reductions, deployed evaluation.","Archives: v0.1 (4 pp) and v0.2 (19 pp, the corpus's numbering reference) kept and served.","Submission package + webform field set staged on the SD archive."]},
|
||||
course:{lane:"pacta · machinery",mut:"free",facts:["14 Jupyter notebooks generated from scripts/build_curriculum_notebooks.py (fix the generator, then the notebook).","llms.txt: the agent-readable index of the whole endeavour."]},
|
||||
key:{lane:"Operator-held",mut:"operator",facts:["Ed25519 signing key, fingerprint 874c8a00… — the log's identity.","Offline; never on the server; the agent never touches it without an explicit per-command grant.","Public half published at two independent locations (site + mirror) for TOFU comparison."]},
|
||||
opstate:{lane:"Operator-held",mut:"operator",facts:["provider/state/transparency-log-main: the true accumulator — appends happen HERE, the mirror is its projection.","Lesson learned in rehearsal: rebuild the tree from operational state, never from published projections."]},
|
||||
sd:{lane:"Operator-held",mut:"operator",facts:["Review kits (rounds 1–15), stamped artifacts (_timestamp_hash8 convention), and execution evidence records.","Kept offline; never in any git repo."]},
|
||||
mirror:{lane:"Published faces",mut:"generated",facts:["The git-published log: 13 leaves, 6 signed heads (sizes 8–13), receipts, provider key, fail-closed verify.py + 11-case selftest.","GENERATED by log-publish from operational state + pacta templates — direct commits here must be mirrored back into the templates (that is the defect the 2026-07-19 audit caught).","Cloning it makes anyone a witness: verify.py --all re-verifies everything offline."]},
|
||||
site:{lane:"Published faces",mut:"generated",facts:["Droplet: caddy → docker (cloud-ltl-1). Homepage rendered live from real leaves — the counts on the page ARE the accumulator.","/v1/sth, /v1/attestation, /v1/proof, /log-public-key; /paper serves v0.9, /paper/v0.2 and /paper/v0.1 the archives.","Read-only; no key material on the server."]},
|
||||
forgejo:{lane:"Published faces",mut:"generated",facts:["cloud-forgejo-1 on the droplet: nightly (03:00) mirror of the ENTIRE saymrwulf GitHub account — disaster-recovery copy, not a curated set."]},
|
||||
pcs:{lane:"Published faces",mut:"free",facts:["The droplet's deployment configuration (compose stack, reverse proxy, reconstruct step), maintained in a private repository.","Deliberately unnamed here: the public estate lists only entities whose existence must be public for trust.","Captured under version control after an audit found the deployment was not."]},
|
||||
book:{lane:"Published faces",mut:"free",facts:["The undergraduate book (twelve chapters + solutions): from 1+1=2 to reading the estate's real proofs.","Audited 2026-07-19: zero coupling to log/paper state — safely independent."]},
|
||||
cloner:{lane:"Consumers",mut:"external",facts:["Anyone: git clone the mirror, run verify.py --all (stdlib + openssl; FAILS CLOSED without signature capability).","Retention makes them a witness: their clone can later expose an equivocating head."]},
|
||||
wardenRun:{lane:"Consumers",mut:"free",facts:["The estate's own dogfood consumer: signs nothing inbound unless a quorum of independently attested verifier backends agrees.","Code lives in pacta (left lane); shown here in its consuming role. The paper calls it the implemented internal prototype."]},
|
||||
agents:{lane:"Consumers",mut:"external",facts:["MCP tools (wallet_status, verify_inbound, request_signature, …) and the self-proving custody card: embedded inclusion proofs a counterparty recomputes rather than trusts."]},
|
||||
swiss:{lane:"Consumers",mut:"external",facts:["The operator's Go PoC of the Swiss Post e-voting system.","Its vendored dalek dependency matches an attested subject at FAMILY level, not the attested version — the paper's version-exactness negative (kept anonymous there).","No receipt code yet; strictly prospective."]},
|
||||
reviewers:{lane:"Consumers",mut:"external",facts:["GPT-5.6 + a Claude reviewer: fifteen adversarial rounds over corpus, log, site, and paper.","Fed via SD kits; findings drove every hardening round; both re-derived the log's cryptography independently."]},
|
||||
};
|
||||
const EDGES = [
|
||||
["srcDalek","dalek","","extract"],["srcAnza","anza","","extract"],
|
||||
["srcRisc0","risc0","","extract"],["srcBet","bet","","extract"],
|
||||
["srcPasta","pasta","","extract"],
|
||||
["dalek","provider","","attest"],["anza","provider","","attest"],
|
||||
["risc0","provider","","attest"],["bet","provider","","attest"],
|
||||
["provider","opstate","append","publish"],
|
||||
["key","opstate","signs heads","publish"],
|
||||
["opstate","mirror","publish","publish"],
|
||||
["provider","mirror","templates","tmpl"],
|
||||
["provider","site","app code","serve"],
|
||||
["mirror","site","published copy","serve"],
|
||||
["paper","site","/paper","serve"],
|
||||
["pcs","site","infra","serve"],
|
||||
["mirror","forgejo","nightly","sync"],
|
||||
["mirror","cloner","clone + verify","consume"],
|
||||
["site","agents","API · custody card","consume"],
|
||||
["mirror","wardenRun","receipts · quorum","consume"],
|
||||
["site","swiss","prospective","pros"],
|
||||
["sd","reviewers","kits","consume"],
|
||||
["dalek","signer","built from","loop1"],
|
||||
["signer","opstate","signs the log","loop1"],
|
||||
["mirror","dalek","contains the signer's own attestation (leaf 8)","loop1"],
|
||||
["corpus","provider","attested (entry 13)","loop2"],
|
||||
["mirror","corpus","carries proofs about its own accumulator","loop2"],
|
||||
];
|
||||
const board=document.getElementById("board"),svg=document.getElementById("edgeSvg");
|
||||
const NS="http://www.w3.org/2000/svg";
|
||||
function anchors(id){const el=document.getElementById(id),b=board.getBoundingClientRect(),r=el.getBoundingClientRect();
|
||||
return {L:{x:r.left-b.left,y:r.top-b.top+r.height/2},R:{x:r.right-b.left,y:r.top-b.top+r.height/2}};}
|
||||
let edgeEls=[];
|
||||
function draw(){
|
||||
svg.innerHTML="";svg.setAttribute("width",board.scrollWidth);
|
||||
svg.setAttribute("height",board.scrollHeight);edgeEls=[];
|
||||
const defs=document.createElementNS(NS,"defs");
|
||||
[["#b6bec9","m0"],["#1e7f4f","m1"],["#3b4d8f","m2"],["#6d4a8f","m3"],
|
||||
["#a86a10","m4"],["#c25e00","m5"],["#7a2ea0","m6"]].forEach(([c,id])=>{
|
||||
const m=document.createElementNS(NS,"marker");
|
||||
m.setAttribute("id",id);m.setAttribute("viewBox","0 0 8 8");
|
||||
m.setAttribute("refX","7");m.setAttribute("refY","4");
|
||||
m.setAttribute("markerWidth","5.5");m.setAttribute("markerHeight","5.5");
|
||||
m.setAttribute("orient","auto");
|
||||
const p=document.createElementNS(NS,"path");
|
||||
p.setAttribute("d","M0 0 L8 4 L0 8 z");p.setAttribute("fill",c);
|
||||
m.appendChild(p);defs.appendChild(m);});
|
||||
svg.appendChild(defs);
|
||||
const mk={extract:"m0",attest:"m1",publish:"m2",tmpl:"m2",serve:"m3",
|
||||
sync:"m3",consume:"m4",pros:"m4",loop1:"m5",loop2:"m6"};
|
||||
EDGES.forEach(([f,t,label,type])=>{
|
||||
const A=anchors(f),B=anchors(t);
|
||||
const back=B.L.x<A.R.x-20;
|
||||
let s,e,d;
|
||||
if(!back){s=A.R;e=B.L;
|
||||
const dx=Math.max(36,(e.x-s.x)*.42);
|
||||
d=`M${s.x} ${s.y} C ${s.x+dx} ${s.y}, ${e.x-dx} ${e.y}, ${e.x-6} ${e.y}`;}
|
||||
else{s=A.L;e=B.R; // return edge (loops): swing beneath the lanes
|
||||
const drop=Math.max(s.y,e.y)+70+(type==="loop2"?36:0);
|
||||
d=`M${s.x} ${s.y} C ${s.x-70} ${s.y+30}, ${s.x-70} ${drop}, ${(s.x+e.x)/2} ${drop}
|
||||
S ${e.x+70} ${e.y+30}, ${e.x+6} ${e.y}`;}
|
||||
const p=document.createElementNS(NS,"path");
|
||||
p.setAttribute("d",d);p.setAttribute("class","e "+type);
|
||||
p.setAttribute("marker-end",`url(#${mk[type]})`);
|
||||
p.dataset.f=f;p.dataset.t=t;svg.appendChild(p);edgeEls.push(p);
|
||||
if(label){const tx=document.createElementNS(NS,"text");
|
||||
tx.setAttribute("class","eLabel "+(type.startsWith("loop")?type:""));
|
||||
const midx=back?(s.x+e.x)/2:(s.x+e.x)/2, midy=back?Math.max(s.y,e.y)+66+(type==="loop2"?36:0):(s.y+e.y)/2-5;
|
||||
tx.setAttribute("x",midx);tx.setAttribute("y",midy);
|
||||
tx.setAttribute("text-anchor","middle");tx.textContent=label;
|
||||
tx.dataset.f=f;tx.dataset.t=t;svg.appendChild(tx);}});
|
||||
}
|
||||
function related(id){const s=new Set([id]);
|
||||
EDGES.forEach(([f,t])=>{if(f===id)s.add(t);if(t===id)s.add(f);});return s;}
|
||||
let pinned=null;
|
||||
function highlight(id){
|
||||
const rel=id?related(id):null;
|
||||
document.querySelectorAll(".node").forEach(n=>{
|
||||
n.classList.toggle("dim",!!rel&&!rel.has(n.id));
|
||||
n.classList.toggle("pinned",n.id===pinned);});
|
||||
edgeEls.forEach(p=>{const on=!rel||(p.dataset.f===id||p.dataset.t===id);
|
||||
p.classList.toggle("faded",!!rel&&!on);
|
||||
p.classList.toggle("hot",!!rel&&on);});
|
||||
}
|
||||
function showPanel(id){
|
||||
const d=DOSSIER[id];if(!d)return;
|
||||
const el=document.getElementById(id);
|
||||
const title=el.querySelector("h3").textContent;
|
||||
const chips=[...el.querySelectorAll(".chip")].map(c=>c.outerHTML).join(" ");
|
||||
const mutText={free:"changes freely",frozen:"frozen / pinned",
|
||||
generated:"generated — fix the source, not this",
|
||||
operator:"operator-only",external:"external party"}[d.mut];
|
||||
const rt = RUNTIME[id] || "";
|
||||
document.getElementById("panel").innerHTML=
|
||||
`<div class="laneTag">${d.lane}</div><h2>${title}</h2>
|
||||
<div class="chips" style="margin-top:.4rem">${chips}</div>
|
||||
${rt ? `<p style="font-size:.78rem;margin:.5rem 0 0"><b>Runtime:</b> ${rt}</p>` : ""}
|
||||
<ul>${d.facts.map(f=>`<li>${f}</li>`).join("")}</ul>
|
||||
<span class="mut ${d.mut}">${mutText}</span>`;
|
||||
}
|
||||
document.querySelectorAll(".node").forEach(n=>{
|
||||
n.tabIndex=0;
|
||||
n.addEventListener("mouseenter",()=>{if(!pinned)highlight(n.id);});
|
||||
n.addEventListener("mouseleave",()=>{if(!pinned)highlight(null);});
|
||||
n.addEventListener("click",e=>{e.stopPropagation();
|
||||
pinned=(pinned===n.id)?null:n.id;
|
||||
highlight(pinned);showPanel(n.id);});
|
||||
n.addEventListener("keydown",e=>{if(e.key==="Enter"||e.key===" "){
|
||||
e.preventDefault();n.click();}});
|
||||
});
|
||||
document.body.addEventListener("click",e=>{
|
||||
if(!e.target.closest(".node")&&pinned){pinned=null;highlight(null);}});
|
||||
window.addEventListener("resize",()=>requestAnimationFrame(draw));
|
||||
requestAnimationFrame(draw);setTimeout(draw,150);
|
||||
</script>
|
||||
'''
|
||||
|
|
@ -188,7 +188,8 @@ def inspect_receipt(attestation_text: str, receipt_text: str,
|
|||
# ---------------------------------------------------------------------------
|
||||
|
||||
_VIEWS = [("/", "Posture"), ("/queue", "Signature queue"),
|
||||
("/incidents", "Incidents & refusals"), ("/inspect", "Receipt inspector")]
|
||||
("/incidents", "Incidents & refusals"), ("/inspect", "Receipt inspector"),
|
||||
("/estate", "Estate map")]
|
||||
|
||||
|
||||
def _page(title: str, active: str, body: str, wallet_dir: str) -> str:
|
||||
|
|
@ -370,6 +371,9 @@ def make_handler(wallet_dir: Path):
|
|||
self._send(_page("incidents", "/incidents", body, wd))
|
||||
elif route == "/inspect":
|
||||
self._send(_page("receipt inspector", "/inspect", render_inspect(None), wd))
|
||||
elif route == "/estate":
|
||||
from .estateview import ESTATE_HTML
|
||||
self._send(ESTATE_HTML)
|
||||
else:
|
||||
self._send(_page("not found", "", "<div class='panel bad'>No such view.</div>", wd), 404)
|
||||
|
||||
|
|
|
|||
|
|
@ -195,3 +195,38 @@ def test_server_routes_and_read_only_guarantee(tmp_path):
|
|||
def test_serve_refuses_non_wallet(tmp_path):
|
||||
with pytest.raises(Exception):
|
||||
serve(tmp_path / "empty")
|
||||
|
||||
|
||||
def test_estate_view_served_with_runtime_dimension(tmp_path):
|
||||
wallet = _seal_wallet(tmp_path)
|
||||
server = serve(wallet.dir, host="127.0.0.1", port=0)
|
||||
port = server.server_address[1]
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
try:
|
||||
with urllib.request.urlopen(f"http://127.0.0.1:{port}/estate") as resp:
|
||||
body = resp.read().decode()
|
||||
assert resp.status == 200
|
||||
assert "ALWAYS ON" in body and "ON-DEMAND" in body and "NOT RUNNING" in body
|
||||
assert "Runtime:" in body # dossier panel wiring
|
||||
assert "lean-transparency-log" in body and "ltl-accumulator-verified" in body
|
||||
finally:
|
||||
server.shutdown()
|
||||
thread.join(timeout=5)
|
||||
|
||||
|
||||
def test_estate_view_and_estate_md_do_not_drift():
|
||||
"""Name-level sync guard between the cockpit estate page and ESTATE.md
|
||||
(the published_assets lesson: two renderings of one model need a
|
||||
tripwire)."""
|
||||
from pacta.estateview import ESTATE_HTML
|
||||
estate_md = Path("ESTATE.md").read_text(encoding="utf-8")
|
||||
for name in ("lean-transparency-log", "ltl-accumulator-verified",
|
||||
"proof-aware-crypto-tooling-agent", "verifying-crypto-with-lean",
|
||||
"dalek-ed25519-verified", "pasta-pallas-verified",
|
||||
"ltl.zkdefi.org", "Forgejo"):
|
||||
assert name in ESTATE_HTML, f"estate view lost: {name}"
|
||||
assert name in estate_md, f"ESTATE.md lost: {name}"
|
||||
# the runtime dimension must exist in BOTH renderings
|
||||
assert "What is running" in estate_md
|
||||
assert "ALWAYS ON" in ESTATE_HTML
|
||||
|
|
|
|||
Loading…
Reference in a new issue