docs(python): add TUTORIAL.md mirroring rust/nodejs + link from READMEs
Completes the parallel tutorial set across all three implementations.
Python now has the same friendly step-by-step walkthrough that the
Rust and Node sides have had since the original tutorial commits.
Python tutorial content mirrors the others 1:1, adapted for the
Python invocation style (.venv/bin/python kez_cli.py …), plus:
• Programmatic section uses Python imports (NostrSecret.from_nsec,
sign_claim, default_registry, etc.) instead of the TS imports
from the Node tutorial.
• Same "Recovery phrases" mini-chapter as rust/nodejs — both 12-word
AND 24-word are explained, with the entropy table, picking guide,
hardware-wallet-incompatibility callout, concrete backup advice
("pencil + paper, numbered words, fireproof, don't split,
don't permute"), and "Working with phrases later" examples
(`identity mnemonic`, `identity from-mnemonic`).
• Notes that `sigchain publish` isn't in the Python CLI yet (only
add/revoke/show/export) — match the actual current surface; the
JSONL the Python CLI produces is byte-compatible with Rust/Node,
so users can build the chain in Python and publish via either
of the other CLIs in the meantime.
• Troubleshooting includes ModuleNotFoundError: kez (a Python-
specific footgun when running outside the venv).
• Links to ../rust/TUTORIAL.md and ../nodejs/TUTORIAL.md as parallel
references throughout.
python/README.md now opens with the same "New to KEZ? Read TUTORIAL.md"
callout as the rust and nodejs READMEs do.
Root README's quick-start blocks for each implementation now reference
BOTH the impl README (reference) AND the impl TUTORIAL (step-by-step,
on-ramp) instead of just the README.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
aeba28d9e5
commit
d0e96c17fb
10
README.md
10
README.md
@ -61,7 +61,9 @@ cargo test # 99 tests
|
||||
cargo install --path crates/kez-cli # → `kez` on PATH
|
||||
kez verify id github:jason
|
||||
```
|
||||
Full guide: [`rust/README.md`](rust/README.md).
|
||||
Full guide: [`rust/README.md`](rust/README.md) (reference) ·
|
||||
[`rust/TUTORIAL.md`](rust/TUTORIAL.md) (step-by-step, recommended
|
||||
for newcomers).
|
||||
|
||||
### Node.js
|
||||
```sh
|
||||
@ -70,7 +72,8 @@ npm install
|
||||
npm test # 91 tests
|
||||
npm run cli -- verify id github:jason
|
||||
```
|
||||
Full guide: [`nodejs/README.md`](nodejs/README.md).
|
||||
Full guide: [`nodejs/README.md`](nodejs/README.md) (reference) ·
|
||||
[`nodejs/TUTORIAL.md`](nodejs/TUTORIAL.md) (step-by-step).
|
||||
|
||||
### Python
|
||||
```sh
|
||||
@ -79,7 +82,8 @@ python3 -m venv .venv
|
||||
.venv/bin/pip install -r requirements.txt
|
||||
.venv/bin/python kez_cli.py identity new
|
||||
```
|
||||
Full guide: [`python/README.md`](python/README.md).
|
||||
Full guide: [`python/README.md`](python/README.md) (reference) ·
|
||||
[`python/TUTORIAL.md`](python/TUTORIAL.md) (step-by-step).
|
||||
|
||||
### Sigchain storage server (optional)
|
||||
```sh
|
||||
|
||||
@ -39,6 +39,13 @@ python/
|
||||
|
||||
## Setup
|
||||
|
||||
> **New to KEZ?** Read [**`TUTORIAL.md`**](TUTORIAL.md) — a friendly
|
||||
> step-by-step walkthrough that takes you from "I have a nostr `nsec`"
|
||||
> to "I have a verified, published sigchain," including the BIP-39
|
||||
> recovery-phrase backup (12 or 24 words). It assumes nothing.
|
||||
>
|
||||
> This README is the reference; the tutorial is the on-ramp.
|
||||
|
||||
```sh
|
||||
cd python
|
||||
python3 -m venv .venv
|
||||
|
||||
526
python/TUTORIAL.md
Normal file
526
python/TUTORIAL.md
Normal file
@ -0,0 +1,526 @@
|
||||
# Tutorial — your first KEZ identity, end to end (Python)
|
||||
|
||||
This is a hands-on walkthrough. By the end you'll have:
|
||||
|
||||
- ✅ A KEZ identity tied to a key you already trust (your existing nostr
|
||||
`nsec`, or a brand-new Ed25519 key with a 12- or 24-word backup
|
||||
phrase).
|
||||
- ✅ A signed proof that *you* control a GitHub account (or DNS domain, or
|
||||
nostr handle, etc.) — verifiable by anyone, no central server needed.
|
||||
- ✅ A sigchain that ties multiple identities together, exported in a
|
||||
portable format, and published where strangers can find it.
|
||||
- ✅ The ability to verify other people's identities the same way.
|
||||
|
||||
If you've used [Keybase](https://keybase.io), the mental model is the same.
|
||||
The difference: KEZ has no required central authority. Your proofs live
|
||||
wherever you publish them; the verifier just walks the links.
|
||||
|
||||
This is the Python implementation. It is **wire-compatible** with the
|
||||
[Rust](../rust/TUTORIAL.md) and [Node](../nodejs/TUTORIAL.md)
|
||||
implementations — a claim signed in any of the three verifies in the
|
||||
other two. The repo-root [`crosstest.sh`](../crosstest.sh) proves it
|
||||
across 84 scenarios.
|
||||
|
||||
For the full protocol spec, see [`../SPEC.md`](../SPEC.md). This document
|
||||
is the friendly cousin.
|
||||
|
||||
> **Time budget:** 10–15 minutes for the first claim. A bit more if you
|
||||
> want to set up DNS or a sigchain publish.
|
||||
|
||||
---
|
||||
|
||||
## 0. Install
|
||||
|
||||
You'll need **Python 3.10+** and standard build tooling for the
|
||||
`cryptography` + `zstandard` native deps (clang/gcc on macOS / Linux,
|
||||
or pre-built wheels on most platforms).
|
||||
|
||||
```sh
|
||||
git clone https://git.ptud.biz/DukeInc/Kez.git
|
||||
cd Kez/python
|
||||
python3 -m venv .venv
|
||||
.venv/bin/pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Verify the CLI works:
|
||||
|
||||
```sh
|
||||
.venv/bin/python kez_cli.py --help
|
||||
```
|
||||
|
||||
You should see subcommands `identity`, `claim`, `verify`, and `sigchain`.
|
||||
|
||||
> **Want a global `kez` command instead?** From inside `python/` run
|
||||
> `.venv/bin/pip install -e .` once. After that, plain `kez claim
|
||||
> create …` works (provided your shell has `.venv/bin` on `PATH`, or
|
||||
> you activate the venv). Substitute `kez` for `.venv/bin/python
|
||||
> kez_cli.py` in every example below.
|
||||
|
||||
> **Optional but recommended:** `export GITHUB_TOKEN=ghp_...` in your
|
||||
> shell before verifying github claims. Anonymous GitHub limits you to
|
||||
> 60 requests/hour; with a token it's 5000/hour. Any read-only token
|
||||
> works; KEZ never sends it anywhere but `api.github.com`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Pick your primary key
|
||||
|
||||
Your **primary key** is the one private key the rest of your identity
|
||||
hangs off of. It signs every claim you make. Two choices:
|
||||
|
||||
### Option A: use your existing nostr key (recommended if you have one)
|
||||
|
||||
If you already use nostr (Damus, Amethyst, primal, etc.), you already
|
||||
have an `nsec1...` private key. Use it. KEZ understands nostr keys
|
||||
natively as Schnorr/secp256k1.
|
||||
|
||||
Export the `nsec` from your nostr client (every client has a way —
|
||||
usually Settings → Keys → Show / Export). Keep it secret; treat it the
|
||||
same as a wallet seed.
|
||||
|
||||
> **Warning.** Pasting your `nsec` into a CLI is fine on a machine you
|
||||
> trust. Don't do it on a shared box, and consider whether you want
|
||||
> shell history to remember it (`unset HISTFILE` for the session, or
|
||||
> prefix the command with a space if `HISTCONTROL=ignorespace`).
|
||||
|
||||
You don't need any command to "register" an existing nsec — just pass
|
||||
it with `--nsec` on the first claim you sign.
|
||||
|
||||
### Option B: generate a fresh primary
|
||||
|
||||
A new nostr keypair:
|
||||
|
||||
```sh
|
||||
.venv/bin/python kez_cli.py identity new
|
||||
```
|
||||
|
||||
Or a new Ed25519 keypair, which comes with a BIP-39 phrase alongside
|
||||
the hex seed (both are equivalent backups):
|
||||
|
||||
```sh
|
||||
.venv/bin/python kez_cli.py identity new --key-type ed25519 # 24-word
|
||||
.venv/bin/python kez_cli.py identity new --key-type ed25519 --mnemonic-words 12 # 12-word
|
||||
```
|
||||
|
||||
Output (24-word, the default):
|
||||
|
||||
```
|
||||
Primary: ed25519:7a3b4c…
|
||||
Public: 7a3b4c…
|
||||
Secret: 9e3f51… (32-byte seed)
|
||||
Mnemonic (24 words): "abandon ability able about above absent academy accident…"
|
||||
```
|
||||
|
||||
> **Save the backup.** Seed *or* phrase — at least one. Lose them both
|
||||
> and the identity is gone. There's no recovery flow.
|
||||
|
||||
### Recovery phrases — what's actually going on
|
||||
|
||||
A KEZ recovery phrase is a [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)
|
||||
mnemonic — the same 2048-word English wordlist that Bitcoin, Ethereum,
|
||||
and most hardware wallets use. The words encode random bits:
|
||||
|
||||
| Phrase length | Random bits | Resulting Ed25519 seed |
|
||||
|---|---|---|
|
||||
| **24 words** | 256 bits of entropy | The 32-byte seed *is* those 256 bits (1:1). Phrase ↔ seed round-trips. |
|
||||
| **12 words** | 128 bits of entropy | 16 bytes → 32-byte seed via `SHA-256("kez-bip39-12-v1" \|\| entropy)`. Phrase → seed only (one-way). |
|
||||
|
||||
#### Picking 12 vs 24
|
||||
|
||||
- **Pick 24 words** when you want full round-trip-ability — i.e. you'd
|
||||
like to be able to *recover the phrase from the hex seed* at any time
|
||||
in the future. Anyone's 32-byte Ed25519 secret can be re-encoded into
|
||||
the unique 24-word phrase that produced it. Bigger security margin
|
||||
(256 bits of entropy vs 128).
|
||||
- **Pick 12 words** when you want a shorter thing to write down on
|
||||
paper or remember. 128 bits of entropy is still enormously beyond
|
||||
brute-forcing. The trade-off: the path is *one-way only* — you can
|
||||
always derive the seed from the phrase, but you cannot derive the
|
||||
phrase from the seed. So if you only ever have the seed, you'll
|
||||
never know what 12-word phrase produced it. **Save the phrase
|
||||
itself**, not just the resulting seed.
|
||||
|
||||
Either way the resulting Ed25519 identity is exactly the same shape;
|
||||
peers can't tell which word count you used. The choice is purely about
|
||||
your backup ergonomics.
|
||||
|
||||
#### ⚠ Not compatible with hardware-wallet derivations
|
||||
|
||||
A KEZ 12-word phrase **does not** produce the same Bitcoin or Ethereum
|
||||
key as the same 12 words typed into a Ledger or MetaMask, and vice
|
||||
versa. The reasons are deliberate:
|
||||
|
||||
1. Other wallets feed the phrase through BIP-39's PBKDF2 to get a
|
||||
64-byte "seed", then run that through BIP-32 hierarchical
|
||||
derivation at a coin-specific path. KEZ doesn't — it takes the
|
||||
raw entropy and uses it directly (24-word case) or hashes it with
|
||||
a domain tag (12-word case).
|
||||
2. KEZ identities aren't part of a derivation tree. There's one
|
||||
identity per phrase; there's no path component.
|
||||
|
||||
That means: **don't paste your existing hardware-wallet recovery
|
||||
phrase into KEZ** expecting to get a key you've already seen. It'll
|
||||
produce a *new* KEZ identity uncorrelated with anything else.
|
||||
|
||||
Conversely: a KEZ phrase you saved is *only* useful for KEZ. A
|
||||
malicious wallet that says "import this phrase" can't extract your
|
||||
existing Bitcoin / Ethereum funds from a KEZ phrase, because the
|
||||
phrase wasn't derived through the same path.
|
||||
|
||||
#### Backing up — concrete advice
|
||||
|
||||
The phrase is the master key to your identity. Practical guidance:
|
||||
|
||||
- **Write it on paper, with a pencil. Number each word (1–12 or 1–24)
|
||||
so you can later verify the order.** A photograph or cloud document
|
||||
is one breach away from compromise.
|
||||
- **Store the paper somewhere fireproof.** Safe-deposit boxes, lockable
|
||||
desk drawers, etched-stainless-steel cards if you're paranoid.
|
||||
- **Never type the phrase into a website, chat app, or password
|
||||
manager that auto-syncs.** Local-only password managers (KeePassXC,
|
||||
1Password locked vault) are OK; cloud-synced managers are a softer
|
||||
target.
|
||||
- **Don't split it across two locations "for safety".** Half a BIP-39
|
||||
phrase weakens the entropy more than it protects against loss. If you
|
||||
need redundancy, make two complete paper copies in different physical
|
||||
locations.
|
||||
- **Don't be cute.** Don't permute the words "because they're easy to
|
||||
remember in this order." The wordlist position matters; reorder and
|
||||
you change the key (and the BIP-39 checksum will reject it on
|
||||
restore anyway).
|
||||
|
||||
### Working with phrases later
|
||||
|
||||
You can generate a fresh phrase without producing a key, or recover
|
||||
the key from a phrase you wrote down earlier:
|
||||
|
||||
```sh
|
||||
# Print a fresh 24-word phrase (or 12, with --words 12). No key derived.
|
||||
.venv/bin/python kez_cli.py identity mnemonic
|
||||
.venv/bin/python kez_cli.py identity mnemonic --words 12
|
||||
|
||||
# Recover the Ed25519 key from a phrase. Word count auto-detected.
|
||||
.venv/bin/python kez_cli.py identity from-mnemonic "abandon ability able about
|
||||
above absent academy accident account accuse achieve acid acoustic acquire
|
||||
across act action actor actress actual adapt add addict address"
|
||||
```
|
||||
|
||||
The recovered output is identical, byte-for-byte, to what was printed
|
||||
when you first ran `identity new` — same `Primary:`, same `Public:`,
|
||||
same `Secret:`.
|
||||
|
||||
Throughout the rest of this tutorial you can substitute
|
||||
`--mnemonic "your phrase here"` anywhere `--ed25519-seed <hex>` appears.
|
||||
Both are accepted on every command that takes a signing key.
|
||||
|
||||
For the rest of this tutorial we'll use a nostr key for examples and
|
||||
write the secret as `nsec1FAKE...` — substitute your real one.
|
||||
|
||||
---
|
||||
|
||||
## 2. Sign your first claim
|
||||
|
||||
A **claim** is just a signed sentence: *"the key I signed this with also
|
||||
controls `<subject>`."* The subject is a `system:identifier` string —
|
||||
`github:tudisco`, `dns:tud.ink`, `nostr:npub1…`, etc.
|
||||
|
||||
Say you want to prove you control the GitHub username `tudisco`.
|
||||
|
||||
```sh
|
||||
.venv/bin/python kez_cli.py claim create github:tudisco \
|
||||
--nsec nsec1FAKE... \
|
||||
--format markdown \
|
||||
--out github-tudisco.kez.md
|
||||
```
|
||||
|
||||
That writes a file containing the human-readable header plus a
|
||||
```kez``` fence with the raw JSON envelope inside (same shape as in the
|
||||
[Rust tutorial](../rust/TUTORIAL.md#2-sign-your-first-claim)).
|
||||
|
||||
### Picking the right format
|
||||
|
||||
Same claim, three packagings — same signature inside:
|
||||
|
||||
| Format | When to use | Command |
|
||||
|---|---|---|
|
||||
| **markdown** | Anywhere you can paste rich text — gists, profile READMEs, social posts. Most human-readable. | `--format markdown` |
|
||||
| **compact** | Tight places: DNS TXT records, QR codes, chat messages. One-liner that decompresses back to the full envelope. | `--format compact` |
|
||||
| **json** | Self-hosted `.well-known/kez.json`, developer tooling, anything that wants the raw envelope. | (default — no flag needed) |
|
||||
|
||||
If you skip `--out`, the proof prints to stdout — handy for piping.
|
||||
|
||||
---
|
||||
|
||||
## 3. Publish the proof
|
||||
|
||||
Same rules as the [Rust](../rust/TUTORIAL.md#3-publish-the-proof) and
|
||||
[Node](../nodejs/TUTORIAL.md#3-publish-the-proof) tutorials — pick the
|
||||
section that matches your subject system. GitHub gist or profile
|
||||
README, DNS TXT at `_kez.<domain>`, nostr (profile bio / kind-1 post /
|
||||
kind-30078), Bluesky post, ActivityPub profile field, your own
|
||||
`/.well-known/kez.json`.
|
||||
|
||||
The `dns:` shortcut prints a ready-to-paste zone file line:
|
||||
|
||||
```sh
|
||||
.venv/bin/python kez_cli.py claim dns tud.ink --nsec nsec1FAKE...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Verify it
|
||||
|
||||
```sh
|
||||
.venv/bin/python kez_cli.py verify id github:tudisco
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
Primary: nostr:npub1tkf...
|
||||
|
||||
Verified identities:
|
||||
- github:tudisco
|
||||
|
||||
Status: valid
|
||||
Confidence: strong
|
||||
```
|
||||
|
||||
Works against any channel — `dns:`, `github:`, `nostr:`, `bluesky:`,
|
||||
`ap:`, `mastodon:`. The verifier fetches the proof from where you
|
||||
published it, decodes the envelope, and verifies the cryptographic
|
||||
signature against the embedded public key.
|
||||
|
||||
**No KEZ server was involved.** Each side proves the claim
|
||||
independently — that's the whole point.
|
||||
|
||||
### Cross-implementation verification
|
||||
|
||||
Wire-compatible with the [Rust](../rust/TUTORIAL.md) and
|
||||
[Node](../nodejs/TUTORIAL.md) CLIs. You can sign in any and verify in
|
||||
any other:
|
||||
|
||||
```sh
|
||||
# Sign in Python…
|
||||
.venv/bin/python kez_cli.py claim create github:tudisco \
|
||||
--mnemonic "your 12 or 24 words…" --out p.kez.json
|
||||
|
||||
# …verify in Rust
|
||||
cd ../rust && cargo run -p kez-cli -- verify file ../python/p.kez.json
|
||||
|
||||
# …or verify in Node
|
||||
cd ../nodejs && npm run cli -- verify file ../python/p.kez.json
|
||||
```
|
||||
|
||||
Same bytes, same signature, all three implementations agree. The repo
|
||||
root's `crosstest.sh` exercises this for every (signer, verifier,
|
||||
format) combination.
|
||||
|
||||
### If verification fails
|
||||
|
||||
- **`not_found`** — proof isn't where the verifier looked. For
|
||||
GitHub, check the gist is public and the filename contains `kez`. For
|
||||
DNS, the TXT record is at `_kez.<domain>`, not `<domain>` itself;
|
||||
give propagation a minute.
|
||||
- **`subject_mismatch`** — you published a proof for one subject but
|
||||
asked the verifier to check a different one.
|
||||
- **`invalid_signature`** — proof was tampered with, or you re-signed
|
||||
with a different key after publishing. Re-sign and re-publish.
|
||||
- **GitHub `403 rate_limited`** — anonymous gets 60 req/hr; export
|
||||
`GITHUB_TOKEN`.
|
||||
- **`ModuleNotFoundError: kez`** — you ran a `python` not from the
|
||||
venv. Use `.venv/bin/python kez_cli.py …` (the launcher inserts the
|
||||
package dir into `sys.path`).
|
||||
|
||||
---
|
||||
|
||||
## 5. Sigchain — link multiple identities together
|
||||
|
||||
A **sigchain** is an append-only log of "this key controls X" events,
|
||||
each signed by your primary. Once you have more than one claim, you
|
||||
want a sigchain so verifiers can discover your full identity graph
|
||||
from a single starting point, and so you can later **revoke** a claim
|
||||
without invalidating the others.
|
||||
|
||||
Chains live at `~/.kez/sigchains/<safe-primary>.jsonl`. The CLI
|
||||
creates the directory on first use.
|
||||
|
||||
```sh
|
||||
.venv/bin/python kez_cli.py sigchain add github:tudisco --nsec nsec1FAKE...
|
||||
.venv/bin/python kez_cli.py sigchain add dns:tud.ink --nsec nsec1FAKE...
|
||||
.venv/bin/python kez_cli.py sigchain show --nsec nsec1FAKE...
|
||||
.venv/bin/python kez_cli.py sigchain revoke github:tudisco --nsec nsec1FAKE...
|
||||
```
|
||||
|
||||
Read-only view of someone else's chain (no secret needed):
|
||||
|
||||
```sh
|
||||
.venv/bin/python kez_cli.py sigchain show --primary nostr:npub1tkf...
|
||||
```
|
||||
|
||||
### Exporting
|
||||
|
||||
```sh
|
||||
.venv/bin/python kez_cli.py sigchain export --nsec nsec1FAKE... --format jsonl
|
||||
.venv/bin/python kez_cli.py sigchain export --nsec nsec1FAKE... --format compact
|
||||
```
|
||||
|
||||
> **Note.** The Python CLI currently supports `sigchain add`,
|
||||
> `revoke`, `show`, and `export`. `sigchain publish` (the one that
|
||||
> POSTs to a kez-sig-server or writes a `.well-known/` bundle) is
|
||||
> available in the Rust and Node CLIs; porting it to Python is a
|
||||
> short follow-up. Until then, you can `export` the chain and upload
|
||||
> it manually, or use the Rust/Node CLI for publishing the chain
|
||||
> built by Python (the on-disk JSONL is byte-compatible).
|
||||
|
||||
---
|
||||
|
||||
## 6. Verifying someone else
|
||||
|
||||
You've done the publishing side. Here's the receiving side — verify
|
||||
someone *else's* identity:
|
||||
|
||||
```sh
|
||||
# Start from any identifier they've published a proof for:
|
||||
.venv/bin/python kez_cli.py verify id github:linus
|
||||
|
||||
# Walk their chain from any known endpoint:
|
||||
.venv/bin/python kez_cli.py sigchain show --primary nostr:npub1abc...
|
||||
```
|
||||
|
||||
If you have the chain bundle on disk:
|
||||
|
||||
```sh
|
||||
.venv/bin/python kez_cli.py verify file ./their-chain.jsonl
|
||||
```
|
||||
|
||||
`verify id` is the friendly day-to-day verb. `sigchain show
|
||||
--primary <id>` is what you'd reach for to see the whole graph at once.
|
||||
|
||||
---
|
||||
|
||||
## 7. Programmatic use — embedding KEZ in a Python app
|
||||
|
||||
You don't have to go through the CLI. The same logic is exported by the
|
||||
`kez` package.
|
||||
|
||||
```python
|
||||
from kez.identity import Identity
|
||||
from kez.keys import NostrSecret
|
||||
from kez.envelope import sign_claim, new_claim_payload
|
||||
from kez.encodings import to_markdown
|
||||
from kez.channels import default_registry
|
||||
|
||||
# Sign a claim
|
||||
secret = NostrSecret.from_nsec("nsec1FAKE...")
|
||||
subject = Identity.parse("github:tudisco")
|
||||
payload = new_claim_payload(subject, secret.identity(), None) # None = now
|
||||
claim = sign_claim(payload, secret)
|
||||
print(to_markdown(claim))
|
||||
|
||||
# Verify a peer
|
||||
registry = default_registry()
|
||||
hit = registry.verify(Identity.parse("dns:tud.ink"))
|
||||
print(hit.status) # "valid"
|
||||
```
|
||||
|
||||
For mnemonic helpers:
|
||||
|
||||
```python
|
||||
from kez.mnemonic import (
|
||||
generate_mnemonic,
|
||||
seed_from_mnemonic,
|
||||
mnemonic_from_seed_24,
|
||||
ed25519_from_mnemonic,
|
||||
generate_ed25519_with_mnemonic,
|
||||
)
|
||||
|
||||
# Round-trip a 24-word phrase
|
||||
secret, phrase = generate_ed25519_with_mnemonic(24)
|
||||
assert ed25519_from_mnemonic(phrase).pubkey_hex() == secret.pubkey_hex()
|
||||
```
|
||||
|
||||
The implementations themselves are short (a few hundred lines each);
|
||||
the package modules are well-named and read as documentation. See
|
||||
[`kez/`](kez/) for the full surface.
|
||||
|
||||
---
|
||||
|
||||
## 8. Quick reference card
|
||||
|
||||
```sh
|
||||
# Generate a fresh primary
|
||||
.venv/bin/python kez_cli.py identity new
|
||||
.venv/bin/python kez_cli.py identity new --key-type ed25519 # 24-word phrase
|
||||
.venv/bin/python kez_cli.py identity new --key-type ed25519 --mnemonic-words 12 # 12-word phrase
|
||||
.venv/bin/python kez_cli.py identity mnemonic [--words 12|24] # phrase only
|
||||
.venv/bin/python kez_cli.py identity from-mnemonic "<phrase>" # recover key
|
||||
|
||||
# Sign a claim
|
||||
.venv/bin/python kez_cli.py claim create <subject> --nsec <nsec>
|
||||
.venv/bin/python kez_cli.py claim create <subject> --ed25519-seed <hex-seed>
|
||||
.venv/bin/python kez_cli.py claim create <subject> --mnemonic "<phrase>"
|
||||
.venv/bin/python kez_cli.py claim create <subject> --nsec <nsec> --format markdown --out file.md
|
||||
.venv/bin/python kez_cli.py claim create <subject> --nsec <nsec> --format compact
|
||||
.venv/bin/python kez_cli.py claim dns <domain> --nsec <nsec> # zone-file output
|
||||
|
||||
# Verify
|
||||
.venv/bin/python kez_cli.py verify id <subject> # live channel fetch
|
||||
.venv/bin/python kez_cli.py verify file <path> # local file
|
||||
|
||||
# Sigchain
|
||||
.venv/bin/python kez_cli.py sigchain add <subject> --nsec <nsec> [--proof-url <url>]
|
||||
.venv/bin/python kez_cli.py sigchain revoke <subject> --nsec <nsec>
|
||||
.venv/bin/python kez_cli.py sigchain show --nsec <nsec> # your own
|
||||
.venv/bin/python kez_cli.py sigchain show --primary <id> # someone else's
|
||||
.venv/bin/python kez_cli.py sigchain export --nsec <nsec> --format jsonl|compact [--out file]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Common confusions
|
||||
|
||||
**"Do I need a sigchain to use KEZ?"** No. A single signed claim,
|
||||
published, works on its own.
|
||||
|
||||
**"Why two key types — nostr and ed25519?"** Different ecosystems use
|
||||
different curves. Nostr is secp256k1/Schnorr; the rest of the world
|
||||
mostly likes Ed25519. KEZ supports both natively so you can use the
|
||||
key you already have rather than spinning up a new one for KEZ.
|
||||
|
||||
**"Is my `nsec` sent to KEZ servers?"** No, never. The CLI uses it
|
||||
locally to sign things. Only the *signed envelope* (public key + claim
|
||||
+ signature) ever leaves your machine.
|
||||
|
||||
**"What if I publish a proof and someone copies it as theirs?"**
|
||||
They can copy the bytes, but the signature inside is over *your*
|
||||
primary. Their primary won't match, so any verifier sees through it
|
||||
immediately.
|
||||
|
||||
**"What if my key is compromised?"** Append a `sigchain revoke
|
||||
<subject>` for the affected subjects, and ideally rotate to a new
|
||||
primary by signing a "this primary is succeeded by <new>" event
|
||||
(planned for the spec; not yet enforced).
|
||||
|
||||
**"Why is the Python version slower than Rust on imports?"** The
|
||||
`cryptography` C extension lazy-loads on first use, so the very first
|
||||
`identity new` or `verify` after a fresh shell can take an extra ~100
|
||||
ms. Steady-state is comparable to Node; both are I/O-bound on the
|
||||
channel HTTP calls for `verify id`.
|
||||
|
||||
---
|
||||
|
||||
## 10. Where to go next
|
||||
|
||||
- The web client at <https://kez.lat> — same protocol, no CLI.
|
||||
- [`../SPEC.md`](../SPEC.md) — the formal protocol.
|
||||
- [`../rust/TUTORIAL.md`](../rust/TUTORIAL.md) and
|
||||
[`../nodejs/TUTORIAL.md`](../nodejs/TUTORIAL.md) — same tutorial for
|
||||
the other two implementations.
|
||||
- [`../rust-sig-server/`](../rust-sig-server/) — run your own
|
||||
sig-server.
|
||||
- The channel module in [`kez/channels.py`](kez/channels.py) — add a
|
||||
new channel in an afternoon (each channel implementation is ~30–80
|
||||
lines).
|
||||
|
||||
That's the whole tutorial. Welcome to KEZ.
|
||||
Loading…
x
Reference in New Issue
Block a user