datumesh 0.4.2__tar.gz → 0.5.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.
- {datumesh-0.4.2 → datumesh-0.5.0}/PKG-INFO +23 -10
- datumesh-0.5.0/README.md +54 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/pyproject.toml +4 -4
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/__init__.py +58 -12
- datumesh-0.5.0/src/datumesh/_classes.py +451 -0
- datumesh-0.5.0/src/datumesh/_define.py +46 -0
- datumesh-0.5.0/src/datumesh/_describe.py +307 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/_envelope.py +135 -23
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/_errors.py +29 -5
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/_keys.py +84 -4
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/_lexical.py +32 -0
- datumesh-0.5.0/src/datumesh/_licence.py +257 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/_mesh.py +37 -17
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/_model.py +57 -8
- datumesh-0.5.0/src/datumesh/_normalisation.py +78 -0
- datumesh-0.5.0/src/datumesh/_objectkey.py +206 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/_ucd15.py +30 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/_unicode.py +11 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/_unitlist.py +1 -1
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh.egg-info/PKG-INFO +23 -10
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh.egg-info/SOURCES.txt +9 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_api.py +4 -4
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_conformance.py +21 -10
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_envelope.py +1 -1
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_spec_0_4_0.py +2 -1
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_spec_0_4_1.py +5 -2
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_spec_0_4_2.py +4 -3
- datumesh-0.5.0/tests/test_spec_0_4_3.py +354 -0
- datumesh-0.5.0/tests/test_spec_0_5_0.py +2039 -0
- datumesh-0.5.0/tests/test_spec_0_5_0_repin.py +785 -0
- datumesh-0.4.2/README.md +0 -41
- {datumesh-0.4.2 → datumesh-0.5.0}/LICENSE +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/setup.cfg +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/_json.py +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/_units.py +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/_uri.py +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/_verify.py +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh/py.typed +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh.egg-info/dependency_links.txt +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh.egg-info/requires.txt +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/src/datumesh.egg-info/top_level.txt +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_bitflips.py +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_blob.py +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_jcs.py +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_keys.py +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_spec_0_2_1.py +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_spec_0_3_0.py +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_unicode.py +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_units.py +0 -0
- {datumesh-0.4.2 → datumesh-0.5.0}/tests/test_uri.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datumesh
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Verify Datumesh receipts (spec-0.
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Verify Datumesh receipts (spec-0.5.0): RFC 8785 canonical JSON, Ed25519 signatures, slice-as-lineage, blobs, units, cell URIs, response envelopes (pending jobs, pins, slice pages, batches, resolved calls), key documents with declarations, mesh documents (loopback URLs for unrooted hosts), licence inheritance and entitlement, classes, key normalisation, object keys, describe answers and define checks.
|
|
5
5
|
Author: Datumesh
|
|
6
6
|
License-Expression: Apache-2.0
|
|
7
7
|
Project-URL: Homepage, https://datumesh.com
|
|
8
8
|
Project-URL: Repository, https://github.com/datumesh-com/datumesh-verify-py
|
|
9
|
-
Project-URL: Specification (spec-0.
|
|
10
|
-
Keywords: datumesh,spec-0.
|
|
9
|
+
Project-URL: Specification (spec-0.5.0), https://github.com/datumesh-com/datumesh-spec/blob/spec-0.5.0/SPEC.md
|
|
10
|
+
Keywords: datumesh,spec-0.5.0,receipt,verification,ed25519,rfc8785,jcs,canonical-json
|
|
11
11
|
Classifier: Development Status :: 3 - Alpha
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
@@ -27,11 +27,11 @@ Dynamic: license-file
|
|
|
27
27
|
|
|
28
28
|
# datumesh
|
|
29
29
|
|
|
30
|
-
Verify [Datumesh](https://datumesh.com) receipts in Python. A receipt is a signed answer for one addressable data cell; this library checks its canonical form (RFC 8785 JSON) and its Ed25519 signature against the signer's published keys, and optionally the recomputation checks (function descriptor, input receipts and slice manifests, and a value you computed) and the content of a blob value, as the [Datumesh specification](https://github.com/datumesh-com/datumesh-spec/blob/spec-0.
|
|
30
|
+
Verify [Datumesh](https://datumesh.com) receipts in Python. A receipt is a signed answer for one addressable data cell; this library checks its canonical form (RFC 8785 JSON) and its Ed25519 signature against the signer's published keys, and optionally the recomputation checks (function descriptor, input receipts and slice manifests, and a value you computed) and the content of a blob value, as the [Datumesh specification](https://github.com/datumesh-com/datumesh-spec/blob/spec-0.5.0/SPEC.md) (revision `spec-0.5.0`) defines. It also checks a mesh's response envelope (including `pending` jobs), resolves the unit of a cell's value, checks a host's mesh document for cross-mesh use, and, separately from verification, checks a computed receipt's licence against the inheritance rule (the entitlement check).
|
|
31
31
|
|
|
32
32
|
```sh
|
|
33
33
|
pip install datumesh
|
|
34
|
-
git clone --depth 1 --branch spec-0.
|
|
34
|
+
git clone --depth 1 --branch spec-0.5.0 https://github.com/datumesh-com/datumesh-spec && cd datumesh-spec
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
```python
|
|
@@ -52,16 +52,29 @@ Website: https://datumesh.com · Specification: https://github.com/datumesh-com/
|
|
|
52
52
|
- `verify(receipt, keys, recompute=None, blob=None) -> VerifyResult`: runs the verification procedure of SPEC 10.1. Never raises for an invalid receipt or bad key material; the result has `ok`, `code` (`"OK"` or an error code), `level` (`"signed"` or `"recomputed"`), `hash`, `key`, `detail` and `path`. Two codes come with success, at level `signed` (SPEC 10.1 step 7.6): `"E_EVAL_UNAVAILABLE"` when you pass `recompute` without a `value` because you could not evaluate the function (the other recomputation checks still run first), and `"E_INPUT_SUPERSEDED"` when your value differs but the evaluation's `lineage` shows that an input was replaced by a later version (the receipt is historical, not wrong). It uses no network: keys are an argument (a key document, a `KeysResult`, a `{key id: public key}` mapping, or a list of these). `recompute` takes the descriptor (leave it out if you have none; `None` is a JSON null and fails the hash check), the input receipts (slice members included), the manifests of slice entries (`slice:<slice id>:<members hash>`), the value you computed and optionally the evaluation's lineage (the library does not evaluate functions, formulas included); `blob` takes the content bytes of a blob value, whose length and SHA-256 are then checked (step 7.7, reported as `blob_checked`).
|
|
53
53
|
- `check_envelope(status, body, keys, request) -> EnvelopeResult`: checks a mesh's response to `https://<mesh>/cell/...` (SPEC 5.5), verifies the receipt inside it, and checks that it answers your request (`request` is `{"cell_id": ..., "asof": ...}`, `asof` only if you asked for one; the receipt's `asof` must be at or before it, for `ok` and `stale` alike). A `not_covered` envelope may carry a formula evaluation `fault` (exposed as `fault`). The envelope itself is unsigned: `stale`, `superseded_by`, a `verified` label and the currency `ok` asserts remain the mesh's claims.
|
|
54
54
|
- Jobs (spec-0.4.1): a `pending` envelope (HTTP 202) carries `job`, `estimate_seconds` and `poll` (exposed on the result) and echoes your request, which must match. When you poll, pass the original request plus `"job": <the job id>`: the answer is checked against the original cell and `asof`, never the job URL, and only then may it be `bad_request` with `E_JOB_UNKNOWN` (ask again).
|
|
55
|
-
- Pins, trust and the mesh asked (spec-0.4.
|
|
56
|
-
- Slice pages (spec-0.4.
|
|
57
|
-
- `check_batch(status, body, keys, requests) -> BatchResult` (spec-0.4.
|
|
55
|
+
- Pins, trust and the mesh asked (spec-0.4.3): a request description may carry `fn_hash` (you pinned the function: `parse_uri`/`format_uri` handle `fn_hash=`) and `mesh` (the host you asked). Answers to a pinned request must echo the pin; with `mesh`, `computed` (this mesh evaluated and signed it) is checked against the signer, and `poll` against the host. Without `mesh`, comparing them with the host you asked is your own check.
|
|
56
|
+
- Slice pages (spec-0.4.3): `check_envelope` with `{"slice_id", "asof"?, "fn_hash"?, "mesh"?, "after"?}` checks a page of the slice endpoint: every member's receipt, its membership, `asof`, trust and key order (`result.members`, `result.next`).
|
|
57
|
+
- `check_batch(status, body, keys, requests) -> BatchResult` (spec-0.4.3): the answer to `POST /cells`, checked as a whole, then item by item against each request (`result.items`); a refusal of the whole batch gives `result.outcome`.
|
|
58
58
|
- `resolve_mesh(host, keys, mesh) -> MeshResult`: checks a host's key document and mesh document (`/.well-known/datumesh-mesh.json`, SPEC 9.6) together, given each response as a `(status, body)` pair; the result has `bindings` and the known `endpoints`, or `E_KEY_UNAVAILABLE`, `E_KEY_DOC` or `E_MESH_DOC`. It uses no network, and verification never needs it.
|
|
59
|
+
- Licences (spec-0.4.3): `inherit_licence(licences, declarations, fn, issued, mesh_licence) -> LicenceResult` applies the inheritance rule of SPEC 8.5 (the strictest input licence wins; a restricted licence only where the function's publisher declared it, open at `issued`) and gives `licence` (None for a refusal, which a mesh answers `unlicensed`) and `reason`. `check_entitlement(receipt, licences, keys) -> EntitlementResult` (SPEC 10.3) says whether a verified computed receipt's licence is the one the rule gives, with one of eight reasons; `keys` is the key document of the publisher's host (the host of `cell.ns`), and another host's is a `ValueError`. `input_licences(receipt, receipts, manifests=None, keys=None)` collects the input licences through the receipts and slice manifests you have. None of this is verification: a receipt that is not entitled is still signature-valid.
|
|
60
|
+
- Key documents may carry `declarations` (spec-0.4.3, SPEC 9.2): `KeysResult.declarations` maps each declared function name to its `Declaration` entries (`licence`, `valid_from`, `valid_to`). A malformed one rejects the whole document (`E_KEY_DOC`); otherwise they change nothing in verification.
|
|
61
|
+
- `E_FOREIGN_UNAVAILABLE` (spec-0.4.3): an `error` envelope at HTTP 502 saying another mesh could not be reached, with `detail` (exposed as `error_detail`): one of `FOREIGN_DETAILS` (`offline`, `network`, `dns`, `tls`, `timeout`, `refused`). `is_unrooted(namespace)` tells names under `.invalid` (SPEC 2.5), whose receipts verify only against a key document supplied out of band.
|
|
62
|
+
- Resolved calls and shadowing (spec-0.5.0, SPEC 5.5, 16.7, 16.9): `EnvelopeResult.resolved` is the `resolved` member of an answer to a short call (`call`, `resolutions` with `kind` one of `RESOLUTION_KINDS`, and `aliases_tx`), and `EnvelopeResult.shadows` says which class a name binds to another hash than its reference class. Both are `None` unless the envelope carries them. A `not_covered` envelope may carry `E_NAMESPACE_SUSPENDED` (SPEC 9.9), and `E_FORMULA_KEY` and `E_FORMULA_DISPATCH_KEY` are formula faults (`FAULT_CODES`).
|
|
63
|
+
- Loopback mesh documents (spec-0.5.0, SPEC 9.6): for an unrooted host (a name under `.invalid`), `resolve_mesh` accepts `http://` URLs on `127.0.0.1`, `[::1]` or `localhost` (an optional port), all on one origin; a `pending` `poll` or a slice `next` on such a loopback address passes only when the request names that unrooted mesh (`mesh`). Rooted hosts stay https-only. A mesh document's `describe` endpoint is `MESH_ENDPOINTS["describe"]`.
|
|
64
|
+
- Classes (spec-0.5.0, SPEC 16.1, 16.2): `define_class(name, descriptor, *, functions=(), classes=(), definer) -> ClassDefinition` checks a class descriptor as its author writes it, flattens it onto its parent (`parent`, found by its class hash among `classes`), checks the flattened descriptor and its methods (`functions`, found by their function hashes), and returns `flattened`, `canonical` (JCS bytes) and `class_hash`. Its `DatumeshError` codes are the `E_CLASS_*` codes of SPEC 10.2 in SPEC 16.2's order, plus `E_CLASS_OWNER` when `name` is not under `definer`. `class_hash(descriptor) -> str` hashes a flattened descriptor; `canonicalize(d, "class")` gives its canonical bytes. A class name is a full name, not part of the hash.
|
|
65
|
+
- Function descriptors (spec-0.5.0, SPEC 7): a `string` argument may declare `classes` (an object key of one of those classes), `default: "asof"` on a valid-time date or instant, and `key_normalisation` (one rule of SPEC 16.4 other than `none`, which is written by absence); a `returns` object of type `string` may declare `classes`. Each changes no existing function hash.
|
|
66
|
+
- Key normalisation (spec-0.5.0, SPEC 16.4): `check_key_normalisation(rule, text) -> None` raises `E_KEY_NORMALISATION` unless `text` is canonical under `rule` (`KEY_NORMALISATIONS`, the closed list). It rejects, never repairs.
|
|
67
|
+
- Object keys (spec-0.5.0, SPEC 16.5): `parse_key(text) -> dict` decodes an object's key encoding (`{"class", "keys"}`, each key `{"type", "value"}` or a nested decoded object; `E_KEY_ENCODING` for anything not canonical, the whole encoding at most 1024 bytes), `format_key(decoded)` is its inverse, `object_json(key, class_hash)` the canonical object JSON (a class-typed key is the typed string of its encoding), `object_id(key, class_hash)` its SHA-256 and `short_id(id)` the first 12 hex digits of a 64-digit id.
|
|
68
|
+
- Describe answers (spec-0.5.0, SPEC 16.8): `check_describe(answer, classes, functions=None) -> DescribeResult` checks a mesh's answer about a class or an object for its own consistency (hashes, ids, call form, methods, `description`, `same_as`) and against the classes you hold (found by their hash) and, for a class's methods, the method function descriptors you hold (`functions`, found by their function hash; without them, `args` and `returns` are not checked): `ok`, `code` (`E_DESCRIBE` for a failure, or a JSON parse code), `detail`, `path`, and `kind` (`"class"` or `"object"`).
|
|
69
|
+
- Define's check of a function (spec-0.5.0, SPEC 2.5): `check_define(name, descriptor) -> FunctionDefinition` checks the name and the descriptor as define does, and that a rooted function reads no unrooted one (`E_UNROOTED_INPUT`); it gives `canonical` and `fn_hash`.
|
|
59
70
|
- `resolved_unit(descriptor, cell) -> str | None`: the unit of a cell's value, such as `USD/EUR` (SPEC 7.1, units.md).
|
|
60
71
|
- `keys_from_response(host, status, body) -> KeysResult`: interprets a response for `https://<host>/.well-known/datumesh.json` (SPEC 9.3).
|
|
61
72
|
- `fetch_keys(host, timeout=10.0) -> KeysResult`: the only function that uses the network. One GET, TLS verified, redirects not followed.
|
|
62
73
|
- `canonicalize(obj, kind=None) -> bytes`, `cell_hash`, `function_hash`, `receipt_hash`, `receipt_payload`, `jcs`: validation, RFC 8785 canonical bytes and SHA-256 hashes. Kinds `"slice"` and `"manifest"`, with `slice_id`, `members_hash` and `slice_entry`, cover slice objects and manifests (SPEC 4.4, 15.13).
|
|
63
74
|
- `parse_uri(uri, signature)`, `format_uri(ref)`, `uri_to_https(uri, mesh)`: cell URIs (SPEC 5).
|
|
64
|
-
- `DatumeshError` (with `.code` and `.path`), `ERROR_CODES`, `SUCCESS_CODES`, `FAULT_CODES`, `SPEC_VERSION`, `UNITS`, `CURRENCIES`.
|
|
75
|
+
- `DatumeshError` (with `.code` and `.path`), `ERROR_CODES`, `SUCCESS_CODES`, `FAULT_CODES`, `FOREIGN_DETAILS`, `REASONS`, `SPEC_VERSION`, `UNITS`, `CURRENCIES`, `KEY_NORMALISATIONS`, `RESOLUTION_KINDS`.
|
|
76
|
+
|
|
77
|
+
Changes are listed in [CHANGELOG.md](CHANGELOG.md).
|
|
65
78
|
|
|
66
79
|
Requires Python 3.10 or later and PyNaCl. String rules use Unicode 15.1 tables shipped with the package, so results do not depend on the Python version's own Unicode database; the unit list is copied from the specification's `units.md`. Choosing which publishers to trust is up to you: a valid signature only means the key holder made the statement.
|
|
67
80
|
|
datumesh-0.5.0/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# datumesh
|
|
2
|
+
|
|
3
|
+
Verify [Datumesh](https://datumesh.com) receipts in Python. A receipt is a signed answer for one addressable data cell; this library checks its canonical form (RFC 8785 JSON) and its Ed25519 signature against the signer's published keys, and optionally the recomputation checks (function descriptor, input receipts and slice manifests, and a value you computed) and the content of a blob value, as the [Datumesh specification](https://github.com/datumesh-com/datumesh-spec/blob/spec-0.5.0/SPEC.md) (revision `spec-0.5.0`) defines. It also checks a mesh's response envelope (including `pending` jobs), resolves the unit of a cell's value, checks a host's mesh document for cross-mesh use, and, separately from verification, checks a computed receipt's licence against the inheritance rule (the entitlement check).
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
pip install datumesh
|
|
7
|
+
git clone --depth 1 --branch spec-0.5.0 https://github.com/datumesh-com/datumesh-spec && cd datumesh-spec
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
import json
|
|
12
|
+
from datumesh import keys_from_response, verify
|
|
13
|
+
|
|
14
|
+
vector = json.load(open("vectors/receipt/stored-number.json"))
|
|
15
|
+
key_doc = vector["keys"]["rates.example.org"]["https"]["body"] # served at https://rates.example.org/.well-known/datumesh.json
|
|
16
|
+
keys = keys_from_response("rates.example.org", 200, key_doc)
|
|
17
|
+
result = verify(vector["input"], keys)
|
|
18
|
+
print(result.ok, result.level, result.key) # True signed rates.example.org#d712b32a2cc5cb7c
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Website: https://datumesh.com · Specification: https://github.com/datumesh-com/datumesh-spec
|
|
22
|
+
|
|
23
|
+
## API
|
|
24
|
+
|
|
25
|
+
- `verify(receipt, keys, recompute=None, blob=None) -> VerifyResult`: runs the verification procedure of SPEC 10.1. Never raises for an invalid receipt or bad key material; the result has `ok`, `code` (`"OK"` or an error code), `level` (`"signed"` or `"recomputed"`), `hash`, `key`, `detail` and `path`. Two codes come with success, at level `signed` (SPEC 10.1 step 7.6): `"E_EVAL_UNAVAILABLE"` when you pass `recompute` without a `value` because you could not evaluate the function (the other recomputation checks still run first), and `"E_INPUT_SUPERSEDED"` when your value differs but the evaluation's `lineage` shows that an input was replaced by a later version (the receipt is historical, not wrong). It uses no network: keys are an argument (a key document, a `KeysResult`, a `{key id: public key}` mapping, or a list of these). `recompute` takes the descriptor (leave it out if you have none; `None` is a JSON null and fails the hash check), the input receipts (slice members included), the manifests of slice entries (`slice:<slice id>:<members hash>`), the value you computed and optionally the evaluation's lineage (the library does not evaluate functions, formulas included); `blob` takes the content bytes of a blob value, whose length and SHA-256 are then checked (step 7.7, reported as `blob_checked`).
|
|
26
|
+
- `check_envelope(status, body, keys, request) -> EnvelopeResult`: checks a mesh's response to `https://<mesh>/cell/...` (SPEC 5.5), verifies the receipt inside it, and checks that it answers your request (`request` is `{"cell_id": ..., "asof": ...}`, `asof` only if you asked for one; the receipt's `asof` must be at or before it, for `ok` and `stale` alike). A `not_covered` envelope may carry a formula evaluation `fault` (exposed as `fault`). The envelope itself is unsigned: `stale`, `superseded_by`, a `verified` label and the currency `ok` asserts remain the mesh's claims.
|
|
27
|
+
- Jobs (spec-0.4.1): a `pending` envelope (HTTP 202) carries `job`, `estimate_seconds` and `poll` (exposed on the result) and echoes your request, which must match. When you poll, pass the original request plus `"job": <the job id>`: the answer is checked against the original cell and `asof`, never the job URL, and only then may it be `bad_request` with `E_JOB_UNKNOWN` (ask again).
|
|
28
|
+
- Pins, trust and the mesh asked (spec-0.4.3): a request description may carry `fn_hash` (you pinned the function: `parse_uri`/`format_uri` handle `fn_hash=`) and `mesh` (the host you asked). Answers to a pinned request must echo the pin; with `mesh`, `computed` (this mesh evaluated and signed it) is checked against the signer, and `poll` against the host. Without `mesh`, comparing them with the host you asked is your own check.
|
|
29
|
+
- Slice pages (spec-0.4.3): `check_envelope` with `{"slice_id", "asof"?, "fn_hash"?, "mesh"?, "after"?}` checks a page of the slice endpoint: every member's receipt, its membership, `asof`, trust and key order (`result.members`, `result.next`).
|
|
30
|
+
- `check_batch(status, body, keys, requests) -> BatchResult` (spec-0.4.3): the answer to `POST /cells`, checked as a whole, then item by item against each request (`result.items`); a refusal of the whole batch gives `result.outcome`.
|
|
31
|
+
- `resolve_mesh(host, keys, mesh) -> MeshResult`: checks a host's key document and mesh document (`/.well-known/datumesh-mesh.json`, SPEC 9.6) together, given each response as a `(status, body)` pair; the result has `bindings` and the known `endpoints`, or `E_KEY_UNAVAILABLE`, `E_KEY_DOC` or `E_MESH_DOC`. It uses no network, and verification never needs it.
|
|
32
|
+
- Licences (spec-0.4.3): `inherit_licence(licences, declarations, fn, issued, mesh_licence) -> LicenceResult` applies the inheritance rule of SPEC 8.5 (the strictest input licence wins; a restricted licence only where the function's publisher declared it, open at `issued`) and gives `licence` (None for a refusal, which a mesh answers `unlicensed`) and `reason`. `check_entitlement(receipt, licences, keys) -> EntitlementResult` (SPEC 10.3) says whether a verified computed receipt's licence is the one the rule gives, with one of eight reasons; `keys` is the key document of the publisher's host (the host of `cell.ns`), and another host's is a `ValueError`. `input_licences(receipt, receipts, manifests=None, keys=None)` collects the input licences through the receipts and slice manifests you have. None of this is verification: a receipt that is not entitled is still signature-valid.
|
|
33
|
+
- Key documents may carry `declarations` (spec-0.4.3, SPEC 9.2): `KeysResult.declarations` maps each declared function name to its `Declaration` entries (`licence`, `valid_from`, `valid_to`). A malformed one rejects the whole document (`E_KEY_DOC`); otherwise they change nothing in verification.
|
|
34
|
+
- `E_FOREIGN_UNAVAILABLE` (spec-0.4.3): an `error` envelope at HTTP 502 saying another mesh could not be reached, with `detail` (exposed as `error_detail`): one of `FOREIGN_DETAILS` (`offline`, `network`, `dns`, `tls`, `timeout`, `refused`). `is_unrooted(namespace)` tells names under `.invalid` (SPEC 2.5), whose receipts verify only against a key document supplied out of band.
|
|
35
|
+
- Resolved calls and shadowing (spec-0.5.0, SPEC 5.5, 16.7, 16.9): `EnvelopeResult.resolved` is the `resolved` member of an answer to a short call (`call`, `resolutions` with `kind` one of `RESOLUTION_KINDS`, and `aliases_tx`), and `EnvelopeResult.shadows` says which class a name binds to another hash than its reference class. Both are `None` unless the envelope carries them. A `not_covered` envelope may carry `E_NAMESPACE_SUSPENDED` (SPEC 9.9), and `E_FORMULA_KEY` and `E_FORMULA_DISPATCH_KEY` are formula faults (`FAULT_CODES`).
|
|
36
|
+
- Loopback mesh documents (spec-0.5.0, SPEC 9.6): for an unrooted host (a name under `.invalid`), `resolve_mesh` accepts `http://` URLs on `127.0.0.1`, `[::1]` or `localhost` (an optional port), all on one origin; a `pending` `poll` or a slice `next` on such a loopback address passes only when the request names that unrooted mesh (`mesh`). Rooted hosts stay https-only. A mesh document's `describe` endpoint is `MESH_ENDPOINTS["describe"]`.
|
|
37
|
+
- Classes (spec-0.5.0, SPEC 16.1, 16.2): `define_class(name, descriptor, *, functions=(), classes=(), definer) -> ClassDefinition` checks a class descriptor as its author writes it, flattens it onto its parent (`parent`, found by its class hash among `classes`), checks the flattened descriptor and its methods (`functions`, found by their function hashes), and returns `flattened`, `canonical` (JCS bytes) and `class_hash`. Its `DatumeshError` codes are the `E_CLASS_*` codes of SPEC 10.2 in SPEC 16.2's order, plus `E_CLASS_OWNER` when `name` is not under `definer`. `class_hash(descriptor) -> str` hashes a flattened descriptor; `canonicalize(d, "class")` gives its canonical bytes. A class name is a full name, not part of the hash.
|
|
38
|
+
- Function descriptors (spec-0.5.0, SPEC 7): a `string` argument may declare `classes` (an object key of one of those classes), `default: "asof"` on a valid-time date or instant, and `key_normalisation` (one rule of SPEC 16.4 other than `none`, which is written by absence); a `returns` object of type `string` may declare `classes`. Each changes no existing function hash.
|
|
39
|
+
- Key normalisation (spec-0.5.0, SPEC 16.4): `check_key_normalisation(rule, text) -> None` raises `E_KEY_NORMALISATION` unless `text` is canonical under `rule` (`KEY_NORMALISATIONS`, the closed list). It rejects, never repairs.
|
|
40
|
+
- Object keys (spec-0.5.0, SPEC 16.5): `parse_key(text) -> dict` decodes an object's key encoding (`{"class", "keys"}`, each key `{"type", "value"}` or a nested decoded object; `E_KEY_ENCODING` for anything not canonical, the whole encoding at most 1024 bytes), `format_key(decoded)` is its inverse, `object_json(key, class_hash)` the canonical object JSON (a class-typed key is the typed string of its encoding), `object_id(key, class_hash)` its SHA-256 and `short_id(id)` the first 12 hex digits of a 64-digit id.
|
|
41
|
+
- Describe answers (spec-0.5.0, SPEC 16.8): `check_describe(answer, classes, functions=None) -> DescribeResult` checks a mesh's answer about a class or an object for its own consistency (hashes, ids, call form, methods, `description`, `same_as`) and against the classes you hold (found by their hash) and, for a class's methods, the method function descriptors you hold (`functions`, found by their function hash; without them, `args` and `returns` are not checked): `ok`, `code` (`E_DESCRIBE` for a failure, or a JSON parse code), `detail`, `path`, and `kind` (`"class"` or `"object"`).
|
|
42
|
+
- Define's check of a function (spec-0.5.0, SPEC 2.5): `check_define(name, descriptor) -> FunctionDefinition` checks the name and the descriptor as define does, and that a rooted function reads no unrooted one (`E_UNROOTED_INPUT`); it gives `canonical` and `fn_hash`.
|
|
43
|
+
- `resolved_unit(descriptor, cell) -> str | None`: the unit of a cell's value, such as `USD/EUR` (SPEC 7.1, units.md).
|
|
44
|
+
- `keys_from_response(host, status, body) -> KeysResult`: interprets a response for `https://<host>/.well-known/datumesh.json` (SPEC 9.3).
|
|
45
|
+
- `fetch_keys(host, timeout=10.0) -> KeysResult`: the only function that uses the network. One GET, TLS verified, redirects not followed.
|
|
46
|
+
- `canonicalize(obj, kind=None) -> bytes`, `cell_hash`, `function_hash`, `receipt_hash`, `receipt_payload`, `jcs`: validation, RFC 8785 canonical bytes and SHA-256 hashes. Kinds `"slice"` and `"manifest"`, with `slice_id`, `members_hash` and `slice_entry`, cover slice objects and manifests (SPEC 4.4, 15.13).
|
|
47
|
+
- `parse_uri(uri, signature)`, `format_uri(ref)`, `uri_to_https(uri, mesh)`: cell URIs (SPEC 5).
|
|
48
|
+
- `DatumeshError` (with `.code` and `.path`), `ERROR_CODES`, `SUCCESS_CODES`, `FAULT_CODES`, `FOREIGN_DETAILS`, `REASONS`, `SPEC_VERSION`, `UNITS`, `CURRENCIES`, `KEY_NORMALISATIONS`, `RESOLUTION_KINDS`.
|
|
49
|
+
|
|
50
|
+
Changes are listed in [CHANGELOG.md](CHANGELOG.md).
|
|
51
|
+
|
|
52
|
+
Requires Python 3.10 or later and PyNaCl. String rules use Unicode 15.1 tables shipped with the package, so results do not depend on the Python version's own Unicode database; the unit list is copied from the specification's `units.md`. Choosing which publishers to trust is up to you: a valid signature only means the key holder made the statement.
|
|
53
|
+
|
|
54
|
+
Licence: Apache-2.0.
|
|
@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "datumesh"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "Verify Datumesh receipts (spec-0.
|
|
7
|
+
version = "0.5.0"
|
|
8
|
+
description = "Verify Datumesh receipts (spec-0.5.0): RFC 8785 canonical JSON, Ed25519 signatures, slice-as-lineage, blobs, units, cell URIs, response envelopes (pending jobs, pins, slice pages, batches, resolved calls), key documents with declarations, mesh documents (loopback URLs for unrooted hosts), licence inheritance and entitlement, classes, key normalisation, object keys, describe answers and define checks."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
license = "Apache-2.0"
|
|
12
12
|
license-files = ["LICENSE"]
|
|
13
13
|
authors = [{ name = "Datumesh" }]
|
|
14
|
-
keywords = ["datumesh", "spec-0.
|
|
14
|
+
keywords = ["datumesh", "spec-0.5.0", "receipt", "verification", "ed25519", "rfc8785", "jcs", "canonical-json"]
|
|
15
15
|
classifiers = [
|
|
16
16
|
"Development Status :: 3 - Alpha",
|
|
17
17
|
"Intended Audience :: Developers",
|
|
@@ -30,7 +30,7 @@ dependencies = ["pynacl>=1.5.0"]
|
|
|
30
30
|
[project.urls]
|
|
31
31
|
Homepage = "https://datumesh.com"
|
|
32
32
|
Repository = "https://github.com/datumesh-com/datumesh-verify-py"
|
|
33
|
-
"Specification (spec-0.
|
|
33
|
+
"Specification (spec-0.5.0)" = "https://github.com/datumesh-com/datumesh-spec/blob/spec-0.5.0/SPEC.md"
|
|
34
34
|
|
|
35
35
|
[tool.setuptools.packages.find]
|
|
36
36
|
where = ["src"]
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
"""datumesh: verify Datumesh receipts (signed answers for addressable data cells).
|
|
2
2
|
|
|
3
|
-
Implements the Datumesh protocol specification, revision ``spec-0.
|
|
3
|
+
Implements the Datumesh protocol specification, revision ``spec-0.5.0``
|
|
4
4
|
(https://github.com/datumesh-com/datumesh-spec), as a verifier library: RFC 8785
|
|
5
5
|
canonical JSON, SHA-256 hashes, Ed25519 receipt signatures, blob content, units,
|
|
6
|
-
cell URIs (pins included), response envelopes
|
|
7
|
-
documents, and slice-as-lineage
|
|
8
|
-
slice objects and manifests), and, separately from verification, cross-mesh
|
|
9
|
-
resolution of a host's mesh document (:func:`resolve_mesh
|
|
6
|
+
cell URIs (pins included), response envelopes (with what a short call resolved and
|
|
7
|
+
which class a name shadows), slice pages and batches, key documents, and slice-as-lineage
|
|
8
|
+
(slice entries, slice objects and manifests), and, separately from verification, cross-mesh
|
|
9
|
+
resolution of a host's mesh document (:func:`resolve_mesh`, with loopback URLs for an unrooted
|
|
10
|
+
host), the publisher's ``declarations`` in key documents, licence inheritance (:func:`inherit_licence`), the
|
|
11
|
+
licence entitlement check (:func:`check_entitlement`) and unrooted namespaces
|
|
12
|
+
(:func:`is_unrooted`). Since spec-0.5.0 also the checks of classes (:func:`define_class`,
|
|
13
|
+
:func:`class_hash`), key normalisation (:func:`check_key_normalisation`), object keys
|
|
14
|
+
(:func:`parse_key`, :func:`object_id`, :func:`short_id`), describe answers (:func:`check_describe`) and
|
|
15
|
+
define's check of a function (:func:`check_define`). It evaluates no
|
|
10
16
|
function, formulas included: the evaluated value comes from the caller. Only
|
|
11
17
|
:func:`fetch_keys` touches the network.
|
|
12
18
|
"""
|
|
@@ -16,18 +22,25 @@ from __future__ import annotations
|
|
|
16
22
|
import hashlib
|
|
17
23
|
from typing import Any, Dict, Optional
|
|
18
24
|
|
|
19
|
-
from . import _model, _units
|
|
20
|
-
from .
|
|
21
|
-
from .
|
|
25
|
+
from . import _classes, _lexical, _model, _units
|
|
26
|
+
from ._classes import ClassDefinition, class_hash, define_class
|
|
27
|
+
from ._define import FunctionDefinition, check_define
|
|
28
|
+
from ._describe import DescribeResult, check_describe
|
|
29
|
+
from ._envelope import RESOLUTION_KINDS, TRUST, BatchResult, EnvelopeResult, MemberResult, check_batch, check_envelope
|
|
30
|
+
from ._errors import ERROR_CODES, FAULT_CODES, FOREIGN_DETAILS, SUCCESS_CODES, DatumeshError, fail
|
|
22
31
|
from ._json import jcs_bytes
|
|
23
|
-
from ._keys import Key, KeysResult, fetch_keys, key_id, keys_from_response
|
|
32
|
+
from ._keys import Declaration, Key, KeysResult, fetch_keys, key_id, keys_from_response
|
|
33
|
+
from ._licence import (ATTRIBUTION, ENTITLED_REASONS, PUBLIC_DOMAIN, REASONS, SHARE_ALIKE, EntitlementResult,
|
|
34
|
+
LicenceResult, check_entitlement, inherit_licence, input_licences, is_unrooted, licence_tier)
|
|
24
35
|
from ._mesh import MESH_ENDPOINTS, MESH_WELL_KNOWN_PATH, MeshResult, resolve_mesh
|
|
36
|
+
from ._normalisation import check_key_normalisation
|
|
37
|
+
from ._objectkey import format_key, object_id, object_json, parse_key, short_id
|
|
25
38
|
from ._uri import format_uri, parse_uri, uri_to_https
|
|
26
39
|
from ._verify import VerifyResult, load, payload_bytes, verify
|
|
27
40
|
|
|
28
|
-
__version__ = "0.
|
|
41
|
+
__version__ = "0.5.0"
|
|
29
42
|
#: The specification revision this package conforms to.
|
|
30
|
-
SPEC_VERSION = "spec-0.
|
|
43
|
+
SPEC_VERSION = "spec-0.5.0"
|
|
31
44
|
#: Every literal unit listed in units.md (index units, ``index:<period>=<value>``, are a pattern and not listed).
|
|
32
45
|
UNITS = tuple(sorted(_units.LITERALS))
|
|
33
46
|
#: The currency codes of units.md 2.1.
|
|
@@ -35,10 +48,43 @@ CURRENCIES = _units.CURRENCIES
|
|
|
35
48
|
|
|
36
49
|
#: The kinds :func:`canonicalize` takes. ``slice`` (a slice object, SPEC 4.4) and ``manifest`` (SPEC 15.13)
|
|
37
50
|
#: are new in spec-0.4.0 and are never inferred.
|
|
38
|
-
KINDS = ("scalar", "cell", "ref", "function", "receipt", "slice", "manifest")
|
|
51
|
+
KINDS = ("scalar", "cell", "ref", "function", "receipt", "slice", "manifest", "class")
|
|
52
|
+
#: The key normalisation rules of 16.4, in their order (spec-0.5.0): a closed list.
|
|
53
|
+
KEY_NORMALISATIONS = _lexical.KEY_NORMALISATIONS
|
|
54
|
+
|
|
55
|
+
_model.VALIDATORS["class"] = _classes.validate_class
|
|
39
56
|
|
|
40
57
|
__all__ = [
|
|
41
58
|
"SPEC_VERSION",
|
|
59
|
+
"ATTRIBUTION",
|
|
60
|
+
"ClassDefinition",
|
|
61
|
+
"Declaration",
|
|
62
|
+
"DescribeResult",
|
|
63
|
+
"FunctionDefinition",
|
|
64
|
+
"KEY_NORMALISATIONS",
|
|
65
|
+
"RESOLUTION_KINDS",
|
|
66
|
+
"check_define",
|
|
67
|
+
"check_describe",
|
|
68
|
+
"check_key_normalisation",
|
|
69
|
+
"class_hash",
|
|
70
|
+
"define_class",
|
|
71
|
+
"format_key",
|
|
72
|
+
"object_id",
|
|
73
|
+
"object_json",
|
|
74
|
+
"parse_key",
|
|
75
|
+
"short_id",
|
|
76
|
+
"ENTITLED_REASONS",
|
|
77
|
+
"EntitlementResult",
|
|
78
|
+
"FOREIGN_DETAILS",
|
|
79
|
+
"LicenceResult",
|
|
80
|
+
"PUBLIC_DOMAIN",
|
|
81
|
+
"REASONS",
|
|
82
|
+
"SHARE_ALIKE",
|
|
83
|
+
"check_entitlement",
|
|
84
|
+
"inherit_licence",
|
|
85
|
+
"input_licences",
|
|
86
|
+
"is_unrooted",
|
|
87
|
+
"licence_tier",
|
|
42
88
|
"BatchResult",
|
|
43
89
|
"MemberResult",
|
|
44
90
|
"TRUST",
|