Skip to content

Capabilities and wiring

A capability is a named, contracted thing a service does: search indexers, take a torrent, serve the library to a player, say whether a person is who they claim to be. The stack’s wiring asks for capabilities rather than naming services, so putting a different service in one’s place is a single setting rather than a hunt for everything that pointed at the old one.

The word is used for three different sets, and they never mix:

Set Where it is declared What it says
Service capabilities provides on a service What the service can be relied on to do
What lemonfiber offers a plugin [requires] in plugin.toml Which extension a plugin needs this build to support
Kernel grants grants on a service in the stack’s manifest What the kernel lets a container do

This page is about the first. The second is at extension points, which lists what this build offers a plugin and refuses anything else. The third is never available to a plugin: a manifest has no field for it. The distinction is set out in the capability vocabulary contract.

The vocabulary below holds seventeen core capabilities. A core name reads area.verb, and a plugin may claim one only with a [[claim]] that binds every probe the vocabulary declares for it, each proved against a recording — so a claim is evidence, not a label. A plugin may also declare capabilities of its own, written <plugin-id>:<name>; those are inert until something asks for them.

lemonfiber wiring lists every link in the stack, what it reaches and how it was settled:

  • outright — one service provides it, and there is nothing to decide;
  • each — the link asked for every provider, not one;
  • contested — several provide it and the link wants one. lemonfiber refuses to pick by install order, recency or precedence, and says so;
  • chosen — you picked, and the ones not picked are named;
  • unfilled — nothing provides it, and the command exits non-zero.

A plugin that claims a core capability can therefore make a link contested the moment it is installed; lemonfiber plugin claims says so before it is.

Terminal window
lemonfiber wiring fill media.serve navidrome --dry-run
lemonfiber wiring fill media.serve navidrome

That is how one service stands in for another, and the only way. The choice is a setting: it is recorded, it shows in lemonfiber history, and lemonfiber undo puts it back. The rehearsal says what the change would leave with nothing filling it before anything is written. Every flag is in the command reference; refusals are the WIRE codes.

Read out of contract/capability-vocabulary.json at the revision this site pins, which is also what lemonfiber plugin capabilities prints. A probe that presents the operator’s credential cannot be answered on a machine holding none, and is reported unproven there rather than failed.

Vocabulary generation 1

indexer.search

Runs a search across the indexers it holds and answers with what they returned.

Holds the operator's indexer accounts, accepts a search on their behalf, and answers with the releases those indexers returned. The accounts it holds, and the list of indexers they belong to, are not readable without the operator's credential.

Declared by: nzbhydra2prowlarr

What a claim is held to:

  • guarded — An anonymous caller is refused the indexers it holds, presenting nothing. An indexer account is an account somebody paid for. A service that would list them to the network has stopped satisfying this whether or not it still searches.
  • indexers — It answers with the indexers it holds, with the operator's credential. Searching is what this is for, and the indexers it would search are the part that can be read without asking anything of a third party.

indexer.proxy

Fetches a page from an indexer that answers a browser rather than a client.

Fetches a page on another service's behalf when the site it comes from challenges a plain client. It holds nothing of the operator's and is reached only from inside the stack, so it answers an anonymous caller with its own name and version.

Declared by: flaresolverr

What a claim is held to:

  • identifies — It answers with its own name and version, presenting nothing. This is the one capability whose claimant deliberately holds no credential, so a refusal would be the wrong evidence. What it must show instead is that the thing behind the port is this and not an empty container.

download.usenet

Takes an NZB, fetches the articles it names, and reports where the finished files are.

Accepts an NZB from whatever asked for it, downloads what it names, and reports the path the finished files are at so a filer can move them. The queue and the provider account behind it are not readable without the operator's credential.

Declared by: sabnzbd

What a claim is held to:

  • guarded — An anonymous caller is refused the queue, presenting nothing. The queue names what the household is downloading and the account paying for it. A client that would answer that to the network has stopped satisfying this.
  • queue — It answers with its queue, with the operator's credential. Reporting where a finished download is requires having a queue to report from, and the queue is the part that can be read without starting one.

download.torrent

Takes a torrent or a magnet, exchanges with peers, and reports where the finished files are.

Accepts a torrent or a magnet from whatever asked for it, exchanges data with that torrent's peers, and reports the path the finished files are at. The queue is not readable without the operator's credential, and what it exchanges leaves through whatever carries its traffic rather than by a route of its own.

Declared by: qbittorrent

What a claim is held to:

  • guarded — An anonymous caller is refused the queue, presenting nothing. A torrent client's queue is the one list in this stack whose disclosure has consequences outside the household, and the client is the only thing that can refuse it.
  • queue — It answers with its queue, with the operator's credential. The same reason the Usenet client's queue is the readable half: it is what the service has to report from, and reading it starts nothing.

network.egress-guard

Carries another service's traffic out through a tunnel and stops it when the tunnel drops.

Carries the traffic of whatever is placed inside its network, out through a tunnel it dials, and stops carrying it the moment the tunnel is not up. It answers, from inside the stack, whether the tunnel is up — because a guard that cannot be asked is one nothing can prove is guarding.

Declared by: gluetun

What a claim is held to:

  • identifies — It answers whether the tunnel is up, presenting nothing. This is the one service whose failure has consequences outside the machine, and the failure it has is silent: a tunnel that dropped looks exactly like one that is up unless something asks.

library.curate

Holds a wanted list for one kind of media, asks for what is missing, and files what arrives.

Holds a list of what the household wants of one kind of media, asks an indexer for what is missing, hands the result to a download client, and files what arrives under the library's own naming. The wanted list is not readable without the operator's credential.

Declared by: binderylidarrradarrsonarr

What a claim is held to:

  • guarded — An anonymous caller is refused the wanted list, presenting nothing. A curating service holds every key the stack was given and can be made to download anything. Its API answering the network at all is the failure; whether the list is interesting is beside the point.
  • wanted — It answers with what it is holding, with the operator's credential. Holding a wanted list is the half of this that can be demonstrated without waiting for something to be released.

subtitles.fetch

Finds subtitles for what is already filed and puts them beside it.

Reads what the library already holds, finds subtitles for it in the languages the operator asked for, and writes them beside the media. Its own configuration and the provider accounts in it are not readable without the operator's credential.

Declared by: bazarr

What a claim is held to:

  • guarded — An anonymous caller is refused its configuration, presenting nothing. It holds subtitle-provider accounts and a path into the library. Neither is something the network may read.
  • status — It answers with its own state, with the operator's credential. What it is configured to fetch, and for what, is the part that can be read without a provider being asked anything.

media.serve

Serves the filed library to a player, with its own catalogue of what it holds.

Holds a catalogue of what is in the library and serves it to a player over HTTP. The catalogue is not readable without a credential — a library server that answers an anonymous caller is publishing the household's collection to every device on the network.

Declared by: calibre-web-automatedaudiobookshelfjellyfinnavidrome

What a claim is held to:

  • guarded — An anonymous caller is refused the catalogue, presenting nothing. This is the probe the whole vocabulary is for. A media server bound to the household network that stops refusing this keeps answering its health probe, keeps appearing green, and has published somebody's library. Nothing else would notice.
  • catalogue — It answers with the catalogue of what it holds, with the operator's credential. Serving a library means having a catalogue of it. A service that refuses everybody including the operator is not serving anything.

identity.source

Answers, for the services that ask, whether a person is who they say they are.

Holds the household's accounts and answers another service's question about whether a given person is one of them. It tells an anonymous caller enough to begin signing in — which server this is — and nothing about who has an account here.

Declared by: jellyfin

What a claim is held to:

  • identifies — It tells an anonymous caller which server this is, presenting nothing. Something has to be answerable before authentication, or nothing could ever authenticate. What that is is the server's identity, and it is the part with nothing of the household's in it.
  • guarded — An anonymous caller is refused the accounts, presenting nothing. The line between the two probes is the whole of this capability: which server, to anybody; who is on it, to nobody.

request.intake

Takes a household member's request for something not yet held and hands it on.

Lets a member of the household ask for something the library does not have, decides whether they may, and hands the request to whatever acquires it. Its own state is answerable to anybody; what has been requested, and by whom, is not.

Declared by: seerr

What a claim is held to:

  • identifies — It answers with its own state, presenting nothing. This is the one service a household member reaches without being an operator, so it has a front door that answers before anybody signs in.
  • guarded — An anonymous caller is refused the requests, presenting nothing. What a household asked for is the household's. A request list readable by the network is a list of what everybody in the house is waiting to watch.

proxy.front

Answers the household's friendly names, forwarding each to the service it names.

Serves every household name in the routing table it is applied, forwarding each to the upstream the table names and serving it over TLS with the certificate the table gives it or one it obtains itself, and answers a name asked for over plain HTTP by sending the caller to HTTPS at the same name. It holds nothing of the household's beyond the table, so it answers anybody.

Declared by: caddy

What a claim is held to:

  • redirects — A name asked for over plain HTTP is sent to HTTPS, presenting nothing. A redirect is, like a refusal, an answer no port proxy produces: only a server that routes by name sends a caller elsewhere. It is also all a proxy serving every name over TLS answers on plain HTTP, whatever the name and the path.

dashboard.show

Shows the household one page of the services it has, grouped, each linked.

Shows the panel list it is applied: groups of entries, each with a name, an icon, a link and a description, and where given a reachability check and a widget, beside the page widgets it is given. It answers the list it shows to anybody on the household network, and no entry or widget on it reads a credential, so the page holds nothing that administers a service.

Declared by: homepage

What a claim is held to:

  • shows — It answers with the groups of entries it shows, presenting nothing. A dashboard is a page anybody in the household may open, so the list it shows is answerable without a credential, and the shape of that answer is what tells a dashboard from an empty port. A fresh one may show no entry yet, and its answer still reads as a list.

quality.sync

Writes the quality profiles lemonfiber chooses into each curator's own configuration.

Writes the quality profiles it is applied into each curator it is given, at that curator's address and with its key, on a schedule of its own, and says what it last applied. It listens on nothing: no request reaches it, so it is demonstrated by running its own program.

Declared by: recyclarr

What a claim is held to:

  • runs — Its own program runs and says what it is, presenting nothing. Nothing can ask it anything over the network, because it listens on nothing. Its own program, run once from the pinned image with no network, is what can be asked, and a program that names itself has started as the service will.

archive.extract

Extracts archived releases where they land and hands each back to the curator that asked for it.

Watches each curator's download folder it is given, extracts the archived releases it finds there, and tells that curator, at the address and with the key it was given for it, that the extraction is ready to import. It answers what it is extracting now to anybody who asks from inside the stack, and that answer names no key.

Declared by: unpackerr

What a claim is held to:

  • working — It answers with what it is working on, presenting nothing. It holds a key to every curator it hands back to, and none of them is in this answer; what is, is the work it has in hand, which a fresh one answers with nothing in hand and an emptied container cannot answer at all.

stream.guard

Guards the media server's bytes, passing only what the media server says a session may have.

Stands in front of the media server and enforces the guard it is applied: the media server filler's declaration verbatim, the one proxy address it trusts to name a client, and where it asks the core about a live-stream pass. On each byte path the declaration names it asks the media server the declared question with the session the request presents, and passes the bytes only on a yes; it refuses, without asking anything, every path the declaration refuses and every request naming an item in a shape no declared question can be asked about. It answers the guard it enforces, all of it, and presents the certificate it is handed.

Declared by: door

What a claim is held to:

  • refuses — A request it cannot ask about is refused without asking, presenting nothing. A guard that passed what it could not ask about would hand out bytes nobody was asked whether a session may have. Refused before anything is asked, the answer is the guard's own, which a fresh guard with nothing behind it gives as surely as one in front of a library.

invite.decline

Lets an invited person refuse an invitation, and switches their account off when they do.

Holds the media server key it switches an invitation off with, and the invitations it is applied, each a token's hash, an account and when it lapses. It serves the page where an invitee declines, and on a decline of an invitation it holds switches that one account off on the media server. It answers which key it holds by its fingerprint, and never the key.

Declared by: decline

What a claim is held to:

  • held — It answers which key it holds, and not the key, presenting nothing. Whether a key is in place is what has to be known before an invitation can be declined at all, and its fingerprint says so without handing anybody the key. A fresh one holds none and says that.

request.gate

Lets the request service reach the curators and the media server through the routes it is applied, and nowhere else.

Admits a call to a curator or the media server only on a route it is applied, only where the call is on that route's list and presents a token whose hash the route holds, and presents the route's own credential upstream. Until it is applied routes it admits nothing. It answers each route's credential by its fingerprint and never its value, and is reached by the request service alone.

Declared by: request-gate

What a claim is held to:

  • guarded — A call presenting no admitted token is refused, presenting nothing. The gate holds credentials that administer every service it routes to, and its whole job is that a caller without an admitted token reaches none of them. Before it is applied routes it admits nothing, which is the same refusal.