verieffect 0.2.0a1__tar.gz
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.
- verieffect-0.2.0a1/LICENSE +21 -0
- verieffect-0.2.0a1/PKG-INFO +405 -0
- verieffect-0.2.0a1/README.md +383 -0
- verieffect-0.2.0a1/pyproject.toml +38 -0
- verieffect-0.2.0a1/setup.cfg +4 -0
- verieffect-0.2.0a1/tests/test_all.py +1686 -0
- verieffect-0.2.0a1/verieffect/__init__.py +2 -0
- verieffect-0.2.0a1/verieffect/cli.py +468 -0
- verieffect-0.2.0a1/verieffect/core.py +727 -0
- verieffect-0.2.0a1/verieffect/probes.py +1108 -0
- verieffect-0.2.0a1/verieffect.egg-info/PKG-INFO +405 -0
- verieffect-0.2.0a1/verieffect.egg-info/SOURCES.txt +15 -0
- verieffect-0.2.0a1/verieffect.egg-info/dependency_links.txt +1 -0
- verieffect-0.2.0a1/verieffect.egg-info/entry_points.txt +2 -0
- verieffect-0.2.0a1/verieffect.egg-info/requires.txt +2 -0
- verieffect-0.2.0a1/verieffect.egg-info/top_level.txt +2 -0
- verieffect-0.2.0a1/verify_receipt.py +412 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bing Feng
|
|
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.
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: verieffect
|
|
3
|
+
Version: 0.2.0a1
|
|
4
|
+
Summary: Independent deterministic verification for AI agents — signed, offline-verifiable receipts
|
|
5
|
+
Author: Bing Feng
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/bfeng1122/verieffect
|
|
8
|
+
Project-URL: Repository, https://github.com/bfeng1122/verieffect
|
|
9
|
+
Keywords: ai,agents,verification,receipts,ed25519,audit
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
15
|
+
Classifier: Topic :: Security
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: cryptography>=41.0
|
|
20
|
+
Requires-Dist: requests>=2.34.2
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# Verieffect
|
|
24
|
+
|
|
25
|
+
**Verify the effect. Not the claim.**
|
|
26
|
+
|
|
27
|
+
*Independent, deterministic verification for AI agents — with signed, offline-verifiable
|
|
28
|
+
receipts as the evidence format.*
|
|
29
|
+
|
|
30
|
+
When an agent says "I uploaded the file" / "the trade filled" / "the backup ran" —
|
|
31
|
+
Verieffect probes ground truth *itself* and issues a receipt anyone can verify offline.
|
|
32
|
+
|
|
33
|
+
```console
|
|
34
|
+
$ # the backup agent's log says: "nightly backup ran ✓"
|
|
35
|
+
$ verieffect claim file_exists --agent backup-cron \
|
|
36
|
+
--target ./backups/nightly.dump --expect '{"min_bytes": 1048576}' --out receipt.json
|
|
37
|
+
{
|
|
38
|
+
"v": 1,
|
|
39
|
+
"claim_id": "830c4620-deb4-4d5e-a4d2-ed83c77f79da",
|
|
40
|
+
"agent_id": "backup-cron",
|
|
41
|
+
"kind": "file_exists",
|
|
42
|
+
"target": "./backups/nightly.dump",
|
|
43
|
+
"expect": { "min_bytes": 1048576 },
|
|
44
|
+
"observed": { "path": "backups\\nightly.dump", "exists": true, "size": 2, "mtime": 1793862129 },
|
|
45
|
+
"verdict": "FAIL",
|
|
46
|
+
...
|
|
47
|
+
"sig": "ed25519:8bfac27e…"
|
|
48
|
+
}
|
|
49
|
+
$ echo $?
|
|
50
|
+
1
|
|
51
|
+
$ python verify_receipt.py --pubkey ~/.verieffect/keys/verieffect_ed25519.pub receipt.json
|
|
52
|
+
VALID: ok (claim 830c4620-deb4-4d5e-a4d2-ed83c77f79da, verdict FAIL)
|
|
53
|
+
```
|
|
54
|
+
(output from a Windows run — observed.path uses backslashes)
|
|
55
|
+
|
|
56
|
+
The file exists — it's just 2 bytes. "Backup ran" was true; "backup landed" wasn't.
|
|
57
|
+
The signed `FAIL` receipt is the evidence, and exit code `1` lets cron alert on it.
|
|
58
|
+
|
|
59
|
+
> **Renamed from AgentProof** (2026-10-30) after finding 5+ existing products on that
|
|
60
|
+
> name. Same project and repo history; the signing key is unchanged, so pre-rename
|
|
61
|
+
> receipts still verify.
|
|
62
|
+
|
|
63
|
+
> **What the signature does and doesn't prove.** The signing key lives next to your
|
|
64
|
+
> agents (same host, MVP). Receipts are therefore **tamper-evident against drift and
|
|
65
|
+
> sloppy self-reports — not tamper-proof against an adversarial agent with local code
|
|
66
|
+
> execution** (such an agent could read the key and forge receipts). Verieffect is a
|
|
67
|
+
> *drift detector* for the well-documented "agent says done, side effect didn't
|
|
68
|
+
> happen" failure mode. Hosted/separate-user verification is on the roadmap. Details:
|
|
69
|
+
> [threat model](docs/receipt-schema.md#threat-model-what-receipts-do-and-dont-prove).
|
|
70
|
+
|
|
71
|
+
- **No LLM in the verdict path.** Verdicts come from deterministic probes: filesystem
|
|
72
|
+
state, HTTP responses, database rows, command exit codes. A model never *opines*
|
|
73
|
+
that work succeeded; evidence shows it.
|
|
74
|
+
- **Signed receipts.** Ed25519 signature + per-agent hash chain, so receipts can't be
|
|
75
|
+
altered without the key, and edits after the fact are detected. (Honest scope: the
|
|
76
|
+
chain detects edits and mid-chain deletions; tail truncation needs an externally
|
|
77
|
+
anchored head hash — see [Checkpointing](#checkpointing-anti-truncation) below.)
|
|
78
|
+
- **Three verdicts, never two.** `PASS` / `FAIL` / `UNVERIFIABLE`, each with its own
|
|
79
|
+
exit code (`0` / `1` / `3`). A network error, timeout or missing binary is never
|
|
80
|
+
laundered into a pass (or a fail). A missing file is never excused as "couldn't
|
|
81
|
+
check." A typo'd or unknown `expect` key is never silently ignored: it is
|
|
82
|
+
`UNVERIFIABLE` and the receipt names the key.
|
|
83
|
+
|
|
84
|
+
## Why
|
|
85
|
+
|
|
86
|
+
Two things are true at once: agent tasks get done wrong surprisingly often, and agents
|
|
87
|
+
tend to report them as done anyway.
|
|
88
|
+
|
|
89
|
+
**Tasks silently done wrong.** An analysis by [Veridatum](https://veridatum.io/) — a
|
|
90
|
+
similar project we compare against below — of the public τ-bench retail benchmark found
|
|
91
|
+
that **118 of 456 simulated GPT-4.1 runs (26%) failed the final-state check**: the
|
|
92
|
+
agent's transcript and the database disagreed. Veridatum is explicit that this is a
|
|
93
|
+
public-benchmark demonstration, not a real-world error rate, and it measures wrong
|
|
94
|
+
outcomes rather than false reports. Our own inference (not Veridatum's claim): nothing
|
|
95
|
+
in the agent's transcript tells you *which* runs were wrong — you find out only by
|
|
96
|
+
checking the system of record.
|
|
97
|
+
|
|
98
|
+
**Agents report failures as success.** The overclaiming evidence comes from the
|
|
99
|
+
research literature:
|
|
100
|
+
|
|
101
|
+
- **LIMBO** ("Where Does Exactly-Once Live?", arXiv:2609.29095): in **90% of episodes
|
|
102
|
+
that produced a duplicate side effect, the agent reported the task as completed.**
|
|
103
|
+
This is the closest published evidence to exactly what Verieffect catches — the
|
|
104
|
+
self-report says "done," the system of record says otherwise.
|
|
105
|
+
- **OverclaimBench** (arXiv:2609.20812): **80.4% of incomplete runs** were reported in
|
|
106
|
+
a misleading way.
|
|
107
|
+
- **"The Completion Illusion"** (IOV Labs; a lab study, not peer-reviewed): agents
|
|
108
|
+
self-reported perfect completion while verified accuracy was 86–96%.
|
|
109
|
+
|
|
110
|
+
Autonomous agent stacks — cron-driven pipelines, CI bots, personal agents — fail the
|
|
111
|
+
same way: the log says "done," the side effect never happened (or happened twice).
|
|
112
|
+
Verieffect makes "done" a verifiable claim.
|
|
113
|
+
|
|
114
|
+
## Related work (read this before assuming we invented it)
|
|
115
|
+
|
|
116
|
+
Verifying an action's *effect* instead of trusting the actor's self-report is a
|
|
117
|
+
published principle, not our invention. We built a working implementation; others
|
|
118
|
+
described the idea or solved adjacent problems:
|
|
119
|
+
|
|
120
|
+
- **[Re-Perceived Effects: A Well-Formedness Contract for Accountable Actuation](https://doi.org/10.5281/zenodo.22768261)**
|
|
121
|
+
(Zain Dana Harper, Sept 2026). The closest prior art: replace the actuator's
|
|
122
|
+
self-report with a *re-perceived* effect read back from the world — re-hash the
|
|
123
|
+
file, check the process return code — with a total checker that yields
|
|
124
|
+
"Unverifiable" on malformed input rather than raising. Same core principle and
|
|
125
|
+
several of the same rules as our `file_exists`/`exit_code` probes. It's a short
|
|
126
|
+
conceptual note; Verieffect is a shipped, tested implementation (200+ test checks, CI,
|
|
127
|
+
dogfooded) and adds the packaging that note names as its own gap: sealed
|
|
128
|
+
receipts re-verifiable offline.
|
|
129
|
+
- **[Beyond Approved Actions: Runtime Validation of Persistent Outcomes in Agent Workflows](https://arxiv.org/abs/2609.31301)**
|
|
130
|
+
(EffectMatch, Sept 2026). Broader than us: binds an application-approved outcome
|
|
131
|
+
to the execution and governs commit/continuation from the comparison. We verify
|
|
132
|
+
individual claims after the fact; they enforce a contract across a workflow.
|
|
133
|
+
- **[Treeship](https://github.com/zerkerlabs/treeship)** (Apache-2.0),
|
|
134
|
+
**[Attestroot](https://pypi.org/project/attestroot/)**,
|
|
135
|
+
**[Logsiegel](https://logsiegel.com)**, **[Tanilo](https://tanilo.io)** — signed,
|
|
136
|
+
hash-chained/Merkle agent receipts with offline verification. Overlapping on the
|
|
137
|
+
*receipt* layer. The distinction: they **record what the agent did** (wrap commands,
|
|
138
|
+
seal transcripts); Attestroot states plainly that a lying agent's capture "would
|
|
139
|
+
seal and verify perfectly." Verieffect **queries the system of record itself** when
|
|
140
|
+
the claim is filed. The probe runs inside Verieffect, not inside the agent. (Offline
|
|
141
|
+
verification later checks the receipt's authenticity; it does not re-probe.)
|
|
142
|
+
- **[Veridatum](https://veridatum.io/)** — the same principle (check the claim against
|
|
143
|
+
the system of record) as closed SaaS for business outcomes (Stripe refunds, orders).
|
|
144
|
+
We're the open-source, developer-facing, technical-claim version.
|
|
145
|
+
- Standards: [in-toto](https://in-toto.io)/[SLSA](https://slsa.dev) attest *artifacts
|
|
146
|
+
and builds*; [C2PA](https://c2pa.org) attests *content provenance*. None answers
|
|
147
|
+
"did this runtime side effect land in my DB at 3am." Adopting their envelope formats
|
|
148
|
+
(RFC 8785 canonical JSON, DSSE) is on our roadmap — we'd rather compose than compete.
|
|
149
|
+
|
|
150
|
+
**What's left that's ours:** a Python-native probe library you can extend, strict
|
|
151
|
+
three-verdict semantics, signed hash-chained receipts with agent-binding and
|
|
152
|
+
truncation detection, a dependency-light offline verifier, and exit codes built for
|
|
153
|
+
cron/CI. Implementation and developer experience — not the underlying idea.
|
|
154
|
+
|
|
155
|
+
## Quick start
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
pip install verieffect # from PyPI; gives you the `verieffect` console script
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Or from a clone (for contributors):
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
python -m venv .venv
|
|
165
|
+
.venv/bin/python -m pip install -r requirements.txt # Windows: .venv/Scripts/python
|
|
166
|
+
# Optional: `pip install .` also gives you a `verieffect` console script.
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# 1. File a claim — targets a file that ships with the repo, so this PASSES on a
|
|
171
|
+
# fresh clone (exit code 0). Keys/receipts land under ~/.verieffect (see below).
|
|
172
|
+
.venv/bin/python -m verieffect.cli claim file_exists --agent my-agent \
|
|
173
|
+
--target ./README.md --expect '{"min_bytes": 1024}' --out receipt.json
|
|
174
|
+
|
|
175
|
+
# 2. Verify the receipt offline (completes the claim→verify loop)
|
|
176
|
+
.venv/bin/python verify_receipt.py \
|
|
177
|
+
--pubkey ~/.verieffect/keys/verieffect_ed25519.pub receipt.json
|
|
178
|
+
|
|
179
|
+
# 3. Audit the whole chain for that agent
|
|
180
|
+
.venv/bin/python -m verieffect.cli audit --agent my-agent
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
On Windows, substitute `.venv/Scripts/python` for `.venv/bin/python` throughout.
|
|
184
|
+
|
|
185
|
+
> **Windows cmd/PowerShell:** single-quoted JSON doesn't work — use double quotes
|
|
186
|
+
> with escaped inner quotes: `--expect "{\"min_bytes\": 1024}"`, and replace `\`
|
|
187
|
+
> line continuations with `^` (cmd) or a backtick (PowerShell).
|
|
188
|
+
|
|
189
|
+
Output (truncated):
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"v": 1,
|
|
194
|
+
"claim_id": "…",
|
|
195
|
+
"agent_id": "my-agent",
|
|
196
|
+
"kind": "file_exists",
|
|
197
|
+
"target": "./README.md",
|
|
198
|
+
"expect": {"min_bytes": 1024},
|
|
199
|
+
"observed": {"path": "README.md", "exists": true, "is_dir": false, "is_regular_file": true,
|
|
200
|
+
"size": 4096, "mtime": 1792090847},
|
|
201
|
+
"verdict": "PASS",
|
|
202
|
+
"checked_at": "2026-10-20T23:12:06Z",
|
|
203
|
+
"prev_receipt_hash": "…",
|
|
204
|
+
"sig": "ed25519:…"
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Exit codes — `claim` (`python -m verieffect.cli claim …`), so shell scripts and
|
|
209
|
+
cron jobs can branch on verification results directly:
|
|
210
|
+
|
|
211
|
+
| Code | Meaning |
|
|
212
|
+
|---|---|
|
|
213
|
+
| `0` | PASS |
|
|
214
|
+
| `1` | FAIL — the observed state contradicts the expectation |
|
|
215
|
+
| `3` | UNVERIFIABLE — ground truth couldn't be reached or judged (network error, timeout, binary not found, malformed/unknown `expect` key, non-object `expect` such as `null`, malformed SHA-256, no assertion, a refused probe — `exit_code` without `allow_shell`, non-GET/HEAD `http_ok` — or a response/output over its size cap). A receipt is still written. |
|
|
216
|
+
| `2` | usage error, or claim aborted before any receipt was written (bad `--expect` JSON incl. duplicate keys, corrupt chain tail, lock timeout) |
|
|
217
|
+
| `4` | internal error (a Verieffect bug — please report it) |
|
|
218
|
+
|
|
219
|
+
Exit codes — offline verifier (`verify_receipt.py`). Verification checks
|
|
220
|
+
authenticity offline. It never re-runs probes, because probing happened when the
|
|
221
|
+
claim was filed. Two modes:
|
|
222
|
+
|
|
223
|
+
- **Default (authenticity only):** `0` = every receipt is authentically signed,
|
|
224
|
+
schema-valid and (in `--chain` mode) correctly chained. This says nothing about
|
|
225
|
+
the verdict — a validly signed **FAIL** receipt exits `0`, because the receipt
|
|
226
|
+
itself is genuine evidence. `1` = invalid/malformed/tampered, `--agent`
|
|
227
|
+
mismatch (single-receipt and chain mode), or an **empty chain** (zero receipts
|
|
228
|
+
prove nothing).
|
|
229
|
+
- **`--require-verdict PASS|FAIL|UNVERIFIABLE`:** additionally gates on the
|
|
230
|
+
verdict (in `--chain` mode: *every* receipt's verdict must match). If the only
|
|
231
|
+
mismatches are `UNVERIFIABLE` receipts, it exits `3`; any other mismatch exits `1`.
|
|
232
|
+
Use `--require-verdict PASS` when the exit code should mean "authentic AND
|
|
233
|
+
passed". Chain mode always prints a summary line such as
|
|
234
|
+
`verdicts: 12 PASS, 1 FAIL, 0 UNVERIFIABLE`.
|
|
235
|
+
- `2` = usage error, `4` = internal verifier error.
|
|
236
|
+
|
|
237
|
+
### Where things live
|
|
238
|
+
|
|
239
|
+
Default root is a **per-user** directory, independent of the current working
|
|
240
|
+
directory: `~/.verieffect` (Windows: `%USERPROFILE%\.verieffect`). Every invocation
|
|
241
|
+
therefore shares one key and one set of chains, and the private key never lands
|
|
242
|
+
inside whatever repo the agent happens to run in.
|
|
243
|
+
|
|
244
|
+
- `~/.verieffect/keys/verieffect_ed25519.{pem,pub}` — Ed25519 keypair, auto-generated
|
|
245
|
+
on first claim (atomically, mode 0600 on POSIX). The `.pem` (private) never leaves
|
|
246
|
+
your machine.
|
|
247
|
+
- `~/.verieffect/chains/chain_<agent>_<hash8>.jsonl` — append-only receipt chain per agent.
|
|
248
|
+
- Override the root with `VERIEFFECT_HOME=/some/path`.
|
|
249
|
+
- **Upgrading from 0.1.0a1 to 0.2.0a1** (old default: `./data` under the CWD): if
|
|
250
|
+
`./data/keys/verieffect_ed25519.pem` exists and `~/.verieffect` doesn't (an
|
|
251
|
+
unrelated `./data/keys` folder is ignored), Verieffect prints the exact move command and
|
|
252
|
+
refuses to mint a second key until you either move the directory
|
|
253
|
+
(`mv ./data ~/.verieffect`, Windows: `move data "%USERPROFILE%\.verieffect"`) or
|
|
254
|
+
set `VERIEFFECT_HOME` to the old path. Nothing is moved automatically.
|
|
255
|
+
- **Crash recovery:** if a crash tears the last chain line mid-write, new claims for
|
|
256
|
+
that agent stop with a pointer to `verieffect repair --agent <id>`. That command
|
|
257
|
+
moves the torn line to `<chain>.torn` and appends a signed `repair_note` receipt.
|
|
258
|
+
The torn receipt's verdict is lost (it may have been a FAIL), so `audit` and
|
|
259
|
+
`verify_receipt.py --chain` always print a **WARNING** listing every repair note.
|
|
260
|
+
Repair notes are not verdicts: `--require-verdict` skips them (one repair does not
|
|
261
|
+
block a strict gate forever), but a chain of *only* repair notes never meets it.
|
|
262
|
+
|
|
263
|
+
## How it compares
|
|
264
|
+
|
|
265
|
+
| | Verieffect | Treeship | Attestroot | Logsiegel | Veridatum |
|
|
266
|
+
|---|---|---|---|---|---|
|
|
267
|
+
| Queries the system of record itself (at claim time) | **Yes (probes)** | No — wraps/records | No — seals transcripts | No — chains logs | Yes |
|
|
268
|
+
| Signed receipts + hash chain | Yes | Yes | Yes (Merkle + transparency log) | Yes | Ledger, not crypto-first |
|
|
269
|
+
| Offline verification | Yes | Yes | Yes | Yes | No (SaaS) |
|
|
270
|
+
| Deployment | Local CLI/library | Local CLI + hub | Local + countersign service | Local OSS | Hosted SaaS |
|
|
271
|
+
| Scope | Technical claims (files/HTTP/DB/exit) | Agent sessions | Agent transcripts | Action trails | Business outcomes (Stripe, orders) |
|
|
272
|
+
| License | MIT | Apache-2.0 | MIT (Pre-Alpha) | Apache-2.0 | Proprietary |
|
|
273
|
+
|
|
274
|
+
The one-column summary: **the receipt tools above prove what the agent *recorded
|
|
275
|
+
doing*; Verieffect checks whether the effect *actually landed*.** A lying agent can
|
|
276
|
+
poison a recording. It can't fake what Verieffect's own probe observes. Honest
|
|
277
|
+
caveat: today the claimer still picks the `target` **and** the `expect`, so a hostile
|
|
278
|
+
agent could choose a check it knows will pass. The roadmap fix is operator-defined
|
|
279
|
+
named checks (`verieffect check <name>`): the operator owns the target and
|
|
280
|
+
expectations, and the agent only names the check. (Veridatum also checks
|
|
281
|
+
the system of record, as hosted SaaS for business outcomes — see
|
|
282
|
+
[Related work](#related-work-read-this-before-assuming-we-invented-it).)
|
|
283
|
+
|
|
284
|
+
## How it fits together
|
|
285
|
+
|
|
286
|
+
```mermaid
|
|
287
|
+
flowchart LR
|
|
288
|
+
A[AI Agent] -->|"claim: I did X"| B[Verieffect CLI/library]
|
|
289
|
+
B --> C{Deterministic Probe}
|
|
290
|
+
C -->|reads| D[Real System State<br/>files · HTTP · DB · exit codes]
|
|
291
|
+
D --> C
|
|
292
|
+
C -->|PASS / FAIL / UNVERIFIABLE| E[Ed25519 Signing]
|
|
293
|
+
E --> F[Signed Receipt]
|
|
294
|
+
F --> G[Per-Agent Hash Chain<br/>append-only JSONL]
|
|
295
|
+
G --> H[Offline Verifier<br/>verify_receipt.py]
|
|
296
|
+
H --> I[Chain Audit<br/>+ external head anchor]
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Claim kinds
|
|
300
|
+
|
|
301
|
+
| Kind | Target | Proves |
|
|
302
|
+
|---|---|---|
|
|
303
|
+
| `file_exists` | path | file present; size bounds / sha256 / mtime freshness / text content |
|
|
304
|
+
| `http_ok` | URL | endpoint answers; status / body substring / body hash (probe makes the request itself). GET/HEAD only unless you explicitly pass `"allow_unsafe_method": true` |
|
|
305
|
+
| `db_row_matches` | `sqlite:///x.db`, `x.jsonl`, `x.json` | record state: SELECT-only queries (`PRAGMA query_only` enforced), JSONL row filters, dotted-path JSON fields |
|
|
306
|
+
| `exit_code` | shell command | command **run by Verieffect when the claim is filed** (not the agent's original run): exit code / stdout content. **Off by default** (`"allow_shell": true` required); runs with the verifier's full OS privileges. Use only for idempotent/read-only commands — for side-effecting work, verify the *effect* (`file_exists`/`db_row_matches`) instead |
|
|
307
|
+
|
|
308
|
+
⚠️ **Trust boundary:** `http_ok` and `exit_code` act on the verifier's machine and
|
|
309
|
+
network position, and every file-reading probe can read whatever the verifier can.
|
|
310
|
+
Only wire claims from agents **you** operate. A claimer who can submit an `exit_code`
|
|
311
|
+
claim can run commands, because the `allow_shell` / `allow_unsafe_method` gates are
|
|
312
|
+
part of the claim itself. There are three known limitations: claim-payload gates,
|
|
313
|
+
no path restriction (file-read oracle) and no SSRF filtering. Each must be fixed
|
|
314
|
+
before the MCP server ships; see
|
|
315
|
+
[Known limitations](docs/receipt-schema.md#known-limitations--fix-gated-on-the-mcp-roadmap).
|
|
316
|
+
|
|
317
|
+
## Offline verification
|
|
318
|
+
|
|
319
|
+
Anyone with the public key can verify receipts or whole chains — the standalone
|
|
320
|
+
verifier needs nothing but `pip install cryptography`:
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
python verify_receipt.py --pubkey ~/.verieffect/keys/verieffect_ed25519.pub receipt.json
|
|
324
|
+
python verify_receipt.py --pubkey ~/.verieffect/keys/verieffect_ed25519.pub \
|
|
325
|
+
--chain ~/.verieffect/chains/chain_my-agent_<hash8>.jsonl --agent my-agent
|
|
326
|
+
|
|
327
|
+
# With an externally anchored head, also catches tail truncation/regeneration:
|
|
328
|
+
python verify_receipt.py --pubkey verieffect_ed25519.pub --chain chain.jsonl \
|
|
329
|
+
--agent my-agent --expect-count 42 --expect-head <sha256-of-last-receipt>
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
`verify_receipt.py` has **zero internal imports** by design — it is the reference
|
|
333
|
+
implementation of the receipt format and never raises on malformed input.
|
|
334
|
+
See [`docs/receipt-schema.md`](docs/receipt-schema.md) for the full spec.
|
|
335
|
+
|
|
336
|
+
### Checkpointing (anti-truncation)
|
|
337
|
+
|
|
338
|
+
A forward-walk audit can't see tail truncation unless the chain head was anchored
|
|
339
|
+
somewhere the operator can't quietly rewrite. `checkpoint` prints the anchor:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
python -m verieffect.cli checkpoint --agent my-agent --json
|
|
343
|
+
# {"agent_id": "my-agent", "count": 42, "head": "0dabf352...", "as_of": "..."}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Wire it into anything append-only you already have: a nightly git commit of the
|
|
347
|
+
output, an OpenTimestamps stamp, a message to a channel only you control. Later:
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
python -m verieffect.cli audit --agent my-agent --expect-count 42 --expect-head 0dabf352...
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**Anchor the head, not just the count.** `--expect-count` alone is not sufficient:
|
|
354
|
+
`verieffect repair` replaces a torn receipt with a repair note one-for-one, so a
|
|
355
|
+
clipped FAIL plus a repair keeps the count unchanged. Only `--expect-head` catches it
|
|
356
|
+
(and `audit` prints the repair-note WARNING either way).
|
|
357
|
+
|
|
358
|
+
### Prefer body hashes over substrings
|
|
359
|
+
|
|
360
|
+
For `http_ok`, `body_contains` is a smoke test — brittle on any dynamic page (dates,
|
|
361
|
+
nonces, A/B variants). `body_sha256` is the strong check for static resources; use
|
|
362
|
+
substring matching only for stable markers (e.g. `"status":"ok"` in an API health
|
|
363
|
+
response).
|
|
364
|
+
|
|
365
|
+
## Design rules
|
|
366
|
+
|
|
367
|
+
1. The probe is owned by the verifier, never the claimer. Self-reported results are
|
|
368
|
+
what we're checking, not what we trust.
|
|
369
|
+
2. Anything not provable deterministically is `UNVERIFIABLE`, not `PASS`. That
|
|
370
|
+
includes an `expect` that asserts nothing (e.g. a SQL query with neither `rows`
|
|
371
|
+
nor `row_count`) and any unknown or wrongly typed `expect` key. Each probe has
|
|
372
|
+
a strict key schema; see [receipt-schema.md](docs/receipt-schema.md#expect-schemas-strict).
|
|
373
|
+
3. `db_row_matches` is read-only (non-SELECT rejected, `query_only` enforced);
|
|
374
|
+
`http_ok` is read-only by default. The verifier never mutates audited systems
|
|
375
|
+
unless the claim explicitly opts in (`allow_shell` / `allow_unsafe_method` — today
|
|
376
|
+
these live in the claim payload, so they guard against accidents, not attackers).
|
|
377
|
+
4. The hash chain is the audit trail — append-only; edits and mid-chain deletions are
|
|
378
|
+
detected by any offline auditor. Anchor the head hash externally (a daily commit,
|
|
379
|
+
an OpenTimestamps stamp, a posted checkpoint) to also detect truncation.
|
|
380
|
+
|
|
381
|
+
## Threat model in one paragraph
|
|
382
|
+
|
|
383
|
+
Verieffect catches **sloppy** agents, not **adversarial** ones: tasks that were done
|
|
384
|
+
wrong, and agents whose "done" report doesn't match what actually landed. The signing key lives on the operator's
|
|
385
|
+
machine, so an attacker with local code execution as the same user can forge
|
|
386
|
+
receipts — no same-host scheme survives that. If your threat is a truly adversarial
|
|
387
|
+
agent, run the verifier as a separate OS user/host (roadmap: hosted verifier).
|
|
388
|
+
Full details in [receipt-schema.md](docs/receipt-schema.md).
|
|
389
|
+
|
|
390
|
+
## Status
|
|
391
|
+
|
|
392
|
+
MVP (Alpha). 200+ test checks (`python tests/test_all.py` with the venv python, or
|
|
393
|
+
`pytest tests/`; CI runs ubuntu+windows × Python 3.10–3.12). Dogfooded on a 24/7
|
|
394
|
+
paper-trading stack since late Oct 2026 (40+ receipts): fills, portfolio state and
|
|
395
|
+
backups, plus a deliberate tamper test that breaks the chain as designed.
|
|
396
|
+
Not yet battle-tested by anyone else.
|
|
397
|
+
Roadmap: named operator-owned checks (`verieffect check <name>`), hosted verifier
|
|
398
|
+
API, `kid` key rotation, `claimed_at`/`checked_at` split, connector library
|
|
399
|
+
(Stripe/broker/Postgres), MCP server.
|
|
400
|
+
|
|
401
|
+
See [examples/](examples/README.md) for runnable examples of all four probe kinds,
|
|
402
|
+
[CONTRIBUTING.md](CONTRIBUTING.md) for adding probes, and
|
|
403
|
+
[SECURITY.md](SECURITY.md) for vulnerability reporting.
|
|
404
|
+
|
|
405
|
+
MIT licensed.
|