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.
107 lines
4.0 KiB
Markdown
107 lines
4.0 KiB
Markdown
# 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`](../SPEC.md); this directory is the Python implementation of that
|
|
spec.
|
|
|
|
It is **wire-compatible** with the [Rust](../rust/) and [Node](../nodejs/)
|
|
implementations: a claim signed here verifies there and vice versa, in every
|
|
direction. The repo-root [`crosstest.sh`](../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
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
.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`](https://cryptography.io) | well-maintained, ships wheels |
|
|
| zstd | [`zstandard`](https://pypi.org/project/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`](../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.
|