gargoyle-receipts 0.7.0.dev0__tar.gz → 0.7.1.dev0__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.
Files changed (70) hide show
  1. {gargoyle_receipts-0.7.0.dev0/gargoyle_receipts.egg-info → gargoyle_receipts-0.7.1.dev0}/PKG-INFO +2 -2
  2. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/README.md +1 -1
  3. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/cli.py +4 -0
  4. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/evidence.py +8 -3
  5. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0/gargoyle_receipts.egg-info}/PKG-INFO +2 -2
  6. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/pyproject.toml +1 -1
  7. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_state.py +10 -0
  8. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/LICENSE +0 -0
  9. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/NOTICE +0 -0
  10. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/__init__.py +0 -0
  11. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/approval.py +0 -0
  12. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/approver_cli.py +0 -0
  13. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/approver_trust.py +0 -0
  14. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/approvers.py +0 -0
  15. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/consumer.py +0 -0
  16. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/entra_login.py +0 -0
  17. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/envelope.py +0 -0
  18. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/execution.py +0 -0
  19. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/fs_server.py +0 -0
  20. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/identity.py +0 -0
  21. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/issuer.py +0 -0
  22. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/issuer_service.py +0 -0
  23. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/jws.py +0 -0
  24. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/key_record_source.py +0 -0
  25. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/mcp_server.py +0 -0
  26. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/notify.py +0 -0
  27. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/opa.py +0 -0
  28. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/pending_store.py +0 -0
  29. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/policy.py +0 -0
  30. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/published_record_source.py +0 -0
  31. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/replay_cache.py +0 -0
  32. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/rules.py +0 -0
  33. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/state.py +0 -0
  34. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/static/approve.html +0 -0
  35. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/static/approve.js +0 -0
  36. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle/verifier.py +0 -0
  37. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle_receipts.egg-info/SOURCES.txt +0 -0
  38. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle_receipts.egg-info/dependency_links.txt +0 -0
  39. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle_receipts.egg-info/entry_points.txt +0 -0
  40. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle_receipts.egg-info/requires.txt +0 -0
  41. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/gargoyle_receipts.egg-info/top_level.txt +0 -0
  42. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/setup.cfg +0 -0
  43. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_annotations.py +0 -0
  44. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_approver_cli.py +0 -0
  45. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_approvers.py +0 -0
  46. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_canonical.py +0 -0
  47. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_cli.py +0 -0
  48. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_console_js.py +0 -0
  49. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_consumer.py +0 -0
  50. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_entra_identity.py +0 -0
  51. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_entra_login.py +0 -0
  52. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_evidence.py +0 -0
  53. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_execution.py +0 -0
  54. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_fs_server.py +0 -0
  55. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_historical.py +0 -0
  56. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_identity.py +0 -0
  57. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_issuer_service.py +0 -0
  58. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_jws.py +0 -0
  59. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_mcp_proxy.py +0 -0
  60. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_mcp_self_issue.py +0 -0
  61. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_mcp_server.py +0 -0
  62. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_notify.py +0 -0
  63. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_opa.py +0 -0
  64. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_policy.py +0 -0
  65. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_published_record.py +0 -0
  66. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_rules.py +0 -0
  67. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_stdio_encoding.py +0 -0
  68. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_threshold.py +0 -0
  69. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_typescript_conformance.py +0 -0
  70. {gargoyle_receipts-0.7.0.dev0 → gargoyle_receipts-0.7.1.dev0}/tests/test_wire_spec.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gargoyle-receipts
3
- Version: 0.7.0.dev0
3
+ Version: 0.7.1.dev0
4
4
  Summary: Signed, single-use, argument-bound receipts and human-approval holds for agent tool calls.
5
5
  Author: Justin LaPorte
6
6
  License-Expression: Apache-2.0
@@ -59,7 +59,7 @@ your MCP server and lets through only what both agreed to.
59
59
 
60
60
  [![PyPI](https://img.shields.io/pypi/v/gargoyle-receipts)](https://pypi.org/project/gargoyle-receipts/)
61
61
 
62
- Status: pre-release (`0.7.0.dev0`, on PyPI as `gargoyle-receipts`). The wire format is `gargoyle/3` and may
62
+ Status: pre-release (`0.7.1.dev0`, on PyPI as `gargoyle-receipts`). The wire format is `gargoyle/3` and may
63
63
  still change before the first release; it is specified in [SPEC.md](SPEC.md).
64
64
 
65
65
  ## What a receipt gives you
@@ -21,7 +21,7 @@ your MCP server and lets through only what both agreed to.
21
21
 
22
22
  [![PyPI](https://img.shields.io/pypi/v/gargoyle-receipts)](https://pypi.org/project/gargoyle-receipts/)
23
23
 
24
- Status: pre-release (`0.7.0.dev0`, on PyPI as `gargoyle-receipts`). The wire format is `gargoyle/3` and may
24
+ Status: pre-release (`0.7.1.dev0`, on PyPI as `gargoyle-receipts`). The wire format is `gargoyle/3` and may
25
25
  still change before the first release; it is specified in [SPEC.md](SPEC.md).
26
26
 
27
27
  ## What a receipt gives you
@@ -246,6 +246,10 @@ def cmd_audit(args) -> int:
246
246
  why = (" (%s)" % r["verdict"]) if r["kind"] == "refused" and r["verdict"] else ""
247
247
  if r.get("released_by"):
248
248
  why += " released by " + ", ".join(r["released_by"])
249
+ if r.get("state"):
250
+ # A state-bound call: the target as it was approved, and as the run left it.
251
+ pre, post = (r["state"].get("pre") or "")[:8], (r["state"].get("post") or "")[:8]
252
+ why += " state %s -> %s" % (pre or "?", post or "?") if post else " state %s" % pre
249
253
  print("%4s %-5s %-8s %-24s %-28s %s%s" % (r["seq"] if r["seq"] is not None else "?",
250
254
  "ok" if r["accepted"] else "FAIL", r["kind"] or "?",
251
255
  (r["tool"] or "?")[:24], (r["principal"] or "?")[:28], r["reason"], why))
@@ -160,7 +160,8 @@ def verify_log(
160
160
  {"accepted", "reason", "tail_sha256", "count", "honored", "refused",
161
161
  "entries": [{"seq", "kind", "tool", "principal", "accepted", "reason", "verdict", "released_by"}, ...]},
162
162
  `reason` the verification outcome, `verdict` what the executor recorded,
163
- `released_by` the approver key ids of the grants an honored held call carried.
163
+ `released_by` the approver key ids of the grants an honored held call carried,
164
+ `state` {"pre", "post"} the target's state hashes when the call was state-bound.
164
165
  Verification continues past a bad entry so the report names every one."""
165
166
  trust = dict(issuer_public_keys=issuer_public_keys, executor_public_keys=executor_public_keys,
166
167
  approver_public_keys=approver_public_keys)
@@ -178,7 +179,7 @@ def verify_log(
178
179
  rc = rc if isinstance(rc, dict) else {}
179
180
  report.append({"seq": None, "kind": None, "tool": rc.get("tool"), "principal": rc.get("principal"),
180
181
  "accepted": False, "reason": REF_EVIDENCE_UNCHAINED if legacy else REF_EVIDENCE_MALFORMED,
181
- "verdict": None, "released_by": []})
182
+ "verdict": None, "released_by": [], "state": None})
182
183
  all_ok = False
183
184
  continue
184
185
  e = raw
@@ -206,8 +207,12 @@ def verify_log(
206
207
  counts[e["kind"]] += 1
207
208
  released_by = [g.get("approver_key_id") for g in (e["grant"] if isinstance(e["grant"], list) else [e["grant"]])
208
209
  if isinstance(g, dict)] if e["grant"] is not None else []
210
+ ex = e["execution"] if isinstance(e["execution"], dict) else {}
211
+ state = ({"pre": ex.get("pre_state_sha256"), "post": ex.get("post_state_sha256")}
212
+ if ex.get("pre_state_sha256") or ex.get("post_state_sha256") else None)
209
213
  report.append({"seq": e["seq"], "kind": e["kind"], "tool": e["tool"], "principal": e["principal"],
210
- "accepted": ok, "reason": reason, "verdict": e["reason"], "released_by": released_by})
214
+ "accepted": ok, "reason": reason, "verdict": e["reason"], "released_by": released_by,
215
+ "state": state})
211
216
  all_ok = all_ok and ok
212
217
  # The chain continues from what the file says, so one bad entry is
213
218
  # reported once rather than cascading into every later one.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gargoyle-receipts
3
- Version: 0.7.0.dev0
3
+ Version: 0.7.1.dev0
4
4
  Summary: Signed, single-use, argument-bound receipts and human-approval holds for agent tool calls.
5
5
  Author: Justin LaPorte
6
6
  License-Expression: Apache-2.0
@@ -59,7 +59,7 @@ your MCP server and lets through only what both agreed to.
59
59
 
60
60
  [![PyPI](https://img.shields.io/pypi/v/gargoyle-receipts)](https://pypi.org/project/gargoyle-receipts/)
61
61
 
62
- Status: pre-release (`0.7.0.dev0`, on PyPI as `gargoyle-receipts`). The wire format is `gargoyle/3` and may
62
+ Status: pre-release (`0.7.1.dev0`, on PyPI as `gargoyle-receipts`). The wire format is `gargoyle/3` and may
63
63
  still change before the first release; it is specified in [SPEC.md](SPEC.md).
64
64
 
65
65
  ## What a receipt gives you
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gargoyle-receipts"
7
- version = "0.7.0.dev0"
7
+ version = "0.7.1.dev0"
8
8
  description = "Signed, single-use, argument-bound receipts and human-approval holds for agent tool calls."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -168,3 +168,13 @@ def test_middleware_refuses_when_the_file_moved_between_card_and_run(tmp_path):
168
168
  chain = verify_chain(gate["envelope"], ex, grant=gate["grant"], issuer_public_keys=ISSUER_KEYS,
169
169
  executor_public_keys={"executor-1": executor.public_key()}, approver_public_keys={"approver-1": approver.public_key()})
170
170
  assert chain["reason"] == ACCEPT_CHAIN_VERIFIED
171
+ # The audit names the state transition.
172
+ from gargoyle.evidence import verify_log
173
+ entry = {"evidence_version": "gargoyle-evidence/2", "seq": 1, "prev_sha256": "0" * 64, "at": NOW.isoformat(), "kind": "honored",
174
+ "tool": "write_file", "args_sha256": gate["envelope"]["request_context"]["args_sha256"], "principal": "agent-7",
175
+ "reason": gate["reason"], "receipt": gate["envelope"], "grant": gate["grant"], "execution": ex}
176
+ from gargoyle.evidence import entry_sha256
177
+ entry["entry_sha256"] = entry_sha256(entry)
178
+ v = verify_log([entry], issuer_public_keys=ISSUER_KEYS, executor_public_keys={"executor-1": executor.public_key()},
179
+ approver_public_keys={"approver-1": approver.public_key()})
180
+ assert v["accepted"] and v["entries"][0]["state"] == {"pre": ex["pre_state_sha256"], "post": ex["post_state_sha256"]}