# NOSTR COMMS MIGRATION — FINAL STATUS

## Infrastructure Status

### What Works
- **Nostr relay**: RUNNING on relay.alexanderwhitestone.com:2929
  - Software: relay29 (khatru29, fiatjaf) — NIP-29 groups
  - Database: LMDB persistent
  - Service: systemd enabled, survived reboots
  - Memory: 6.7MB, CPU: 8.3s total
  - Accepts WebSocket connections (verified via netcat)
- **Agent keys**: 7 keypairs exist in ~/.timmy/nostr/agent_keys.json
  - Timmy, Claude, Gemini, Groq, Grok, Hermes, Alexander
- **DM bridge**: RUNNING nostr-bridge (polls every 60s for DMs, creates Gitea issues)
  - Fixed double-URL bug (http://https://forge -> https://forge)
- **Gitea reporting**: gitea_report.py exists for posting status to issues
- **Relay source**: /root/nostr-relay/ — Go binary, LMDB backend, NIP-29 groups

### What's Blocked
- **NIP-42 AUTH handshake**: The relay requires authentication before accepting events
  - Relay returns `["AUTH", challenge]` after EVENT submission
  - We sign a kind 22242 auth event but relay rejects with "signature is invalid"
  - Tested: nostr-sdk v0.44.2, pynostr, coincurve raw — all produce invalid signatures
  - Root cause likely: the nostr Python SDK's sign_event() uses ECDSA not schnorr for 22242
  - The relay29/khatru29 implementation validates using go-nostr schnorr

### What Needs to Happen
1. **Fix NIP-42 auth** — Option A: disable auth requirement on relay (add `state.AllowEvent` returning true in main.go). Option B: fix the Python signature to use proper schnorr.
2. **Create NIP-29 group** — Group code was generated but metadata posting failed due to auth.
3. **Wire Hermes to Nostr** — Replace Telegram send_message with Nostr relay POST.
4. **Deprecate Telegram** — Set to fallback-only mode.
5. **Alexander's phone client** — Needs a Nostr client installed (Damus on iOS).

## The Epic and Issues (Filed on timmy-home)

| Issue | Assignee | Priority | Status |
|-------|----------|----------|--------|
| [EPIC] Sovereign Comms Migration | — | — | FILED |
| P0: Wire Timmy Hermes to Nostr | Timmy | P0 | BLOCKED (auth) |
| P0: Create Nostr group NIP-29 | Allegro | P0 | BLOCKED (auth) |
| P1: Build Nostr clients per wizard | Ezra | P1 | NOT STARTED |
| P1: Alexander receive-side | Allegro | P1 | NOT STARTED |
| P1: Deprecate Telegram fallback | Allegro | P1 | NOT STARTED |
| P2: Nostr-to-Gitea bridge | ClawCode | P2 | BRIDGE EXISTS (URL bug fixed) |

## Files Created This Session

- `~/.timmy/nostr/post_via_vps.py` — Nostr client with raw websocket posting
- `~/.timmy/nostr/post_raw.py` — Direct coincurve + websocket implementation
- `~/.timmy/nostr/post_nip42.py` — NIP-42 auth implementation
- `~/.timmy/nostr/post_via_vps.py` — SSH-to-VPS relay posting
- `~/.timmy/nostr/nostr_client.py` — Full Nostr client (sign + post)
- `~/.timmy/nostr/COMMS_MIGRATION.md` — Integration guide with all docs
- `~/.timmy/nostr/COMMS_STATUS.md` — This file
- `~/.timmy/nostr/group_config.json` — Group config (code changes each attempt)

## Key Findings

1. **The relay is live and healthy.** It works — we just can't write to it yet because auth is broken.
2. **pynostr's sign_event() works for regular events** — tested successfully, produces valid signatures.
3. **NIP-42 auth (kind 22242) is the blocker** — The relay's khatru29 implementation validates the 22242 event's schnorr signature against the challenge. Our signatures don't match what the Go code expects.
4. **The DM bridge works** — it polls for new DMs and creates Gitea issues. It just needs the correct GITEA URL (fixed: https://forge.alexanderwhitestone.com).
5. **coincurve.sign_schnorr() produces valid 64-byte schnorr signatures** — The issue might be that pynostr's sign_event() uses a different algorithm than what khatru29 expects for the 22242 kind.
6. **The relay's private key** is in the RELAY_PRIVKEY env var — could use admin powers to bypass auth or create the group directly.

## Next Session Action Plan

### Quick Fix (5 min)
On the VPS, add to /root/nostr-relay/main.go relay29 options:
```go
state.AllowEvent = func(context.Context, nostr.Event, string) (bool, string) {
    return true, "" // allow all events, no auth required
}
```
Then rebuild and restart. This opens the relay for writes so we can create the group and test the full pipeline.

### Proper Fix (30 min)
The pynostr Event class doesn't have sign_schnorr() — it uses sign_event() which does standard Nostr signing (sha256 of serialized event + schnorr of the id). But for NIP-42 auth, the signed payload should be the challenge string, not the event id. Need to sign the challenge directly with coincurve's sign_schnorr() on the raw challenge bytes, then build the event manually.

### Full Pipeline (1 hr)
Once auth works:
1. Create the NIP-29 group (kind 39000 with d tag)
2. Post test messages (kind 1 and kind 9)
3. Wire Hermes morning report to Nostr client instead of Telegram
4. Add Alexander to the group
5. Set Telegram to fallback-only

## Nostr Relay Access

- **WebSocket**: ws://relay.alexanderwhitestone.com:2929 (or ws://127.0.0.1:2929 on VPS)
- **Timmy npub**: npub1qwyndfwvwy4edlwgtg3jlssawg7aj36t78fqyk30ehtyd82j22nqzt5m94
- **Timmy hex_pub**: 038936a5cc712b96fdc85a232fc21d723dd9474bf1d2025a2fcdd6469d5252a6
- **Keys file**: ~/.timmy/nostr/agent_keys.json
- **Group code**: Will be set once group creation succeeds
- **Bridge service**: nostr-bridge.service — polls DMs every 60s, creates Gitea issues
- **Bridge code**: /root/nostr-relay/dm_bridge_mvp.py — uses nostr-sdk (not pynostr)
