provared 0.2.0__tar.gz → 0.4.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 (49) hide show
  1. {provared-0.2.0 → provared-0.4.0}/PKG-INFO +52 -6
  2. provared-0.2.0/src/provared.egg-info/PKG-INFO → provared-0.4.0/README.md +201 -176
  3. {provared-0.2.0 → provared-0.4.0}/pyproject.toml +4 -2
  4. provared-0.4.0/src/provared/dev.py +133 -0
  5. {provared-0.2.0 → provared-0.4.0}/src/provared/encoding.py +1 -1
  6. {provared-0.2.0 → provared-0.4.0}/src/provared/guard.py +100 -6
  7. {provared-0.2.0 → provared-0.4.0}/src/provared/recorder.py +4 -1
  8. {provared-0.2.0 → provared-0.4.0}/src/provared/signatures.py +8 -0
  9. provared-0.2.0/README.md → provared-0.4.0/src/provared.egg-info/PKG-INFO +222 -157
  10. {provared-0.2.0 → provared-0.4.0}/src/provared.egg-info/SOURCES.txt +4 -0
  11. provared-0.4.0/tests/test_check_before.py +44 -0
  12. provared-0.4.0/tests/test_dev.py +97 -0
  13. {provared-0.2.0 → provared-0.4.0}/tests/test_recorder.py +45 -0
  14. provared-0.4.0/tests/test_signatures.py +47 -0
  15. {provared-0.2.0 → provared-0.4.0}/LICENSE +0 -0
  16. {provared-0.2.0 → provared-0.4.0}/LICENSE-CC-BY-4.0.txt +0 -0
  17. {provared-0.2.0 → provared-0.4.0}/setup.cfg +0 -0
  18. {provared-0.2.0 → provared-0.4.0}/src/provared/__init__.py +0 -0
  19. {provared-0.2.0 → provared-0.4.0}/src/provared/_js.py +0 -0
  20. {provared-0.2.0 → provared-0.4.0}/src/provared/actions.py +0 -0
  21. {provared-0.2.0 → provared-0.4.0}/src/provared/approval.py +0 -0
  22. {provared-0.2.0 → provared-0.4.0}/src/provared/blockstamp.py +0 -0
  23. {provared-0.2.0 → provared-0.4.0}/src/provared/book.py +0 -0
  24. {provared-0.2.0 → provared-0.4.0}/src/provared/check.py +0 -0
  25. {provared-0.2.0 → provared-0.4.0}/src/provared/compare.py +0 -0
  26. {provared-0.2.0 → provared-0.4.0}/src/provared/cover.py +0 -0
  27. {provared-0.2.0 → provared-0.4.0}/src/provared/der.py +0 -0
  28. {provared-0.2.0 → provared-0.4.0}/src/provared/fields.py +0 -0
  29. {provared-0.2.0 → provared-0.4.0}/src/provared/headers.py +0 -0
  30. {provared-0.2.0 → provared-0.4.0}/src/provared/jws.py +0 -0
  31. {provared-0.2.0 → provared-0.4.0}/src/provared/keys.py +0 -0
  32. {provared-0.2.0 → provared-0.4.0}/src/provared/pass_.py +0 -0
  33. {provared-0.2.0 → provared-0.4.0}/src/provared/seal.py +0 -0
  34. {provared-0.2.0 → provared-0.4.0}/src/provared/service.py +0 -0
  35. {provared-0.2.0 → provared-0.4.0}/src/provared/slip.py +0 -0
  36. {provared-0.2.0 → provared-0.4.0}/src/provared/standing.py +0 -0
  37. {provared-0.2.0 → provared-0.4.0}/src/provared/stub.py +0 -0
  38. {provared-0.2.0 → provared-0.4.0}/src/provared/timestamp.py +0 -0
  39. {provared-0.2.0 → provared-0.4.0}/src/provared/tools.py +0 -0
  40. {provared-0.2.0 → provared-0.4.0}/src/provared/tree.py +0 -0
  41. {provared-0.2.0 → provared-0.4.0}/src/provared/webauthn.py +0 -0
  42. {provared-0.2.0 → provared-0.4.0}/src/provared.egg-info/dependency_links.txt +0 -0
  43. {provared-0.2.0 → provared-0.4.0}/src/provared.egg-info/requires.txt +0 -0
  44. {provared-0.2.0 → provared-0.4.0}/src/provared.egg-info/top_level.txt +0 -0
  45. {provared-0.2.0 → provared-0.4.0}/tests/test_record_function.py +0 -0
  46. {provared-0.2.0 → provared-0.4.0}/tests/test_records.py +0 -0
  47. {provared-0.2.0 → provared-0.4.0}/tests/test_review2.py +0 -0
  48. {provared-0.2.0 → provared-0.4.0}/tests/test_tree.py +0 -0
  49. {provared-0.2.0 → provared-0.4.0}/tests/test_vectors.py +0 -0
@@ -1,12 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: provared
3
- Version: 0.2.0
4
- Summary: Proof of what an AI agent was allowed to do, and what it then did. Evidence, not a verdict.
3
+ Version: 0.4.0
4
+ Summary: Proof of what an AI agent was allowed to do, and what it then did. A passkey-signed permission with limits a machine can check, a signed receipt for each action, and a checker. Evidence, not a verdict.
5
5
  Author: Pavel Izmaylov
6
6
  License-Expression: Apache-2.0 AND CC-BY-4.0
7
+ Project-URL: Homepage, https://prova.red
7
8
  Project-URL: Source, https://github.com/provared/provared
8
9
  Project-URL: Documentation, https://github.com/provared/provared/tree/main/python
9
10
  Project-URL: Issues, https://github.com/provared/provared/issues
11
+ Keywords: ai-agent,agent,audit-trail,permission,authorization,receipt,tamper-evident,passkey,webauthn,jws,ed25519,ml-dsa,post-quantum,evidence,langchain
10
12
  Classifier: Development Status :: 3 - Alpha
11
13
  Classifier: Programming Language :: Python :: 3
12
14
  Classifier: Topic :: Security :: Cryptography
@@ -17,7 +19,7 @@ License-File: LICENSE-CC-BY-4.0.txt
17
19
  Requires-Dist: cryptography>=48
18
20
  Dynamic: license-file
19
21
 
20
- # Provared for Python
22
+ # <img src="https://prova.red/favicon.svg" alt="" width="36"> Provared for Python
21
23
 
22
24
  Proof of what an AI agent was allowed to do, and what it then did.
23
25
 
@@ -32,7 +34,7 @@ particular to Python.
32
34
 
33
35
  ## Status
34
36
 
35
- **Version 0.2.0, a draft**, the same version as the JavaScript library.
37
+ **Version 0.4.0, a draft**, the same version as the JavaScript library.
36
38
  Published as it is, by one author in their own time; fixes as time
37
39
  allows. It gives the same answer, case for case, as the shared test
38
40
  files in [`test-vectors/`](https://github.com/provared/provared/tree/main/test-vectors) show: the same result, the
@@ -44,7 +46,10 @@ needs, `cryptography`, does not yet have SLH-DSA (FIPS 205), the method
44
46
  of a seal's third signature. So a seal's third signature is reported as
45
47
  "not checked on this device", and never as a pass, as the checking page
46
48
  in a browser reports it. Seals are checked in full by the JavaScript
47
- command-line checker.
49
+ command-line checker. The check before acting, and so the stub writer,
50
+ goes on after a seal whose Ed25519 and ML-DSA-87 signatures were checked
51
+ here, and its answer says what was not checked (`notChecked`); any
52
+ other signature left unchecked still allows nothing.
48
53
 
49
54
  ## Install
50
55
 
@@ -58,6 +63,44 @@ You need Python 3.11 or later. The one dependency is `cryptography`,
58
63
  version 48 or later: Python has no signatures built in, and version 48
59
64
  is the first whose ready-built packages hold ML-DSA (FIPS 204).
60
65
 
66
+ ## A first record
67
+
68
+ The permission is signed with a **development stand-in for a passkey**
69
+ (`provared.dev`): a key made in memory, which no person confirmed. Every
70
+ permission it signs says so in its issuer's name, and a checker shows
71
+ that name. A real permission is signed by a person, in a browser, with a
72
+ real passkey (the browser part of the JavaScript library, and the
73
+ checking page).
74
+
75
+ ```python
76
+ from provared import check_book
77
+ from provared.dev import development_recorder
78
+
79
+ SEND = 'provared.message.send'
80
+
81
+ # A permission: the agent may send messages, at most two.
82
+ made = development_recorder({'actions': [SEND], 'limits': [{'action': SEND, 'count': 2}]})
83
+
84
+ # Each action is asked for first, taken only if the permission allows it,
85
+ # and receipted after. The third message is outside it, so it is not sent.
86
+ for i in range(1, 4):
87
+ outcome = made.writer.act({'action': SEND}, lambda i=i: print(f'message {i} sent'))
88
+ if not outcome['done']:
89
+ print(f"message {i} not sent: {outcome['answer']['breaches'][0]['message']}")
90
+
91
+ book = made.writer.book() # the record, one entry to a line
92
+ summary = check_book(book, issuer_keys=made.issuer_keys)['summary']
93
+ print(summary['intact'], summary['withinSlips']) # True True
94
+ ```
95
+
96
+ The whole file is [`examples/first_record.py`](https://github.com/provared/provared/blob/main/python/examples/first_record.py).
97
+ `development_slip(fields)` gives the signed slip, its fingerprint, a
98
+ book holding it, the stand-in's thumbprint (`issuer_keys`) and the
99
+ agent's keys without opening the writer; `development_passkey()` gives
100
+ the stand-in alone, whose `sign(challenge)` signs a prepared slip,
101
+ approval or cancellation. The issuer's name always ends with
102
+ " (development)", whatever name is given.
103
+
61
104
  ## Use
62
105
 
63
106
  ```python
@@ -143,7 +186,10 @@ place into a `functools.partial` is no longer a parameter, so it is not
143
186
  in the record, and nor is any change that a decorator inside makes. The
144
187
  folder
145
188
  [`examples/`](https://github.com/provared/provared/tree/main/python/examples)
146
- shows this with three widely used agent frameworks. `key_set_from_seeds` and `key_set_seeds` keep an
189
+ shows this with three widely used agent frameworks, and the folder
190
+ [`integrations/`](https://github.com/provared/provared/tree/main/python/integrations)
191
+ holds small packages for two of them, each of which puts every tool call
192
+ of an agent behind the writer in one line. `key_set_from_seeds` and `key_set_seeds` keep an
147
193
  agent's keys between runs as two 32-byte seeds.
148
194
 
149
195
  Names follow Python's way (`check_book`, `write_stub`). Options may be
@@ -1,176 +1,201 @@
1
- Metadata-Version: 2.4
2
- Name: provared
3
- Version: 0.2.0
4
- Summary: Proof of what an AI agent was allowed to do, and what it then did. Evidence, not a verdict.
5
- Author: Pavel Izmaylov
6
- License-Expression: Apache-2.0 AND CC-BY-4.0
7
- Project-URL: Source, https://github.com/provared/provared
8
- Project-URL: Documentation, https://github.com/provared/provared/tree/main/python
9
- Project-URL: Issues, https://github.com/provared/provared/issues
10
- Classifier: Development Status :: 3 - Alpha
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Topic :: Security :: Cryptography
13
- Requires-Python: >=3.11
14
- Description-Content-Type: text/markdown
15
- License-File: LICENSE
16
- License-File: LICENSE-CC-BY-4.0.txt
17
- Requires-Dist: cryptography>=48
18
- Dynamic: license-file
19
-
20
- # Provared for Python
21
-
22
- Proof of what an AI agent was allowed to do, and what it then did.
23
-
24
- *Evidence, not a verdict.*
25
-
26
- This is the Python version of Provared. The format, the reasons for it
27
- and what a record does and does not prove are described in the
28
- repository's own [README](https://github.com/provared/provared/blob/main/README.md), the
29
- [format description](https://github.com/provared/provared/blob/main/spec/provared-format.md) and the
30
- [list of threats](https://github.com/provared/provared/blob/main/docs/threat-model.md). This page says only what is
31
- particular to Python.
32
-
33
- ## Status
34
-
35
- **Version 0.2.0, a draft**, the same version as the JavaScript library.
36
- Published as it is, by one author in their own time; fixes as time
37
- allows. It gives the same answer, case for case, as the shared test
38
- files in [`test-vectors/`](https://github.com/provared/provared/tree/main/test-vectors) show: the same result, the
39
- same codes and the same words. Every shared file passes in both
40
- languages.
41
-
42
- One difference is stated, not hidden. The one package this version
43
- needs, `cryptography`, does not yet have SLH-DSA (FIPS 205), the method
44
- of a seal's third signature. So a seal's third signature is reported as
45
- "not checked on this device", and never as a pass, as the checking page
46
- in a browser reports it. Seals are checked in full by the JavaScript
47
- command-line checker.
48
-
49
- ## Install
50
-
51
- ```
52
- pip install provared
53
- ```
54
-
55
- Or, from a copy of the repository, `pip install ./python`.
56
-
57
- You need Python 3.11 or later. The one dependency is `cryptography`,
58
- version 48 or later: Python has no signatures built in, and version 48
59
- is the first whose ready-built packages hold ML-DSA (FIPS 204).
60
-
61
- ## Use
62
-
63
- ```python
64
- from provared import check_book
65
-
66
- with open('record.jsonl', encoding='utf-8') as f:
67
- result = check_book(f.read(), issuer_keys=['<the thumbprint of the passkey you trust>'])
68
-
69
- summary = result['summary']
70
- print(summary['intact'], summary['withinSlips'], summary['firstBreach'])
71
- ```
72
-
73
- The answer is the same JSON as the JavaScript library gives (see "What
74
- the checker returns" in the repository's README).
75
-
76
- Before an agent acts:
77
-
78
- ```python
79
- from provared import check_before
80
-
81
- answer = check_before(book_text, {
82
- 'slip': slip_fingerprint,
83
- 'action': 'supplies.order',
84
- 'amount': {'unit': 'GBP', 'value': 20},
85
- 'with': 'supplier',
86
- }, issuer_keys=[passkey_thumbprint])
87
-
88
- if not answer['allowed']:
89
- print(answer['problems'], answer['breaches'])
90
- ```
91
-
92
- Beside an agent, the stub writer asks the check before acting, takes the
93
- action only if the answer is yes, and writes the stub (see "Beside an
94
- agent" in the repository's README):
95
-
96
- ```python
97
- from provared import open_recorder
98
-
99
- writer = open_recorder(book_text, slip_fingerprint, private_keys, issuer_keys=[passkey_thumbprint])
100
- result = writer.act(
101
- {'action': 'supplies.order', 'amount': {'unit': 'GBP', 'value': 20}, 'with': 'supplier'},
102
- lambda: place_the_order(),
103
- )
104
- book_text, held = writer.snapshot() # keep both together, with the book
105
- ```
106
-
107
- Every call is an ordinary function. A writer's calls wait for one
108
- another, also across threads. The other side is asked in a thread of
109
- its own and given 30 seconds. An asynchronous agent calls the writer
110
- through `asyncio.to_thread`. A call to the writer from inside an action
111
- it is taking is refused at once, from the same thread or from a thread
112
- that carries the action's context; from a new thread that the action
113
- starts without it, the call waits until the action ends, so an action
114
- must not wait for such a call. `record_tools` puts an agent's tools
115
- behind the writer. `record_function` puts one ordinary function behind
116
- it and keeps the function's name, parameters and description, so that an
117
- agent framework describes it to its model as it would the function:
118
-
119
- ```python
120
- from provared import record_function
121
-
122
- def place_order(item: str, value: int) -> str:
123
- """Order supplies from the supplier."""
124
- ...
125
-
126
- place_order = record_function(writer, place_order, 'supplies.order', with_='supplier',
127
- amount=lambda args: {'unit': 'GBP', 'value': args['value']})
128
- ```
129
-
130
- A call that the slip does not allow raises `NotTaken`, and the function
131
- is not run. The arguments must be plain data (text, numbers, lists,
132
- dicts with text for names, True, False, None). A function with a
133
- parameter whose type can never be plain data (a class of its own, such
134
- as a model or a date, a tuple or a set) is refused when it is recorded,
135
- and so is a generator function. A whole number that JavaScript cannot
136
- hold exactly (beyond 2^53 - 1, unless a float holds it exactly) is
137
- refused, since two such numbers could share one fingerprint. The
138
- function is run with a copy of the arguments: a change it makes to a
139
- list or a dict it was handed does not reach the caller, and a default
140
- that is a list or a dict is a fresh copy at each call. The record holds the
141
- arguments the recorded function is called with: an argument bound in
142
- place into a `functools.partial` is no longer a parameter, so it is not
143
- in the record, and nor is any change that a decorator inside makes. The
144
- folder
145
- [`examples/`](https://github.com/provared/provared/tree/main/python/examples)
146
- shows this with three widely used agent frameworks. `key_set_from_seeds` and `key_set_seeds` keep an
147
- agent's keys between runs as two 32-byte seeds.
148
-
149
- Names follow Python's way (`check_book`, `write_stub`). Options may be
150
- given as keyword arguments (`issuer_keys`, `expected_root`,
151
- `stamp_services`, `seal_keys`, `vouchers`, `disclosures`,
152
- `cancellations`, `without_methods`), or as one dict with the names the
153
- JavaScript library uses (`issuerKeys` and so on). Records and answers
154
- are dicts and lists, with the same member names as in the format.
155
-
156
- `provared.check` holds the checker alone, for anyone who audits the
157
- checking.
158
-
159
- ## Tests
160
-
161
- From the `python` folder:
162
-
163
- ```
164
- python -m venv .venv
165
- .venv/bin/pip install "cryptography>=48" # on Windows: .venv\Scripts\pip
166
- PYTHONPATH=src .venv/bin/python -m unittest discover -s tests
167
- ```
168
-
169
- The tests read the shared test files in `../test-vectors/`, which the
170
- JavaScript library writes (`node tools/make-vectors.mjs`) and checks
171
- (`node --test test/vectors.test.mjs`).
172
-
173
- ## Licences
174
-
175
- As for the rest of the repository: the Apache License 2.0 for the code,
176
- and Creative Commons Attribution 4.0 for the documents.
1
+ # <img src="https://prova.red/favicon.svg" alt="" width="36"> Provared for Python
2
+
3
+ Proof of what an AI agent was allowed to do, and what it then did.
4
+
5
+ *Evidence, not a verdict.*
6
+
7
+ This is the Python version of Provared. The format, the reasons for it
8
+ and what a record does and does not prove are described in the
9
+ repository's own [README](https://github.com/provared/provared/blob/main/README.md), the
10
+ [format description](https://github.com/provared/provared/blob/main/spec/provared-format.md) and the
11
+ [list of threats](https://github.com/provared/provared/blob/main/docs/threat-model.md). This page says only what is
12
+ particular to Python.
13
+
14
+ ## Status
15
+
16
+ **Version 0.4.0, a draft**, the same version as the JavaScript library.
17
+ Published as it is, by one author in their own time; fixes as time
18
+ allows. It gives the same answer, case for case, as the shared test
19
+ files in [`test-vectors/`](https://github.com/provared/provared/tree/main/test-vectors) show: the same result, the
20
+ same codes and the same words. Every shared file passes in both
21
+ languages.
22
+
23
+ One difference is stated, not hidden. The one package this version
24
+ needs, `cryptography`, does not yet have SLH-DSA (FIPS 205), the method
25
+ of a seal's third signature. So a seal's third signature is reported as
26
+ "not checked on this device", and never as a pass, as the checking page
27
+ in a browser reports it. Seals are checked in full by the JavaScript
28
+ command-line checker. The check before acting, and so the stub writer,
29
+ goes on after a seal whose Ed25519 and ML-DSA-87 signatures were checked
30
+ here, and its answer says what was not checked (`notChecked`); any
31
+ other signature left unchecked still allows nothing.
32
+
33
+ ## Install
34
+
35
+ ```
36
+ pip install provared
37
+ ```
38
+
39
+ Or, from a copy of the repository, `pip install ./python`.
40
+
41
+ You need Python 3.11 or later. The one dependency is `cryptography`,
42
+ version 48 or later: Python has no signatures built in, and version 48
43
+ is the first whose ready-built packages hold ML-DSA (FIPS 204).
44
+
45
+ ## A first record
46
+
47
+ The permission is signed with a **development stand-in for a passkey**
48
+ (`provared.dev`): a key made in memory, which no person confirmed. Every
49
+ permission it signs says so in its issuer's name, and a checker shows
50
+ that name. A real permission is signed by a person, in a browser, with a
51
+ real passkey (the browser part of the JavaScript library, and the
52
+ checking page).
53
+
54
+ ```python
55
+ from provared import check_book
56
+ from provared.dev import development_recorder
57
+
58
+ SEND = 'provared.message.send'
59
+
60
+ # A permission: the agent may send messages, at most two.
61
+ made = development_recorder({'actions': [SEND], 'limits': [{'action': SEND, 'count': 2}]})
62
+
63
+ # Each action is asked for first, taken only if the permission allows it,
64
+ # and receipted after. The third message is outside it, so it is not sent.
65
+ for i in range(1, 4):
66
+ outcome = made.writer.act({'action': SEND}, lambda i=i: print(f'message {i} sent'))
67
+ if not outcome['done']:
68
+ print(f"message {i} not sent: {outcome['answer']['breaches'][0]['message']}")
69
+
70
+ book = made.writer.book() # the record, one entry to a line
71
+ summary = check_book(book, issuer_keys=made.issuer_keys)['summary']
72
+ print(summary['intact'], summary['withinSlips']) # True True
73
+ ```
74
+
75
+ The whole file is [`examples/first_record.py`](https://github.com/provared/provared/blob/main/python/examples/first_record.py).
76
+ `development_slip(fields)` gives the signed slip, its fingerprint, a
77
+ book holding it, the stand-in's thumbprint (`issuer_keys`) and the
78
+ agent's keys without opening the writer; `development_passkey()` gives
79
+ the stand-in alone, whose `sign(challenge)` signs a prepared slip,
80
+ approval or cancellation. The issuer's name always ends with
81
+ " (development)", whatever name is given.
82
+
83
+ ## Use
84
+
85
+ ```python
86
+ from provared import check_book
87
+
88
+ with open('record.jsonl', encoding='utf-8') as f:
89
+ result = check_book(f.read(), issuer_keys=['<the thumbprint of the passkey you trust>'])
90
+
91
+ summary = result['summary']
92
+ print(summary['intact'], summary['withinSlips'], summary['firstBreach'])
93
+ ```
94
+
95
+ The answer is the same JSON as the JavaScript library gives (see "What
96
+ the checker returns" in the repository's README).
97
+
98
+ Before an agent acts:
99
+
100
+ ```python
101
+ from provared import check_before
102
+
103
+ answer = check_before(book_text, {
104
+ 'slip': slip_fingerprint,
105
+ 'action': 'supplies.order',
106
+ 'amount': {'unit': 'GBP', 'value': 20},
107
+ 'with': 'supplier',
108
+ }, issuer_keys=[passkey_thumbprint])
109
+
110
+ if not answer['allowed']:
111
+ print(answer['problems'], answer['breaches'])
112
+ ```
113
+
114
+ Beside an agent, the stub writer asks the check before acting, takes the
115
+ action only if the answer is yes, and writes the stub (see "Beside an
116
+ agent" in the repository's README):
117
+
118
+ ```python
119
+ from provared import open_recorder
120
+
121
+ writer = open_recorder(book_text, slip_fingerprint, private_keys, issuer_keys=[passkey_thumbprint])
122
+ result = writer.act(
123
+ {'action': 'supplies.order', 'amount': {'unit': 'GBP', 'value': 20}, 'with': 'supplier'},
124
+ lambda: place_the_order(),
125
+ )
126
+ book_text, held = writer.snapshot() # keep both together, with the book
127
+ ```
128
+
129
+ Every call is an ordinary function. A writer's calls wait for one
130
+ another, also across threads. The other side is asked in a thread of
131
+ its own and given 30 seconds. An asynchronous agent calls the writer
132
+ through `asyncio.to_thread`. A call to the writer from inside an action
133
+ it is taking is refused at once, from the same thread or from a thread
134
+ that carries the action's context; from a new thread that the action
135
+ starts without it, the call waits until the action ends, so an action
136
+ must not wait for such a call. `record_tools` puts an agent's tools
137
+ behind the writer. `record_function` puts one ordinary function behind
138
+ it and keeps the function's name, parameters and description, so that an
139
+ agent framework describes it to its model as it would the function:
140
+
141
+ ```python
142
+ from provared import record_function
143
+
144
+ def place_order(item: str, value: int) -> str:
145
+ """Order supplies from the supplier."""
146
+ ...
147
+
148
+ place_order = record_function(writer, place_order, 'supplies.order', with_='supplier',
149
+ amount=lambda args: {'unit': 'GBP', 'value': args['value']})
150
+ ```
151
+
152
+ A call that the slip does not allow raises `NotTaken`, and the function
153
+ is not run. The arguments must be plain data (text, numbers, lists,
154
+ dicts with text for names, True, False, None). A function with a
155
+ parameter whose type can never be plain data (a class of its own, such
156
+ as a model or a date, a tuple or a set) is refused when it is recorded,
157
+ and so is a generator function. A whole number that JavaScript cannot
158
+ hold exactly (beyond 2^53 - 1, unless a float holds it exactly) is
159
+ refused, since two such numbers could share one fingerprint. The
160
+ function is run with a copy of the arguments: a change it makes to a
161
+ list or a dict it was handed does not reach the caller, and a default
162
+ that is a list or a dict is a fresh copy at each call. The record holds the
163
+ arguments the recorded function is called with: an argument bound in
164
+ place into a `functools.partial` is no longer a parameter, so it is not
165
+ in the record, and nor is any change that a decorator inside makes. The
166
+ folder
167
+ [`examples/`](https://github.com/provared/provared/tree/main/python/examples)
168
+ shows this with three widely used agent frameworks, and the folder
169
+ [`integrations/`](https://github.com/provared/provared/tree/main/python/integrations)
170
+ holds small packages for two of them, each of which puts every tool call
171
+ of an agent behind the writer in one line. `key_set_from_seeds` and `key_set_seeds` keep an
172
+ agent's keys between runs as two 32-byte seeds.
173
+
174
+ Names follow Python's way (`check_book`, `write_stub`). Options may be
175
+ given as keyword arguments (`issuer_keys`, `expected_root`,
176
+ `stamp_services`, `seal_keys`, `vouchers`, `disclosures`,
177
+ `cancellations`, `without_methods`), or as one dict with the names the
178
+ JavaScript library uses (`issuerKeys` and so on). Records and answers
179
+ are dicts and lists, with the same member names as in the format.
180
+
181
+ `provared.check` holds the checker alone, for anyone who audits the
182
+ checking.
183
+
184
+ ## Tests
185
+
186
+ From the `python` folder:
187
+
188
+ ```
189
+ python -m venv .venv
190
+ .venv/bin/pip install "cryptography>=48" # on Windows: .venv\Scripts\pip
191
+ PYTHONPATH=src .venv/bin/python -m unittest discover -s tests
192
+ ```
193
+
194
+ The tests read the shared test files in `../test-vectors/`, which the
195
+ JavaScript library writes (`node tools/make-vectors.mjs`) and checks
196
+ (`node --test test/vectors.test.mjs`).
197
+
198
+ ## Licences
199
+
200
+ As for the rest of the repository: the Apache License 2.0 for the code,
201
+ and Creative Commons Attribution 4.0 for the documents.
@@ -4,8 +4,9 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "provared"
7
- version = "0.2.0"
8
- description = "Proof of what an AI agent was allowed to do, and what it then did. Evidence, not a verdict."
7
+ version = "0.4.0"
8
+ description = "Proof of what an AI agent was allowed to do, and what it then did. A passkey-signed permission with limits a machine can check, a signed receipt for each action, and a checker. Evidence, not a verdict."
9
+ keywords = ["ai-agent", "agent", "audit-trail", "permission", "authorization", "receipt", "tamper-evident", "passkey", "webauthn", "jws", "ed25519", "ml-dsa", "post-quantum", "evidence", "langchain"]
9
10
  readme = "README.md"
10
11
  authors = [{ name = "Pavel Izmaylov" }]
11
12
  license = "Apache-2.0 AND CC-BY-4.0"
@@ -21,6 +22,7 @@ classifiers = [
21
22
  ]
22
23
 
23
24
  [project.urls]
25
+ Homepage = "https://prova.red"
24
26
  Source = "https://github.com/provared/provared"
25
27
  Documentation = "https://github.com/provared/provared/tree/main/python"
26
28
  Issues = "https://github.com/provared/provared/issues"