Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion site/proofs.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="theme-color" content="#f7f7f5"><title>Evidence proofs — AgentScope</title><meta name="description" content="Inspect historical Habitat proof exports and independently check their content hashes."><style>
:root{--bg:#f7f7f5;--ink:#0b0d10;--mut:#626a73;--line:#dedfdf;--card:#fff;--blue:#315cff;--green:#137a43;--amber:#8a5a12;--red:#a32929}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.6 system-ui,-apple-system,"Segoe UI",sans-serif}.wrap{width:min(920px,calc(100% - 32px));margin:auto}nav{height:72px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between}nav a{color:inherit;text-decoration:none;font-weight:700}.mono{font:11px ui-monospace,monospace;letter-spacing:.08em;text-transform:uppercase;color:var(--blue)}h1{font:800 clamp(34px,6vw,56px)/1.05 ui-monospace,monospace;letter-spacing:-.06em;margin:54px 0 14px}.lede{color:var(--mut);max-width:700px}.notice{border:1px solid #e3d4b4;background:#fffaf0;padding:14px 16px;border-radius:9px;color:#604c2a;font-size:13px;margin:26px 0}.list{display:grid;gap:12px;margin:28px 0}.proof{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:20px}.proof h2{font:800 17px ui-monospace,monospace;margin:0 0 5px}.meta{color:var(--mut);font-size:12px}.claim{margin:14px 0}.checks{display:flex;flex-wrap:wrap;gap:7px}.check{border:1px solid var(--line);border-radius:6px;padding:4px 8px;font:10px ui-monospace,monospace}.ok{color:var(--green);border-color:#b8ddc6}.warn{color:var(--amber);border-color:#e5d3ad}.bad{color:var(--red);border-color:#e6bcbc}.proof a{display:inline-block;margin-top:14px;color:var(--blue);font:700 12px ui-monospace,monospace}.empty{padding:20px;border:1px dashed var(--line);color:var(--mut)}footer{border-top:1px solid var(--line);padding:24px 0 40px;color:var(--mut);font-size:12px}a:focus-visible{outline:3px solid var(--blue);outline-offset:3px}@media(max-width:500px){.proof{padding:15px}.checks{gap:5px}}</style></head><body><div class="wrap"><nav><a href="/">agentscope /</a><a href="/demo.html">Demo ↗</a></nav><main><p class="mono">Habitat · portable evidence</p><h1>Proof snapshots.</h1><p class="lede">Historical Habitat proof exports copied into AgentScope. Each bundle is checked in your browser for its embedded SHA-256 content digest and claim/action consistency.</p><div class="notice"><strong>Important:</strong> a matching content hash detects changes to the exported bundle, but does not authenticate who published it. These are historical records, not live status. An unsigned proof does not independently establish that the claimed work happened in the outside world.</div><div id="proof-list" class="list" aria-live="polite"><div class="empty">Loading proof index…</div></div></main><footer>AgentScope · Evidence is labelled by what can actually be checked. <a href="/">Back to AgentScope</a></footer></div>
:root{--bg:#f7f7f5;--ink:#0b0d10;--mut:#626a73;--line:#dedfdf;--card:#fff;--blue:#315cff;--green:#137a43;--amber:#8a5a12;--red:#a32929}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.6 system-ui,-apple-system,"Segoe UI",sans-serif}.wrap{width:min(920px,calc(100% - 32px));margin:auto}nav{height:72px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between}nav a{color:inherit;text-decoration:none;font-weight:700}.mono{font:11px ui-monospace,monospace;letter-spacing:.08em;text-transform:uppercase;color:var(--blue)}h1{font:800 clamp(34px,6vw,56px)/1.05 ui-monospace,monospace;letter-spacing:-.06em;margin:54px 0 14px}.lede{color:var(--mut);max-width:700px}.notice{border:1px solid #e3d4b4;background:#fffaf0;padding:14px 16px;border-radius:9px;color:#604c2a;font-size:13px;margin:26px 0}.list{display:grid;gap:12px;margin:28px 0}.proof{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:20px}.proof h2{font:800 17px ui-monospace,monospace;margin:0 0 5px}.meta{color:var(--mut);font-size:12px;overflow-wrap:anywhere}.claim{margin:14px 0}.checks{display:flex;flex-wrap:wrap;gap:7px}.check{border:1px solid var(--line);border-radius:6px;padding:4px 8px;font:10px ui-monospace,monospace}.ok{color:var(--green);border-color:#b8ddc6}.warn{color:var(--amber);border-color:#e5d3ad}.bad{color:var(--red);border-color:#e6bcbc}.proof a{display:inline-block;margin-top:14px;color:var(--blue);font:700 12px ui-monospace,monospace}.empty{padding:20px;border:1px dashed var(--line);color:var(--mut)}footer{border-top:1px solid var(--line);padding:24px 0 40px;color:var(--mut);font-size:12px}a:focus-visible{outline:3px solid var(--blue);outline-offset:3px}@media(max-width:500px){.proof{padding:15px}.checks{gap:5px}}</style></head><body><div class="wrap"><nav><a href="/">agentscope /</a><a href="/demo.html">Demo ↗</a></nav><main><p class="mono">Habitat · portable evidence</p><h1>Proof snapshots.</h1><p class="lede">Historical Habitat proof exports copied into AgentScope. Each bundle is checked in your browser for its embedded SHA-256 content digest and claim/action consistency.</p><div class="notice"><strong>Important:</strong> a matching content hash detects changes to the exported bundle, but does not authenticate who published it. These are historical records, not live status. An unsigned proof does not independently establish that the claimed work happened in the outside world.</div><div id="proof-list" class="list" aria-live="polite"><div class="empty">Loading proof index…</div></div></main><footer>AgentScope · Evidence is labelled by what can actually be checked. <a href="/">Back to AgentScope</a></footer></div>
<script>
function canonical(v){if(Array.isArray(v))return "["+v.map(canonical).join(",")+"]";if(v&&typeof v==="object")return "{"+Object.keys(v).sort().map(k=>JSON.stringify(k)+":"+canonical(v[k])).join(",")+"}";return JSON.stringify(v)}
async function verify(b){const e=[];for(const k of ["proof_version","generated_at","claim","ledger","content_sha256"])if(!(k in b))e.push("Missing "+k);if(b.proof_version!=="1")e.push("Unsupported proof version");if(!b.claim||typeof b.claim!=="object")e.push("Claim object missing");if(!b.ledger||!Array.isArray(b.ledger.actions))e.push("Ledger actions missing");if(b.ledger&&!["intact","failed"].includes(b.ledger.integrity))e.push("Invalid ledger integrity");if(e.length)return{ok:false,errors:e};const input={...b};delete input.generated_at;delete input.content_sha256;delete input.signature;const digest=[...new Uint8Array(await crypto.subtle.digest("SHA-256",new TextEncoder().encode(canonical(input))))].map(x=>x.toString(16).padStart(2,"0")).join("");if(digest!==b.content_sha256)e.push("Content hash mismatch");const c=b.claim,ev=c.evidence||{};if(c.status==="verified"){if(!ev.action_id)e.push("Verified claim lacks evidence action ID");const action=b.ledger.actions.find(x=>x.id===ev.action_id);if(!action)e.push("Evidence action absent from ledger");else if(action.status!==c.expected_status||action.action!==c.action)e.push("Evidence action does not match claim")}return{ok:!e.length,errors:e,digest,signature:b.signature?"present":"absent"}}
Expand Down
6 changes: 6 additions & 0 deletions test_generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,5 +168,11 @@ def test_missing_source_cannot_be_acknowledged_into_all_clear(self):
self.assertNotIn("All clear", rendered)


def test_proof_index_wraps_long_digest_metadata(self):
root = Path(__file__).resolve().parent
proofs = (root / "site" / "proofs.html").read_text(encoding="utf-8")
self.assertIn(".meta{color:var(--mut);font-size:12px;overflow-wrap:anywhere}", proofs)


if __name__ == "__main__":
unittest.main()
Loading