Files
kindexr/docs/ARCHITECTURE.md
enki 1b7b70426c feat: Phase 0 bootstrap — kindexr boots, migrates, serves /health
- config: koanf-based loading (defaults → YAML → KINDEXR_ env vars)
- db: embedded SQLite migrations with BEGIN/END-aware statement splitter
- server: chi router, GET /health returns JSON stats
- cmd/kindexr: graceful SIGTERM shutdown
- cmd/kindexr-cli: stub
- deploy: systemd unit, example config, nginx snippet
- all packages covered by race-clean tests
2026-05-16 18:45:15 -07:00

3.3 KiB

Architecture

nzbstr is a two-halves daemon:

  • Reader (Phase 1+): subscribes to Nostr relays for kind 2003 (torrent) and kind 2004 (torrent comment) events, indexes them locally in SQLite, and exposes a Torznab-compatible HTTP API.
  • Writer (Phase 4+): watches a download client (qBittorrent / Transmission / Deluge) for completed downloads, builds NIP-35 events signed by your npub, and publishes them to configured outbox relays.

Data flow

  Sonarr / Radarr / Lidarr / Readarr / Prowlarr
                       |
                       v   Torznab HTTP/XML
                       |
                  +-----------+
                  |  nzbstr   |
                  |  reader   |  <-- subscribes to relays via WebSocket
                  |  writer   |  --> publishes via WebSocket
                  +-----------+
                       ^                ^
                       |                |
                       v                v
                Nostr relays      qBittorrent / Transmission / Deluge
                (NIP-35 events)   (download client)

Components

cmd/nzbstr — main daemon

Entry point. Loads config, opens the SQLite database (applying any pending migrations), starts the HTTP server, and installs signal handlers for graceful shutdown.

internal/config

koanf-based configuration loading. Load order: compiled-in defaults → YAML file → environment variables (NZBSTR_ prefix). Missing files are silently skipped so the daemon can start with all defaults.

internal/db

SQLite access via modernc.org/sqlite (pure Go, no CGO). Includes an embedded migration runner that applies numbered SQL files from internal/db/migrations/ on startup. Uses WAL journal mode and a single writer connection for safe concurrent reads.

internal/server

chi-based HTTP server. Phase 0 exposes only /health. Phase 1 adds /api?t=caps, /api?t=search, etc.

internal/nostr (Phase 1+)

Relay subscription loop using github.com/nbd-wtf/go-nostr. NIP-77 negentropy bootstrap for efficient historical sync. Parses kind 2003 events into the internal Torrent struct and writes them to SQLite.

internal/torznab (Phase 1+)

chi routes for the Torznab API. Auth middleware validates API keys. Returns XML in Torznab RSS format. Categories mapped from NIP-35 i tags to newznab integer codes.

internal/enrich (Phase 2+)

Title parser (extracts season/episode/quality/source from release names) and TMDB client for backfilling IMDB/TMDB/TVDB IDs on events that lack them.

internal/wot (Phase 3+)

Web-of-Trust filter. Fetches the operator's kind 3 follow list and optionally follows-of-follows to build an allowed-pubkey set. Integrates with NIP-51 curation sets.

internal/publisher (Phase 4+)

qBittorrent / Transmission / watch-dir integration. Polls for completed downloads in the configured category, builds kind 2003 events, signs them via NIP-46 bunker or local key, and publishes to outbox relays.

Storage

Single SQLite file at /var/lib/nzbstr/nzbstr.db. Schema managed by embedded SQL migrations in internal/db/migrations/. FTS5 virtual table on (title, description) with triggers to keep it in sync.

Deployment

Single static binary + systemd unit. No external services required. Sits behind nginx for TLS termination. See deploy/nzbstr.service and deploy/nginx.conf.example.