Kez/README.md
Tudisco d0db6f00f1 Initial implementation of KEZ — protocol, two impls, and storage server
KEZ is a portable, decentralized identity graph: a person signs claims
linking their many accounts, publishes those claims in places only the
claimed account can publish to, and anyone can verify the connections
without trusting a central server.

Layout
------
- SPEC.md            Language-agnostic protocol spec (v0.2)
- rust/              Rust implementation: kez-core, kez-channels, kez-cli
- nodejs/            TypeScript port at full parity
- rust-sig-server/   Optional axum + SQLite storage server for sigchains
- crosstest.sh       Cross-implementation interop harness

Capabilities (both implementations, byte-compatible)
----------------------------------------------------
- Two primary-key algorithms: nostr/secp256k1 Schnorr (BIP-340) and
  Ed25519 (RFC 8032). Identifiers: nostr:npub1... and ed25519:<hex>.
- JCS (RFC 8785) canonicalization for everything signed.
- Four proof encodings: JSON envelope, compact (kez:z1:<base64url(zstd(json))>),
  Markdown fence, DNS TXT.
- Five channel plugins (no API keys, no auth needed for any of them):
    dns:        system resolver, _kez.<domain> TXT records
    github:     public gist scan + <user>/<user> profile README fallback
    nostr:      kind-30078 events from default relays
    bluesky:    public AppView author feed
    ap:         WebFinger + actor JSON (alias mastodon:)
- Identical CLI surface:
    kez identity new [--key-type nostr|ed25519]
    kez claim create <subject> (--nsec | --ed25519-seed) [--format ...] [--out ...]
    kez claim dns <domain>     (--nsec | --ed25519-seed)
    kez verify file <path>
    kez verify id <identifier>
    kez sigchain add|revoke|show|export|publish
- Sigchains: append-only signed log per primary, hash-chained per spec §6,
  stored locally at ~/.kez/sigchains/, exportable as JSONL or kez:zc1: bundle.
- Sigchain publish destinations: chain server, web (file dump), DNS (zone
  record print), nostr (kind-30078 wrapping event).

kez-sig-server
--------------
Optional storage tier. Axum + SQLite, single binary, no external deps.

- No auth — the cryptography is the access control. The server validates
  every signature, every seq, every prev hash before storing.
- REST API: POST /v1/sigchains/{scheme}/{id}/events (append signed event,
  201 with new head hash or 4xx); GET /{scheme}/{id} (full chain as JSONL);
  GET /head; GET /healthz.
- Designed for one central instance for now; the design doesn't preclude
  running more later (clients gain a configurable list, verifiers
  reconcile per spec §6.2).
- Channel-based publishing remains the always-available fallback if the
  server is unavailable.

Tests
-----
- rust/                 99 tests
- rust-sig-server/      10 integration tests (real HTTP, real SQLite)
- nodejs/               91 tests (vitest)
- crosstest.sh          19 cross-impl scenarios — proves JCS bytes,
                        Schnorr + Ed25519 sigs, all four claim encodings,
                        and the sigchain JSONL bundle are byte-compatible
                        between Rust and Node in both directions.

What's not done yet
-------------------
- verify id consulting the sigchain for revocations (data path exists,
  just not wired into the verifier output).
- rotate and add_device sigchain ops (types reserved).
- expires_at enforcement during claim verification.
- Typed VerificationStatus.status reflecting the five failure modes.
- Auth-required publishers (GitHub gist, Bluesky, ActivityPub).
2026-05-24 14:41:00 -06:00

104 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# KEZ
KEZ is a portable, decentralized identity graph. It lets a person say:
> "These accounts, keys, domains, and identities are all me."
…without depending on any central authority. Every connection is proven by a
cryptographic signature against a key the user already controls (a nostr key,
an Ed25519 key, etc.), and the proofs are published in places only the
claimed account itself can publish to (their gist, their DNS, their nostr
relay event). Anyone can verify the graph without trusting a server.
## Repository layout
```
.
├── SPEC.md ← The protocol. Language-agnostic, normative.
├── rust/ ← Rust implementation (kez-core, kez-channels, kez-cli)
├── nodejs/ ← TypeScript/Node implementation (same shape, same CLI)
├── rust-sig-server/ ← Optional HTTP store for sigchains (axum + SQLite)
├── crosstest.sh ← Interop test: artifacts move between implementations
└── README.md ← (this file)
```
Two parallel implementations. **Wire-compatible**: a claim signed in Rust
verifies in Node and vice versa. The cross-test harness proves it.
A separate [`rust-sig-server/`](rust-sig-server/) crate provides an optional
HTTP storage tier for sigchains — useful when a user doesn't want to set up
DNS/hosting/nostr, but **never required**; the protocol stays decentralized.
## Quick start
### Rust
```sh
cd rust
cargo build
cargo test # 81 tests
cargo run -p kez-cli -- verify id github:jason
```
Full guide: [`rust/README.md`](rust/README.md).
### Node.js
```sh
cd nodejs
npm install
npm test # 72 tests
npm run cli -- verify id github:jason
```
Full guide: [`nodejs/README.md`](nodejs/README.md).
## Cross-testing
```sh
./crosstest.sh
```
Runs 19 scenarios that swap implementations at the artifact boundary:
| # | Scenario |
|---|---|
| 12 | nostr-signed JSON claim, both directions |
| 34 | nostr-signed compact claim, both directions |
| 56 | nostr-signed markdown claim, both directions |
| 78 | nostr-signed DNS zone form, both directions |
| 910 | ed25519-signed JSON claim, both directions |
| 1112 | ed25519-signed compact claim, both directions |
| 1314 | ed25519-signed markdown claim, both directions |
| 15 | rust builds 3-event nostr sigchain → node parses + shows |
| 16 | rust-exported sigchain JSONL == node-exported JSONL (byte-identical) |
| 17 | node builds 3-event nostr sigchain → rust parses + shows |
| 18 | rust builds ed25519 sigchain → node parses + shows |
| 19 | node builds ed25519 sigchain → rust parses + shows |
If all 19 pass: JCS canonicalization, both signature suites (BIP-340 Schnorr
and Ed25519), the compact `kez:z1:` zstd+base64url encoding, the Markdown
fence, the DNS TXT shape, and the sigchain JSONL bundle format are all
byte-compatible across implementations.
Pass `-v` for verbose output (echoes intermediate commands and proofs).
## What ships in v0.2
- **Five channel plugins** in each implementation: `dns:`, `github:`,
`nostr:`, `bluesky:`, `ap:` (alias `mastodon:`).
- **Four wire encodings**: JSON, compact, Markdown fence, DNS TXT.
- **Two primary-key algorithms**: nostr/secp256k1 Schnorr (BIP-340) and
Ed25519 (RFC 8032).
- **JCS (RFC 8785)** canonicalization for everything signed.
- **No API keys required for any channel.**
## What's not done yet
Tracked in both [`rust/README.md`](rust/README.md#whats-not-done-yet) and the
spec:
- Sigchain walker (types exist; no append/walk/revoke flow yet).
- `expires_at` enforcement during verify.
- Typed `VerificationStatus.status` reflecting the five failure modes.
## License
Dual-licensed under MIT or Apache-2.0.