4.9 KiB
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:
[tool.poetry.dependencies]
timmy-nostr = {git = "https://gitea.hermes.local/rockachopa/timmy-nostr.git", tag = "v0.1.0"}
Import pattern:
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):
"dependencies": {
"timmy-nostr": "rockachopa/timmy-nostr#v0.1.0"
}
Import pattern:
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:
- Keypair generation — highest duplication, clearest interface.
- NIP-01 event construction/signing — used by all three repos.
- NIP-07 browser auth — currently in
timmy-towerandtoken-gated-economy. - 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:
# 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-nostrmust be stood up and tagged before any migration can begin.
Action Items
- Create
rockachopa/timmy-nostrrepo with the module structure above. - Implement keypair generation + NIP-01 signing as v0.1.0.
- Replace
Timmy-time-dashboardinline Nostr code (if any) withtimmy-nostrimport once v0.1.0 is tagged. - Add
src/infrastructure/clients/nostr_client.pyas the thin application-layer wrapper (see ROADMAP.md §2.6). - File issues in
timmy-towerandtoken-gated-economyto migrate their duplicate implementations.