J8 — Customising the stack
Status: Accepted · Audience: Operator
The journey
Section titled “The journey”$ git clone https://github.com/lemonfiber/lemonfiber-media-stack ~/dev/media-stack$ vim ~/dev/media-stack/compose.yml # add a service$ vim ~/dev/media-stack/stack.toml # declare it$ lemonfiber up tv --stack-dir ~/dev/media-stack
✓ manifest valid schema_version 1 · 20 services · 10 forms ✓ 8 services healthylemonfiber validates the fork’s manifest contract and operates it. It has no opinion about the contents beyond the contract.
Who this is for, and why they matter disproportionately
Section titled “Who this is for, and why they matter disproportionately”The experienced operator evaluates the project publicly, answers questions in forums, and is the person newcomers ask for advice. Their objection — “it hides what’s actually happening” — kills adoption among exactly the audience the product is built for.
They’re also right to be suspicious. A tool that wraps a system opaquely, then breaks or is abandoned, leaves its users with something they can’t operate.
The guarantee underneath everything
Section titled “The guarantee underneath everything”$ cd ~/dev/media-stack$ docker compose --profile search --profile usenet --profile tv up -dThe stack runs with no lemonfiber binary anywhere (F1-R1).
This is the load-bearing property. Everything else in this journey is convenience; this is what makes adopting lemonfiber a reversible decision, and therefore a low-risk one.
Showing its work
Section titled “Showing its work”$ lemonfiber up tv --dry-run
docker compose \ --project-name lemonfiber \ --profile search --profile usenet --profile torrent \ --profile tv --profile subs \ up -dNothing is generated that the operator can’t read (F1-R2). Debugging is
tractable because the command can be pasted into a shell.
Adding a service requires no Rust
Section titled “Adding a service requires no Rust”Three edits, all data:
- A service entry in
compose.ymlwith exactly one profile. - A manifest entry — ports, health endpoint, description.
- Inclusion in whichever forms should carry it.
No lemonfiber change, no release (F1-R5). This is the direct payoff of
ADR-0002 keeping forms
as data rather than code.
For a service lemonfiber doesn’t know, lifecycle and status work generically;
features needing specific knowledge — seeding, queue health — report as
unsupported for it rather than failing (F1-R10, F1-R11).
Editing in place instead of forking
Section titled “Editing in place instead of forking”Files lemonfiber materialises can be edited directly. Modifications are detected
by content and never silently overwritten on upgrade — a diff is shown and
the operator chooses (F1-R4).
Similarly, hand-tuning a service’s settings is preserved rather than reverted by
the next seed (C9-R4). Tuning worth keeping can be adopted into the baseline so
it survives rebuilds too (C9-R6).
Opting out entirely
Section titled “Opting out entirely”A service or configuration area can be marked unmanaged: lemonfiber reports
its state but never writes to it, and stops reporting drift for it (F1-R7,
F1-R8).
Taken to its limit, this is a stack lemonfiber only observes.
Where it goes wrong
Section titled “Where it goes wrong”| Situation | Behaviour |
|---|---|
| Manifest invalid | Refused, naming the specific violation and location — all violations in one pass, not one per run (F1-R9) |
| Unsupported schema version | Refused, naming both versions |
| Fork breaks the single-mount rule | Consequence reported; not refused. It’s their system (F1-R13) |
| Operator removes a service lemonfiber uses | Reports which features become unavailable; doesn’t refuse |
| Scripting against output | Machine-readable output is a stable, versioned interface (F1-R12) |
Related
Section titled “Related”This page lives in another repository Rendered from lemonfiber/spec at 1d10402, 2026-09-09. Read the source of this page