Skip to content

feat(peernet): add peer network foundation and metadata sharing - #1068

Open
javi11 wants to merge 1 commit into
mainfrom
feat/peernet-metadata-sharing
Open

javi11 wants to merge 1 commit into
mainfrom
feat/peernet-metadata-sharing

Conversation

@javi11

@javi11 javi11 commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds an opt-in peer network, disabled by default, that lets AltMount instances exchange signed release records and health attestations. This is the first PR of a stack; bundles (sharing the finished import itself) follow in a second PR on top.

Design: docs/superpowers/specs/2026-11-12-peer-network-design.md
User docs: docs/docs/3. Configuration/peer-network.md

Foundation

  • Node identity (Ed25519) and mutual TLS 1.3 on a dedicated peer listener (default port 8470), started only when "accept incoming" is on. It serves peer routes only and accepts none of the main API's credentials.
  • Open network: user-entered peer addresses plus peer exchange. No DHT and no shipped bootstrap nodes.
  • Private networks: one instance hosts, issues single-use invites, 48-hour membership certificates and a signed revocation list.
  • Exposure level per network: off, receive only, share metadata. "Share bundles" is reserved and disabled in the UI.
  • Release and file content IDs with a background index.
  • Settings section, status API, documentation.

Metadata sharing

  • Signed, canonical release records and health attestations, 2 KB cap, strict decoders with fuzz tests.
  • Pull and push record exchange with per-peer cursors, quotas, per-relayer caps, fair eviction, a node-wide and per-relayer ingest rate, and back-off for hostile peers.
  • Publishing from healthy local checks. Category and indexer exclusions are applied before anything is signed. Passwords embedded in NZB names are stripped and a title that still contains the password is not published.
  • Network evidence can shorten one scheduled health check (or skip one in a private network). It never writes a health status and never lengthens an interval. Evidence needs corroboration from publishers this node has verified for 24 hours, one per network prefix, with a minimum check coverage.
  • Library streams matched by exact release ID are labelled when they are healthy on the network.

Security model

Peer data is advisory and every peer input is treated as hostile. Peer strings are length-capped, sanitised and rendered as text only; no peer input reaches a path, a template, a command or SQL text. Outbound connections refuse loopback always and private addresses unless a private network enables them, never follow redirects, and send nothing until the other side has completed mutual TLS as the expected node.

Changes outside the feature

  • internal/health: optional observer and adviser hooks (nil by default), check statistics on health events, checker.go split into smaller files. No behaviour change when the feature is off.
  • internal/providerbackbone: the backbone dataset cache moved out of the API handler so it can be shared.
  • internal/nzbname: the importer's password-in-name patterns moved to a shared package.
  • internal/metadata: optional file observer and a RenameDirectory method.
  • Migrations 044-056 for SQLite and PostgreSQL.

Test plan

  • go build ./..., go vet ./...
  • go test -race ./... (see note on internal/par2repair below)
  • internal/database tests against PostgreSQL 16, migrations applied from empty
  • golangci-lint clean on the new packages (run per package)
  • Frontend: type check, unit tests, production build
  • Fuzzing of record, page and push decoders
  • Two instances over a LAN: host and join a private network, publish at level 2, receive at level 1, decode received payloads and confirm no path, host, category, indexer or password is present
  • Run with real providers end to end (the two-instance run used a stub NNTP server, so health verdicts there were synthetic)
  • Health-check shortening and the stream label exercised live between two nodes holding the same release
  • Open-network peering between two publicly reachable nodes
  • Settings UI checked at phone width and in dark theme

Notes for reviewers

  • internal/par2repair TestRunJobPrefetchSpansFileBoundaries fails intermittently under -race. This PR does not touch that package.
  • A whole-repo golangci-lint run fails to load internal/encryption with the current toolchain, independent of this PR.
  • Library files imported in the older metadata format have a file ID but no release ID, so they are not shared yet. The settings page shows how many files can be shared. The follow-up PR addresses this.
  • Media IDs are only known for imports that came through the Stremio addon.

Adds an opt-in peer network, disabled by default, that lets AltMount
instances exchange signed release records and health attestations.

Foundation:
- node identity (Ed25519), mutual TLS 1.3 on a dedicated peer listener
- open network with user-entered peers and peer exchange
- private networks with a host, single-use invites, 48-hour membership
  certificates and a signed revocation list
- per-network exposure levels (off, receive only, share metadata;
  share bundles is reserved and not available yet)
- release and file content IDs with a background index
- settings UI, status API and documentation

Metadata sharing:
- signed, canonical release records and health attestations (2 KB cap)
- pull and push record exchange with cursors, quotas, per-relayer caps,
  fair eviction, rate limits and back-off
- publishing from healthy local checks, with category and indexer
  exclusions; passwords embedded in NZB names are never published
- network evidence can shorten one scheduled health check, or skip one
  in a private network; it never writes a health status
- library streams matched by exact release ID are labelled when they
  are healthy on the network

Peer data is advisory and every peer input is treated as hostile.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant