1.1.0: reach nntmux over its Newznab API (search + getnzb, metamesh key) with a per-release .nzb cache; drop the MariaDB/storage coupling
Publish image / publish (push) Failing after 13s

This commit is contained in:
Worph
2026-10-04 12:30:11 +00:00
parent 45f88dcdfe
commit 0d9d0132de
14 changed files with 1143 additions and 1506 deletions
+42 -15
View File
@@ -1,8 +1,8 @@
# meta-feeder-usenet
Usenet feeder sidecar for the MetaMesh gateway tier. Bridges a Usenet header
scan **we run ourselves** (an [nntmux](https://github.com/NNTmux/newznab-tmux)
sidecar) into the MetaMesh network.
scan **we run ourselves** — the **NNTmux** app ([nntmux](https://github.com/NNTmux/newznab-tmux),
worph/AppStore) — into the MetaMesh network, over nntmux's Newznab API.
## Why this exists
@@ -33,11 +33,11 @@ Design record:
## Shape
```
[nntmux sidecar: PHP + MariaDB + Redis] [usenet-feeder: Rust, FeederPlugin]
header-scan groups (few conns) ──► reads `releases` (searchname/size/guid)
collate → releases ──► gunzips <nzb_root>/<c>/<guid>.nzb.gz
writes <guid>.nzb.gz ──► mints 0x1003 from the Message-ID set
(CBP purged; the gz is the only ──► emits the record + a relative
[NNTmux app: PHP + MariaDB + Redis] [usenet-feeder: Rust, FeederPlugin]
header-scan groups (few conns) ◄── t=search&q=… (Newznab API, `metamesh` key)
collate → releases ◄── /getnzb?id=<guid> once per release → cache
writes <guid>.nzb.gz mints 0x1003 from the Message-ID set
(CBP purged; the .nzb is the only emits the record + a relative
durable home of the msg-ids) `manifest_url` onto its own /blob route
│
▼
@@ -51,9 +51,34 @@ Design record:
provider → materialise → play
```
The sidecar is **persistent cache, cheap to lose** — everything in it is
re-derivable by re-scanning. Same pattern as the Tribler sidecar: stateful
upstream, but the gateway still sees a stateless HTTP feeder.
### nntmux over its API, not its database (1.1)
Until 1.1 the feeder read nntmux's MariaDB with SQL and gunzipped
`<guid>.nzb.gz` off nntmux's storage volume. That made the two a single unit:
the feeder held nntmux's DB password, depended on its table layout and its NZB
shard depth, and had to mount its storage. nntmux is now its own app, and the
feeder uses the same contract any Newznab client uses (the torznab feeder does
the same with Prowlarr):
| Need | Call | nntmux counter |
|---|---|---|
| search | `GET {nntmux_url}/api/v1/api?t=search&q=…&extended=1&apikey=…` | `apirequests` |
| a release's `.nzb` (Message-IDs) | `GET {nntmux_url}/getnzb?id=<guid>.nzb&r=<key>` | `downloadrequests` |
| compute (by guid) | `t=details&id=<guid>` | `apirequests` |
| reachability (`/health`) | `t=caps` (no key) | — |
- ⚠ The API is under **`/api/v1/api`**; `/api` is a 404. `t=get` redirects to
`/getnzb` on the *public* `APP_URL` host, so the feeder calls `/getnzb` on the
internal host directly.
- Every search hit must carry its `0x1003` cid (else meta-watch shows
"Unavailable"), so a hit costs one `/getnzb` **the first time it is seen**.
Each `.nzb` is cached under `<state>/gateway/usenet/nntmux-nzb/<guid>.nzb`
(a collated release never changes) and reused by repeat searches, `/compute`
and `/blob`.
- nntmux caps every role per 24 h (Admin 1000/1000, User 10/10) and throttles
`/api` to 60/min. Use the NNTmux app's dedicated **`metamesh`** user (its role
has raised caps); the feeder paces itself to ≤ 50 requests/min, and stops a
query's downloads once nntmux reports a cap (`500`/`501`).
Like every feeder this one is **meta-core-free and blockstore-free**: it finds
records and serves bytes; the core owns hashing-into-the-blockstore, the
@@ -68,18 +93,20 @@ and there is no hot reload.
| Field | Env seed | What |
|---|---|---|
| `db_url` | `NNTMUX_DB_URL` | MariaDB DSN of nntmux's catalog, e.g. `mysql://nntmux:secret@nntmux-db:3306/nntmux`. Blank → self-scan search is off. |
| `nzb_root` | `NNTMUX_NZB_PATH` | nntmux's NZB directory **as mounted into this container**. Must be the same volume nntmux's `PATH_TO_NZBS` points at, or every release resolves to `NotFound`. |
| `nntmux_url` | `NNTMUX_URL` | Base URL of the NNTmux app as this container reaches it (default `http://nntmux`); the API path is appended. |
| `nntmux_api_key` | `NNTMUX_API_KEY` | API key of the nntmux user the feeder searches as (the `metamesh` user). Blank → self-scan search is off. |
| `indexers` | — (config page only) | `[{host, api_key}]` Newznab keys for redeeming `nzb-release` locators. `host` is the bare authority (`api.nzbgeek.info` — no scheme, no `/api`). |
Both halves are optional; with neither configured the plugin reports Degraded.
A grab-only deployment (keys, no nntmux) is valid.
Both halves are optional; with neither configured the plugin reports Degraded,
as it does when nntmux is unreachable or refuses the key. A grab-only deployment
(keys, no nntmux) is valid. A pre-1.1 `config.json` (`db_url`/`nzb_root`) is
ignored with a warning.
Infra env: `META_FEEDER_HTTP_LISTEN` (default `0.0.0.0:8080`),
`META_FEEDER_STATE_DIR` (default `/data/meta-feeder`), `RUST_LOG`.
Scan policy — which groups, backfill depth, scan interval, connection budget —
is the **gateway maintainer's** call and is configured on the nntmux sidecar.
is the **gateway maintainer's** call and is configured in the NNTmux app.
Keep the connection budget conservative: the scanner and playback draw on the
same provider slots, so a greedy value does not merely over-scan, it stalls
playback mid-stream.