- Clicking a publisher row fills the override form with their npub and copies it to clipboard
- Settings page: section-level descriptions explaining what each setting does and how it affects kindexr
- Removed Mute button from publishers table (effectively same as Block; already-muted rows still show as blocked)
- qBittorrent: fix session cookie parsing for newer versions that use QBT_SID_<port>= instead of SID=
- qBittorrent: empty categories now fetches all completed torrents instead of returning nothing
- Update README with Deluge/qBit publishing docs and Settings UI table
- Use pool_max_idle_per_host(0) on reqwest Client so each Deluge JSON-RPC
call gets a fresh TCP connection; Twisted HTTP closes keep-alive conns
aggressively causing "connection closed before message completed" errors
- Remove always-visible restart banner from Settings page; the old
condition (settings_count > 0) was permanently true after any save
- Drop unused HashMap import in deluge.rs
- Update dev config: Deluge enabled with labels=[], dev password set
Both torrent clients now configurable from /ui/settings with enabled
toggle, URL, password, poll interval, and labels/categories fields.
Settings persist to DB and apply on next restart.
Deluge uses a JSON-RPC HTTP API exposed by the Web UI plugin (port 8112),
not the daemon binary protocol (port 58846). The integration logs in,
fetches completed torrents, filters by configured labels, and enqueues
them the same way as the qBittorrent poller.
- DelugeConfig in config.rs (enabled, url, password, poll_interval_secs, labels)
- src/publisher/deluge.rs: DelugeClient with login/_session_id cookie
handling, core.get_torrents_status, label + is_finished filtering
- Watcher: deluge_poll_loop alongside qb_poll_loop; each only starts
if its respective client is configured/enabled
- Example config updated with deluge section and Web UI port note
- list_api_keys and delete_api_key DB functions
- API keys section at top of Settings: table of existing keys (masked),
generate new key with label, revoke button per key
- New key displayed once in a green copy box after generation with a
clipboard copy button; not shown again after leaving the page
Track the last seen event timestamp per relay in the DB. On startup,
subscribe to kind 2003 per relay using that timestamp as since instead
of always recomputing from backfill_days. Fresh relays with no stored
timestamp fall back to the full backfill window.
- Follow depth column: badges (you/direct/2nd/—) instead of raw 0/1/2
- Column tooltip explains hop distance in plain English
- Trust override form: "Local override only — nothing is broadcast to Nostr"
- publisher_count stat now counts only publishers with torrents_n > 0;
previously counted every pubkey inserted by the WoT follow-graph
builder which inflated the number with non-publishing follows
- NetworkConfig/ProxyConfig in config.rs: mode=direct|tor|i2p|socks5
with socks5_addr() helper that maps tor→9050, i2p→4446, socks5→url
- build_http_client() routes reqwest through SOCKS5 when configured
- build_nostr_opts() routes nostr-sdk WebSocket connections through
SOCKS5 using Connection::new().proxy(addr)
- Shared http_client passed to Enricher and BlossomClient so proxy
applies to TMDB and Blossom blob fetches as well
- nostr_opts passed to Reader, WotBuilder, ProfileFetcher
- Dashboard: network status card showing proxy mode, listen address,
IPv4/IPv6; relay section now shows X connected / Y configured
- Publishers: rename vouch form to "Override publisher trust", add
clear note that this is local-only (not broadcast to Nostr), explain
WoT comes from NIP-02 kind 3; rename column headers for clarity
- Example and dev configs updated with network.proxy section
relay_url was being destructured as .. and thrown away, so
update_relay_last_event was called with an empty string and
matched no rows. Capture relay_url from the notification and
pass it through so each relay row gets its last_event updated.
NIP-35:
- Accept 64-char SHA256 info hashes (BitTorrent v2) in addition to 40-char SHA1 (v1)
- Parse `size` tag as explicit total size; file-size sum is now a fallback only
- Parse URL hint from third element of `x` tag as blossom_url fallback
- Parse `magnet` tag on ingest and extract trackers from it
- Emit `magnet` tag in published NIP-35 events
NIP-56:
- Handle `e` tags in kind 1984 reports: look up the torrent event's
publisher and penalise them the same as a direct `p` tag report
- BlossomClient: unauthenticated fetch, kind 24242 signed upload
- Parser: capture `url` tag from NIP-35 events as blossom_url
- Reader: spawn background fetch + cache of blob when url tag present
- Writer: include `url` tag in published NIP-35 events when blob uploaded
- Watcher: upload .torrent file to Blossom before publishing, attach URL
- Migration 007: adds blossom_url + torrent_blob columns to torrents
- Route GET /torrent/<info_hash>.torrent serves cached blobs
- Config: blossom.enabled + blossom.server (disabled by default)
insert_torrent now upserts into publishers so torrent authors appear
in the publishers table immediately, making the name/nip05 join work
on the indexed and dashboard pages. Migration 006 backfills existing
torrent pubkeys on fresh DB opens.
Fetch kind 0 metadata events for known publishers in background
batches (150 at a time, hourly). Store name, nip05, picture, about
in new columns (migration 005). UI now shows avatar + display name
instead of raw pubkeys across indexed, dashboard, and publishers views.
Adds the full writer/publisher stack: NIP-35 event signing and relay
delivery, qBittorrent polling with publish-delay queue, lava_torrent
.torrent file parsing, TMDB inline lookup before publish, and
kindexr-cli identity/publish subcommands.
- Migration 003: add wot_level/muted/report_count to publishers;
create curation_items and reports tables
- WoT builder (wot/follows.rs): fetches operator kind-3 contact list
to build depth-1 and depth-2 trust graph; ingests kind-10000 mute
list; runs at startup and refreshes every 24h
- Trust scoring (wot/trust.rs): recomputes trust from wot_level and
report_count after each WoT build
- Reader: subscribes to kind-30004 (curation sets) from configured
pubkeys; subscribes to kind-1984 (reports) from anyone; WoT/block/
mute check gates kind-2003 ingest when wot_only=true; auto-block
publishers that hit report threshold
- Search: curated events (referenced by kind-30004) sort first in
both FTS and full-scan paths
- Publisher CLI: list, info, block, unblock, trust, mute subcommands
- Fix .gitignore: /bin/ not bin/ (was shadowing src/bin/)
- tvsearch/movie/music/book endpoints with ID filters (tvdbid, imdbid,
tmdbid), season/ep, and type-specific default category restrictions
- Title parser (enrich/parser.rs) extracts season, episode, quality,
source from scene-format names; fills DB fields not supplied by tags
- TMDB enricher (enrich/tmdb.rs) runs as background task after insert
when no TMDB ID in event; uses reqwest against TMDB v3 API directly
- Adult content exclusion at ingest via curation.exclude_categories
- Simplified server.rs routing: t= param now part of SearchQuery
- Remove unused tmdb-api alpha crate; use reqwest directly
- fix two stale nzbstr comment refs
- migration 002: drop api_keys.visibility and curation_set
- remove Visibility from APIKey struct, GetAPIKey, CreateAPIKey, CLI
- omit torznab size/seeders/peers attrs when data is absent
- reset relay backoff on successful connection (>= 30s uptime)
- use last_event from relays table as since on reconnect
- fix TestSearchWithResults to actually query the test server DB
Reader (internal/nostr):
- Connects to all configured relays via WebSocket
- Subscribes to kind 2003 (NIP-35) events since (now - backfill_days)
- Parses x/title/file/tracker/i/t tags into DB rows
- Reconnects with exponential backoff (5s → 5min) on disconnect
Torznab API (internal/torznab):
- GET /api?t=caps — XML capabilities doc (exact shape Sonarr expects)
- GET /api?t=search — FTS5 full-text search over title+description
- All other t= types (tvsearch, movie, etc.) route to the same search handler
- API key auth via ?apikey= query param; 401 XML error on missing/bad key
- Magnet links built from info_hash + event tracker tags
DB (internal/db/queries.go):
- InsertTorrent with upsert + related rows (trackers, tags, files)
- Search with FTS5 + optional category filter (parent cat expands to range)
- GetAPIKey, CreateAPIKey, UpsertRelay, UpdateRelaySync/LastEvent
CLI (cmd/kindexr-cli):
- apikey create --label <name> [--visibility all|wot|curated]
Health endpoint now reports live relays_connected count.