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.
- {provared-0.2.0 → provared-0.4.0}/PKG-INFO +52 -6
- provared-0.2.0/src/provared.egg-info/PKG-INFO → provared-0.4.0/README.md +201 -176
- {provared-0.2.0 → provared-0.4.0}/pyproject.toml +4 -2
- provared-0.4.0/src/provared/dev.py +133 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/encoding.py +1 -1
- {provared-0.2.0 → provared-0.4.0}/src/provared/guard.py +100 -6
- {provared-0.2.0 → provared-0.4.0}/src/provared/recorder.py +4 -1
- {provared-0.2.0 → provared-0.4.0}/src/provared/signatures.py +8 -0
- provared-0.2.0/README.md → provared-0.4.0/src/provared.egg-info/PKG-INFO +222 -157
- {provared-0.2.0 → provared-0.4.0}/src/provared.egg-info/SOURCES.txt +4 -0
- provared-0.4.0/tests/test_check_before.py +44 -0
- provared-0.4.0/tests/test_dev.py +97 -0
- {provared-0.2.0 → provared-0.4.0}/tests/test_recorder.py +45 -0
- provared-0.4.0/tests/test_signatures.py +47 -0
- {provared-0.2.0 → provared-0.4.0}/LICENSE +0 -0
- {provared-0.2.0 → provared-0.4.0}/LICENSE-CC-BY-4.0.txt +0 -0
- {provared-0.2.0 → provared-0.4.0}/setup.cfg +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/__init__.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/_js.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/actions.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/approval.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/blockstamp.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/book.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/check.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/compare.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/cover.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/der.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/fields.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/headers.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/jws.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/keys.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/pass_.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/seal.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/service.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/slip.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/standing.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/stub.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/timestamp.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/tools.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/tree.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared/webauthn.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared.egg-info/dependency_links.txt +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared.egg-info/requires.txt +0 -0
- {provared-0.2.0 → provared-0.4.0}/src/provared.egg-info/top_level.txt +0 -0
- {provared-0.2.0 → provared-0.4.0}/tests/test_record_function.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/tests/test_records.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/tests/test_review2.py +0 -0
- {provared-0.2.0 → provared-0.4.0}/tests/test_tree.py +0 -0
- {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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
##
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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.
|
|
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"
|