protect-mcp 0.7.6 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -11,11 +11,11 @@ import {
11
11
  readInstalledConnectorPilots,
12
12
  simulate,
13
13
  writeConnectorPilots
14
- } from "./chunk-JCMDLN5I.mjs";
14
+ } from "./chunk-CXW2EIRM.mjs";
15
15
  import {
16
16
  ProtectGateway,
17
17
  validateCredentials
18
- } from "./chunk-VTPZ4G5I.mjs";
18
+ } from "./chunk-GHR65WVD.mjs";
19
19
  import {
20
20
  buildActionReadback,
21
21
  evaluateCedar,
@@ -26,7 +26,7 @@ import {
26
26
  policySetFromSource,
27
27
  runEvaluatorSelfTest,
28
28
  signDecision
29
- } from "./chunk-WIPWNWMJ.mjs";
29
+ } from "./chunk-IDUH2O4Q.mjs";
30
30
  import "./chunk-PQJP2ZCI.mjs";
31
31
 
32
32
  // src/cli.ts
@@ -60,6 +60,8 @@ Usage:
60
60
  protect-mcp digest [--today] [--dir <path>]
61
61
  protect-mcp receipts [--last <n>] [--dir <path>]
62
62
  protect-mcp record [--dir <path>] [--live] [--no-open]
63
+ protect-mcp claim [--no <cap>] [--only <c,c>] [--count <verdict>] [--dir <path>] [--output <path>]
64
+ protect-mcp verify-claim <claim.json> [--key <public-hex>]
63
65
  protect-mcp bundle [--output <path>] [--dir <path>]
64
66
  protect-mcp simulate --policy <path> [--log <path>] [--tier <tier>] [--json]
65
67
  protect-mcp report [--period <days>d] [--format md|json] [--output <path>] [--dir <path>]
@@ -98,6 +100,8 @@ Commands:
98
100
  digest Generate a human-readable summary of agent activity
99
101
  receipts Show recent persisted signed receipts
100
102
  record Open a local, searchable view of your record in the browser
103
+ claim Attest a signed, position-blind claim over your record (e.g. no egress)
104
+ verify-claim Verify a claim attestation offline (signature + predicate + commitment)
101
105
  bundle Export an offline-verifiable audit bundle
102
106
 
103
107
  Examples:
@@ -196,7 +200,7 @@ async function handleInit(argv) {
196
200
  let keypair;
197
201
  {
198
202
  const { randomBytes } = await import("crypto");
199
- const { ed25519 } = await import("./ed25519-BSHMMVNX.mjs");
203
+ const { ed25519 } = await import("./ed25519-SQA3S2RV.mjs");
200
204
  const { bytesToHex } = await import("./utils-6AYZFE5A.mjs");
201
205
  const privateKey = randomBytes(32);
202
206
  const publicKey = ed25519.getPublicKey(privateKey);
@@ -1989,7 +1993,13 @@ function mapRecordEntry(e) {
1989
1993
  if (e.receipt_hash) digest = String(e.receipt_hash);
1990
1994
  else if (e.digest) digest = String(e.digest);
1991
1995
  else if (p.payload_digest && p.payload_digest.output_hash) digest = String(p.payload_digest.output_hash);
1992
- return { ts, tool, verdict, reason, hook, signed, id: String(e.request_id || p.request_id || ""), digest };
1996
+ const enr = p && typeof p.enrichment === "object" && p.enrichment || typeof e.enrichment === "object" && e.enrichment || null;
1997
+ const caps = enr && Array.isArray(enr.capabilities) ? enr.capabilities.map(String) : [];
1998
+ const sw = p && typeof p.swarm === "object" && p.swarm || null;
1999
+ const agent = sw && (sw.agent_name || sw.agent_id || sw.agent_type) ? String(sw.agent_name || sw.agent_id || sw.agent_type) : "main agent";
2000
+ const tm = p && typeof p.timing === "object" && p.timing || null;
2001
+ const dur = tm && typeof tm.tool_duration_ms === "number" ? tm.tool_duration_ms : 0;
2002
+ return { ts, tool, verdict, reason, hook, signed, caps, agent, dur, id: String(e.request_id || p.request_id || ""), digest, raw: e };
1993
2003
  }
1994
2004
  async function handleRecord(argv) {
1995
2005
  const { readFileSync, existsSync, writeFileSync } = await import("fs");
@@ -2105,53 +2115,319 @@ var RECORD_HTML = `<!doctype html><html lang="en"><head><meta charset="utf-8"><m
2105
2115
  body{margin:0;background:var(--paper);color:var(--ink);font:15px/1.5 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;-webkit-font-smoothing:antialiased}
2106
2116
  .wrap{max-width:1000px;margin:0 auto;padding:26px 22px 60px}
2107
2117
  h1{font-size:24px;margin:0 0 4px;letter-spacing:-.012em}
2108
- .meta{color:var(--faint);font-size:12.5px;font-family:ui-monospace,Menlo,Consolas,monospace}
2109
- .bar{margin:18px 0 12px}
2118
+ .meta{color:var(--faint);font-size:12.5px;font-family:ui-monospace,Menlo,Consolas,monospace;display:flex;align-items:center}
2119
+ .pulse{width:7px;height:7px;border-radius:100px;background:var(--g);display:inline-block;margin-left:8px;animation:pl 1.6s ease-in-out infinite}
2120
+ @keyframes pl{0%,100%{opacity:.3}50%{opacity:1}}
2121
+ @media (prefers-reduced-motion:reduce){.pulse{animation:none}}
2122
+ .stats{display:flex;gap:15px;flex-wrap:wrap;align-items:center;margin:14px 0 10px;font-size:13px}
2123
+ .stat{display:flex;align-items:center;gap:6px;color:var(--soft)}
2124
+ .stat b{color:var(--ink);font-weight:680}
2125
+ .dot{width:8px;height:8px;border-radius:100px;display:inline-block}
2126
+ .dot.g{background:var(--g)}.dot.a{background:var(--a)}.dot.r{background:var(--r)}
2127
+ .stat.sig{margin-left:auto;color:var(--g);font-weight:600}
2128
+ .actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:0 0 16px}
2129
+ .btn{cursor:pointer;font:inherit;font-size:12.5px;padding:7px 13px;border-radius:8px;border:1px solid var(--line);background:#fff;color:var(--ink);transition:border-color .12s}
2130
+ .btn:hover{border-color:var(--ink)}
2131
+ .btn.p{background:var(--ink);color:var(--paper);border-color:var(--ink)}
2132
+ .btn:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
2133
+ .vhint{font-size:12px;color:var(--faint);margin-left:auto;font-family:ui-monospace,Menlo,monospace}
2134
+ .attest{margin:0 0 12px;font-size:12.5px;color:var(--soft);display:flex;gap:8px;align-items:center;flex-wrap:wrap}
2135
+ .cmd{font-family:ui-monospace,Menlo,monospace;font-size:12px;background:#efece4;border:1px solid var(--line);border-radius:6px;padding:3px 8px;color:var(--ink)}
2136
+ .btn2{cursor:pointer;font:inherit;font-size:12px;padding:4px 9px;border-radius:7px;border:1px solid var(--line);background:#fff;color:var(--ink)}
2137
+ .btn2:hover{border-color:var(--ink)}
2138
+ .bar{margin:6px 0 12px}
2110
2139
  input{width:100%;padding:10px 13px;border:1px solid var(--line);border-radius:9px;background:#fff;font:inherit}
2111
2140
  .chips{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:14px}
2112
2141
  .chip{cursor:pointer;font-size:12px;padding:3px 10px;border-radius:100px;border:1px solid var(--line);background:#fff;color:var(--soft)}
2113
2142
  .chip.on{background:var(--ink);color:var(--paper);border-color:var(--ink)}
2114
2143
  .count{color:var(--faint);font-size:12px;font-family:ui-monospace,Menlo,monospace;margin-bottom:8px}
2115
2144
  .row{border:1px solid var(--line);border-radius:9px;background:#fcfbf7;padding:11px 13px;margin-bottom:8px;cursor:pointer}
2145
+ .row.blocked{background:#fbf3f0}.row.held{background:#fbf7ee}
2116
2146
  .top{display:flex;gap:9px;align-items:center;flex-wrap:wrap}
2117
2147
  .pill{font-size:11px;font-weight:600;padding:2px 9px;border-radius:100px}
2118
2148
  .pill.allowed{background:var(--gb);color:var(--g)}.pill.held{background:var(--ab);color:var(--a)}.pill.blocked{background:var(--rb);color:var(--r)}
2119
2149
  .tag{font-size:11px;padding:1px 7px;border-radius:100px;background:var(--paper);border:1px solid var(--line);color:var(--faint)}
2150
+ .cap{font-size:10px;padding:1px 6px;border-radius:100px;background:#eef0ea;border:1px solid var(--line);color:var(--soft)}
2151
+ .badge{font-size:10.5px;font-weight:600;padding:1px 7px;border-radius:100px}
2152
+ .badge.sgn{background:var(--gb);color:var(--g)}
2153
+ .badge.log{background:var(--paper);color:var(--faint);border:1px solid var(--line)}
2154
+ .dg{font-size:10.5px;color:var(--faint);font-family:ui-monospace,Menlo,monospace}
2120
2155
  .when{margin-left:auto;font-size:12px;color:var(--faint);font-family:ui-monospace,Menlo,monospace}
2121
2156
  .det{margin-top:8px;padding-top:8px;border-top:1px solid var(--line);font-size:12px;color:var(--soft);font-family:ui-monospace,Menlo,monospace;white-space:pre-wrap;word-break:break-all;display:none}
2122
2157
  .row.open .det{display:block}
2123
2158
  .foot{margin-top:22px;color:var(--faint);font-size:12px;line-height:1.6;border-top:1px solid var(--line);padding-top:14px}
2124
2159
  .foot b{color:var(--soft)}
2160
+ .viewtoggle{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden}
2161
+ .viewtoggle button{border:0;background:#fff;color:var(--soft);font:inherit;font-size:12.5px;padding:7px 12px;cursor:pointer}
2162
+ .viewtoggle button.on{background:var(--ink);color:var(--paper)}
2163
+ .agent{border:1px solid var(--line);border-radius:10px;background:#fcfbf7;margin-bottom:10px;overflow:hidden}
2164
+ .ahead{display:flex;gap:9px;align-items:center;flex-wrap:wrap;padding:11px 13px;cursor:pointer}
2165
+ .atwist{color:var(--faint);font-size:11px;transition:transform .12s;display:inline-block}
2166
+ .agent.open .atwist{transform:rotate(90deg)}
2167
+ .acount{font-size:12px;color:var(--faint)}
2168
+ .akids{display:none;padding:2px 12px 12px 24px;border-top:1px solid var(--line)}
2169
+ .agent.open .akids{display:block}
2170
+ .act{display:flex;gap:10px;align-items:center;flex-wrap:wrap;padding:9px 4px;cursor:pointer;border-bottom:1px solid var(--line)}
2171
+ .akids .act:last-child{border-bottom:0}
2172
+ .act:hover{background:rgba(0,0,0,.02)}
2173
+ .act.blocked{background:#fbf3f0}.act.held{background:#fbf7ee}
2174
+ .act .det{flex-basis:100%}
2175
+ .act.open .det{display:block}
2176
+ .ev{display:flex;gap:8px;align-items:center;font-size:12px;color:var(--faint);padding:5px 10px;margin-top:6px}
2177
+ .evdot{width:6px;height:6px;border-radius:100px;background:var(--faint);display:inline-block}
2178
+ .evre{color:var(--soft)}
2179
+ .badge.blk{background:var(--rb);color:var(--r)}
2125
2180
  </style></head><body><div class="wrap">
2126
2181
  <h1>Your record</h1>
2127
- <div class="meta" id="meta"></div>
2182
+ <div class="meta"><span id="meta"></span><span id="live"></span></div>
2183
+ <div class="stats" id="stats"></div>
2184
+ <div class="actions">
2185
+ <div class="viewtoggle"><button id="vlist" class="on" onclick="setView('list')">List</button><button id="vtree" onclick="setView('tree')">Tree</button></div>
2186
+ <button class="btn p" onclick="exportJsonl()">Export receipts (.jsonl)</button>
2187
+ <button class="btn" onclick="exportMd()">Export report (.md)</button>
2188
+ <button class="btn" id="cpv" onclick="copyVerify()">Copy verify command</button>
2189
+ <span class="vhint">verify offline: npx @veritasacta/verify</span>
2190
+ </div>
2128
2191
  <div class="bar"><input id="q" placeholder="Search your record: tool, reason, verdict"></div>
2129
2192
  <div class="chips" id="chips"></div>
2130
2193
  <div class="count" id="count"></div>
2194
+ <div class="attest" id="attest"></div>
2131
2195
  <div id="list"></div>
2132
- <div class="foot">Signed decisions from your own gate. This page is local and nothing was uploaded. Verify authoritatively with <b>npx protect-mcp receipts</b> or the open verifier. protect-mcp governs proposed actions before they run.</div>
2196
+ <div class="foot">Signed decisions from your own gate, on this machine. Nothing was uploaded. Each row is Ed25519-signed, and the exports carry the signatures, so anyone you hand them to (an allocator, an auditor, a counterparty) verifies offline with <b>npx @veritasacta/verify</b>, our code removed. For a Merkle-rooted evidence pack: <b>npx protect-mcp bundle</b>. To prove a claim over this record without revealing it (e.g. no egress): <b>npx protect-mcp claim --no net.egress</b>, checked offline with <b>npx protect-mcp verify-claim</b>. protect-mcp governs proposed actions before they run.</div>
2133
2197
  </div>
2134
2198
  <script>
2135
- var RECORDS=__DATA__;var META=__META__;var Q="",ACT={};
2199
+ var RECORDS=__DATA__;var META=__META__;var Q="",ACT={},VIEW="list",OPEN={};var NL=String.fromCharCode(10);
2136
2200
  function esc(s){return String(s).replace(/[&<>"]/g,function(c){return{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c]})}
2137
2201
  function vlabel(v){return v==="allowed"?"Allowed":v==="held"?"Held":"Blocked"}
2138
2202
  function when(ts){if(!ts)return"";var d=new Date(ts);return d.toLocaleDateString(undefined,{month:"short",day:"numeric"})+" "+d.toLocaleTimeString(undefined,{hour:"2-digit",minute:"2-digit"})}
2139
- function fvals(key){var m={};RECORDS.forEach(function(r){var v=key==="Decision"?vlabel(r.verdict):r[key.toLowerCase()];if(v){m[v]=(m[v]||0)+1}});return Object.keys(m).sort(function(a,b){return m[b]-m[a]}).slice(0,8).map(function(v){return[v,m[v]]})}
2140
- function match(r){if(ACT.Decision&&vlabel(r.verdict)!==ACT.Decision)return false;if(ACT.Tool&&r.tool!==ACT.Tool)return false;if(ACT.Reason&&r.reason!==ACT.Reason)return false;if(Q){var h=(r.tool+" "+r.reason+" "+vlabel(r.verdict)+" "+r.hook).toLowerCase();if(h.indexOf(Q)<0)return false}return true}
2203
+ function counts(rows){var c={allowed:0,held:0,blocked:0,signed:0};rows.forEach(function(r){c[r.verdict]=(c[r.verdict]||0)+1;if(r.signed)c.signed++});return c}
2204
+ function fvals(key){var m={};RECORDS.forEach(function(r){var vs=key==="Decision"?[vlabel(r.verdict)]:(key==="Capability"?(r.caps||[]):[r[key.toLowerCase()]]);vs.forEach(function(v){if(v){m[v]=(m[v]||0)+1}})});return Object.keys(m).sort(function(a,b){return m[b]-m[a]}).slice(0,10).map(function(v){return[v,m[v]]})}
2205
+ function match(r){if(ACT.Decision&&vlabel(r.verdict)!==ACT.Decision)return false;if(ACT.Tool&&r.tool!==ACT.Tool)return false;if(ACT.Reason&&r.reason!==ACT.Reason)return false;if(ACT.Capability&&(r.caps||[]).indexOf(ACT.Capability)<0)return false;if(Q){var h=(r.tool+" "+r.reason+" "+vlabel(r.verdict)+" "+r.hook+" "+(r.caps||[]).join(" ")).toLowerCase();if(h.indexOf(Q)<0)return false}return true}
2206
+ function filtered(){return RECORDS.filter(match)}
2207
+ function dl(name,text,type){var b=new Blob([text],{type:type||"text/plain"});var u=URL.createObjectURL(b);var a=document.createElement("a");a.href=u;a.download=name;document.body.appendChild(a);a.click();a.remove();setTimeout(function(){URL.revokeObjectURL(u)},1500)}
2208
+ function stamp(){return new Date().toISOString().replace(/[:.]/g,"-").slice(0,19)}
2209
+ function exportJsonl(){var rows=filtered();if(!rows.length)return;var lines=rows.map(function(r){return JSON.stringify(r.raw||r)}).join(NL);dl("protect-mcp-record-"+stamp()+".jsonl",lines,"application/x-ndjson")}
2210
+ function exportMd(){var rows=filtered();if(!rows.length)return;var c=counts(rows);var head=["# Agent decision record","",rows.length+" decisions from "+META.file,c.allowed+" allowed, "+c.held+" held, "+c.blocked+" blocked, "+c.signed+" signed.","","Generated locally by protect-mcp. These are signed receipts; verify offline with npx @veritasacta/verify (our code removed).","","| When | Decision | Tool | Reason | Hook | Signed |","|---|---|---|---|---|---|"];var body=rows.slice(0,3000).map(function(r){return "| "+(r.ts||"")+" | "+vlabel(r.verdict)+" | "+String(r.tool||"").replace(/\\|/g,"/")+" | "+String(r.reason||"").replace(/\\|/g,"/")+" | "+(r.hook||"")+" | "+(r.signed?"yes":"no")+" |"});dl("protect-mcp-record-"+stamp()+".md",head.concat(body).join(NL)+NL,"text/markdown")}
2211
+ function copyAttest(){var a=document.getElementById("attest");var cmd=a?a.getAttribute("data-cmd"):"";try{navigator.clipboard&&cmd&&navigator.clipboard.writeText(cmd)}catch(e){}var b=document.getElementById("cpa");if(b){var t=b.textContent;b.textContent="Copied";setTimeout(function(){b.textContent=t},1200)}}
2212
+ function copyVerify(){var cmd="npx @veritasacta/verify";try{navigator.clipboard&&navigator.clipboard.writeText(cmd)}catch(e){}var b=document.getElementById("cpv");if(b){var t=b.textContent;b.textContent="Copied";setTimeout(function(){b.textContent=t},1200)}}
2213
+ function renderStats(){var c=counts(RECORDS);var p=[];p.push('<span class="stat"><b>'+RECORDS.length+'</b> decisions</span>');p.push('<span class="stat"><span class="dot g"></span>'+c.allowed+' allowed</span>');if(c.held)p.push('<span class="stat"><span class="dot a"></span>'+c.held+' held</span>');p.push('<span class="stat"><span class="dot r"></span>'+c.blocked+' blocked</span>');p.push('<span class="stat sig">'+c.signed+' signed, verifiable offline</span>');document.getElementById("stats").innerHTML=p.join("")}
2214
+ function renderList(rows){var html="";rows.slice(0,800).forEach(function(r){var sig=r.signed?'<span class="badge sgn">signed</span>':'<span class="badge log">log</span>';var dg=r.digest?'<span class="dg">'+esc(String(r.digest).slice(0,10))+'</span>':'';var ct=(r.caps||[]).map(function(c){return '<span class="cap">'+esc(c)+'</span>'}).join('');var rk="row:"+(r.id||"")+"|"+(r.ts||"");html+='<div class="row '+r.verdict+(OPEN[rk]?" open":"")+'" data-k="'+esc(rk)+'"><div class="top"><span class="pill '+r.verdict+'">'+vlabel(r.verdict)+"</span><b>"+esc(r.tool)+'</b><span class="tag">'+esc(r.reason)+"</span>"+ct+(r.hook?'<span class="tag">'+esc(r.hook)+"</span>":"")+sig+dg+'<span class="when">'+esc(when(r.ts))+'</span></div><div class="det">'+esc(JSON.stringify(r.raw||r,null,2))+"</div></div>"});document.getElementById("list").innerHTML=html||'<p style="color:#8a837a">No records match.</p>';}
2215
+ function isLifecycle(r){var h=r.hook||"";return h==="SessionStart"||h==="SessionEnd"||h==="Stop"||h==="SubagentStart"||h==="SubagentStop"||h==="TaskCreated"||h==="TaskCompleted"||h==="ConfigChange"||h==="Notification"||h==="PreCompact";}
2216
+ function buildTree(rows){var ags={},order=[];rows.forEach(function(r){var a=r.agent||"main agent";if(!ags[a]){ags[a]={name:a,byId:{},items:[],caps:{},blocked:0,actions:0};order.push(a);}var g=ags[a];(r.caps||[]).forEach(function(c){g.caps[c]=(g.caps[c]||0)+1;});if(isLifecycle(r)){g.items.push({t:"e",ts:r.ts,r:r});return;}var id=r.id||("_"+r.ts);var n=g.byId[id];if(!n){n={t:"a",id:id,tool:r.tool,verdict:r.verdict,caps:(r.caps||[]).slice(),ts:r.ts,dur:0,signed:!!r.signed,raw:r.raw};g.byId[id]=n;g.items.push(n);g.actions++;}if(r.hook==="PostToolUse"){if(r.dur)n.dur=r.dur;if(!n.raw)n.raw=r.raw;}else{n.verdict=r.verdict;if((r.caps||[]).length)n.caps=r.caps.slice();n.raw=r.raw;n.ts=r.ts;}if(r.signed)n.signed=true;});order.forEach(function(a){var g=ags[a];g.blocked=g.items.filter(function(it){return it.t==="a"&&it.verdict==="blocked";}).length;g.items.sort(function(x,y){return (x.ts<y.ts)?-1:1;});});return order.map(function(a){return ags[a];});}
2217
+ function renderTree(ags){if(!ags.length){document.getElementById("list").innerHTML='<p style="color:#8a837a">No records match.</p>';return;}var html="",N=0;ags.forEach(function(g,gi){var capstr=Object.keys(g.caps).sort(function(a,b){return g.caps[b]-g.caps[a];}).slice(0,5).map(function(c){return '<span class="cap">'+esc(c)+'</span>';}).join('');var ak="ag:"+g.name;var op=(OPEN.hasOwnProperty(ak)?OPEN[ak]:(ags.length===1||gi===0))?" open":"";html+='<div class="agent'+op+'" data-k="'+esc(ak)+'"><div class="ahead"><span class="atwist">\u25B8</span><b>'+esc(g.name)+'</b><span class="acount">'+g.actions+' action'+(g.actions===1?'':'s')+'</span>'+(g.blocked?'<span class="badge blk">'+g.blocked+' blocked</span>':'')+capstr+'</div><div class="akids">';g.items.forEach(function(it){if(N++>1500)return;if(it.t==="e"){var r=it.r;html+='<div class="ev"><span class="evdot"></span>'+esc(r.hook||r.tool)+' <span class="evre">'+esc(r.reason)+'</span><span class="when">'+esc(when(r.ts))+'</span></div>';}else{var ct=(it.caps||[]).map(function(c){return '<span class="cap">'+esc(c)+'</span>';}).join('');var dur=it.dur?'<span class="dg">'+it.dur+'ms</span>':'';var ik="act:"+it.id;html+='<div class="act '+it.verdict+(OPEN[ik]?" open":"")+'" data-k="'+esc(ik)+'"><span class="pill '+it.verdict+'">'+vlabel(it.verdict)+'</span><b>'+esc(it.tool)+'</b>'+ct+(it.signed?'<span class="badge sgn">signed</span>':'')+dur+'<span class="when">'+esc(when(it.ts))+'</span><div class="det">'+esc(JSON.stringify(it.raw||{},null,2))+'</div></div>';}});html+='</div></div>';});if(N>1500)html+='<p style="color:#8a837a;font-size:12px;margin-top:10px">Showing the first 1500 items. Search or pick a facet to narrow.</p>';document.getElementById("list").innerHTML=html;}
2218
+ function setView(v){VIEW=v;document.getElementById("vlist").className=v==="list"?"on":"";document.getElementById("vtree").className=v==="tree"?"on":"";render();}
2141
2219
  function render(){
2142
2220
  document.getElementById("meta").textContent=META.count+" decisions from "+META.file+(META.signed?" (signed)":" (decision log)")+" - all local"+(META.live?" \xB7 live, updating":"");
2143
- var chips="";["Decision","Tool","Reason"].forEach(function(key){fvals(key).forEach(function(p){var on=ACT[key]===p[0];chips+='<span class="chip'+(on?" on":"")+'" data-k="'+key+'" data-v="'+esc(p[0])+'">'+esc(p[0])+" "+p[1]+"</span>"})});
2221
+ document.getElementById("live").innerHTML=META.live?'<span class="pulse"></span>':"";
2222
+ renderStats();
2223
+ var chips="";["Decision","Tool","Reason","Capability"].forEach(function(key){fvals(key).forEach(function(p){var on=ACT[key]===p[0];chips+='<span class="chip'+(on?" on":"")+'" data-k="'+key+'" data-v="'+esc(p[0])+'">'+esc(p[0])+" "+p[1]+"</span>"})});
2144
2224
  document.getElementById("chips").innerHTML=chips;
2145
2225
  var rows=RECORDS.filter(match);
2146
- document.getElementById("count").textContent=rows.length+" of "+RECORDS.length+" records";
2147
- var html="";rows.slice(0,800).forEach(function(r){html+='<div class="row"><div class="top"><span class="pill '+r.verdict+'">'+vlabel(r.verdict)+"</span><b>"+esc(r.tool)+'</b><span class="tag">'+esc(r.reason)+"</span>"+(r.hook?'<span class="tag">'+esc(r.hook)+"</span>":"")+'<span class="tag">'+(r.signed?"signed":"log")+'</span><span class="when">'+esc(when(r.ts))+'</span></div><div class="det">'+esc(JSON.stringify(r,null,2))+"</div></div>"});
2148
- document.getElementById("list").innerHTML=html||'<p style="color:#8a837a">No records match.</p>'}
2226
+ document.getElementById("count").textContent=rows.length+" of "+RECORDS.length+" records"+(VIEW==="tree"?" \xB7 grouped by agent":"");
2227
+ var _at=document.getElementById("attest");if(ACT.Capability){var _cmd="npx protect-mcp claim --no "+ACT.Capability;_at.setAttribute("data-cmd",_cmd);_at.innerHTML='Prove it over this record, revealing nothing: <span class="cmd">'+esc(_cmd)+'</span><button class="btn2" id="cpa" onclick="copyAttest()">Copy</button>';}else{_at.innerHTML="";_at.removeAttribute("data-cmd");}
2228
+ if(VIEW==="tree"){renderTree(buildTree(rows));}else{renderList(rows);}}
2149
2229
  document.getElementById("q").addEventListener("input",function(e){Q=e.target.value.toLowerCase().trim();render()});
2150
2230
  document.getElementById("chips").addEventListener("click",function(e){var c=e.target.closest(".chip");if(!c)return;var k=c.getAttribute("data-k"),v=c.getAttribute("data-v");ACT[k]=ACT[k]===v?undefined:v;render()});
2151
- document.getElementById("list").addEventListener("click",function(e){var row=e.target.closest(".row");if(row)row.classList.toggle("open")});
2231
+ document.getElementById("list").addEventListener("click",function(e){var ah=e.target.closest(".ahead");if(ah){var ag=ah.parentNode;ag.classList.toggle("open");var ak=ag.getAttribute("data-k");if(ak)OPEN[ak]=ag.classList.contains("open");return;}var el=e.target.closest(".act")||e.target.closest(".row");if(el){el.classList.toggle("open");var k=el.getAttribute("data-k");if(k)OPEN[k]=el.classList.contains("open");}});
2152
2232
  render();
2153
- if(META.live){var poll=function(){fetch('/data',{cache:'no-store'}).then(function(r){return r.json()}).then(function(d){RECORDS=d.recs||[];META.count=RECORDS.length;if(typeof d.signed==='boolean')META.signed=d.signed;render()}).catch(function(){})};poll();setInterval(poll,2000);}
2233
+ if(META.live){var poll=function(){fetch('/data',{cache:'no-store'}).then(function(r){return r.json()}).then(function(d){var nr=d.recs||[];var changed=nr.length!==RECORDS.length;RECORDS=nr;META.count=RECORDS.length;if(typeof d.signed==='boolean')META.signed=d.signed;if(changed)render()}).catch(function(){})};poll();setInterval(poll,2000);}
2154
2234
  </script></body></html>`;
2235
+ async function handleClaim(argv) {
2236
+ const { readFileSync, existsSync, writeFileSync } = await import("fs");
2237
+ const { join } = await import("path");
2238
+ const { buildClaim } = await import("./claim-TUDH2WPB.mjs");
2239
+ let dir = process.cwd();
2240
+ const di = argv.indexOf("--dir");
2241
+ if (di !== -1 && argv[di + 1]) dir = argv[di + 1];
2242
+ let predicate = null;
2243
+ const noIdx = argv.indexOf("--no"), onlyIdx = argv.indexOf("--only"), nvIdx = argv.indexOf("--no-verdict"), cvIdx = argv.indexOf("--count");
2244
+ if (noIdx !== -1 && argv[noIdx + 1]) predicate = { kind: "no_capability", capability: argv[noIdx + 1] };
2245
+ else if (onlyIdx !== -1 && argv[onlyIdx + 1]) predicate = { kind: "only_capabilities", capabilities: argv[onlyIdx + 1].split(",").map((s) => s.trim()).filter(Boolean) };
2246
+ else if (nvIdx !== -1 && argv[nvIdx + 1]) predicate = { kind: "no_verdict", verdict: argv[nvIdx + 1] };
2247
+ else if (cvIdx !== -1 && argv[cvIdx + 1]) predicate = { kind: "count_verdict", verdict: argv[cvIdx + 1] };
2248
+ if (!predicate) {
2249
+ process.stderr.write(`
2250
+ ${bold("protect-mcp claim")}
2251
+
2252
+ Attest a signed, position-blind claim over your record:
2253
+ --no <capability> no action used it, e.g. ${dim("--no net.egress")}
2254
+ --only <c1,c2,...> all actions confined to these capabilities
2255
+ --no-verdict <verdict> e.g. ${dim("--no-verdict blocked")}
2256
+ --count <verdict> how many, e.g. ${dim("--count blocked")}
2257
+ --anchor also record the claim digest in the public append-only
2258
+ log so a counterparty can trust it is complete (only the
2259
+ hash is sent; your record stays local)
2260
+
2261
+ Example: ${bold("npx protect-mcp claim --no net.egress --anchor")}
2262
+
2263
+ `);
2264
+ process.exit(0);
2265
+ return;
2266
+ }
2267
+ const keyPath = join(dir, "keys", "gateway.json");
2268
+ if (!existsSync(keyPath)) {
2269
+ process.stderr.write(`
2270
+ ${bold("protect-mcp claim")}
2271
+
2272
+ No signing key at ${keyPath}. A claim must be signed. Run ${bold("npx protect-mcp init")} first.
2273
+
2274
+ `);
2275
+ process.exit(1);
2276
+ return;
2277
+ }
2278
+ let key;
2279
+ try {
2280
+ key = JSON.parse(readFileSync(keyPath, "utf-8"));
2281
+ } catch {
2282
+ process.stderr.write(`
2283
+ protect-mcp claim: ${keyPath} is not valid JSON.
2284
+
2285
+ `);
2286
+ process.exit(1);
2287
+ return;
2288
+ }
2289
+ if (!key.privateKey || !key.publicKey) {
2290
+ process.stderr.write(`
2291
+ protect-mcp claim: ${keyPath} is missing privateKey/publicKey.
2292
+
2293
+ `);
2294
+ process.exit(1);
2295
+ return;
2296
+ }
2297
+ const recPath = join(dir, ".protect-mcp-receipts.jsonl");
2298
+ if (!existsSync(recPath)) {
2299
+ process.stderr.write(`
2300
+ ${bold("protect-mcp claim")}
2301
+
2302
+ No signed receipts in ${dir}. Run the gate with signing on, then try again.
2303
+
2304
+ `);
2305
+ process.exit(0);
2306
+ return;
2307
+ }
2308
+ const receipts = readFileSync(recPath, "utf-8").split(/\r?\n/).map((l) => l.trim()).filter(Boolean).map((l) => {
2309
+ try {
2310
+ return JSON.parse(l);
2311
+ } catch {
2312
+ return null;
2313
+ }
2314
+ }).filter((x) => x !== null);
2315
+ if (!receipts.length) {
2316
+ process.stderr.write(`
2317
+ protect-mcp claim: no readable receipts in ${recPath}.
2318
+
2319
+ `);
2320
+ process.exit(0);
2321
+ return;
2322
+ }
2323
+ const pack = buildClaim(receipts, predicate, { privateKey: key.privateKey, publicKey: key.publicKey, kid: key.kid || "gateway", issuer: "protect-mcp" }, (/* @__PURE__ */ new Date()).toISOString());
2324
+ const oi = argv.indexOf("--output");
2325
+ const out = oi !== -1 && argv[oi + 1] ? argv[oi + 1] : join(dir, "claim-" + Date.now() + ".json");
2326
+ writeFileSync(out, JSON.stringify(pack, null, 2) + "\n");
2327
+ process.stdout.write(`
2328
+ ${bold("\u{1F6E1}\uFE0F Signed claim")}
2329
+ `);
2330
+ process.stdout.write(` ${pack.claim.statement}: ${pack.claim.holds ? green("holds") : yellow("does not hold")} ${dim("(" + pack.claim.matched + " matched of " + pack.scope.total + " decisions)")}
2331
+ `);
2332
+ process.stdout.write(` ${dim("Position-blind: reveals decision categories, never tool inputs, outputs, or data. Ed25519-signed.")}
2333
+ `);
2334
+ process.stdout.write(` Written to ${out}
2335
+ `);
2336
+ process.stdout.write(` Hand it to anyone. They verify offline: ${bold("npx protect-mcp verify-claim " + out)}
2337
+ `);
2338
+ if (argv.indexOf("--anchor") !== -1) {
2339
+ const { anchorClaim } = await import("./claim-TUDH2WPB.mjs");
2340
+ const li = argv.indexOf("--log");
2341
+ const logBase = li !== -1 && argv[li + 1] ? argv[li + 1] : void 0;
2342
+ process.stdout.write(`
2343
+ ${dim("Anchoring the claim digest to the public append-only log (only the hash leaves your machine)...")}
2344
+ `);
2345
+ const res = await anchorClaim(
2346
+ pack,
2347
+ { privateKey: key.privateKey, publicKey: key.publicKey, kid: key.kid || "gateway", issuer: "protect-mcp" },
2348
+ { log: logBase, issuedAt: (/* @__PURE__ */ new Date()).toISOString() }
2349
+ );
2350
+ if (res.ok) {
2351
+ const sidecar = out.replace(/\.json$/, "") + ".anchor.json";
2352
+ writeFileSync(sidecar, JSON.stringify({ log: logBase || "https://scopeblind.com", seq: res.seq, entry_url: res.entry_url, anchored_at: res.anchored_at, claim_digest: res.claim_digest, envelope: res.envelope }, null, 2) + "\n");
2353
+ process.stdout.write(` ${green("Anchored")} as log entry ${bold("#" + res.seq)}${res.already_anchored ? dim(" (already present)") : ""} ${dim(res.entry_url || "")}
2354
+ `);
2355
+ process.stdout.write(` ${dim("A counterparty can now confirm this exact claim existed at " + (res.anchored_at || "this time") + " and cannot be quietly re-cut.")}
2356
+ `);
2357
+ process.stdout.write(` ${dim("Anchor record written to " + sidecar + ". Only the digest was sent; your record stayed local.")}
2358
+ `);
2359
+ process.stdout.write(` ${dim("To anchor as your enrolled org identity (a key a counterparty can pin), see")} ${bold("scopeblind.com/enroll")}
2360
+ `);
2361
+ } else {
2362
+ process.stdout.write(` ${yellow("Anchor skipped")} ${dim("(" + (res.error || "unavailable") + "). The claim above is complete and verifiable offline without it.")}
2363
+ `);
2364
+ }
2365
+ }
2366
+ process.stdout.write(`
2367
+ `);
2368
+ process.exit(0);
2369
+ }
2370
+ async function handleVerifyClaim(argv) {
2371
+ const { readFileSync, existsSync } = await import("fs");
2372
+ const { verifyClaim } = await import("./claim-TUDH2WPB.mjs");
2373
+ const file = argv.find((a) => !a.startsWith("--"));
2374
+ if (!file || !existsSync(file)) {
2375
+ process.stderr.write(`
2376
+ ${bold("protect-mcp verify-claim")} <claim.json> [--key <public-hex>]
2377
+
2378
+ Provide a claim pack file.
2379
+
2380
+ `);
2381
+ process.exit(2);
2382
+ return;
2383
+ }
2384
+ let pack;
2385
+ try {
2386
+ pack = JSON.parse(readFileSync(file, "utf-8"));
2387
+ } catch {
2388
+ process.stderr.write(`
2389
+ protect-mcp verify-claim: ${file} is not valid JSON.
2390
+
2391
+ `);
2392
+ process.exit(2);
2393
+ return;
2394
+ }
2395
+ if (!pack || pack.type !== "scopeblind.claim.v1") {
2396
+ process.stderr.write(`
2397
+ protect-mcp verify-claim: not a scopeblind.claim.v1 pack.
2398
+
2399
+ `);
2400
+ process.exit(2);
2401
+ return;
2402
+ }
2403
+ const ki = argv.indexOf("--key");
2404
+ const v = verifyClaim(pack, ki !== -1 ? argv[ki + 1] : void 0);
2405
+ const ok = (b) => b ? green("\u2713") : red("\u2717");
2406
+ process.stdout.write(`
2407
+ ${bold("protect-mcp verify-claim")}
2408
+ `);
2409
+ process.stdout.write(` Claim: ${pack.claim ? pack.claim.statement : "(none)"}
2410
+ `);
2411
+ process.stdout.write(` Holds: ${v.holds ? green("yes") : yellow("no")} ${dim("(" + v.matched + " matched of " + v.total + " decisions)")}
2412
+ `);
2413
+ process.stdout.write(` Signature: ${ok(v.authentic)} ${v.authentic ? "valid" : "INVALID"} ${dim("issuer kid " + (pack.issuer && pack.issuer.kid || "?"))}
2414
+ `);
2415
+ process.stdout.write(` Commitment: ${ok(v.root_ok)} ${v.root_ok ? "Merkle root matches the " + v.total + " disclosed decisions" : "MISMATCH"}
2416
+ `);
2417
+ process.stdout.write(` Predicate: ${ok(v.predicate_ok)} ${v.predicate_ok ? "recomputed independently and matches" : "MISMATCH"}
2418
+ `);
2419
+ process.stdout.write(`
2420
+ ${v.valid ? green("VALID") : red("INVALID")} attestation.
2421
+ `);
2422
+ process.stdout.write(` ${dim("Proves the pack came from the issuer key and the claim is true over the disclosed decision")}
2423
+ `);
2424
+ process.stdout.write(` ${dim("categories (verdict + capabilities), which reveal no tool inputs, outputs, or data. Completeness")}
2425
+ `);
2426
+ process.stdout.write(` ${dim("of the disclosed set is attested by the issuer; pin the key or anchor to the log for more.")}
2427
+
2428
+ `);
2429
+ process.exit(v.valid ? 0 : 1);
2430
+ }
2155
2431
  async function handleBundle(argv) {
2156
2432
  const { readFileSync, writeFileSync, existsSync } = await import("fs");
2157
2433
  const { join } = await import("path");
@@ -2316,7 +2592,7 @@ ${bold("protect-mcp quickstart")}
2316
2592
  const { randomBytes } = await import("crypto");
2317
2593
  let keypair;
2318
2594
  try {
2319
- const { ed25519 } = await import("./ed25519-BSHMMVNX.mjs");
2595
+ const { ed25519 } = await import("./ed25519-SQA3S2RV.mjs");
2320
2596
  const { bytesToHex } = await import("./utils-6AYZFE5A.mjs");
2321
2597
  const privateKey = randomBytes(32);
2322
2598
  const publicKey = ed25519.getPublicKey(privateKey);
@@ -2531,7 +2807,7 @@ ${bold("protect-mcp registry status")}
2531
2807
  async function handleKillerDemo(argv) {
2532
2808
  const { mkdtempSync } = await import("fs");
2533
2809
  const { tmpdir } = await import("os");
2534
- const { ed25519 } = await import("./ed25519-BSHMMVNX.mjs");
2810
+ const { ed25519 } = await import("./ed25519-SQA3S2RV.mjs");
2535
2811
  const { bytesToHex } = await import("./utils-6AYZFE5A.mjs");
2536
2812
  const { randomBytes } = await import("crypto");
2537
2813
  const artifacts = await import("@veritasacta/artifacts");
@@ -2539,7 +2815,7 @@ async function handleKillerDemo(argv) {
2539
2815
  createSelectiveDisclosurePackage,
2540
2816
  signCommittedDecision,
2541
2817
  verifySelectiveDisclosurePackage
2542
- } = await import("./signing-committed-QXCW24RF.mjs");
2818
+ } = await import("./signing-committed-TGWXSLAO.mjs");
2543
2819
  const registryMod = await import("./receipt-registry-6CAOY6RP.mjs");
2544
2820
  const dir = resolveCli(flagValue(argv, "--dir") || mkdtempSync(joinCli(tmpdir(), "scopeblind-killer-demo-")));
2545
2821
  mkdirSyncCli(dir, { recursive: true });
@@ -2808,7 +3084,7 @@ async function handleVerifyDisclosure(argv) {
2808
3084
  process.stderr.write("Usage: protect-mcp verify-disclosure --receipt <committed-receipt.json> --disclosure <selective-disclosure.json>\\n");
2809
3085
  process.exit(1);
2810
3086
  }
2811
- const { verifySelectiveDisclosurePackage } = await import("./signing-committed-QXCW24RF.mjs");
3087
+ const { verifySelectiveDisclosurePackage } = await import("./signing-committed-TGWXSLAO.mjs");
2812
3088
  const receipt = JSON.parse(readFileSyncCli(resolveCli(receiptPath), "utf-8"));
2813
3089
  const disclosure = JSON.parse(readFileSyncCli(resolveCli(disclosurePath), "utf-8"));
2814
3090
  const result = verifySelectiveDisclosurePackage(receipt, disclosure);
@@ -3304,7 +3580,7 @@ ${bold("protect-mcp init-hooks")}
3304
3580
  if (!existsSync(keysDir)) mkdirSync(keysDir, { recursive: true });
3305
3581
  const { randomBytes: rb } = await import("crypto");
3306
3582
  try {
3307
- const { ed25519 } = await import("./ed25519-BSHMMVNX.mjs");
3583
+ const { ed25519 } = await import("./ed25519-SQA3S2RV.mjs");
3308
3584
  const { bytesToHex } = await import("./utils-6AYZFE5A.mjs");
3309
3585
  const privateKey = rb(32);
3310
3586
  const publicKey = ed25519.getPublicKey(privateKey);
@@ -3649,6 +3925,14 @@ async function main() {
3649
3925
  await handleRecord(args.slice(1));
3650
3926
  return;
3651
3927
  }
3928
+ if (args[0] === "claim") {
3929
+ await handleClaim(args.slice(1));
3930
+ return;
3931
+ }
3932
+ if (args[0] === "verify-claim") {
3933
+ await handleVerifyClaim(args.slice(1));
3934
+ return;
3935
+ }
3652
3936
  if (args[0] === "init-hooks") {
3653
3937
  await handleInitHooks(args.slice(1));
3654
3938
  process.exit(0);
@@ -3837,7 +4121,7 @@ async function main() {
3837
4121
  if (useHttp) {
3838
4122
  const portIdx = args.indexOf("--port");
3839
4123
  const httpPort = portIdx >= 0 && args[portIdx + 1] ? parseInt(args[portIdx + 1]) : 3e3;
3840
- const { startHttpTransport } = await import("./http-transport-JBORN27G.mjs");
4124
+ const { startHttpTransport } = await import("./http-transport-D7C64PIA.mjs");
3841
4125
  startHttpTransport({ port: httpPort, config, serverCommand: childCommand });
3842
4126
  return;
3843
4127
  }
@@ -15,7 +15,8 @@ import {
15
15
  ristretto255,
16
16
  ristretto255_hasher,
17
17
  x25519
18
- } from "./chunk-LJQOALYR.mjs";
18
+ } from "./chunk-FFVJL3KQ.mjs";
19
+ import "./chunk-JIDDQUSQ.mjs";
19
20
  import "./chunk-D733KAPG.mjs";
20
21
  import "./chunk-PQJP2ZCI.mjs";
21
22
  export {