5.4 KiB
5.4 KiB
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
- Relay returns
What Needs to Happen
- Fix NIP-42 auth — Option A: disable auth requirement on relay (add
state.AllowEventreturning true in main.go). Option B: fix the Python signature to use proper schnorr. - Create NIP-29 group — Group code was generated but metadata posting failed due to auth.
- Wire Hermes to Nostr — Replace Telegram send_message with Nostr relay POST.
- Deprecate Telegram — Set to fallback-only mode.
- 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
- The relay is live and healthy. It works — we just can't write to it yet because auth is broken.
- pynostr's sign_event() works for regular events — tested successfully, produces valid signatures.
- 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.
- 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).
- 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.
- 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:
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:
- Create the NIP-29 group (kind 39000 with d tag)
- Post test messages (kind 1 and kind 9)
- Wire Hermes morning report to Nostr client instead of Telegram
- Add Alexander to the group
- 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)