Feature catalogue
Rendered from spec at76d46f3 (2026-10-25) · its source · Edit this page
Status: Accepted
v1 is the seven product areas (A–G) — the contract the technical spec is
written against, where every architecture and implementation decision must trace
to a feature requirement here, not the other way round. The ecosystem adds
four areas (H–K) plus F3, catalogued below the v1 areas and sequenced before
1.0.0 on the same train. Area L — Release & distribution spans both, and
carries one release: L1 ships 1.0.0, which is the scoped work of getting there
rather than a summary of it. The generated feature board lists every feature with
how far it is built and the version it ships in; index.json is
the same board for tools: the counts, the version train, and the board.json in
each area’s directory that lists its features.
Every feature carries two answers that are easy to confuse and are kept apart.
status is about the specification — draft until it is agreed, then
accepted, and citable. maturity is about the implementation — planned,
building, built, shipped (with the version that carried it), or
withdrawn. It is derived rather than written: scripts/maturity.py reads every
repository’s tracker and every version manifest, and the maturity workflow opens
the pull request that rewrites it whenever the trackers move a feature
(OPS-R78). withdrawn alone is set by hand. A feature is routinely accepted and planned for a year;
conflating the two would lose whichever question is asked less often.
built and shipped are also worth keeping apart. A feature whose every
requirement is met sits in built until the version carrying it is released —
which is a different fact from being half-written, and building used to have to
mean both. It is the state OPS-R54 can actually
gate on: shipped means out in a released version, so demanding it before a
release is a gate nothing could ever pass.
A requirement added to a built or shipped feature reopens it: it is
building again until the implementation tracker ticks every requirement it
defines, and status_lint.py refuses a catalogue that says otherwise
(OPS-R73).
How to read a feature doc
Each follows the same shape:
| Section | Contains |
|---|---|
| Purpose | What problem it solves, for whom. One paragraph. |
| Behaviour | What the user sees and does. Functional only — no crates, no env vars, no module names. |
| States | The states it can be in and what moves between them. |
| Edge cases | What happens when it goes wrong. Usually the longest section, deliberately. |
| Acceptance criteria | Numbered, testable, RFC 2119. A1-R1, A1-R2, … |
Requirement IDs
Requirements live inside their feature — there is no separate requirements
tree. An ID is <feature>-R<n>: A1-R3 is the third acceptance criterion of
feature A1.
IDs are permanent. A requirement that’s removed is marked Withdrawn in place; its number is never reused. Tests and commits cite these IDs.
Audiences
Two, and they need naming because most features serve only one:
| Audience | Who | What they touch |
|---|---|---|
| Operator | The person who sets it up and keeps it running | lemonfiber (CLI/TUI/web), occasionally a service admin UI |
| Household | Everyone else in the home | Seerr to request, Jellyfin to watch. Never lemonfiber. |
A household member has exactly one account — their Jellyfin login, which Seerr authenticates against. The multi-account problem is an operator problem only.
A — Getting started
The hardest part of the product, and where most users are lost today.
| ID | Feature | Audience |
|---|---|---|
| A1 | Prerequisites & account guidance | Operator |
| A2 | Setup wizard | Operator |
| A3 | Credential validation | Operator |
| A4 | Reconfiguration | Operator |
| A5 | Migration from an existing stack | Operator |
| A6 | Clean uninstall | Operator |
| A7 | Credential management & rotation | Operator |
B — Running it
| ID | Feature | Audience |
|---|---|---|
| B1 | Forms & partial stacks | Operator |
| B2 | Lifecycle control | Operator |
| B3 | Live dashboard | Operator |
| B4 | Log viewing | Operator |
| B5 | Notifications & alerting | Both |
| B6 | Controlling a stack on another machine | Operator |
| B8 | Autostart & boot persistence | Operator |
| B9 | Open notification back-ends | Both |
| B10 | Hosting long-running commands | Operator |
B7 (remote access for the household) is deferred — household features are LAN-only until it lands. It returns as I1 in
0.22.0, once a self-hosted overlay control plane (Headscale + self-hosted relay) makes it possible without the proprietary control plane that blocked it (Tailscale). See ADR-0010 for the related runtime decision and the roadmap.
C — Trust & correctness
Features that exist because P3 demands the tool prove things rather than assume them.
| ID | Feature | Audience |
|---|---|---|
| C1 | Diagnostics (doctor) | Operator |
| C2 | VPN verification | Operator |
| C3 | Auto-remediation | Operator |
| C4 | Support bundle | Operator |
| C5 | Storage & hardlink management | Operator |
| C6 | Web UI security & binding policy | Operator |
| C7 | Queue health & stuck items | Operator |
| C8 | Provider health & quota tracking | Operator |
| C9 | Config drift detection & seed policy | Operator |
D — Content & household
| ID | Feature | Audience |
|---|---|---|
| D1 | Service auto-wiring | Operator |
| D2 | Quality presets in plain language | Operator |
| D3 | First-content walkthrough | Operator |
| D4 | Household request flow | Household |
| D5 | Disk space management | Operator |
| D6 | Household identity & invitations | Both |
| D7 | Request approval & quotas | Both |
| D8 | Parental controls | Both |
| D9 | “Where is my show?” pipeline trace | Both |
| D10 | Bandwidth & scheduling | Operator |
| D11 | Watching what the house holds | Household |
E — Maintenance
| ID | Feature | Audience |
|---|---|---|
| E1 | Stack updates | Operator |
| E2 | Self-update | Operator |
| E3 | Backup & restore | Operator |
| E4 | Rollback | Operator |
| E5 | Changelog & release notes | Operator |
F — Extensibility
| ID | Feature | Audience |
|---|---|---|
| F1 | Customisation & escape hatches | Operator |
| F2 | Service catalogue | Operator |
| F3 | Plugin manifests | Operator |
| F4 | The capability vocabulary | Operator |
| F5 | The plugin catalogue and what vouches for a plugin | Operator |
| F6 | Plugin lifecycle | Operator |
| F7 | Plugin provenance | Operator |
| F8 | Recipes and named adapters | Operator |
| F9 | Capabilities of the bundled services | Operator |
| F10 | Writing a plugin | Operator |
| F11 | Executing contributed code | Operator |
G — Cross-cutting UX
These are not screens; they are properties every other feature must exhibit. G4 and G5 in particular are the connective tissue that makes the whole catalogue read as one product.
| ID | Feature | Audience |
|---|---|---|
| G1 | Interface tiers (CLI / TUI / web) | Both |
| G2 | Plain-language layer & in-product help | Both |
| G3 | Accessibility | Both |
| G4 | Error & remedy model | Both |
| G5 | The front door | Both |
| G6 | Client app guidance | Household |
| G7 | Overall health summary | Operator |
| G8 | Privacy stance | Both |
| G9 | Mobile client handoff | Both |
The ecosystem
Everything below ships before 1.0.0, on the same train as everything
above it: authored to the same falsifiable-requirement bar, and gated by the same rule — no version ships a
requirement it has not finished (OPS-R54).
The through-line is the project’s wedge applied outward — not just wiring these
services, but proving the wire works (a health check, a valid upstream
credential, and a synthetic action read back), the thing no adjacent tool does.
Areas H, I and K are Accepted, as is F3. The runtime pillar (J) is Draft — proposed and open for comment: it reopens a non-goal (ADR-0010) and carries the most design risk, so it is not binding until reviewed.
H — Ecosystem glue
The tools the community bolts on, bundled and — the differentiator — verified.
| ID | Feature | Audience |
|---|---|---|
| H1 | Cross-seeding | Operator |
| H2 | Announce-driven grabbing | Operator |
| H3 | Quality-profile sync | Operator |
| H4 | Subtitles | Both |
| H5 | Queue self-healing | Operator |
| H6 | Library cleanup | Both |
| H7 | Transcoding | Operator |
| H8 | Playback statistics | Both |
I — Remote access & identity
Reaching the stack from outside the home, and the one-account gate it requires — both without a proprietary control plane.
| ID | Feature | Audience |
|---|---|---|
| I1 | Remote access for the household | Both |
| I2 | Household identity & single sign-on | Both |
J — Runtime & platform · Draft
Freeing the stack from a single container runtime, while keeping both proofs (VPN egress, hardlinks) passing on every engine (ADR-0010).
| ID | Feature | Audience |
|---|---|---|
| J1 | Container-engine abstraction | Operator |
| J2 | Running under Podman | Operator |
| J3 | Running natively, without containers | Operator |
K — Observability
Open-source-native metrics and monitoring — a second opinion, delivery-confirmed.
Also in existing areas: F3–F11 (the plugin system), B9 (open notification back-ends, extends B5), and G9 (mobile handoff, extends G6).
L — Release & distribution
Turning the built product into something a stranger can install and run. Each major release is its own scoped work — the distribution itself, not the features that came before it.
| ID | Feature | Audience | Ships in |
|---|---|---|---|
| L1 | v1 release engineering | Operator | 1.0.0 |
N — Companion
The fourth surface, on a device that is not the one the stack runs on. One application for both audiences, deciding which by who signs in (ADR-0017).
Specified in full and locked by no version: the app follows what the main repositories have shipped rather than leading them, so nothing on the release train waits on it. It is pinned with the rest once it has caught up.
| ID | Feature | Audience |
|---|---|---|
| N1 | The companion app | Both |
| N2 | The operator’s companion | Operator |
| N3 | The household’s companion | Household |
| N4 | What the app uses of the device | Both |
Traceability
Three links, all of which CI checks:
Journey ──exercises──▶ Feature ──contains──▶ Requirement ◀──implements── Code
▲
└──cites── Architecture doc
- Every journey names the features it exercises.
- Every feature owns numbered requirements.
- Every architecture and per-repo doc cites the requirement IDs it satisfies.
A technical decision that cites no requirement is unjustified, and should be challenged in review. That’s the rule that keeps the technical spec written against the features rather than alongside them.