Add a Python port of the KEZ CLI under python/, mirroring the Rust and Node implementations command-for-command and byte-for-byte: - Pure-Python JCS (RFC 8785), BIP-340 Schnorr, and Bech32; cryptography for Ed25519 and zstandard for the compact zstd framing. - Full CLI: identity new, claim create/dns, verify file, and sigchain add/revoke/show/export. Wire Python into crosstest.sh with 35 new scenarios covering Python against both Rust and Node, in every direction, across all wire formats, both key types, DNS proofs, and sigchains (incl. JSONL byte parity). All 55 scenarios pass. Update root README and .gitignore for the new implementation.
4.0 KiB
KEZ — Python Implementation
KEZ is a portable, decentralized identity graph. It lets one person say:
"These accounts, keys, domains, and identities are all me."
…without depending on any central authority. Every connection is proven by a
signature against a key the user already controls. The protocol is specified in
../SPEC.md; this directory is the Python implementation of that
spec.
It is wire-compatible with the Rust and Node
implementations: a claim signed here verifies there and vice versa, in every
direction. The repo-root crosstest.sh proves it.
What's in this directory
python/
├── pyproject.toml Package metadata + entry point (`kez`)
├── requirements.txt Runtime deps (cryptography, zstandard)
├── kez_cli.py Standalone launcher (used by ../crosstest.sh)
└── kez/
├── jcs.py RFC 8785 JSON canonicalization
├── bech32.py Bech32 (nsec/npub) encode/decode
├── schnorr.py Pure-Python BIP-340 Schnorr over secp256k1
├── identity.py `system:identifier` parsing + normalization
├── keys.py NostrSecret / Ed25519Secret signers + verification
├── envelope.py Envelope, claim & sigchain-event payloads, sign/verify
├── encodings.py JSON / compact (kez:z1:) / markdown / DNS / JSONL bundle
├── sigchain.py Append-only signed sigchain + on-disk storage
├── channels.py parse_proof across all four wire encodings
└── cli.py The `kez` command-line interface
Setup
cd python
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
Then run the CLI either through the launcher or the installed entry point:
.venv/bin/python kez_cli.py identity new
# or, after `.venv/bin/pip install -e .`:
.venv/bin/kez identity new
Crypto stack
| Concern | Choice | Why |
|---|---|---|
| JCS (RFC 8785) | hand-rolled (jcs.py) |
KEZ payloads are strings/ints/objects only; a tiny dependency-free canonicalizer guarantees byte-identical output |
| secp256k1 Schnorr (BIP-340) | pure-Python reference (schnorr.py) |
the native coincurve/secp256k1 bindings fail to build on recent CPython; signing fixed-size digests is fast enough for a CLI. Signs with zero aux-rand to match Rust/Node exactly |
| Ed25519 (RFC 8032) | cryptography |
well-maintained, ships wheels |
| zstd | zstandard |
level 3, matching the other impls; decompressobj handles frames without a content-size header |
| Bech32 | hand-rolled (bech32.py) |
the BIP-173 reference is small and avoids a dependency |
All signing is deterministic, so the same claim signs identically every time.
CLI reference
kez identity new [--key-type nostr|ed25519]
kez claim create <subject> (--nsec <nsec> | --ed25519-seed <hex>)
[--format json|compact|markdown] [--out <path>]
kez claim dns <domain> (--nsec <nsec> | --ed25519-seed <hex>)
kez verify file <path>
kez sigchain add <subject> (--nsec | --ed25519-seed) [--proof-url <url>]
kez sigchain revoke <subject> (--nsec | --ed25519-seed)
kez sigchain show [--primary <id> | --nsec | --ed25519-seed]
kez sigchain export [--primary <id> | --nsec | --ed25519-seed]
[--format jsonl|compact] [--out <path>]
Sigchain state lives in ~/.kez/sigchains/<primary-with-colons-as-underscores>.jsonl
— the same paths the Rust and Node CLIs use, so chains built by one are
readable by the others.
What's not done yet
Matching the gap list in ../rust/README.md, the Python
CLI implements claim, verify file, and sigchain add/revoke/show/export.
Not yet ported: verify id channel resolution (network fetch), sigchain publish, and the rotate/add_device ops.
License
Dual-licensed under MIT or Apache-2.0.