Official SDK for the Grundnorm grounded source-of-law truth layer — resolve a legal norm by open identifier (ELI/ECLI) + date and verify its cryptographic seal locally.
Project description
grundnorm (Python SDK)
Resolve a legal norm by its open identifier (ELI/ECLI) and a date, and get back its canonical, sealed meaning as an independently verifiable signed object. The SDK recomputes the content hash and verifies every Ed25519 signature locally — you never have to trust the server.
- Deterministic, zero-LLM read path. Honest
not_foundinstead of a guess. - Point-in-time:
[validFrom, validUntil). - Python 3.9+. One dependency:
cryptography.
Install
pip install grundnorm
Use
from grundnorm import GrundnormClient
# Defaults to the public GDPR demonstrator. For a pilot:
# GrundnormClient(endpoint="https://.../api/grundnorm/resolve", api_key="nlk_...")
client = GrundnormClient()
r = client.resolve(
id="http://data.europa.eu/eli/reg/2016/679/art_5",
jurisdiction="EU",
at="2026-07-07", # omit for "today"
)
if r.status == "found":
print(r.norm["atoms"]) # subject / modality / action / condition / exception / scope + evidence
print(r.verification["ok"]) # True only if hash recomputes AND all signatures verify
resolve() verifies the seal by default. Skip with verify=False, or verify a stored envelope later:
from grundnorm import verify
v = verify(norm) # {"ok", "cryptographically_ok", "hash_ok", "view_consistent", "status_re_derivable", "signatures", "quorum", "attestation"}
v["ok"] proves cryptographic soundness, not trustworthiness. True iff the bytes, the view and
every signature check out (1-3 below) — exactly what a third party re-derives from the response alone.
It does not establish that the signers are independent institutions, that the record is externally
anchored, or that its meaning is jurist-correct. A demo record signed by one party is ok: True. Read
quorum and attestation before treating a record as authoritative.
What verify() re-derives (no trust required)
- Hash:
sha256(canonicalize(norm["canonical"]["content"]))equalsnorm["seal"]["contentHash"], wherecanonicalize= JSON with keys sorted recursively (UTF-16 order), no whitespace, UTF-8. - View integrity: the English
atoms/purposeare exactly what the sealedcanonical.contentprojects to (view_consistent);canonical.contentis the source of truth. - Signatures: each
seal.signatures[].signatureHexis a valid Ed25519 signature by that signer'spublicKeyHexover the UTF-8 bytes of theseal.contentHashhex string. - Status (when a classification witness is present): each atom's status
(
fixed/needs_review/for_the_court) re-executes from the deterministic rule applied to the sealed per-atom signals (status_re_derivable). A mismatch failsok.None= no witness (older seals) → not re-derivable, no penalty. Bounded: proves the rule was applied to sealed inputs; it does not prove the deontic decomposition is faithful to the article (that is the jurist gate).
What it REPORTS but does not prove
quorum— a signature count (valid_signatures,has_sovereign,meets= >=2 incl. one sovereign). Not proof of institutional independence.attestation— honest flags the SDK cannot prove:custody("unverified"for the demo — one party regardless of pinning),independence(as asserted in the record),ledger("unanchored"for demo/local seals),key_provenance("pinned_oob"when a signer's key matches the SDK's out-of-band snapshot, else"in_band_response"),classification("rule_reexecuted","not_present", or"rule_unsupported"when the witness declares a rule version this SDK does not implement).- Key pinning (
key_provenance, per-signaturepinned) is tamper-evidence of a snapshot, not proof of institutional independence: a single-party demo key is pinned yetcustodystays"unverified". It is not a key-transparency log.
Errors
- Nothing sealed for
(id, date)→ returnsNotFound(a normal outcome). - Bad key, bad input, or server error → raises
GrundnormError(.status,.code).
Demonstrator note: the demo corpus (GDPR sample) is signed by demo keys, not institutions, and its accuracy is not yet jurist-graded. See the project's
DEMO-TRUTHFULNESS.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file grundnorm-0.3.0.tar.gz.
File metadata
- Download URL: grundnorm-0.3.0.tar.gz
- Upload date:
- Size: 15.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0399d02330b07b705e66e461b0f66dff62d4ab5b1ab0a013de321f02ece42aa9
|
|
| MD5 |
515858ce0d2d9c972cf4c3f5f3bf49cf
|
|
| BLAKE2b-256 |
c16103d9db29954fb667768d789af8fbd33e4df4b11b92d871383f0a0d8e70dd
|
File details
Details for the file grundnorm-0.3.0-py3-none-any.whl.
File metadata
- Download URL: grundnorm-0.3.0-py3-none-any.whl
- Upload date:
- Size: 10.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0668fc4493a798e6a367769ce7f47c932b86c6440a79f756c82ff7aad107899a
|
|
| MD5 |
b9f23db138da02df812066c2ba6a2b75
|
|
| BLAKE2b-256 |
f6ae5bc7a581457011f7d51630f43e2cbb025c3c6f69b5dc03a525fac20bfd58
|