# 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.
