Manielle d177e7b710
Test / cargo test (push) Failing after 2m35s
gutenberg: bind by title when Open Library lists no work, and answer work pages by searching
Two reasons a book's page in meta-read showed no Gutenberg edition, both found
on the live stack with Frankenstein (OL450063W, etext 84):

- An edition was bound to its work only when Open Library's search lists the
  etext under exactly one work (id_project_gutenberg). For many classics it
  lists it under none: no edition of OL450063W carries etext 84. Such an etext
  is now bound by title and author: same main title (main_title), an author
  with the same surname, and the most published of those works, since Open
  Library holds many small "Frankenstein" works by Mary Shelley. The record
  carries the same claim plus anchorMethod=title, so the gateway records
  matchedBy/openlibrary:OL...W and meta-read grades it Matched, below an
  identifier binding. An etext several works list (an unmerged duplicate)
  still gets no binding: gutenberg_etext keeps "several" apart from "none".
- A work page asks for openlibraryid:OL...W with no text, which fell through to
  the catalogue browse (the 32 most downloaded books), so a book outside them
  never reached its own page. Such a query now searches Gutendex for the work's
  main title and its first author's surname (work_search_text); the gateway
  still keeps only the records bound to the work.

Tests: title binding graded Matched while an identifier binding stays
Anchored; a work query searches "frankenstein shelley"; main_title, surname,
the most-published choice, ties and non-matches; Etext one/several/unlisted
and its cache form.
2026-10-19 19:01:28 +00:00
2026-09-12 16:37:22 +02:00
2026-07-14 16:07:43 +02:00
2026-09-12 16:37:22 +02:00
2026-09-12 16:37:22 +02:00
2026-06-14 21:34:28 +02:00

meta-feeder-book

Book feeder sidecar for MetaMesh. One binary, book-feeder, hosts two upstreams:

Upstream Serves What it is
gutenberg document × book Public-domain editions from Project Gutenberg (via the Gutendex API), with an epub url-locator per record
openlibrary card × book Work cards from Open Library — the book identity tier (TMDB : video :: AniList : comics :: Open Library : books)

The two meet through openlibraryid. A Gutenberg edition carries it only when Open Library itself records the link: exactly one Open Library work lists the etext's Project Gutenberg id (search.json?q=id_project_gutenberg:<id>). The edition then also carries the transient trust claim anchored=true, anchorSource=openlibrary, which the gateway stores as anchoredBy/openlibrary:OL…W. There is no title or author matching.

An Open Library card is addressed compute_card_cid("openlibrary", "OL…W"), the bare work id, byte-identical to meta-read's card_cid::openlibrary. A work with no cover or no description gets no card.

Role in MetaMesh

A feeder is a stateless HTTP sidecar. It finds records and fetches bytes; it does not talk to meta-core or the libp2p blockstore. The gateway core that calls it owns the meta-core store-back and the blockstore seeding. A gateway registers this feeder as a RemoteFeederPlugin pointing at its / and then drives the contract:

Endpoint Purpose
GET /manifest feeder identity + capabilities
GET /health liveness
POST /query, POST /query_stream structured search against the upstream
POST /compute enrichment / outcome compute
GET /fetch/:upstream_id/:record_id fetch a record's bytes
GET /blob/:upstream_id/:cid fetch a content-addressed blob
GET /config, GET /config/schema, GET|PUT /config/values runtime config UI + API

Configuration

Env var Default Notes
META_FEEDER_HTTP_LISTEN 0.0.0.0:8080 HTTP listen address
META_FEEDER_STATE_DIR /data/meta-feeder redb cache + state
OPENLIBRARY_CONTACT — e-mail or URL sent in the Open Library User-Agent. First-boot seed for the contact config field; a value saved in the config UI wins
OPENLIBRARY_TOP_N 12 cards per search
OPENLIBRARY_DISCOVERY_N 20 cards per browse row (popular:true → Open Library trending, fresh:true → recently added)
RUST_LOG info tracing filter

No upstream API key is required. Open Library asks API clients to identify themselves, so set a contact in production: an identified client gets a larger request budget. Both upstreams share one Open Library rate budget. The config page edits the gutenberg upstream, and the contact saved there is read by both.

Image

ghcr.io/worph/meta-feeder-book

Exposes 8080. Built and pushed by CI on every push to main (the main tag) and on v* tags (semver tags).

Build locally

The build context is the repo root (the Cargo workspace). meta-feeder-sdk is a git dependency pinned by tag, so the builder needs network access to github.com:

docker build -f feeder-plugin/book-feeder/Dockerfile -t ghcr.io/worph/meta-feeder-book:dev .

Tests (cargo is not needed on the host; wiremock stands in for every upstream):

docker run --rm -v "$PWD":/src -w /src -e CARGO_TARGET_DIR=/target rust:1.89 \
  cargo test -p book-feeder

Repo layout

Cargo.toml                      # workspace: members = feeder-plugin/*
feeder-plugin/book-feeder/      # this feeder's crate + Dockerfile
  src/gutenberg.rs              #   `gutenberg` upstream
  src/openlibrary.rs            #   `openlibrary` upstream (cards)
  src/openlibrary_client.rs     #   Open Library client (budget, cache, binding lookup)
crates/meta-feeder-sdk/         # old vendored SDK copy — no longer built (excluded)

meta-feeder-sdk comes from worph/meta-feeder-sdk at the tag in feeder-plugin/book-feeder/Cargo.toml. An SDK change reaches this feeder only when it is tagged and the pin is bumped.

S
Description
No description provided
Readme
419 KiB
Languages
Rust 97.2%
HTML 2.4%
Dockerfile 0.4%