nostrclient/CLAUDE.md
Padreug 115e869225
Some checks failed
ci.yml / fix: queue outgoing events when relay connection is down (pull_request) Failing after 0s
fix: queue outgoing events when relay connection is down
When all relay connections are temporarily lost, EVENT messages published
by extensions (nostrmarket, events) are now queued in a bounded deque
(max 100) instead of being silently dropped. On reconnection, queued
events are flushed to all connected relays. Dead relay queues are also
drained before restart to preserve in-flight events.

Closes aiolabs/nostrclient#1

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 20:09:26 +02:00

59 lines
3.2 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Nostrclient is an LNbits extension that acts as an always-on Nostr relay multiplexer. Multiple Nostr clients connect to a single WebSocket endpoint, which fans out requests to multiple configured Nostr relays and aggregates/deduplicates responses. It rewrites subscription IDs per-client to prevent conflicts.
## Build & Development Commands
All commands use `uv` as the Python package manager:
```bash
make format # Format: prettier + black + ruff --fix
make check # All checks: mypy, pyright, black --check, ruff check, prettier --check
make test # Run pytest (DEBUG=true PYTHONUNBUFFERED=1 uv run pytest)
make mypy # Type check (excludes nostr/ directory)
make pre-commit # Run pre-commit hooks on all files
```
Individual checks: `make checkblack`, `make checkruff`, `make checkprettier`.
CI runs lint then pytest with `LNBITS_BACKEND_WALLET_CLASS=FakeWallet`.
## Architecture
**Request flow:** Nostr Clients → WebSocket → NostrRouter → RelayManager → Nostr Relays
Key components:
- **NostrRouter** (`router.py`) — One per client WebSocket connection. Rewrites subscription IDs (original → hashed → original) to isolate clients. Two async tasks: `_client_to_nostr` (forward requests) and `_nostr_to_client` (deliver aggregated responses).
- **NostrClient** (`nostr/client/client.py`) — Singleton orchestrator. Owns the RelayManager. Polls MessagePool and dispatches events via callbacks to routers.
- **RelayManager** (`nostr/relay_manager.py`) — Manages connections to multiple relays. Caches subscriptions so new relays receive existing subscriptions. Runs health checks via `check_and_restart_relays()`.
- **Relay** (`nostr/relay.py`) — Individual relay WebSocket connection with retry/backoff, ping latency tracking, and error counting.
- **MessagePool** (`nostr/message_pool.py`) — Thread-safe event aggregation with deduplication by event ID across all relays.
**Hybrid threading model:** Relay connections use threads (via `RelayManager.open_connections()`); client communication uses asyncio. The bridge is in `tasks.py` where `subscribe_events()` runs in a thread executor.
**Lifecycle** (`__init__.py`): `nostrclient_start()` spawns three background tasks (init relays, subscribe events, check relays). `nostrclient_stop()` cancels tasks, stops routers, closes the client.
## API Endpoints (views_api.py)
- REST endpoints under `/api/v1/` for relay CRUD and config (admin-authenticated)
- WebSocket endpoints: `/api/v1/{ws_id}` (private, encrypted ID) and `/api/v1/relay` (public, if enabled)
## Database
Three migrations in `migrations.py`: relays table, config table (JSON `extra` field), config owner scoping. CRUD in `crud.py` uses LNbits database abstraction.
## Code Quality Notes
- **mypy excludes `nostr/*`** — this is a custom Nostr protocol implementation, not a third-party package
- **Ruff rules:** F, E, W, I, A, C, N, UP, RUF, B
- **Frontend:** Vue.js + Quasar via LNbits base templates (`templates/nostrclient/index.html`)
- Pub key helpers in `helpers.py` normalize between hex and bech32 (npub1) formats