proofgate-runtime 0.1.0__tar.gz → 0.2.0__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 (20) hide show
  1. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/PKG-INFO +9 -5
  2. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/README.md +8 -4
  3. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/pyproject.toml +1 -1
  4. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/src/proofgate_runtime/__init__.py +1 -1
  5. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/src/proofgate_runtime/cli.py +12 -6
  6. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/src/proofgate_runtime/mcp_server.py +10 -3
  7. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/src/proofgate_runtime/receipts.py +4 -3
  8. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/src/proofgate_runtime.egg-info/PKG-INFO +9 -5
  9. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/tests/test_receipts.py +4 -3
  10. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/LICENSE +0 -0
  11. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/setup.cfg +0 -0
  12. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/src/proofgate_runtime/claims.py +0 -0
  13. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/src/proofgate_runtime/guard.py +0 -0
  14. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/src/proofgate_runtime.egg-info/SOURCES.txt +0 -0
  15. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/src/proofgate_runtime.egg-info/dependency_links.txt +0 -0
  16. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/src/proofgate_runtime.egg-info/entry_points.txt +0 -0
  17. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/src/proofgate_runtime.egg-info/top_level.txt +0 -0
  18. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/tests/test_claims.py +0 -0
  19. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/tests/test_guard.py +0 -0
  20. {proofgate_runtime-0.1.0 → proofgate_runtime-0.2.0}/tests/test_mcp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proofgate-runtime
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Evidence-first claim verification, signed receipts, lite injection guard and MCP server (stdlib only).
5
5
  Author-email: gamAI <info@gamai.io>
6
6
  License: Apache-2.0
@@ -19,7 +19,7 @@ Dynamic: license-file
19
19
  # ProofGate Runtime
20
20
 
21
21
  **Evidence-first claim verification, signed receipts, and a lite injection guard for AI agents.**
22
- [![PyPI](https://img.shields.io/pypi/v/proofgate-runtime.svg)](https://pypi.org/project/proofgate-runtime/) v0.1.0 · Python 3.10+ · stdlib only · Apache-2.0
22
+ [![PyPI](https://img.shields.io/pypi/v/proofgate-runtime.svg)](https://pypi.org/project/proofgate-runtime/) v0.2.0 · Python 3.10+ · stdlib only · Apache-2.0
23
23
 
24
24
  Agent says "done". ProofGate asks: **where is the evidence?**
25
25
 
@@ -72,7 +72,7 @@ echo '{"posted":true,"total":"2808.00"}' > result.json
72
72
  printf 'shared-secret' > key.bin
73
73
 
74
74
  proofgate make-receipt intent.json result.json --key-file key.bin > receipt.json
75
- proofgate verify-receipt receipt.json --key-file key.bin --require-signature
75
+ proofgate verify-receipt receipt.json --key-file key.bin
76
76
  ```
77
77
 
78
78
  Tamper with `result.json` or the receipt and verification fails closed.
@@ -107,8 +107,8 @@ Tools: `verify_claims`, `make_receipt`, `verify_receipt`, `check_injection`, `ru
107
107
  - **No anchor/lease service.** That is [`sigil`](https://github.com/gamai-io/sigil) (grants, receipts, anchor, MCP middleware). ProofGate v0.1 is the local verification core.
108
108
  - **No embedding-based guard.** This is the lexical layer; the full multilingual cascade is [`gamai-guard`](https://github.com/gamai-io/gamai-guard).
109
109
  - **No independent security audit.** Treat v0.1 as a reference implementation, not a certified product.
110
- - **MCP receipts are unsigned.** Signing over MCP would expose the key to the client; use the CLI with a protected key file, or call the library.
111
- - **PyPI 0.1.0 is available.** Install with `pip install proofgate-runtime`.
110
+ - **MCP receipts are signed only when `PROOFGATE_HMAC_KEY` is set** and are rejected as unsigned otherwise. Set it in the server environment; never put the key in a client or in git.
111
+ - **PyPI 0.2.0 is available.** Install with `pip install proofgate-runtime`.
112
112
 
113
113
  ## Design rules
114
114
 
@@ -126,6 +126,10 @@ Tools: `verify_claims`, `make_receipt`, `verify_receipt`, `check_injection`, `ru
126
126
  - [`evidence-gate-runtime`](https://github.com/gamai-io/evidence-gate-runtime) — fuller runtime with replay.
127
127
  - [`floor-copilot`](https://github.com/gamai-io/floor-copilot) — production case study: invoice intake with 17% VAT gate.
128
128
 
129
+ ## v0.2 breaking change
130
+
131
+ `verify_receipt()` now requires a valid signature by default. For legacy unsigned receipts, call `verify_receipt(..., require_signature=False)` or the CLI with `--allow-unsigned`. Create signed receipts with `proofgate make-receipt ... --key-file`; the MCP server signs when `PROOFGATE_HMAC_KEY` is set.
132
+
129
133
  ## Honest status
130
134
 
131
135
  - Claim verification: tested (BHS/EU/US money formats, predicates, tamper detection).
@@ -1,7 +1,7 @@
1
1
  # ProofGate Runtime
2
2
 
3
3
  **Evidence-first claim verification, signed receipts, and a lite injection guard for AI agents.**
4
- [![PyPI](https://img.shields.io/pypi/v/proofgate-runtime.svg)](https://pypi.org/project/proofgate-runtime/) v0.1.0 · Python 3.10+ · stdlib only · Apache-2.0
4
+ [![PyPI](https://img.shields.io/pypi/v/proofgate-runtime.svg)](https://pypi.org/project/proofgate-runtime/) v0.2.0 · Python 3.10+ · stdlib only · Apache-2.0
5
5
 
6
6
  Agent says "done". ProofGate asks: **where is the evidence?**
7
7
 
@@ -54,7 +54,7 @@ echo '{"posted":true,"total":"2808.00"}' > result.json
54
54
  printf 'shared-secret' > key.bin
55
55
 
56
56
  proofgate make-receipt intent.json result.json --key-file key.bin > receipt.json
57
- proofgate verify-receipt receipt.json --key-file key.bin --require-signature
57
+ proofgate verify-receipt receipt.json --key-file key.bin
58
58
  ```
59
59
 
60
60
  Tamper with `result.json` or the receipt and verification fails closed.
@@ -89,8 +89,8 @@ Tools: `verify_claims`, `make_receipt`, `verify_receipt`, `check_injection`, `ru
89
89
  - **No anchor/lease service.** That is [`sigil`](https://github.com/gamai-io/sigil) (grants, receipts, anchor, MCP middleware). ProofGate v0.1 is the local verification core.
90
90
  - **No embedding-based guard.** This is the lexical layer; the full multilingual cascade is [`gamai-guard`](https://github.com/gamai-io/gamai-guard).
91
91
  - **No independent security audit.** Treat v0.1 as a reference implementation, not a certified product.
92
- - **MCP receipts are unsigned.** Signing over MCP would expose the key to the client; use the CLI with a protected key file, or call the library.
93
- - **PyPI 0.1.0 is available.** Install with `pip install proofgate-runtime`.
92
+ - **MCP receipts are signed only when `PROOFGATE_HMAC_KEY` is set** and are rejected as unsigned otherwise. Set it in the server environment; never put the key in a client or in git.
93
+ - **PyPI 0.2.0 is available.** Install with `pip install proofgate-runtime`.
94
94
 
95
95
  ## Design rules
96
96
 
@@ -108,6 +108,10 @@ Tools: `verify_claims`, `make_receipt`, `verify_receipt`, `check_injection`, `ru
108
108
  - [`evidence-gate-runtime`](https://github.com/gamai-io/evidence-gate-runtime) — fuller runtime with replay.
109
109
  - [`floor-copilot`](https://github.com/gamai-io/floor-copilot) — production case study: invoice intake with 17% VAT gate.
110
110
 
111
+ ## v0.2 breaking change
112
+
113
+ `verify_receipt()` now requires a valid signature by default. For legacy unsigned receipts, call `verify_receipt(..., require_signature=False)` or the CLI with `--allow-unsigned`. Create signed receipts with `proofgate make-receipt ... --key-file`; the MCP server signs when `PROOFGATE_HMAC_KEY` is set.
114
+
111
115
  ## Honest status
112
116
 
113
117
  - Claim verification: tested (BHS/EU/US money formats, predicates, tamper detection).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "proofgate-runtime"
7
- version = "0.1.0"
7
+ version = "0.2.0"
8
8
  description = "Evidence-first claim verification, signed receipts, lite injection guard and MCP server (stdlib only)."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -3,7 +3,7 @@ from .claims import evaluate, check, verify, demo, EvidenceError
3
3
  from .guard import check_injection
4
4
  from .receipts import make_receipt, verify_receipt, verify_chain, canonical_json, sha256_hex
5
5
 
6
- __version__ = "0.1.0"
6
+ __version__ = "0.2.0"
7
7
 
8
8
  __all__ = [
9
9
  "evaluate", "check", "verify", "demo", "EvidenceError",
@@ -43,12 +43,13 @@ def main(argv: list[str] | None = None) -> int:
43
43
  m = sub.add_parser("make-receipt", help="create a receipt from intent/result JSON files")
44
44
  m.add_argument("intent")
45
45
  m.add_argument("result")
46
- m.add_argument("--key-file", default=None, help="optional HMAC key file")
46
+ m.add_argument("--key-file", default=None, help="HMAC key file (required unless --allow-unsigned)")
47
+ m.add_argument("--allow-unsigned", action="store_true", help="create an unsigned receipt (not recommended)")
47
48
 
48
49
  v = sub.add_parser("verify-receipt", help="verify a receipt JSON file")
49
50
  v.add_argument("receipt")
50
51
  v.add_argument("--key-file", default=None)
51
- v.add_argument("--require-signature", action="store_true")
52
+ v.add_argument("--allow-unsigned", action="store_true", help="accept an unsigned receipt")
52
53
 
53
54
  g = sub.add_parser("check-injection", help="lexical prompt-injection pre-filter")
54
55
  g.add_argument("text")
@@ -63,12 +64,17 @@ def main(argv: list[str] | None = None) -> int:
63
64
  _print(check(claims or [], args.reported_hash))
64
65
  return 0
65
66
  if args.cmd == "make-receipt":
66
- _print(make_receipt(_load(args.intent), _load(args.result), key=_key(args.key_file)))
67
+ key = _key(args.key_file)
68
+ if key is None and not args.allow_unsigned:
69
+ print("refusing unsigned receipt: pass --key-file or --allow-unsigned", file=sys.stderr)
70
+ return 2
71
+ _print(make_receipt(_load(args.intent), _load(args.result), key=key))
67
72
  return 0
68
73
  if args.cmd == "verify-receipt":
69
- _print(verify_receipt(_load(args.receipt), key=_key(args.key_file),
70
- require_signature=args.require_signature))
71
- return 0
74
+ res = verify_receipt(_load(args.receipt), key=_key(args.key_file),
75
+ require_signature=not args.allow_unsigned)
76
+ _print(res)
77
+ return 0 if res["valid"] else 1
72
78
  if args.cmd == "check-injection":
73
79
  _print(check_injection(args.text, args.tool))
74
80
  return 0
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import json
6
+ import os
6
7
  import sys
7
8
  from typing import Any
8
9
 
@@ -74,18 +75,24 @@ def call_tool(name: str, args: dict) -> dict:
74
75
  if name == "make_receipt":
75
76
  if "intent" not in args or "result" not in args:
76
77
  return _text({"error": "intent and result are required"}, True)
77
- return _text(make_receipt(args["intent"], args["result"], meta=args.get("meta")))
78
+ key = os.environ.get("PROOFGATE_HMAC_KEY") or None
79
+ rec = make_receipt(args["intent"], args["result"], key=key, meta=args.get("meta"))
80
+ if not key:
81
+ rec["_warning"] = "unsigned: set PROOFGATE_HMAC_KEY to sign MCP receipts"
82
+ return _text(rec)
78
83
  if name == "verify_receipt":
79
84
  if not isinstance(args.get("receipt"), dict):
80
85
  return _text({"error": "receipt object is required"}, True)
81
- return _text(verify_receipt(args["receipt"], require_signature=bool(args.get("require_signature"))))
86
+ return _text(verify_receipt(args["receipt"],
87
+ require_signature=bool(args.get("require_signature", True))))
82
88
  if name == "check_injection":
83
89
  return _text(check_injection(args.get("text", ""), args.get("tool")))
84
90
  if name == "runtime_info":
85
91
  return _text({"name": "proofgate-runtime", "version": __version__,
86
92
  "capabilities": ["verify_claims", "make_receipt", "verify_receipt",
87
93
  "check_injection_lite"],
88
- "signed_receipts": "HMAC-SHA256 via CLI key file"})
94
+ "signed_receipts": "HMAC-SHA256; MCP uses PROOFGATE_HMAC_KEY; unsigned rejected by default",
95
+ "require_signature_default": True})
89
96
  return _text({"error": "unknown tool: %s" % name}, True)
90
97
 
91
98
 
@@ -61,7 +61,7 @@ def make_receipt(intent: Any, result: Any, key: str | bytes | None = None,
61
61
 
62
62
 
63
63
  def verify_receipt(receipt: dict, key: str | bytes | None = None,
64
- require_signature: bool = False) -> dict:
64
+ require_signature: bool = True) -> dict:
65
65
  """Verify hashes and, when present, the signature. Fail closed."""
66
66
  errors: list[str] = []
67
67
  if not isinstance(receipt, dict):
@@ -100,14 +100,15 @@ def verify_receipt(receipt: dict, key: str | bytes | None = None,
100
100
  "receipt_hash": receipt.get("receipt_hash")}
101
101
 
102
102
 
103
- def verify_chain(receipts: list[dict], key: str | bytes | None = None) -> dict:
103
+ def verify_chain(receipts: list[dict], key: str | bytes | None = None,
104
+ require_signature: bool = True) -> dict:
104
105
  """Verify a simple prev_hash chain (first prev_hash = GENESIS)."""
105
106
  errors: list[str] = []
106
107
  prev = "GENESIS"
107
108
  for i, rec in enumerate(receipts or []):
108
109
  if rec.get("prev_hash") != prev:
109
110
  errors.append("E_CHAIN_AT_%d" % i)
110
- res = verify_receipt(rec, key=key)
111
+ res = verify_receipt(rec, key=key, require_signature=require_signature)
111
112
  if not res["valid"]:
112
113
  errors.extend("%s@%d" % (e, i) for e in res["errors"])
113
114
  prev = rec.get("receipt_hash", "")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proofgate-runtime
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Evidence-first claim verification, signed receipts, lite injection guard and MCP server (stdlib only).
5
5
  Author-email: gamAI <info@gamai.io>
6
6
  License: Apache-2.0
@@ -19,7 +19,7 @@ Dynamic: license-file
19
19
  # ProofGate Runtime
20
20
 
21
21
  **Evidence-first claim verification, signed receipts, and a lite injection guard for AI agents.**
22
- [![PyPI](https://img.shields.io/pypi/v/proofgate-runtime.svg)](https://pypi.org/project/proofgate-runtime/) v0.1.0 · Python 3.10+ · stdlib only · Apache-2.0
22
+ [![PyPI](https://img.shields.io/pypi/v/proofgate-runtime.svg)](https://pypi.org/project/proofgate-runtime/) v0.2.0 · Python 3.10+ · stdlib only · Apache-2.0
23
23
 
24
24
  Agent says "done". ProofGate asks: **where is the evidence?**
25
25
 
@@ -72,7 +72,7 @@ echo '{"posted":true,"total":"2808.00"}' > result.json
72
72
  printf 'shared-secret' > key.bin
73
73
 
74
74
  proofgate make-receipt intent.json result.json --key-file key.bin > receipt.json
75
- proofgate verify-receipt receipt.json --key-file key.bin --require-signature
75
+ proofgate verify-receipt receipt.json --key-file key.bin
76
76
  ```
77
77
 
78
78
  Tamper with `result.json` or the receipt and verification fails closed.
@@ -107,8 +107,8 @@ Tools: `verify_claims`, `make_receipt`, `verify_receipt`, `check_injection`, `ru
107
107
  - **No anchor/lease service.** That is [`sigil`](https://github.com/gamai-io/sigil) (grants, receipts, anchor, MCP middleware). ProofGate v0.1 is the local verification core.
108
108
  - **No embedding-based guard.** This is the lexical layer; the full multilingual cascade is [`gamai-guard`](https://github.com/gamai-io/gamai-guard).
109
109
  - **No independent security audit.** Treat v0.1 as a reference implementation, not a certified product.
110
- - **MCP receipts are unsigned.** Signing over MCP would expose the key to the client; use the CLI with a protected key file, or call the library.
111
- - **PyPI 0.1.0 is available.** Install with `pip install proofgate-runtime`.
110
+ - **MCP receipts are signed only when `PROOFGATE_HMAC_KEY` is set** and are rejected as unsigned otherwise. Set it in the server environment; never put the key in a client or in git.
111
+ - **PyPI 0.2.0 is available.** Install with `pip install proofgate-runtime`.
112
112
 
113
113
  ## Design rules
114
114
 
@@ -126,6 +126,10 @@ Tools: `verify_claims`, `make_receipt`, `verify_receipt`, `check_injection`, `ru
126
126
  - [`evidence-gate-runtime`](https://github.com/gamai-io/evidence-gate-runtime) — fuller runtime with replay.
127
127
  - [`floor-copilot`](https://github.com/gamai-io/floor-copilot) — production case study: invoice intake with 17% VAT gate.
128
128
 
129
+ ## v0.2 breaking change
130
+
131
+ `verify_receipt()` now requires a valid signature by default. For legacy unsigned receipts, call `verify_receipt(..., require_signature=False)` or the CLI with `--allow-unsigned`. Create signed receipts with `proofgate make-receipt ... --key-file`; the MCP server signs when `PROOFGATE_HMAC_KEY` is set.
132
+
129
133
  ## Honest status
130
134
 
131
135
  - Claim verification: tested (BHS/EU/US money formats, predicates, tamper detection).
@@ -6,9 +6,10 @@ from proofgate_runtime.receipts import make_receipt, verify_receipt, canonical_j
6
6
  class ReceiptTests(unittest.TestCase):
7
7
  def test_unsigned_receipt(self):
8
8
  r = make_receipt({"op": "x"}, {"ok": True})
9
- out = verify_receipt(r)
9
+ out = verify_receipt(r, require_signature=False)
10
10
  self.assertTrue(out["valid"], out)
11
11
  self.assertFalse(out["signed"])
12
+ self.assertFalse(verify_receipt(r)["valid"])
12
13
 
13
14
  def test_signed_receipt(self):
14
15
  r = make_receipt({"op": "x"}, {"ok": True}, key="secret")
@@ -29,9 +30,9 @@ class ReceiptTests(unittest.TestCase):
29
30
  self.assertFalse(out["valid"])
30
31
  self.assertIn("E_RESULT_DIGEST", out["errors"])
31
32
 
32
- def test_require_signature(self):
33
+ def test_require_signature_default(self):
33
34
  r = make_receipt({"op": "x"}, {"ok": True})
34
- out = verify_receipt(r, require_signature=True)
35
+ out = verify_receipt(r)
35
36
  self.assertFalse(out["valid"])
36
37
  self.assertIn("E_UNSIGNED", out["errors"])
37
38