- 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
3.5 KiB
Publishing (Writer side)
nzbstr's writer side (Phase 4+) watches a download client for completed downloads and publishes them as NIP-35 kind 2003 events to configured Nostr relays.
Overview
qBittorrent / Transmission / watch_dir
|
v (poll completed downloads in tagged category)
nzbstr publisher
|
v (build kind 2003 event)
NIP-46 bunker signer OR local nsec
|
v (WebSocket publish)
outbox_relays (e.g. wss://sovbit.host)
|
v (also stored locally)
local SQLite DB
Configuration
Enable the publisher in config.yaml:
publisher:
enabled: true
signer:
mode: "bunker"
bunker_uri: "bunker://..."
outbox_relays:
- "wss://sovbit.host"
- "wss://relay.damus.io"
client:
type: "qbittorrent"
qbittorrent:
url: "http://127.0.0.1:8080"
username: "admin"
password: "${QBIT_PASSWORD}"
category: "publish-nostr"
enrich_before_publish: true
Signing modes
NIP-46 bunker (recommended)
Run a self-hosted NIP-46 bunker (e.g. nsecbunker) on a trusted machine. nzbstr connects as a client and requests signatures. Your nsec never touches the publishing machine.
signer:
mode: "bunker"
bunker_uri: "bunker://<pubkey>?relay=wss://relay.example.com&secret=<secret>"
ncryptsec (NIP-49)
Encrypted private key stored on disk. nzbstr prompts for the passphrase at startup.
signer:
mode: "ncryptsec"
ncryptsec: "ncryptsec1..."
Local nsec (not recommended)
Unencrypted private key. Only use on air-gapped or fully trusted machines.
signer:
mode: "local"
nsec: "nsec1..."
Set the nsec value via environment variable instead of writing it to the config file:
NZBSTR_PUBLISHER_SIGNER_NSEC=nsec1... nzbstr --config /etc/nzbstr/config.yaml
Download clients
qBittorrent
nzbstr polls the qBittorrent Web API for torrents in the configured category (publish-nostr by default). Only torrents whose state is pausedUP (seeding completed) or uploading are considered.
Tag your torrents in qBittorrent with the publish category to opt them in. This prevents accidentally publishing everything in your client.
watch_dir (Phase 4+)
Drop .torrent files into the configured watch directory. nzbstr picks them up, parses the metainfo, builds the event, and publishes. Useful for scripted workflows.
Event construction
For each completed download:
- Parse
.torrentmetainfo: extract info-hash, title, file list, tracker list, total size. - If
enrich_before_publish: true, run the title through the title parser and optionally TMDB lookup to backfillimdb_id,tmdb_id,tvdb_id, season, episode, quality, source. - Build a kind 2003 event with the appropriate
x,title,file,tracker,i, andttags. - Sign via the configured signer.
- Publish to all
outbox_relays. - Store in local SQLite as if ingested (so it appears in your own Torznab results immediately).
Security notes
- NIP-46 bunker on a public-facing seedbox is the right answer security-wise. Run a self-hosted bunker on a trusted internal machine with nzbstr as a client. Your nsec never leaves the bunker.
- If you use
localmode, useProtectSystem=strictin the systemd unit (already set) and ensure the config file has mode 0640 and is owned by thenzbstruser. - The
categoryfilter in qBittorrent config is important — do not set it to a category that contains torrents you didn't deliberately choose to publish.