ODIN Open Proof Envelope (OPE): canonical JSON, CID hashing, Ed25519 signatures, verifiable envelopes & bundles
Project description
ODIN OPE (Open Proof Envelope)
Lightweight, production-grade primitives for verifiable AI↔AI / AI↔Human communication.
This package gives you deterministic canonical JSON, CID hashing, Ed25519 signatures,
and helpers to build/verify envelopes & export bundles used across the ODIN ecosystem.
- Deterministic canonical JSON → stable hashes and signatures
- CID:
sha256:<hex>computed over canonical bytes - Envelope signing: sign
"{cid}|{trace_id}|{ts}"with Ed25519 - Bundle signing: sign
"{bundle_cid}|{trace_id}|{exported_at}" - Signer backends: local seed (default). Optional extras for GCP KMS / AWS KMS / Azure KV.
Install
pip install odin-ope
# or with extras: pip install "odin-ope[gcpkms]" # etc.
Quick start
from odin_ope.signers import FileSigner
from odin_ope.envelope import build_envelope, sign_envelope
from odin_ope.verify import verify_envelope, build_jwks_for_signers
from odin_ope.bundle import build_bundle, sign_bundle, verify_bundle
# 1. Create a signer (Ed25519, deterministic seed for demo)
seed_b64u = "A"*43 # 32-byte Ed25519 seed as base64url (example placeholder)
signer = FileSigner(seed_b64u=seed_b64u) # kid derived from public key
# 2. Build and sign an envelope
payload = {"invoice_id": "INV-1", "amount": 100.25, "currency": "USD"}
env = build_envelope(payload, payload_type="openai.tooluse.invoice.v1", target_type="invoice.iso20022.v1")
signed_env = sign_envelope(env, signer)
# 3. Build JWKS and verify the envelope
jwks = build_jwks_for_signers([signer])
ok, reason = verify_envelope(signed_env, jwks)
assert ok, reason # (or use verify_envelope_or_raise for exceptions)
# 4. Create and sign a bundle
receipts = [
{"hop": 0, "receipt_hash": "h0", "prev_receipt_hash": None},
{"hop": 1, "receipt_hash": "h1", "prev_receipt_hash": "h0"}
]
bundle = build_bundle(trace_id=signed_env["trace_id"], receipts=receipts)
sig = sign_bundle(bundle, signer)
ok2, reason2 = verify_bundle(bundle, sig, jwks, kid=signer.kid)
assert ok2, reason2
See examples/end_to_end.py for a full, annotated script.
FAQ: Common errors
Q: ModuleNotFoundError: No module named 'odin_ope'
A: Make sure your PYTHONPATH includes the src directory, or install the package in editable mode: pip install -e .
Q: ValueError: Ed25519 seed must be 32 bytes
A: The seed for FileSigner must be a base64url-encoded string representing exactly 32 bytes.
Q: Envelope verification fails with cid_mismatch
A: The payload was likely modified after signing. Always verify before mutating envelopes.
Q: How do I generate a valid Ed25519 seed?
A: Use os.urandom(32) and encode with base64.urlsafe_b64encode(seed).rstrip(b'=').
Q: How do I run the tests?
A: PYTHONPATH=src pytest (or on Windows: $env:PYTHONPATH='src'; python -m pytest)
Reason Codes
| Code | Meaning |
|---|---|
| cid_mismatch | Payload hash changed after signing |
| missing_sig_or_kid | Envelope missing sender_sig or kid |
| kid_not_found | KID not present in supplied JWKS |
| signature_invalid | Signature verification failed |
| timestamp_skew | ts outside allowed skew window |
| schema_error | Structural or field validation error |
| not_yet_valid | not_before is in the future |
| expired | expires_at is in the past |
These map 1:1 to exception subclasses (see odin_ope.exceptions).
CLI
The package installs a odin-ope CLI:
odin-ope sign-envelope --payload payload.json --payload-type t --target-type tgt --seed <base64url-seed>
odin-ope verify-envelope --envelope env.json --jwks jwks.json --json --max-skew 300 --strict
Use --json for structured output and --no-skew to disable time skew checks.
Development
Install dev extras and run tests + type checking:
pip install -e .[dev]
pytest -q
mypy src/odin_ope
python scripts/gen_sbom.py --out sbom.json # generate CycloneDX SBOM
Programmatic version: import odin_ope; print(odin_ope.__version__).
\n+## Development
\n+Install dev extras and run tests + type checking:
\n+```
pip install -e .[dev]
pytest -q
mypy src/odin_ope
## Publishing
## Publishing
A GitHub Actions workflow is included (`.github/workflows/publish.yml`).
Create a repository, push, add `PYPI_API_TOKEN` secret, then tag a release like `v0.9.0`.
The workflow produces:
- Signed artifacts published to PyPI
- CycloneDX SBOM artifact (download from workflow run)
- Build provenance attestation (GitHub Attestations UI / API)
---
## License
Apache-2.0
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 odin_ope-0.9.0.tar.gz.
File metadata
- Download URL: odin_ope-0.9.0.tar.gz
- Upload date:
- Size: 16.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
96575b727e418d1a4544c4dbd7a10ecd4c2a0c1b94a7fc630421012f8e5546cc
|
|
| MD5 |
f9f5b8119526e45dd072dc535f7fdd16
|
|
| BLAKE2b-256 |
44eb9008ecff71a917591b58019d8b957bbbbb95a66f2846bec45b4ade87d208
|
File details
Details for the file odin_ope-0.9.0-py3-none-any.whl.
File metadata
- Download URL: odin_ope-0.9.0-py3-none-any.whl
- Upload date:
- Size: 15.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6f96cc3af5bb511d3b4e90269a0f5048c1d78f1118b7435eec312f4d8e773c34
|
|
| MD5 |
ec2a6809c796c39184e305398e22edab
|
|
| BLAKE2b-256 |
aa5dd2796487acda128af30881d0047f9c330f74f1a148840f27dda999d03301
|