Commit Graph
25 Commits
Author SHA1 Message Date
Daan MeijerandClaude Opus 5.5 41859636d3 Seed relay: nodes get their headers through the seed API
Test and deploy / test (push) Successful in 1m28s
Test and deploy / deploy (push) Successful in 2m5s
The seed API keeps a pool of NNTP connections (SEED_NNTP_CONNECTIONS) and
serves GET /api/v1/seed/ranges/:id/chunk?from=&to= for leased ranges:
the gzip stream the news server sent, passed on unopened. seed:node fetches
that way by default (SEED_FETCH=relay), so a node needs only a token and
SEED_CONNECTIONS; SEED_FETCH=direct keeps its own NNTP connections. All
fetching then comes from the indexer's IP (the account allows two).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 15:24:39 +02:00
Daan MeijerandClaude Opus 5.5 3d5cfb2527 Seeding nodes lease first, connect for the lease, close when idle
Test and deploy / test (push) Successful in 1m36s
Test and deploy / deploy (push) Successful in 1m58s
A failed NNTP login gives the range back uncounted (no attempt used), and a
node with nothing to lease holds no connection. Replaces log-in-before-lease,
which kept idle connections and logged in again every minute while idle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 15:18:38 +02:00
Daan MeijerandClaude Opus 5.5 70036847cf Seeding nodes log in before leasing; TLS through tunnels
Test and deploy / test (push) Successful in 1m29s
Test and deploy / deploy (push) Successful in 1m59s
- seed:node logs in to NNTP before taking a range and keeps the connection
  across ranges (replaced after 2 minutes idle or a failed range). A node
  that can't reach its news server takes no range: two ranges were set
  aside as failed while an office node's tunnel was being set up
  (ECONNREFUSED 127.0.0.1:1563, five leases each). After an IP-address
  limit refusal it waits 5 minutes.
- NNTP_TLS_SERVERNAME: the hostname the TLS certificate is checked
  against when NNTP_HOST is a tunnel ("Hostname/IP does not match
  certificate's altnames: Host: localhost").
- seed-node/README: the IP-address limit and tunnelling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 14:14:15 +02:00
Daan MeijerandClaude Opus 5.5 04f945055f XoverClient times replies only, not idle time between commands
Test and deploy / test (push) Successful in 1m12s
Test and deploy / deploy (push) Successful in 1m37s
A seeding node that waited over two minutes for the seed API (restarting)
had its idle NNTP connection closed by its own timeout, and gave its range
back. Now the timeout runs only while a reply is awaited.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 13:40:15 +02:00
Daan MeijerandClaude Opus 5.5 00f6187b8f Seed leases respect the backfill's disk limits
Test and deploy / test (push) Successful in 1m8s
Test and deploy / deploy (push) Successful in 1m37s
No new range while the database's NVMe part or the hdd tablespace is over
BACKFILL_MAX_NVME_GB / BACKFILL_MAX_HDD_GB; nodes ask again a minute later.
seed:status says when leasing is paused. The NVMe part was at 47 GB of 50
with a plan for all of a.b.ath queued.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 13:30:46 +02:00
Daan MeijerandClaude Opus 5.5 f9e9295d3e seed-node: step-by-step guide for running a node on a remote server
Test and deploy / test (push) Successful in 1m12s
Test and deploy / deploy (push) Failing after 43s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 13:11:17 +02:00
Daan MeijerandClaude Opus 5.5 2b14b0dca5 seed:node retries 404 from the proxy (seed API restarting)
Test and deploy / test (push) Successful in 1m0s
Test and deploy / deploy (push) Successful in 1m42s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 12:17:54 +02:00
Daan MeijerandClaude Opus 5.5 e2acfae98d seed:node posts batches in order beside fetching
Test and deploy / test (push) Successful in 57s
Test and deploy / deploy (push) Successful in 1m44s
At most two posts waiting; a failed post stops the chain, so nothing is
stored past a missing batch. The first remote node did 7k headers/s while
waiting for every post.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 12:09:16 +02:00
Daan MeijerandClaude Opus 5.5 8c36744088 Seed orchestrator: remote nodes lease ranges and post results over HTTPS
Test and deploy / test (push) Successful in 1m4s
Test and deploy / deploy (push) Successful in 1m37s
- seed_nodes (hashed bearer tokens) and seed_ranges (planned ranges with
  lease, resume point and counts); migration …140.
- SeedOrchestrator: lease (oldest plan first, FOR UPDATE SKIP LOCKED; a
  lease without a batch for 15 min passes to the next node and resumes at
  next_id), batch (stores files and fragments, moves next_id), finish,
  release, plan (from below live indexing / the backfill / earlier plans
  down to N days back; takes over the group's backfill), status.
- Seed API under /api/v1/seed (lease, ranges/:id/batches|done|release,
  status) behind a node-token middleware; JSON bodies up to 64 MB after
  gzip.
- seed:node runs a node: no database or Redis, outgoing HTTPS only, N
  connections, gzip batches, retries for network errors and 5xx, drops a
  range on 409, gives its ranges back on SIGTERM. seed-node/ has a compose
  file that builds the image from this repository.
- seed:token, seed:plan, seed:status for the operator. Header parsing moved
  to lib/ingest and the range loop to lib/seed/runner, shared by seed:range
  and seed:node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 12:02:01 +02:00
Daan MeijerandClaude Opus 5.5 97bdf8e982 Seeding node: gzip overviews, batched SFV lookups, fragments beside fetching
Test and deploy / test (push) Successful in 1m0s
Test and deploy / deploy (push) Successful in 1m43s
- lib/nntp/xover_client: a minimal one-connection client for seed:range
  with XFEATURE COMPRESS GZIP (a zlib stream after "224 … [COMPRESS=GZIP]",
  then a "." line; 420 for an empty range). About 6x less header traffic.
  Overviews come out with the field names nntp-js uses.
- SfvService.claimAll: one sfv_entries lookup per stored batch.
- Fragments go through the regular ingest path beside fetching (at most
  two batches in flight); a failure fails the run at the end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 11:11:41 +02:00
Daan MeijerandClaude Opus 5.5 68eb9a44fd Edge aggregation: seed:range indexes history one row per file
Test and deploy / test (push) Successful in 1m36s
Test and deploy / deploy (push) Successful in 3m37s
A seeding node fetches a range over one NNTP connection, parses headers
with the live rules, groups them into files in memory (lib/aggregate) and
stores per complete file its release, the file and a completed binary
(SeedService), instead of one parts row per article. What doesn't complete
within the window goes through the regular ingest path as fragments.
`--dry-run` only fetches and aggregates, to measure a node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 10:41:18 +02:00
Daan MeijerandClaude Opus 5.5 7ca338d4f3 Extension filter via a releases.extensions column
Test and deploy / test (push) Successful in 1m28s
Test and deploy / deploy (push) Successful in 58m9s
The first version matched extensions in search_text, which was wrong and
slow: file names enter search_text as stems without extension (so plainly
posted .mkv files were missed), and matching the TOASTed text took 3-4 s
for common types.

- Migration …130: releases.extensions text[] (two to four characters with
  a letter; .7z.001 and .r00 count as 7z and rar), kept current by triggers
  on files (name, release) and on releases (archive listing), filled with
  one sequential pass over files, with a GIN index.
- Search: ext matches with `extensions && ?`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 19:54:38 +02:00
Daan MeijerandClaude Opus 5.5 e1392608d8 Search filters for size (MB) and file extensions
Test and deploy / test (push) Successful in 1m27s
Test and deploy / deploy (push) Successful in 2m9s
- API: min_size and max_size in MB, ext as a comma-separated list. An
  extension matches as a whole word in search_text (file and entry names,
  dots as spaces), so "iso" finds .iso files but not "ISOLATION", which a
  plain search word matched as a substring. The trigram index serves it.
- Web UI: a size range and an extension field in the filter row, kept in
  the URL like the other filters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 19:43:55 +02:00
Daan MeijerandClaude Opus 5.5 95f1d427df Inspect fewer files: skip later volumes under random stems
Test and deploy / test (push) Successful in 1m25s
Test and deploy / deploy (push) Successful in 2m58s
Of 5,000 waiting inspection jobs, 3,212 were queued only because their
stem looked random, and 2,044 of those were later RAR volumes
(xyz.part47.rar, .r00) or par2 recovery volumes (.vol03+04.par2). Their
first segment adds nothing: the set is listed from its first volume and
named from its index .par2, as for readable names already. Each one cost a
~700 KB download; inspection pulled ~85 MB/s and fell behind the backfill.

- isCandidate() rejects later volumes (isLaterVolume) before the random-
  name rule.
- worker:file only queues files that are candidates, so the ~30% that
  inspection skips anyway no longer take queue slots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 09:32:40 +02:00
Daan MeijerandClaude Opus 5.5 2178847513 Stop refilling parts of binaries that are already a file
Test and deploy / test (push) Successful in 52s
Test and deploy / deploy (push) Successful in 1m23s
Crossposts, reposts and backfill ranges keep bringing articles of files
that worker:file already created. Ingest inserted their header parts again,
and GC's copied-parts pass (which searched all binaries for parts on every
batch) removed only 150k-450k per sweep: parts grew to 68M rows, 56M of
them belonging to finished files, and the 23 GB table paused the backfill.

- Ingest skips header parts (no per-part CRC) for binaries with a file_id.
- GC drops copied parts with a cursor over binaries, 2000 per statement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 02:53:11 +02:00
Daan MeijerandClaude Opus 5.5 d3e774a489 Add Gitea Actions: test every push, deploy master to homelab
Test and deploy / test (push) Successful in 4m20s
Test and deploy / deploy (push) Successful in 1m55s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 20:04:48 +02:00
Daan MeijerandClaude Opus 5.5 be8a3d4580 Spotnet import, header backfill, staging and counter grouping, faster search
Spotnet (CONCEPT D29):
- Index free.pt fully and import spots with their descriptions, categories
  and NZBs; a spot renames its release, since Spotnet names are more
  trustworthy than names derived from subjects.
- Find passwords in spot descriptions too, and tell "needs a password we
  have" from "needs a password we don't have".

Content inspection:
- List the contents of 7z (.7z.001) archives, SFV sets, nicovideo uploads
  and other archive-like files; mark yEncBin .bin containers as encrypted.
- Take real names from yEnc headers for randomly named files, and merge
  uploads that were split across posts.

Storage (D31, D32):
- Store segment lists zstd-compressed behind the FileStore accessor.
- group:backfill indexes older headers at low priority and pauses when
  the NVMe or HDD part of the database gets too large.

Grouping (D35, D36):
- Lone header segments wait in staged_segments until a second segment of
  their file arrives, which drops the per-article junk binaries.
- Counter grouping joins random-subject uploads by poster, [i/M] counter,
  part count and day.

Search and maintenance (D33, D34):
- A trigram-indexed search_text column, smaller indexes, estimated stats.
- Passes that clean up nonsense names; weekly index maintenance; GC budgets
  per kind.
- Web UI: grey out stale results while a search runs and cancel superseded
  requests. Count only up to 1,000 matches for the page, which made filter
  clicks take seconds (Newznab keeps the 10,000 cap).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 19:59:23 +02:00
Daan Meijer bcb9be5da9 implemented a lot of features 2026-09-29 00:32:01 +02:00
Daan Meijer 62f1767df3 omgebouwd naar adonisjs, werkt nog niet echt goed 2026-06-03 23:41:09 +02:00
Daan Meijer dfd3336447 download seems to work, par2 verification worked at least once 2026-06-01 00:36:34 +02:00
Daan Meijer ba6cb101a6 crc checks work 2026-05-31 22:48:00 +02:00
Daan Meijer fc947953e4 application restructuring, added crc check when decoding yenc files 2026-05-31 22:07:14 +02:00
Daan Meijer efbc1237d6 downloader can handle concurrent connections 2026-05-31 21:23:18 +02:00
Daan Meijer b5647521fd yenc functions decode 1 part files correctly 2026-05-31 21:12:05 +02:00
Daan Meijer d5b359191c first commit: decoding doesn't work well yet 2026-05-27 07:31:40 +02:00