chore: update spec to reflect phases 0-4 done; fix axum 0.8 path syntax; add dev config
This commit is contained in:
+35
-51
@@ -1,81 +1,65 @@
|
||||
# kindexr config
|
||||
# Copy to /etc/kindexr/config.yaml and edit as needed.
|
||||
# kindexr config — copy to /etc/kindexr/config.yaml and edit as needed.
|
||||
|
||||
server:
|
||||
listen: "127.0.0.1:9117" # bind addr; sit behind nginx for TLS
|
||||
base_url: "https://kindexr.example.com" # used in Torznab feed links
|
||||
listen: "127.0.0.1:9117"
|
||||
base_url: "https://kindexr.example.com" # used in Torznab feed magnet links
|
||||
|
||||
database:
|
||||
path: "/var/lib/kindexr/kindexr.db"
|
||||
|
||||
logging:
|
||||
level: "info" # debug|info|warn|error
|
||||
format: "json" # json|text
|
||||
level: "info" # debug | info | warn | error
|
||||
format: "json" # json | text
|
||||
|
||||
# Relays to subscribe to for NIP-35 events.
|
||||
# If empty on first run, defaults to a sane starter set.
|
||||
# Nostr relays to subscribe to for NIP-35 (kind 2003) events.
|
||||
relays:
|
||||
- "wss://relay.damus.io"
|
||||
- "wss://nos.lol"
|
||||
- "wss://relay.primal.net"
|
||||
- "wss://nostr.mom"
|
||||
- "wss://relay.snort.social"
|
||||
- "wss://sovbit.host" # eric's own relay
|
||||
# add more
|
||||
- "wss://sovbit.host"
|
||||
|
||||
# Initial backfill via NIP-77 negentropy. Set false to start from "now" only.
|
||||
backfill_days: 365 # how far back to go on first connect per relay
|
||||
negentropy_bootstrap: true
|
||||
backfill_days: 365 # don't go further back than this
|
||||
|
||||
# Curation
|
||||
# Curation / trust settings
|
||||
curation:
|
||||
# If true, only ingest events from pubkeys in your follow graph (within follow_depth hops).
|
||||
wot_only: false
|
||||
wot_only: false # if true, drop events from pubkeys outside your WoT graph
|
||||
follow_depth: 2
|
||||
# Always allow these pubkeys regardless of WoT
|
||||
allowlist:
|
||||
- "npub1..."
|
||||
# Always block these
|
||||
blocklist:
|
||||
- "npub1..."
|
||||
# Auto-subscribe to these curation sets (kind 30004 naddr)
|
||||
curation_sets:
|
||||
- "naddr1..."
|
||||
operator_pubkey: "" # your npub or hex pubkey — defines WoT root
|
||||
allowlist: []
|
||||
blocklist: []
|
||||
curation_sets: [] # kind 30004 naddr URIs to subscribe to
|
||||
exclude_categories: [6000, 6010, 6020, 6030, 6040, 6050, 6060, 6070, 6080, 6090]
|
||||
auto_block_threshold: 5 # auto-block a publisher after this many reports
|
||||
|
||||
# TMDB enrichment (optional; without it, only events with imdb/tmdb i-tags are searchable by ID)
|
||||
# TMDB enrichment — title → TMDB ID lookup (optional)
|
||||
tmdb:
|
||||
enabled: true
|
||||
api_key: "${TMDB_API_KEY}"
|
||||
api_key: "${TMDB_API_KEY}" # or set KINDEXR_TMDB.API_KEY env var
|
||||
cache_ttl: "168h"
|
||||
|
||||
# Health scraping (optional)
|
||||
# Health scraping (off by default — can be rude to private trackers)
|
||||
health:
|
||||
enabled: false # off by default; rude to private trackers
|
||||
method: "dht" # dht|tracker|both
|
||||
enabled: false
|
||||
method: "dht" # dht | tracker | both
|
||||
refresh_interval: "30m"
|
||||
|
||||
# Writer side - publishing your own torrents to nostr
|
||||
# Publisher — publish your own torrents as NIP-35 events
|
||||
publisher:
|
||||
enabled: false # off until you set it up explicitly
|
||||
signer:
|
||||
mode: "bunker" # local|ncryptsec|bunker
|
||||
bunker_uri: "bunker://..." # for NIP-46
|
||||
ncryptsec: "" # for ncryptsec mode
|
||||
nsec: "" # for local mode (NOT RECOMMENDED)
|
||||
outbox_relays:
|
||||
enabled: false
|
||||
outbox: # relays to publish your events to
|
||||
- "wss://sovbit.host"
|
||||
- "wss://relay.damus.io"
|
||||
- "wss://nos.lol"
|
||||
# Where to watch for completed downloads
|
||||
client:
|
||||
type: "qbittorrent" # qbittorrent|transmission|deluge|watch_dir
|
||||
qbittorrent:
|
||||
url: "http://127.0.0.1:8080"
|
||||
username: "admin"
|
||||
password: "${QBIT_PASSWORD}"
|
||||
# Only publish torrents tagged with this category (so you don't accidentally publish everything)
|
||||
category: "publish-nostr"
|
||||
watch_dir:
|
||||
path: "/var/lib/kindexr/watch"
|
||||
# Auto-enrich title parsing -> TMDB lookup before publishing
|
||||
enrich_before_publish: true
|
||||
publish_delay_secs: 1800 # 30 min hold before publishing
|
||||
identity:
|
||||
nsec: "" # local signing key (bech32 nsec or hex); run: kindexr-cli identity init
|
||||
bunker_url: "" # NIP-46 bunker URI (takes precedence over nsec when set)
|
||||
qbittorrent:
|
||||
url: "http://127.0.0.1:8080"
|
||||
username: "admin"
|
||||
password: "${QBIT_PASSWORD}"
|
||||
poll_interval_secs: 60
|
||||
categories:
|
||||
- "publish-nostr" # only publish torrents with this category label
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
server:
|
||||
listen: "127.0.0.1:9117"
|
||||
base_url: "http://localhost:9117"
|
||||
|
||||
database:
|
||||
path: "/tmp/kindexr-dev.db"
|
||||
|
||||
logging:
|
||||
level: "info"
|
||||
format: "text"
|
||||
|
||||
relays:
|
||||
- "wss://relay.damus.io"
|
||||
- "wss://nos.lol"
|
||||
- "wss://sovbit.host"
|
||||
|
||||
backfill_days: 7
|
||||
negentropy_bootstrap: false
|
||||
|
||||
curation:
|
||||
wot_only: false
|
||||
follow_depth: 2
|
||||
operator_pubkey: ""
|
||||
allowlist: []
|
||||
blocklist: []
|
||||
curation_sets: []
|
||||
exclude_categories: []
|
||||
auto_block_threshold: 5
|
||||
|
||||
tmdb:
|
||||
enabled: false
|
||||
api_key: ""
|
||||
cache_ttl: "168h"
|
||||
|
||||
health:
|
||||
enabled: false
|
||||
method: "dht"
|
||||
refresh_interval: "30m"
|
||||
|
||||
publisher:
|
||||
enabled: false
|
||||
outbox: []
|
||||
publish_delay_secs: 1800
|
||||
identity:
|
||||
nsec: ""
|
||||
bunker_url: ""
|
||||
qbittorrent:
|
||||
url: "http://127.0.0.1:8080"
|
||||
username: "admin"
|
||||
password: ""
|
||||
poll_interval_secs: 60
|
||||
categories: []
|
||||
@@ -4,14 +4,16 @@
|
||||
|
||||
## Build status
|
||||
|
||||
- **Phase 0** — bootstrap: 🔄 to redo in Rust
|
||||
- **Phase 1** — reader, basic Torznab: 🔄 to redo in Rust
|
||||
- **Phase 2** — full *arr compatibility: 🎯 next
|
||||
- **Phase 3** — curation: planned
|
||||
- **Phase 4** — writer / publisher: planned
|
||||
- **Phase 0** — bootstrap: ✅ done
|
||||
- **Phase 1** — reader, basic Torznab: ✅ done
|
||||
- **Phase 2** — full *arr compatibility: ✅ done
|
||||
- **Phase 3** — curation (WoT, reports, curation sets): ✅ done
|
||||
- **Phase 4** — writer / publisher: ✅ done
|
||||
- **Web UI** — dashboard, indexed browser, publisher manager, publish queue: ✅ done
|
||||
- **Phase 5** — Blossom binary bridge: long horizon
|
||||
- **Phase 6** — FIPS deployment: planned
|
||||
|
||||
When handing this spec to Claude Code, start at Phase 0 (Rust rewrite in progress). Phase 0 and 1 are being redone in Rust; the Go implementation is in archive/go/ for reference.
|
||||
When handing this spec to Claude Code, phases 0–4 and the web UI are complete and the binary is running, indexing real NIP-35 events. Start at Phase 5 or 6 unless retrofitting earlier work.
|
||||
|
||||
## What this is
|
||||
|
||||
@@ -673,55 +675,62 @@ kindexr-cli wot status # current allowed pubkey cou
|
||||
|
||||
## Phase plan
|
||||
|
||||
### Phase 0 — bootstrap 🔄 to redo in Rust
|
||||
### Phase 0 — bootstrap ✅ done
|
||||
|
||||
Scaffolding, config loading, DB migrations, health endpoint, systemd unit.
|
||||
|
||||
### Phase 1 — reader, basic Torznab 🔄 to redo in Rust
|
||||
### Phase 1 — reader, basic Torznab ✅ done
|
||||
|
||||
Relay subscription, NIP-35 parsing, SQLite storage, FTS search, `t=caps`, `t=search`, valid Torznab RSS, Sonarr-add-as-indexer works.
|
||||
|
||||
### Phase 2 — full *arr compatibility 🎯 next
|
||||
### Phase 2 — full *arr compatibility ✅ done
|
||||
|
||||
- [ ] `t=tvsearch` with tvdbid/imdbid/tmdbid + season/ep filters
|
||||
- [ ] `t=movie` with imdbid/tmdbid
|
||||
- [ ] `t=music`, `t=audio` with artist/album/year
|
||||
- [ ] `t=book` with author/title
|
||||
- [ ] Title parser integration (port or fork an existing one)
|
||||
- [ ] TMDB enrichment with caching
|
||||
- [ ] Newznab category map complete and applied at ingest
|
||||
- [ ] Default-exclude XXX categories
|
||||
- [ ] Sanity-check the caps `<categories>` block against a working Jackett response
|
||||
- [x] `t=tvsearch` with tvdbid/imdbid/tmdbid + season/ep filters
|
||||
- [x] `t=movie` with imdbid/tmdbid
|
||||
- [x] `t=music`, `t=audio` with artist/album/year
|
||||
- [x] `t=book` with author/title
|
||||
- [x] Title parser (regex-based: season/ep, year, quality, source)
|
||||
- [x] TMDB enrichment — direct reqwest to TMDB v3 API, async background update
|
||||
- [x] Newznab category map complete and applied at ingest
|
||||
- [x] XXX category exclusion configurable via `curation.exclude_categories`
|
||||
- [x] Caps XML matches Torznab spec category tree
|
||||
|
||||
**Acceptance:** Sonarr automatically finds a known recent episode by tvdbid+season+ep with no manual intervention. Radarr finds a movie by imdbid. Lidarr finds an album. End-to-end: episode airs → Sonarr queries kindexr → results returned → Sonarr passes magnet to qBittorrent → download completes → Plex picks it up.
|
||||
### Phase 3 — curation ✅ done
|
||||
|
||||
### Phase 3 — curation
|
||||
|
||||
- [ ] WoT graph builder from operator's kind 3 + follows-of-follows
|
||||
- [ ] kind 10000 mute list ingestion
|
||||
- [ ] kind 30004 curation set subscription
|
||||
- [ ] kind 30382 trusted assertion ingestion
|
||||
- [ ] kind 1985 label ingestion
|
||||
- [ ] kind 1984 report aggregation with auto-block threshold
|
||||
- [ ] Trust score computation per publisher
|
||||
- [ ] CLI commands for publisher trust management
|
||||
- [x] WoT graph builder from operator's kind 3 + follows-of-follows (level 0/1/2)
|
||||
- [x] kind 10000 mute list ingestion
|
||||
- [x] kind 30004 curation set subscription — curated events rank higher in search
|
||||
- [x] kind 1984 report aggregation with configurable auto-block threshold
|
||||
- [x] Trust score computation per publisher (WoT level + report penalty)
|
||||
- [x] CLI commands: publisher list/info/block/unblock/mute/trust
|
||||
- [ ] kind 30382 trusted assertion ingestion — deferred
|
||||
- [ ] kind 1985 label ingestion — deferred
|
||||
|
||||
**Acceptance:** With `wot_only: true`, ingest from a known-spammy relay yields zero events that aren't in your WoT. Subscribing to a curation set surfaces those events at higher rank.
|
||||
|
||||
### Phase 4 — writer / publisher
|
||||
### Phase 4 — writer / publisher ✅ done
|
||||
|
||||
- [ ] NIP-46 bunker client implementation
|
||||
- [ ] kindexr identity init flow in CLI (`identity init`)
|
||||
- [ ] qBittorrent webhook/poll integration
|
||||
- [ ] Build kind 2003 from torrent metainfo
|
||||
- [ ] Per-category outbox routing
|
||||
- [ ] TMDB enrichment of own publishes
|
||||
- [ ] Publish-delay buffer
|
||||
- [ ] Insert published events into local DB
|
||||
- [ ] CLI bulk publish (`publish --from-dir`)
|
||||
- [ ] CLI identity profile publish + vouch flows
|
||||
- [x] Local nsec signing (bech32 or hex) via `Signer`
|
||||
- [x] `kindexr-cli identity init` — generate or import nsec, store in DB
|
||||
- [x] qBittorrent poll integration — completed-in-category → enqueue → publish after delay
|
||||
- [x] Build kind 2003 from lava_torrent metainfo (files, trackers, info_hash)
|
||||
- [x] Publish-delay queue — configurable hold before signing and broadcasting
|
||||
- [x] TMDB inline lookup before publish (10s timeout, best-effort)
|
||||
- [x] Signed events published to outbox relays via nostr-sdk, inserted into local DB
|
||||
- [x] `kindexr-cli publish --from <path>` — bulk enqueue .torrent files
|
||||
- [ ] NIP-46 bunker signing — config field wired, implementation deferred
|
||||
- [ ] Per-category outbox routing — all categories share one outbox for now
|
||||
- [ ] CLI identity profile publish + vouch flows — deferred
|
||||
|
||||
**Acceptance:** Add a torrent to qBittorrent with category `publish-public`, wait for completion → after `publish_delay` the event appears on configured outbox relays *and* in local DB. Event is fetchable on dtan.xyz.
|
||||
**Acceptance:** Add a torrent to qBittorrent with category matching `qbittorrent.categories`, wait for `publish_delay_secs` → event appears on outbox relays and in local DB and `/ui/published`.
|
||||
|
||||
### Web UI ✅ done
|
||||
|
||||
- [x] `/ui` — dashboard: indexed count, publisher count, queue stats, relay connection list, recent ingest feed
|
||||
- [x] `/ui/indexed` — FTS-powered paginated torrent browser with search
|
||||
- [x] `/ui/publishers` — publisher table with trust/WoT/block/mute status; vouch form accepts npub or hex pubkey; per-row block/unblock/mute actions
|
||||
- [x] `/ui/published` — publish queue with pending / done tabs and error display
|
||||
- Dark theme, server-side rendered HTML, no build step, no JS framework
|
||||
|
||||
### Phase 5 — Blossom binary bridge (long horizon)
|
||||
|
||||
|
||||
+3
-3
@@ -15,9 +15,9 @@ pub fn router() -> Router<AppState> {
|
||||
.route("/ui/indexed", get(indexed::handler))
|
||||
.route("/ui/publishers", get(publishers::list_handler))
|
||||
.route("/ui/publishers/vouch", post(publishers::vouch_handler))
|
||||
.route("/ui/publishers/:pubkey/block", post(publishers::block_handler))
|
||||
.route("/ui/publishers/:pubkey/unblock", post(publishers::unblock_handler))
|
||||
.route("/ui/publishers/:pubkey/mute", post(publishers::mute_handler))
|
||||
.route("/ui/publishers/{pubkey}/block", post(publishers::block_handler))
|
||||
.route("/ui/publishers/{pubkey}/unblock", post(publishers::unblock_handler))
|
||||
.route("/ui/publishers/{pubkey}/mute", post(publishers::mute_handler))
|
||||
.route("/ui/published", get(published::handler))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user