From a0c35202f379505b115576df3cf27024f2a05e38 Mon Sep 17 00:00:00 2001 From: "Claude (Opus 4.6)" Date: Mon, 23 Mar 2026 22:47:25 +0000 Subject: [PATCH] [claude] ADR-024: canonical Nostr identity in timmy-nostr (#1223) (#1230) --- .../024-nostr-identity-canonical-location.md | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 docs/adr/024-nostr-identity-canonical-location.md diff --git a/docs/adr/024-nostr-identity-canonical-location.md b/docs/adr/024-nostr-identity-canonical-location.md new file mode 100644 index 00000000..256ce61d --- /dev/null +++ b/docs/adr/024-nostr-identity-canonical-location.md @@ -0,0 +1,160 @@ +# ADR-024: Canonical Nostr Identity Location + +**Status:** Accepted +**Date:** 2026-03-23 +**Issue:** #1223 +**Refs:** #1210 (duplicate-work audit), ROADMAP.md Phase 2 + +--- + +## Context + +Nostr identity logic has been independently implemented in at least three +repos (`replit/timmy-tower`, `replit/token-gated-economy`, +`rockachopa/Timmy-time-dashboard`), each building keypair generation, event +publishing, and NIP-07 browser-extension auth in isolation. + +This duplication causes: + +- Bug fixes applied in one repo but silently missed in others. +- Diverging implementations of the same NIPs (NIP-01, NIP-07, NIP-44). +- Agent time wasted re-implementing logic that already exists. + +ROADMAP.md Phase 2 already names `timmy-nostr` as the planned home for Nostr +infrastructure. This ADR makes that decision explicit and prescribes how +other repos consume it. + +--- + +## Decision + +**The canonical home for all Nostr identity logic is `rockachopa/timmy-nostr`.** + +All other repos (`Timmy-time-dashboard`, `timmy-tower`, +`token-gated-economy`) become consumers, not implementers, of Nostr identity +primitives. + +### What lives in `timmy-nostr` + +| Module | Responsibility | +|--------|---------------| +| `nostr_id/keypair.py` | Keypair generation, nsec/npub encoding, encrypted storage | +| `nostr_id/identity.py` | Agent identity lifecycle (NIP-01 kind:0 profile events) | +| `nostr_id/auth.py` | NIP-07 browser-extension signer; NIP-42 relay auth | +| `nostr_id/event.py` | Event construction, signing, serialisation (NIP-01) | +| `nostr_id/crypto.py` | NIP-44 encryption (XChaCha20-Poly1305 v2) | +| `nostr_id/nip05.py` | DNS-based identifier verification | +| `nostr_id/relay.py` | WebSocket relay client (publish / subscribe) | + +### What does NOT live in `timmy-nostr` + +- Business logic that combines Nostr with application-specific concepts + (e.g. "publish a task-completion event" lives in the application layer + that calls `timmy-nostr`). +- Reputation scoring algorithms (depends on application policy). +- Dashboard UI components. + +--- + +## How Other Repos Reference `timmy-nostr` + +### Python repos (`Timmy-time-dashboard`, `timmy-tower`) + +Add to `pyproject.toml` dependencies: + +```toml +[tool.poetry.dependencies] +timmy-nostr = {git = "https://gitea.hermes.local/rockachopa/timmy-nostr.git", tag = "v0.1.0"} +``` + +Import pattern: + +```python +from nostr_id.keypair import generate_keypair, load_keypair +from nostr_id.event import build_event, sign_event +from nostr_id.relay import NostrRelayClient +``` + +### JavaScript/TypeScript repos (`token-gated-economy` frontend) + +Add to `package.json` (once published or via local path): + +```json +"dependencies": { + "timmy-nostr": "rockachopa/timmy-nostr#v0.1.0" +} +``` + +Import pattern: + +```typescript +import { generateKeypair, signEvent } from 'timmy-nostr'; +``` + +Until `timmy-nostr` publishes a JS package, use NIP-07 browser extension +directly and delegate all key-management to the browser signer — never +re-implement crypto in JS without the shared library. + +--- + +## Migration Plan + +Existing duplicated code should be migrated in this order: + +1. **Keypair generation** — highest duplication, clearest interface. +2. **NIP-01 event construction/signing** — used by all three repos. +3. **NIP-07 browser auth** — currently in `timmy-tower` and `token-gated-economy`. +4. **NIP-44 encryption** — lowest priority, least duplicated. + +Each step: implement in `timmy-nostr` → cut over one repo → delete the +duplicate → repeat. + +--- + +## Interface Contract + +`timmy-nostr` must expose a stable public API: + +```python +# Keypair +keypair = generate_keypair() # -> NostrKeypair(nsec, npub, privkey_bytes, pubkey_bytes) +keypair = load_keypair(encrypted_nsec, secret_key) + +# Events +event = build_event(kind=0, content=profile_json, keypair=keypair) +event = sign_event(event, keypair) # attaches .id and .sig + +# Relay +async with NostrRelayClient(url) as relay: + await relay.publish(event) + async for msg in relay.subscribe(filters): + ... +``` + +Breaking changes to this interface require a semver major bump and a +migration note in `timmy-nostr`'s CHANGELOG. + +--- + +## Consequences + +- **Positive:** Bug fixes in cryptographic or protocol code propagate to all + repos via a version bump. +- **Positive:** New NIPs are implemented once and adopted everywhere. +- **Negative:** Adds a cross-repo dependency; version pinning discipline + required. +- **Negative:** `timmy-nostr` must be stood up and tagged before any + migration can begin. + +--- + +## Action Items + +- [ ] Create `rockachopa/timmy-nostr` repo with the module structure above. +- [ ] Implement keypair generation + NIP-01 signing as v0.1.0. +- [ ] Replace `Timmy-time-dashboard` inline Nostr code (if any) with + `timmy-nostr` import once v0.1.0 is tagged. +- [ ] Add `src/infrastructure/clients/nostr_client.py` as the thin + application-layer wrapper (see ROADMAP.md §2.6). +- [ ] File issues in `timmy-tower` and `token-gated-economy` to migrate their + duplicate implementations.