Files
kindexr/docs/PUBLISHING.md
T
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

112 lines
3.5 KiB
Markdown

# 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`:
```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](https://github.com/kind-0/nsecbunker)) on a trusted machine. nzbstr connects as a client and requests signatures. Your nsec never touches the publishing machine.
```yaml
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.
```yaml
signer:
mode: "ncryptsec"
ncryptsec: "ncryptsec1..."
```
### Local nsec (not recommended)
Unencrypted private key. Only use on air-gapped or fully trusted machines.
```yaml
signer:
mode: "local"
nsec: "nsec1..."
```
Set the `nsec` value via environment variable instead of writing it to the config file:
```sh
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:
1. Parse `.torrent` metainfo: extract info-hash, title, file list, tracker list, total size.
2. If `enrich_before_publish: true`, run the title through the title parser and optionally TMDB lookup to backfill `imdb_id`, `tmdb_id`, `tvdb_id`, season, episode, quality, source.
3. Build a kind 2003 event with the appropriate `x`, `title`, `file`, `tracker`, `i`, and `t` tags.
4. Sign via the configured signer.
5. Publish to all `outbox_relays`.
6. 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 `local` mode, use `ProtectSystem=strict` in the systemd unit (already set) and ensure the config file has mode 0640 and is owned by the `nzbstr` user.
- The `category` filter in qBittorrent config is important — do not set it to a category that contains torrents you didn't deliberately choose to publish.