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