prae-cli 0.4.0 → 0.5.0

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/lib/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import "./evaluate-BilZez15.js";
3
- import { t as main } from "./cli-BvAYSK5w.js";
4
- import "./src-CAUNqZJ1.js";
3
+ import { t as main } from "./cli-ZfwhJKRw.js";
4
+ import "./src-CXU0_W1P.js";
5
5
  import "./views-DalPkBZZ.js";
6
6
 
7
7
  //#region src/bin.ts
@@ -1,5 +1,5 @@
1
1
  import { t as evaluate } from "./evaluate-BilZez15.js";
2
- import { p as seal, t as Ledger } from "./src-CAUNqZJ1.js";
2
+ import { m as seal, t as Ledger } from "./src-CXU0_W1P.js";
3
3
  import { a as renderPacks, c as renderShell, i as renderOverview, l as VERDICT_LABEL$1, n as renderApprovals, o as esc, r as renderCompliance, s as renderNav, t as navFor } from "./views-DalPkBZZ.js";
4
4
  import { existsSync, readFileSync } from "node:fs";
5
5
  import { mkdir, readFile, writeFile } from "node:fs/promises";
@@ -864,6 +864,10 @@ const USAGE = `prae — operator policy for agent harnesses
864
864
  prae receipt <ledger.jsonl> <seq> --key <private.pem> [--out <file>]
865
865
  Export one decision as a self-contained signed receipt.
866
866
 
867
+ prae verify-receipt <receipt.json>
868
+ Check one receipt with nothing but the file: recompute its hash, verify
869
+ its signature, and print the key fingerprint to compare against your own.
870
+
867
871
  prae report <ledger.jsonl> [--out <file.html>]
868
872
  Render the decision record as a printable evidence report.
869
873
 
@@ -972,7 +976,7 @@ async function mcp(root, packPath, ledgerPath) {
972
976
  const chosen = packPath ?? bundled;
973
977
  if (chosen === void 0) return fail("mcp needs --pack <file>; no bundled secret-catcher found");
974
978
  const pack = await loadPack(chosen);
975
- const { Ledger: Ledger$1 } = await import("./src-CBK_quVO.js");
979
+ const { Ledger: Ledger$1 } = await import("./src-Kubj3MTX.js");
976
980
  const ledger = ledgerPath === void 0 ? void 0 : Ledger$1.open(ledgerPath);
977
981
  console.error(`prae mcp: serving ${resolve$1(root)} under ${pack.pack} ${pack.version}` + (ledgerPath === void 0 ? " (no ledger)" : ` · ledger ${ledgerPath}`));
978
982
  await serveMcp({
@@ -1024,7 +1028,7 @@ async function explain(path, tool, pairs) {
1024
1028
  * a key that arrived beside it is checked against nothing.
1025
1029
  */
1026
1030
  async function keygen(dir) {
1027
- const { generateKeypair, fingerprint } = await import("./src-CBK_quVO.js");
1031
+ const { generateKeypair, fingerprint } = await import("./src-Kubj3MTX.js");
1028
1032
  const { writeFileSync: writeFileSync$1 } = await import("node:fs");
1029
1033
  const keys = generateKeypair();
1030
1034
  const priv = join(dir, "prae-signing.pem");
@@ -1040,7 +1044,7 @@ async function keygen(dir) {
1040
1044
  console.log("private key with `signingKey` to sign every row.");
1041
1045
  }
1042
1046
  async function receipt(path, seq, keyPath, out) {
1043
- const { Ledger: Ledger$1, makeReceipt, fingerprint } = await import("./src-CBK_quVO.js");
1047
+ const { Ledger: Ledger$1, makeReceipt, fingerprint } = await import("./src-Kubj3MTX.js");
1044
1048
  const { readFileSync: readFileSync$1 } = await import("node:fs");
1045
1049
  const { createPublicKey } = await import("node:crypto");
1046
1050
  const privateKey = readFileSync$1(keyPath, "utf8");
@@ -1061,6 +1065,27 @@ async function receipt(path, seq, keyPath, out) {
1061
1065
  console.log("The verifier compares that fingerprint against the one they");
1062
1066
  console.log("pinned — a receipt is only as good as the key they trust.");
1063
1067
  }
1068
+ /**
1069
+ * `prae verify-receipt`: check a single self-contained receipt.
1070
+ *
1071
+ * The counterparty's command. It needs nothing but the receipt file — no
1072
+ * ledger, no PRAE account, no trust in the machine that issued it. The verdict
1073
+ * and its wording live in `receipt-check.ts` as a pure function; this reads the
1074
+ * file and sets the exit code, so scripts can branch on it.
1075
+ */
1076
+ async function verifyReceiptCmd(path) {
1077
+ const { checkReceipt } = await import("./receipt-check-Dql0uqxw.js");
1078
+ let parsed;
1079
+ try {
1080
+ parsed = JSON.parse(await readFile(path, "utf8"));
1081
+ } catch {
1082
+ return fail(`cannot read a receipt from ${path}`);
1083
+ }
1084
+ const result = checkReceipt(parsed);
1085
+ for (const line of result.lines) if (result.ok) console.log(line);
1086
+ else console.error(line);
1087
+ if (!result.ok) process.exit(1);
1088
+ }
1064
1089
  async function verify(path, keyPath) {
1065
1090
  let ledger;
1066
1091
  try {
@@ -1074,7 +1099,7 @@ async function verify(path, keyPath) {
1074
1099
  const last = entries.at(-1);
1075
1100
  console.log(`${result.entries} entries · chain intact${last === void 0 ? "" : ` · seal ${seal(last)}`}`);
1076
1101
  if (keyPath !== void 0) {
1077
- const { verifySignature, fingerprint } = await import("./src-CBK_quVO.js");
1102
+ const { verifySignature, fingerprint } = await import("./src-Kubj3MTX.js");
1078
1103
  const { readFileSync: readFileSync$1 } = await import("node:fs");
1079
1104
  const publicKey = readFileSync$1(keyPath, "utf8");
1080
1105
  let signed = 0;
@@ -1150,7 +1175,7 @@ async function report(path, out) {
1150
1175
  * that step is a proposal a human approves, and it starts from this list.
1151
1176
  */
1152
1177
  async function instructions(from, asJson) {
1153
- const { ingest } = await import("./src-CCeafjdm.js");
1178
+ const { ingest } = await import("./src-C4fnUm9V.js");
1154
1179
  const result = ingest(from);
1155
1180
  if (asJson) {
1156
1181
  console.log(JSON.stringify(result, null, 2));
@@ -1186,7 +1211,7 @@ async function instructions(from, asJson) {
1186
1211
  * on purpose rather than something that happened to their policy.
1187
1212
  */
1188
1213
  async function propose(from, options) {
1189
- const [{ ingest }, { extract }] = await Promise.all([import("./src-CCeafjdm.js"), import("./src-aU5WW02C.js")]);
1214
+ const [{ ingest }, { extract }] = await Promise.all([import("./src-C4fnUm9V.js"), import("./src-BfYsUDW9.js")]);
1190
1215
  const read = ingest(from);
1191
1216
  if (read.files.length === 0) {
1192
1217
  console.log(`No instruction files found at or above ${from}.`);
@@ -1246,8 +1271,8 @@ async function consoleOut(path, argv) {
1246
1271
  const instructionsDir = flag(argv, "instructions");
1247
1272
  if (instructionsDir !== void 0) {
1248
1273
  const [{ ingest }, { extract }, { renderInstructions }] = await Promise.all([
1249
- import("./src-CCeafjdm.js"),
1250
- import("./src-aU5WW02C.js"),
1274
+ import("./src-C4fnUm9V.js"),
1275
+ import("./src-BfYsUDW9.js"),
1251
1276
  import("./instructions-D4VOn2lk.js")
1252
1277
  ]);
1253
1278
  const read = ingest(instructionsDir);
@@ -1369,6 +1394,11 @@ async function main(argv) {
1369
1394
  if (path === void 0) fail("verify needs a ledger file");
1370
1395
  return verify(path, flag(rest, "key"));
1371
1396
  }
1397
+ case "verify-receipt": {
1398
+ const path = rest[0];
1399
+ if (path === void 0) fail("verify-receipt needs a receipt file");
1400
+ return verifyReceiptCmd(path);
1401
+ }
1372
1402
  case "report": {
1373
1403
  const path = rest[0];
1374
1404
  if (path === void 0) fail("report needs a ledger file");
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import "./evaluate-BilZez15.js";
2
- import { a as planWrite, i as dshHome, n as applyWrite, o as praeRow, r as defaultProfile, s as profileAt, t as main } from "./cli-BvAYSK5w.js";
3
- import "./src-CAUNqZJ1.js";
2
+ import { a as planWrite, i as dshHome, n as applyWrite, o as praeRow, r as defaultProfile, s as profileAt, t as main } from "./cli-ZfwhJKRw.js";
3
+ import "./src-CXU0_W1P.js";
4
4
  import "./views-DalPkBZZ.js";
5
5
 
6
6
  export { applyWrite, defaultProfile, dshHome, main, planWrite, praeRow, profileAt };
@@ -0,0 +1,39 @@
1
+ import { c as verifyReceipt, i as fingerprint } from "./src-CXU0_W1P.js";
2
+
3
+ //#region src/receipt-check.ts
4
+ /** Shape we require before we will call it a receipt at all. */
5
+ function looksLikeReceipt(value) {
6
+ if (typeof value !== "object" || value === null) return false;
7
+ const r = value;
8
+ return typeof r.signature === "string" && typeof r.publicKey === "string" && typeof r.entry === "object" && r.entry !== null;
9
+ }
10
+ function checkReceipt(parsed) {
11
+ if (!looksLikeReceipt(parsed)) return {
12
+ ok: false,
13
+ lines: ["✗ not a receipt: expected `entry`, `signature` and `publicKey` fields"]
14
+ };
15
+ const result = verifyReceipt(parsed);
16
+ if (!result.ok) return {
17
+ ok: false,
18
+ lines: [`✗ receipt does NOT verify: ${result.reason}`]
19
+ };
20
+ const { verdict, tool, rule, subject } = parsed.entry;
21
+ return {
22
+ ok: true,
23
+ lines: [
24
+ "✓ receipt verifies — authentic and unaltered",
25
+ ` ${[
26
+ verdict,
27
+ tool,
28
+ rule === void 0 ? void 0 : `· ${rule}`,
29
+ subject === void 0 ? void 0 : `· ${subject}`
30
+ ].filter((part) => part !== void 0).join(" ")}`,
31
+ ` sealed by key ${fingerprint(parsed.publicKey)}`,
32
+ " Compare that fingerprint to the one you pinned out of band.",
33
+ " It matches → this is a decision that key’s holder sealed, unchanged."
34
+ ]
35
+ };
36
+ }
37
+
38
+ //#endregion
39
+ export { checkReceipt };
@@ -76,6 +76,23 @@ function makeReceipt(entry, privateKeyPem, publicKeyPem) {
76
76
  publicKey: publicKeyPem
77
77
  };
78
78
  }
79
+ /** Verify a receipt from nothing but the receipt. */
80
+ function verifyReceipt(receipt) {
81
+ if (receipt.receipt !== "prae/1") return {
82
+ ok: false,
83
+ reason: `unknown receipt format ${String(receipt.receipt)}`
84
+ };
85
+ const { seq, prev, hash, sig,...record } = receipt.entry;
86
+ if (hashEntry(record, seq, prev) !== hash) return {
87
+ ok: false,
88
+ reason: "entry contents do not match their seal; the row was altered"
89
+ };
90
+ if (!verifySignature(receipt.entry, receipt.signature, receipt.publicKey)) return {
91
+ ok: false,
92
+ reason: "signature does not verify; not sealed by this key"
93
+ };
94
+ return { ok: true };
95
+ }
79
96
  /**
80
97
  * A short identifier for a public key, for pinning by eye.
81
98
  *
@@ -222,4 +239,4 @@ var Ledger = class Ledger {
222
239
  };
223
240
 
224
241
  //#endregion
225
- export { generateKeypair as a, verifySignature as c, chain as d, hashEntry as f, fingerprint as i, GENESIS as l, readEntries as n, makeReceipt as o, seal as p, verifyEntries as r, signEntry as s, Ledger as t, canonical as u };
242
+ export { generateKeypair as a, verifyReceipt as c, canonical as d, chain as f, fingerprint as i, verifySignature as l, seal as m, readEntries as n, makeReceipt as o, hashEntry as p, verifyEntries as r, signEntry as s, Ledger as t, GENESIS as u };
@@ -0,0 +1,3 @@
1
+ import { a as generateKeypair, c as verifyReceipt, d as canonical, f as chain, i as fingerprint, l as verifySignature, m as seal, n as readEntries, o as makeReceipt, p as hashEntry, r as verifyEntries, s as signEntry, t as Ledger, u as GENESIS } from "./src-CXU0_W1P.js";
2
+
3
+ export { GENESIS, Ledger, canonical, chain, fingerprint, generateKeypair, hashEntry, makeReceipt, readEntries, seal, signEntry, verifyEntries, verifyReceipt, verifySignature };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prae-cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Operator policy for agent harnesses: validate packs, explain a decision, verify a decision log, and provision a governed DSH profile.",
5
5
  "keywords": [
6
6
  "dsh-plugin",
@@ -1,3 +0,0 @@
1
- import { a as generateKeypair, c as verifySignature, d as chain, f as hashEntry, i as fingerprint, l as GENESIS, n as readEntries, o as makeReceipt, p as seal, r as verifyEntries, s as signEntry, t as Ledger, u as canonical } from "./src-CAUNqZJ1.js";
2
-
3
- export { GENESIS, Ledger, canonical, chain, fingerprint, generateKeypair, hashEntry, makeReceipt, readEntries, seal, signEntry, verifyEntries, verifySignature };
File without changes
File without changes