proofseal 0.0.1 → 0.1.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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +13 -0
  3. package/README.md +210 -2
  4. package/dist/cli/index.d.ts +2 -0
  5. package/dist/cli/index.js +440 -0
  6. package/dist/cli/index.js.map +1 -0
  7. package/dist/config.d.ts +19 -0
  8. package/dist/config.js +58 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/core/canonical.d.ts +16 -0
  11. package/dist/core/canonical.js +29 -0
  12. package/dist/core/canonical.js.map +1 -0
  13. package/dist/core/hash.d.ts +32 -0
  14. package/dist/core/hash.js +81 -0
  15. package/dist/core/hash.js.map +1 -0
  16. package/dist/core/marker-lint.d.ts +5 -0
  17. package/dist/core/marker-lint.js +55 -0
  18. package/dist/core/marker-lint.js.map +1 -0
  19. package/dist/core/paths.d.ts +10 -0
  20. package/dist/core/paths.js +13 -0
  21. package/dist/core/paths.js.map +1 -0
  22. package/dist/harness/quantize.d.ts +38 -0
  23. package/dist/harness/quantize.js +76 -0
  24. package/dist/harness/quantize.js.map +1 -0
  25. package/dist/harness/run.d.ts +61 -0
  26. package/dist/harness/run.js +137 -0
  27. package/dist/harness/run.js.map +1 -0
  28. package/dist/history/gitinfo.d.ts +16 -0
  29. package/dist/history/gitinfo.js +69 -0
  30. package/dist/history/gitinfo.js.map +1 -0
  31. package/dist/history/jsonl.d.ts +28 -0
  32. package/dist/history/jsonl.js +71 -0
  33. package/dist/history/jsonl.js.map +1 -0
  34. package/dist/history/queries.d.ts +43 -0
  35. package/dist/history/queries.js +86 -0
  36. package/dist/history/queries.js.map +1 -0
  37. package/dist/index.d.ts +18 -0
  38. package/dist/index.js +19 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/keys/derive.d.ts +28 -0
  41. package/dist/keys/derive.js +59 -0
  42. package/dist/keys/derive.js.map +1 -0
  43. package/dist/manifest/schema.d.ts +1068 -0
  44. package/dist/manifest/schema.js +102 -0
  45. package/dist/manifest/schema.js.map +1 -0
  46. package/dist/manifest/seal.d.ts +41 -0
  47. package/dist/manifest/seal.js +185 -0
  48. package/dist/manifest/seal.js.map +1 -0
  49. package/dist/manifest/verify.d.ts +102 -0
  50. package/dist/manifest/verify.js +246 -0
  51. package/dist/manifest/verify.js.map +1 -0
  52. package/dist/mcp/server.d.ts +1 -0
  53. package/dist/mcp/server.js +138 -0
  54. package/dist/mcp/server.js.map +1 -0
  55. package/package.json +50 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 rudycelekli
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE ADDED
@@ -0,0 +1,13 @@
1
+ ProofSeal
2
+ Copyright (c) 2026 rudycelekli
3
+
4
+ This product generalizes ideas from open work published by the Agentics
5
+ Foundation community:
6
+
7
+ - ruflo witness manifests (https://github.com/ruvnet/claude-flow) — the
8
+ seal/verify loop and signed-manifest concept.
9
+ - RuVector marker verification — the marker-claim concept.
10
+
11
+ ProofSeal is an independent implementation; it contains no code from those
12
+ projects. Thanks to @rUv and the Agentics Foundation for publishing this
13
+ work openly.
package/README.md CHANGED
@@ -1,5 +1,213 @@
1
1
  # ProofSeal
2
2
 
3
- README claims that stay true. v0.1.0 is imminent — this is a name reservation placeholder.
3
+ **README claims that stay true.**
4
4
 
5
- MIT (c) rudycelekli
5
+ Your README says "verify p50: 120ms" and "the #142 fix is in." Six months and forty commits later, nothing has re-checked either one. ProofSeal seals your claims once, then `proofseal verify` classifies every claim as **pass / drift / regressed / missing** — in CI, on every commit — and when one breaks, `proofseal history --bisect` tells you the seal-snapshot range where it happened.
6
+
7
+ ```
8
+ Before: "verify p50: 120ms" sits in your README for a year. It's wrong. You find out from an angry issue.
9
+ After: CI fails the day it stops being true, and `proofseal history --bisect` points at the range that broke it.
10
+ ```
11
+
12
+ ## How it works
13
+
14
+ ```mermaid
15
+ flowchart LR
16
+ A[claims in proofseal.json<br/>file-hash · marker · harness] --> B[proofseal seal]
17
+ B --> C[sealed manifest<br/>commit-bound integrity seal]
18
+ B --> D[history log<br/>one snapshot per seal]
19
+ C --> E[proofseal verify]
20
+ E --> F[pass / drift / regressed / missing<br/>exit 0 · 0 · 1 · 1]
21
+ D --> G[proofseal history --bisect]
22
+ G --> H[seal-snapshot range that<br/>broke the claim]
23
+ ```
24
+
25
+ 1. **You declare claims once** — "this file hashes to X", "this fix's marker is still in the code", "this seeded command still prints these numbers".
26
+ 2. **`seal` records them** into a manifest bound to the current commit, and appends a snapshot to the history log. It prints the files to commit.
27
+ 3. **`verify` re-checks everything** — locally or in CI — and classifies each claim. Harmless change is `drift` (exit 0); broken promise is `regressed` (exit 1).
28
+ 4. **`history --bisect` remembers** — every seal is a snapshot, so when a claim regresses, you get the seal-snapshot range where it happened (between snapshots, not down to a single commit — granularity is how often you seal, so seal in CI on main and bisect also prints how many commits the range spans).
29
+
30
+ ## 60-second quickstart
31
+
32
+ ```bash
33
+ npm install --save-dev proofseal
34
+
35
+ npx proofseal init # scaffolds proofseal.json + proofs/ + a sample claim
36
+
37
+ # Add claims one at a time...
38
+ npx proofseal claim add --id fix-null-check --type marker \
39
+ --file src/parser.ts --marker "guard: input ?? fallback" \
40
+ --desc "Null-input fix from #142 is still present"
41
+
42
+ # optional — batch-author many at once (requires a claims.json you author)
43
+ npx proofseal claim add --from-file claims.json
44
+
45
+ npx proofseal seal # hash files, seal manifest, append history snapshot
46
+ # → prints "Now commit these files: proofseal.json, proofs/…" — commit them all
47
+ npx proofseal verify # exit 0 = pass/drift, 1 = regressed/missing/seal mismatch, 2 = precondition
48
+ ```
49
+
50
+ When something regresses later:
51
+
52
+ ```bash
53
+ npx proofseal history --bisect
54
+ # fix-null-check: last pass 428088b4e1d2 → regressed at a91c03ffe002
55
+ # range spans 14 commits — seal more often (e.g. in CI on main) for tighter localization
56
+ ```
57
+
58
+ Bisect localizes between **seal snapshots**, not commits — seal in CI on main (single writer, linear history) for the tightest ranges. If branches seal concurrently, add `proofs/history.jsonl merge=union` to `.gitattributes`: entries are ordered by `issuedAt`, so union-merge interleaving is safe.
59
+
60
+ ## Claim types
61
+
62
+ | Type | What it asserts | On change |
63
+ |------|-----------------|-----------|
64
+ | `file-hash` | The file's sha256 matches the sealed hash | Any content change classifies as **regressed** — for a file-hash claim the hash *is* the expectation, so there is no drift state |
65
+ | `marker` | A distinctive substring is still present in the file (proves a fix survived refactors) | File edited but marker intact → **drift**; marker gone → **regressed**; file gone → **missing** |
66
+ | `harness` | A seeded command's numeric output stays deterministic (round-half-even quantization; falls back to rtol/atol tolerance against a committed reference vector) | Exact-hash match → **pass**; within tolerance → **drift**; outside tolerance → **regressed** |
67
+
68
+ `drift` exists only for marker and harness claims. Exit-code contract: `0` for pass/drift, `1` for regressed/missing/seal mismatch, `2` for preconditions (e.g. the build-output directory was never built, a harness command isn't installed, or seal outputs weren't committed — each precondition is named in the output).
69
+
70
+ ## Run it in CI
71
+
72
+ Copy this into `.github/workflows/proofseal.yml`:
73
+
74
+ ```yaml
75
+ name: Verify proofseal manifest
76
+
77
+ on:
78
+ push:
79
+ branches: [main]
80
+ pull_request:
81
+ branches: [main]
82
+
83
+ jobs:
84
+ verify:
85
+ runs-on: ubuntu-latest
86
+ steps:
87
+ # fetch-depth: 1 is sufficient — verify re-derives the key from the
88
+ # manifest's embedded gitCommit and never shells out to git.
89
+ - uses: actions/checkout@v4
90
+ with:
91
+ fetch-depth: 1
92
+
93
+ - uses: actions/setup-node@v4
94
+ with:
95
+ node-version: 22
96
+
97
+ # Note: manifest.gitCommit intentionally lags HEAD — it records the
98
+ # commit that was checked out at seal time, not the verifying commit.
99
+ - name: Verify sealed manifest
100
+ run: npx proofseal verify
101
+ ```
102
+
103
+ Two things first-time CI users hit:
104
+
105
+ - **Commit the seal outputs.** `seal` writes the manifest, the rewritten `proofseal.json`, and any harness reference vectors — and prints the exact list. If CI says `reference-vector-not-found`, you sealed locally and didn't commit the outputs.
106
+ - **`manifest.gitCommit` ≠ HEAD is normal.** It records the commit at seal time. Verification works against it directly.
107
+
108
+ ## The seal (threat model)
109
+
110
+ ProofSeal's manifest carries a **commit-bound integrity seal**: the sealing key is derived from `sha256(gitCommit:salt:'proofseal/v1')`, so there is no key to generate, store, or rotate, and the manifest can be verified outside any git context — exported in a tarball, attached to a release — while staying bound to the exact commit it sealed, even on `fetch-depth: 1` clones.
111
+
112
+ What it is: **tamper-evidence**. A corrupted or hand-edited manifest fails verification.
113
+ What it is not: **third-party authentication**. Anyone with the public commit SHA can re-derive the key, so the seal proves *which commit* a manifest belongs to — not *who* sealed it. If you need to prove identity, use cosign on top; the two compose (see benchmarks below).
114
+
115
+ Zero runtime crypto dependencies — Node's built-in crypto, plus commander, zod, and the MCP SDK.
116
+
117
+ ## MCP server
118
+
119
+ ProofSeal ships an MCP stdio server so agents can verify and seal claims directly:
120
+
121
+ ```bash
122
+ claude mcp add proofseal -- npx proofseal mcp start
123
+ ```
124
+
125
+ Seven tools:
126
+
127
+ - `verify_claims` — run full verification, returns seal status + per-claim classification
128
+ - `seal_manifest` — refresh claims, seal, append a history snapshot
129
+ - `check_drift` — report drift-only deltas without failing
130
+ - `claim_history` — timeline of statuses for one claim
131
+ - `find_regression` — bisect history for the seal-snapshot range that introduced a regression (with SHA reachability and commit-count info when git can resolve them)
132
+ - `run_harness` — execute one harness claim and report pass/drift/regressed
133
+ - `list_claims` — enumerate configured claims
134
+
135
+ ## Library API
136
+
137
+ ```ts
138
+ import { seal, verify } from 'proofseal';
139
+
140
+ const sealed = await seal({ root: '.' });
141
+ // sealed.filesWritten → the files to commit
142
+
143
+ const result = await verify({ root: '.' });
144
+ // result.summary → { totalClaims, pass, drift, regressed, missing }
145
+ // result.exitCode → 0 | 1 | 2
146
+ ```
147
+
148
+ ## Benchmarks
149
+
150
+ ProofSeal, cosign, and in-toto solve different problems and **they stack**: ProofSeal for claim semantics and history, cosign for artifact identity, in-toto for supply-chain steps. The comparison below is about *adoption cost* and *which capabilities exist* — all tools installed and measured on the same machine (three bench fixtures in `bench/fixtures/`, 100 claims each; Apple M4 Pro, Node v22.19.0). Full report with provenance, seeds, and per-mutation breakdown: [`bench/results/report.md`](bench/results/report.md).
151
+
152
+ | Metric | ProofSeal | checksum script | cosign (keyless) | cosign (keypair) | in-toto |
153
+ |---|---|---|---|---|---|
154
+ | Setup steps (count) | 4 | 3 | 3 | 3 | 4 |
155
+ | Setup time (median, s) | 12.14 | 0.03 | N/A — requires interactive OIDC | 3.07 | 1.47 |
156
+ | Config LOC | 7 | 109 | 53 | 53 | 64 |
157
+ | Secrets to manage (count) | 0 | 0 | 0 | 1 | 1 |
158
+ | Verify latency p50 / p95 (ms) | 117.9 / 270 | 677.3 / 729.4 | N/A — requires interactive OIDC | 714.4 / 1252.7 | 264.5 / 272.2 |
159
+ | Tamper signaled (% of 45) | 100% (45/45) | 100% (45/45) | N/A — requires interactive OIDC | 100% (45/45) | 100% (45/45) |
160
+ | Four-state classification (ProofSeal taxonomy) | 45/45 (100%) | N/A — taxonomy absent | N/A — taxonomy absent | N/A — taxonomy absent | N/A — taxonomy absent |
161
+ | Drift vs regression distinction | yes | N/A — capability absent | N/A — capability absent | N/A — capability absent | N/A — capability absent |
162
+ | Temporal history + bisection | yes | N/A — capability absent | N/A — capability absent | N/A — capability absent | N/A — capability absent |
163
+
164
+ Reading the table: every real tool signals 100% of the 45 seeded mutations, each judged under its own semantics — a cosign hard FAIL on a benign append is correct for cosign's security model, and a practitioner-grade `SHA256SUMS` over all git-tracked files catches every byte-level change. ProofSeal's difference is not a detection score the others failed; it's the rows below the headline: the four-state classification (pass/drift/regressed/missing) and seal history with bisection are capabilities the other tools don't have. The four-state row is ProofSeal's own taxonomy, so competitors are marked N/A rather than scored against it. The cosign-keyless cells marked N/A require an interactive OIDC browser flow that can't run headless; its setup steps, config LOC, and secrets were measured statically.
165
+
166
+ ## FAQ
167
+
168
+ **Why does `seal` exit 1 but still write history when a claim fails?**
169
+
170
+ A failing snapshot is what lets `history --bisect` localize the regression later. Skipping the snapshot would destroy the evidence.
171
+
172
+ **Can I use `verify --json` in CI?**
173
+
174
+ Yes — the JSON schema is pinned (v1) and the exit-code contract (0/1/2) is stable. Drift does not fail the build; regressions and missing claims do.
175
+
176
+ **What happens when a teammate clones and verifies?**
177
+
178
+ It works — verify needs only the committed manifest, `proofseal.json`, and reference vectors. That's why `seal` prints the commit checklist: it lists every file verify needs that isn't committed yet (including `proofseal.json` when it's still untracked), so the outputs travel together.
179
+
180
+ ## Support
181
+
182
+ ProofSeal is maintained by one person. Realistic expectations:
183
+
184
+ - **Bugs:** open an issue with the [bug template](https://github.com/rudycelekli/proofseal/issues/new?template=bug.yml) — OS, node version, exact command, and the `verify --json` output. That JSON is usually enough to fix it without back-and-forth.
185
+ - **Response time:** issues are triaged daily; expect a first response within ~48h.
186
+ - **Windows:** best-effort in v0.1 (see Limitations) — reports are still very welcome and get fixed.
187
+
188
+ ## ⚠️ Limitations
189
+
190
+ Read this before adopting. ProofSeal v0.1 is built and maintained by a solo maintainer; the surface is small on purpose.
191
+
192
+ - **Content-hashed build outputs break file-hash claims every build.** A Vite output like `index.Dk3mP9qR.js` gets a new name and hash on every rebuild. Don't put file-hash claims on hashed bundles; claim the source, or use a marker on a stable file.
193
+ - **Non-deterministic test suites can't be harness claims.** Harness claims require seeded, deterministic numeric output. Parallel test runners, wall-clock timings, and unordered output will produce permanent drift or regression noise.
194
+ - **Cross-platform hash divergence on built artifacts.** `seal` records the platform, and `verify` warns when the sealed platform differs from the verifying one — but binary artifacts may legitimately differ across platforms. Seal and verify built artifacts on the same platform, or claim only platform-independent files.
195
+ - **git autocrlf can flip text hashes.** Pin line endings with `.gitattributes` (`* text=auto eol=lf`) before sealing file-hash claims on text files. When a regression is caused purely by line endings, verify says so in the claim detail.
196
+ - **Windows is best-effort and untested in v0.1.** The suite is tested on ubuntu and macos; the Windows CI lane runs non-blocking (`continue-on-error`) until it has been observed green, at which point Windows is promoted to a supported tier. Claim paths are normalized to forward slashes on all platforms, but known residuals exist: harness commands are spawned through the platform shell, so POSIX-isms (`python3`, `&&`) may fail under cmd.exe, and cmd.exe reports command-not-found as exit 9009 rather than 127.
197
+ - **Bisect granularity is seal frequency, and rewritten history orphans recorded SHAs.** `history --bisect` localizes between seal snapshots, not commits — it prints how many commits the range spans, but the fix is to seal more often (CI on main: single writer, linear history). Squash-merge, rebase, or force-push can leave history pointing at SHAs git no longer knows; bisect tags those `(unreachable — rewritten history?)`. If branches seal concurrently, add `proofs/history.jsonl merge=union` to `.gitattributes` — entries are ordered by `issuedAt`, so interleaving is safe.
198
+ - **Markers in minified or generated code are fragile.** Minifiers rename and reflow; put markers in source, not in `dist/`.
199
+ - **Benchmark scope.** All numbers above are from three synthetic fixtures (npm lib, Python tool, docs site; 100 claims each). Repos with thousands of claims, huge files, or heavy harness suites have not been measured.
200
+
201
+ ## Prior art & credits
202
+
203
+ ProofSeal is a fresh implementation, but it stands on ideas from the [ruvnet](https://github.com/ruvnet) open-source ecosystem:
204
+
205
+ - **ruflo** — the sealed witness-manifest concept (sealing fix claims into a verifiable manifest, with regression bisection over seal history) is the direct inspiration for ProofSeal's seal/verify/bisect loop.
206
+ - **RuVector** — its marker-based "is the fix still present in the live tree" verification pattern shaped ProofSeal's marker claim type and drift-vs-regression distinction.
207
+ - The broader ruvnet agent ecosystem informed the MCP tool design: fail-open tool results and structured-content-first responses.
208
+
209
+ Thanks to [rUv](https://github.com/ruvnet) and the Agentics Foundation community for publishing this work openly — ProofSeal generalizes those ideas into a standalone tool any repo can adopt.
210
+
211
+ ## License
212
+
213
+ MIT © [rudycelekli](https://github.com/rudycelekli)
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};