Per-repo specifications
Rendered from spec at76d46f3 (2026-10-25) · its source · Edit this page
Status: Accepted
The diagram, the repository table and the sentence counting it are generated
from repos.toml by
scripts/gen_repos.py — edit the registry, not the
page. just repos rewrites them and just generated refuses a hand edit. The
prose around them is written by hand.
Where 20-architecture describes the system, this section describes each repository — its structure, its rules, and what is specific to working inside it.
The repos
flowchart TD
spec["spec<br/>canonical"]
gh[".github<br/>community health files"]
subgraph impl[Implementation]
lemonfiber["lemonfiber<br/>Rust binary"]
web["lemonfiber-web<br/>the web surface"]
companion["lemonfiber-companion<br/>the phone surface"]
sdk["sdk-ts<br/>the TypeScript client"]
sdkphp["sdk-php<br/>the PHP client"]
sdkpy["sdk-python<br/>the Python client"]
homeassistant["integration-home-assistant<br/>lemonfiber in Home Assistant"]
mcp["integration-mcp<br/>lemonfiber for AI assistants"]
stack["lemonfiber-media-stack<br/>retiring"]
decline["lemonfiber-decline<br/>the decline service"]
gate["lemonfiber-request-gate<br/>the request gate"]
plugins["lemonfiber-plugins<br/>the reviewed catalogue"]
servarrkit["adapter-kit-servarr<br/>what the Servarr adapters share"]
tap["homebrew-tap<br/>generated formula"]
site["website-lemonfiber.app<br/>the public frontpage"]
docs["website-docs.lemonfiber.app<br/>the documentation site"]
contribute["website-contribute.lemonfiber.app<br/>the contributor site"]
kit["website-kit<br/>what both sites run"]
lfdev["tool-lfdev<br/>the developer command line"]
brand["brand<br/>design tokens"]
parts["plugin-*<br/>twenty-one first-party plugins"]
end
web -->|submodule, pinned| lemonfiber
sdk -->|pinned| web
lemonfiber -->|contract artefact| sdk
lemonfiber -->|contract artefact| sdkphp
companion -->|pinned; every call goes through it| sdkphp
lemonfiber -->|contract artefact| sdkpy
homeassistant -->|vendored at a pinned commit| sdkpy
mcp -->|vendored at a pinned commit| sdkpy
plugins -->|submodule, pinned; the bundle embedded| lemonfiber
plugins -->|bundle pins a revision and its tree| parts
lemonfiber -->|release CI generates| tap
brand -->|values mirrored| site
lemonfiber -->|docs submodule, pinned| docs
brand -->|docs submodule, pinned| docs
spec -->|board snapshot, specification| site
spec -->|board snapshot| lfdev
lemonfiber -->|docs submodule, pinned| contribute
servarrkit -->|taken by commit by the Servarr adapters| parts
kit -->|taken by commit| docs
kit -->|taken by commit| contribute
gh -.->|inherited by| impl
spec -.->|governs all| impl
| Repo | Spec | Language | What’s specific about it |
|---|---|---|---|
spec |
../README.md | Markdown | This repository — canonical, and cited by every change to the rest |
lemonfiber |
lemonfiber.md · lemonfiber-tui.md · lemonfiber-reference.md | Rust | Three surfaces, one core; the submodule; the build |
lemonfiber-web |
lemonfiber-web.md | TypeScript | Two surfaces, one component library; draws the API, implements nothing |
lemonfiber-companion |
lemonfiber-companion.md | PHP | The fourth surface, and the first that does not run on the machine it operates |
sdk-ts |
sdk-ts.md | TypeScript | Owns the stream’s hard parts so no consumer reimplements them |
sdk-php |
sdk-php.md | PHP | The same contract, implemented as a peer rather than translated |
sdk-python |
sdk-python.md | Python | An asynchronous and a synchronous client over one generated contract |
integration-home-assistant |
integration-home-assistant.md | Python | The first program to hold an integration key rather than a person’s credential |
integration-mcp |
integration-mcp.md | Python | Every tool generated from the contract, so an assistant can do nothing the web API does not offer |
lemonfiber-media-stack |
lemonfiber-media-stack.md | YAML/TOML | Retiring (ADR-0045): each part is a plugin’s repository, and this is archived once the compose cutover reads none of it |
lemonfiber-decline |
lemonfiber-decline.md | Rust | An image the stack runs, declared in a plugin.toml of its own; holds one Jellyfin key and answers the household |
lemonfiber-request-gate |
lemonfiber-request-gate.md | Rust | An image the stack runs, declared in a plugin.toml of its own; holds the *arr keys and publishes no port |
lemonfiber-plugins |
lemonfiber-plugins.md | TOML | Reviewed and signed, and never a runtime dependency |
plugin-jellyfin |
first-party-plugins.md | Rust | The bundle’s media server: an adapter image in front of Jellyfin, so the train tags it |
plugin-prowlarr |
first-party-plugins.md | Rust | The bundle’s part for the indexer manager, Prowlarr: an adapter image in front of the upstream’s, which is pinned by revision, so the train tags it |
plugin-flaresolverr |
first-party-plugins.md | TOML | The bundle’s part for the solver that gets past an indexer’s bot protection, FlareSolverr; its image is the upstream’s, pinned by revision |
plugin-nzbhydra2 |
first-party-plugins.md | Rust | The bundle’s part for the Usenet indexer search, NZBHydra2: an adapter image in front of the upstream’s, which is pinned by revision, so the train tags it |
plugin-sabnzbd |
first-party-plugins.md | Rust | The bundle’s part for the Usenet download client, SABnzbd: an adapter image in front of the upstream’s, which is pinned by revision, so the train tags it |
plugin-gluetun |
first-party-plugins.md | TOML | The bundle’s part for the egress guard, Gluetun, and the one part taking the egress-guard shape; its image is the upstream’s, pinned by revision |
plugin-qbittorrent |
first-party-plugins.md | Rust | The bundle’s part for the torrent client, qBittorrent, run inside the egress guard’s network: an adapter image in front of the upstream’s, which is pinned by revision, so the train tags it |
plugin-sonarr |
first-party-plugins.md | Rust | The bundle’s part for the television curator, Sonarr: an adapter image in front of the upstream’s, which is pinned by revision, so the train tags it |
plugin-radarr |
first-party-plugins.md | Rust | The bundle’s part for the film curator, Radarr: an adapter image in front of the upstream’s, which is pinned by revision, so the train tags it |
plugin-lidarr |
first-party-plugins.md | Rust | The bundle’s part for the music curator, Lidarr: an adapter image in front of the upstream’s, which is pinned by revision, so the train tags it |
plugin-bindery |
first-party-plugins.md | TOML | The bundle’s part for the book curator, Bindery; its image is the upstream’s, pinned by revision |
plugin-bazarr |
first-party-plugins.md | Rust | The bundle’s part for the subtitle fetcher, Bazarr: an adapter image in front of the upstream’s, which is pinned by revision, so the train tags it |
plugin-recyclarr |
first-party-plugins.md | TOML | The bundle’s part for quality sync, Recyclarr; its image is the upstream’s, pinned by revision |
plugin-unpackerr |
first-party-plugins.md | TOML | The bundle’s part for archive extraction, Unpackerr; its image is the upstream’s, pinned by revision |
plugin-seerr |
first-party-plugins.md | Rust | The bundle’s part for the request service, Seerr: an adapter image in front of the upstream’s, which is pinned by revision, so the train tags it |
plugin-calibre-web-automated |
first-party-plugins.md | TOML | The bundle’s part for the book library, Calibre-Web Automated; its image is the upstream’s, pinned by revision |
plugin-audiobookshelf |
first-party-plugins.md | TOML | The bundle’s part for the audiobook server, Audiobookshelf; its image is the upstream’s, pinned by revision |
plugin-navidrome |
first-party-plugins.md | TOML | The bundle’s part for the music server, Navidrome; its image is the upstream’s, pinned by revision |
plugin-homepage |
first-party-plugins.md | TOML | The bundle’s part for the dashboard, Homepage; its image is the upstream’s, pinned by revision |
plugin-door |
first-party-plugins.md | Dockerfile | The bundle’s part for the guarded door, Caddy rebuilt without its file capability; builds its image, so the train tags it |
plugin-caddy |
first-party-plugins.md | Dockerfile | The bundle’s part for the proxy, Caddy rebuilt without its file capability, listening on 8080 and 8443; builds its image, so the train tags it |
adapter-kit-servarr |
adapter-kit-servarr.md | Rust | One copy of the Servarr client, taken by each Servarr adapter at a commit |
homebrew-tap |
homebrew-tap.md | Ruby | Generated; exists so brew works |
website-lemonfiber.app |
website-lemonfiber.md | Astro | The org is the motor; roadmap read, not written |
website-docs.lemonfiber.app |
website-docs.md | Astro | It renders; it does not own — every page pinned to the repo that wrote it |
website-contribute.lemonfiber.app |
website-contribute.md | Astro | How to change the code; links to the rules on the frontpage rather than rendering them |
website-kit |
website-kit.md | TypeScript | One copy of the mirror machinery, taken by each site at a commit |
tool-lfdev |
tool-lfdev.md | Python | Every write is a pull request under the person’s own credentials |
brand |
brand.md | CSS/SVG | Tokens are generated; the marks are not open |
.github |
this page | Markdown | Org-wide community health files; no spec of its own |
Those forty-three are the repositories this specification governs, which is not the same as every repository in the organisation and is no longer close to it. A plugin is a repository too, and where one comes from is the reviewed catalogue’s to answer (F5) rather than this register’s — so the org has repositories this file does not list, on purpose, and the sentence says so rather than making a claim nothing checks. A first-party plugin is listed, because the bundle the core embeds pins it (first-party plugins), and the release train cuts it with the core where it builds an image (OPS-R86).
.github carries the community health files GitHub serves for a repo that does
not define its own — the code of conduct, the security policy, the issue templates
and the org profile — so a sibling repo inherits them rather than copying them
(tooling).
The REPO-R namespace
Per-repo requirements use REPO-R## — obligations specific to one repository’s
structure or tooling, distinct from behaviour (A2-R4), architecture (ARCH-R1),
governance (GOV-R2) and quality (Q-R1).
The relationships that matter
lemonfiber-media-stack → lemonfiber (submodule). lemonfiber embeds a
pinned commit of lemonfiber-media-stack and validates its schema_version at
build time (ARCH-R6). They version
independently; the pin says exactly which stack a given binary ships
(versioning).
lemonfiber → homebrew-tap (generation). lemonfiber’s release CI regenerates the
formula once the Homebrew publish job is enabled at 1.0.0 (L1-R3). The tap is
downstream of every lemonfiber release and is otherwise inert.
The documented repos → website-docs.lemonfiber.app (submodules). The
documentation site shows the .docs/, README and policy files of lemonfiber,
lemonfiber-web, lemonfiber-companion, sdk-ts, sdk-php,
lemonfiber-media-stack, lemonfiber-plugins, brand, homebrew-tap, .github
and plugin-template, each pinned to an exact revision and rendered rather than
copied (ADR-0015).
The specification is not among them: it is rendered on the frontpage, at the revision
the board snapshot read
(ADR-0040).
The repositories a contributor works in → website-contribute.lemonfiber.app
(submodules). The contributor site renders each repository’s README.md and
AGENTS.md, the core’s architecture notes and the developer command line’s guide, pinned
the same way, and links to the specification’s rules on the frontpage.
spec → website-lemonfiber.app and tool-lfdev (the board snapshot). The report
publishes one snapshot of where every version stands; the frontpage renders it and the
specification at the revision it read, and lfdev answers from the same file
(board snapshot).
Everything ← spec (governance). No change to any of them lands without
citing this repository (50-governance).
lemonfiber → the SDKs (generation). lemonfiber emits one contract artefact
from its own serde types; sdk-ts and sdk-php generate their types from it
and hand-write only behaviour (ADR-0014).
The one property to remember per repo
lemonfiber— logic cannot render. The core crate has no UI dependency, so a surface can never grow behaviour of its own.lemonfiber-media-stack— it holds what the bundle has not moved yet, and retires once nothing reads it. The project lemonfiber writes from it runs under plaindocker compose, which is what makes adopting the tool reversible.homebrew-tap— nobody writes it by hand. It holds a placeholder until the publish job turns it into generated output, and exists only because Homebrew requires a repo of that name.website-lemonfiber.app— the org is the motor. Roadmap and status are read from the org at build time, never hand-authored, so the page cannot drift from reality.website-docs.lemonfiber.app— it renders; it does not own. Every page is another repo’s file at a revision the site names, so a wrong sentence is fixed where it was written.
Related
- 20-architecture — the system across repos
- 40-quality — the standards all code is held to
- 50-governance — how change enters each repo