Skip to content

The roadmap

Nothing on this page is written by hand. Every figure is read from two files this site pins: the specification’s generated feature board with the version manifests behind it, and the implementation status the binary’s repository keeps in the same pull request as the work it describes.

That has a consequence worth stating plainly. This page is as fresh as the revisions at the foot of it, not as fresh as this morning — the site renders a pinned checkout so a reader following instructions gets the instructions that match the release they installed. When the pins move, these numbers move.

  • 3 of 12 milestones complete
  • 225 of 239 recorded deliverables done
  • 73 features and 1022 requirements, in areas A–L

A number here is a count of marks in the file behind it. Nothing is weighted: where a deliverable is half built, it counts as not done rather than as half, because “half” is a judgement the source file does not make.

Releases run as a train — one version in flight at a time, its goals fixed before the work starts, and a gate that refuses to tag until every goal is demonstrably built. The version train explains the mechanism; this is the train itself.

It is one sequence, and it ends at 1.0.0 — the point where everything specified is built and the interfaces stop moving. No version ships a stub: a release refuses while a feature it locks is not yet built.

Each version below names the milestone it serves, how many of its locked requirements the status file marks done, and the features it carries, each with how far the catalogue says it is built. Expand one to see them.

13 of 23 versions released38 of 73 features shipped

Milestones are the sequence the work is done in; versions are how it ships. One milestone can span several versions, and a version serves exactly one milestone.

Each milestone’s own mark is the one written beside its heading upstream. That can differ from the rows recorded beneath it, because a requirement is often recorded in the table of the milestone it grew out of rather than the one it belongs to — so a milestone whose recorded rows are all done can still be in progress. Where they differ, the heading is the milestone’s status and the rows are what has been written down here.

  1. M0

    Specification

    Done

    In the spec repo. Recorded here for context only.

    Recorded in another repository

    • M0.5

      Governance in force

      Done

      CI, templates, and the citation-gated workflow are in force (DCO, CODEOWNERS, spec-references bot, labeler, SonarCloud gate, OpenSSF hardening).

      Recorded in another repository

      • M1

        lemonfiber-media-stack standalone

        In progress

        The stack ships embedded as a submodule under assets/ and is read at build time; the manifest and compose fragments live there. The stack's own standalone CI lives in the lemonfiber-media-stack repo, where every form is resolved by docker compose config on each change.

        Recorded in another repository

        • M2

          Core: manifest, compose driver, CLI

          Done

          0.1.0, released. The parts everything else is built on: the manifest and its compile-time schema check, the compose driver, and the CLI they are reached through.

          9 of 9 recorded deliverables done

          What is recorded for it

          • Workspace + cargo-dist scaffoldDone
          • stack.toml parser + validation (compile-time schema check)Done
          • Embedded assets (include_dir! + --stack-dir)Done
          • Platform detection (macOS / Linux / Linux-Desktop / WSL2)Done
          • Compose command builder (pure, golden-tested)Done
          • Form closure + composition (B1-R4, B1-R5)Done
          • up / down / restart / ps / logs / pullDone
          • .env read/write (comment- and order-preserving)Done
          • config get/set/show (with secret redaction)Done
        • M3

          Setup wizard + doctor

          In progress

          The product-thesis milestone, built partly ahead of order: the diagnostics harness and the first check landed before the wizard. Shipped as 0.2.0. The free-space projection was completed later, once the download-client read the dashboard work introduced made the queue reachable.

          44 of 44 recorded deliverables done

          What is recorded for it

          • doctor — Check trait, remedy per findingDoneC1-R1
          • Findings that share a cause say soDoneC1-R10
          • Filesystem checks wait as long as a disk may takeDoneC1-R14
          • Diagnostics — one check runnable by name, and how long a disruptive one lastsDoneC1-R5
          • VPN leak test (exec gluetun + client, compare IPs)Done
          • VPN egress verification — the comparison, and what it rests onDoneC2-R1
          • Queue health — the assessmentDoneC7-R1
          • Queue health — watching it across the servicesDoneC7-R9
          • Queue health — reading the service's historyDoneC7-R4
          • VPN port forwarding — the client, and keeping it alignedDoneC2-R4
          • VPN egress, re-checked while traffic movesDoneC2-R9
          • Storage — the consequence, and the services configured to matchDoneC5-R3
          • The choices that are decisions, not faultsDoneC2-R13
          • Storage — the single-mount ruleDoneC5-R5
          • VPN killswitch, actually testedDoneC2-R7
          • Preflight / Environment check (Docker present vs unreachable, Compose ≥ min)DoneA2-R9
          • Empirical hardlink test (create / stat / inode + link-count)DoneC5-R1
          • Storage-mode detection (fs type, network mount, exFAT, WSL2)DoneC5-R2
          • Free-space check on the data rootDoneC5-R6
          • Degraded-link detection (was linking, now not)DoneC5-R11
          • Permission distinction (operator vs service PUID/PGID)DoneC5-R10
          • Data-root availability supervisor (stop on loss, no auto-restart)DoneC5-R7
          • Credential validation against live servicesDoneA3-R1
          • A pasted key is taken as the key that was meantDoneA3-R5
          • Credentials never reach an outcomeDoneA3-R6
          • A wait that did not answer says how long it wasDoneA3-R7
          • The tunnel's exit country is reportedDoneA3-R9
          • One outage, said onceDoneA3-R11
          • A certificate that was not trusted is namedDoneA3-R12
          • Proceeding with an unvalidated credentialDoneA3-R13
          • Every setup answer says what changing it costsDoneA4-R2
          • A change is classified before it is made, and a consequential one is confirmedDoneA4-R3
          • Nothing is written before the difference has been shownDoneA4-R4
          • A replacement credential is proven before the working one is discardedDoneA4-R8
          • A change that cannot be applied safely leaves the file alone and says whyDoneA4-R11
          • Every setup answer is individually revisableDoneA4-R1
          • Moving the data location carries the library or refusesDoneA4-R5
          • Adding or dropping a way of downloadingDoneA4-R6
          • A hand-edit to configuration is never overwritten silentlyDoneA4-R9
          • A quality preset change affects future acquisitions onlyDoneA4-R12
          • VPN port-forward validation + ProtonVPN NAT-PMP guidanceDoneA3-R8
          • Prerequisites / account guidance (dependency map before credentials)DoneA1-R1..R13
          • Wizard state machine (resumable, review-before-write, non-interactive guard)DoneA2-R1..R5
          • Jellyfin native-mode + PUID/PGID offers (platform-aware)DoneA2-R6
        • M4

          Seed & backup

          In progress

          Spans 0.3.0 (backup & restore — the E3 rows below, complete) and 0.4.0 (auto-wiring & seed). Wiring services to each other and recording it so it can be undone. The lemonfiber seed command exists and wires the first edge — qBittorrent's web UI password (D1-R16): it reads the temporary password from the container's log, replaces it with a generated one through the client, and records the generated one in QBITTORRENT_PASSWORD where the forwarded-port push reads it. It also wires each media-filing *arr's root folders — one per media type, under /data/media — reading the application's key from its config and skipping an application that has not written one yet. It now also registers each *arr's download clients: SABnzbd where its generated key is on disk, qBittorrent where its password was minted this run — or, on a later run when nothing is minted, where the recorded password is read back from QBITTORRENT_PASSWORD, so an *arr that came up after the first seed still learns about qBittorrent. It now also runs Prowlarr's app sync in the other direction: each of those media-filing *arrs is registered back into Prowlarr as an application, so Prowlarr pushes it the shared indexers. And it makes Jellyfin the identity source for Seerr: Jellyfin has no key on disk, so lemonfiber mints its admin password by driving Jellyfin's own first-run setup, records it, and signs Seerr in through Jellyfin — never re-pointing an already-initialised Seerr, whose existing sign-ins are the household's.

          18 of 19 recorded deliverables done

          What is recorded for it

          • service::Client port (Servarr shape)Done
          • Servarr-shape adapter (identity, register client/root folder)DoneD1-R11
          • Seed orchestration (skip-if-absent, preserve operator edits)DoneD1-R2..R6
          • Download-client credentials (read own key / generate)DoneD1-R1
          • Change journal (read-back + undo)In progressE4-R1
          • Backup & restore (quiesced capture, verify-before-replace, retention)DoneE3-R1..R4
          • Drift detection & seed policy (baseline + three-way comparison)DoneC9-R1..R6
          • Drift detection & seed policy (severity, re-baselining, full reset, stale update)DoneC9-R7
          • Drift detection & seed policy (secrets withheld)DoneC9-R11
          • Download-client / root-folder / Prowlarr / Jellyfin→Seerr wiringDoneD1-R7
          • The book *arr is told where the indexers areDoneD1-R15
          • Quality presets in plain languageDoneD2-R1..R12
          • Pipeline trace — "where is my show?"DoneD9-R1..R7
          • Pipeline trace — service disagreementDoneD9-R8
          • Pipeline trace — honest about what it could not readDoneD9-R10
          • Pipeline trace — history and the stuck-item landing pointDoneD9-R9
          • Pipeline trace — series and season aggregationDoneD9-R13
          • Pipeline trace — the household's own viewDoneD9-R12
          • First-content walkthroughDoneD3-R1..R13
        • M5

          Trust checks

          In progress

          The P3 trust pillar made continuous, across three versions: 0.5.0 (how the product speaks — errors, notifications, health), 0.6.0 and 0.7.0. The VPN egress and killswitch proof, storage and hardlink verification, and queue health with its stuck-item categories shipped as 0.6.0; the provider checks, the support bundle and auto-remediation are 0.7.0. Those are recorded in the M3 table above, beside the setup-time checks they grew out of, rather than repeated here: a requirement named in two rows is one the release gate can read either way.

          6 of 6 recorded deliverables done

          What is recorded for it

          • Provider health — what the accounts have leftDoneC8-R1
          • Provider health — what the provider itself saysDoneC8-R2
          • Indexer caps and when they resetDoneC8-R5
          • Provider health in the no-downloads diagnosisDoneC8-R8
          • Support bundleDoneC4-R1..R14
          • Auto-remediationDoneC3-R1..R15
        • M6

          Live TUI

          In progress

          The second surface over the same core (ratatui, per ADR-0003), spanning 0.8.0 (operating a running stack: forms, lifecycle, logs and the diagnostics that tie them together) and 1.0.0 (the interactive surfaces over it). The dashboard's read-only model shipped under 0.5.0 and its screen and refresh loop are built — the two rows below say so — so what remains of the dashboard is its layout polish and a *measured* idle cost rather than a renderer.

          27 of 32 recorded deliverables done

          What is recorded for it

          • Live dashboard — the read-only modelDoneB3-R2
          • Live dashboard — the surface, and the loop it refreshes inDoneB3-R1
          • Idle cost — 1 Hz under 2% CPU, resident under 50 MBNot startedB3-R14
          • Forms and profiles — what the manifest guaranteesDoneB1-R1
          • Addressing an operationDoneB2-R4
          • Starting, and what a service is doingDoneB2-R1
          • Starting — narrated while it happensDoneB2-R2
          • Operations say what they will affect firstDoneB2-R3
          • Stopping — what is still coming downDoneB2-R13
          • One lifecycle operation at a timeDoneB2-R14
          • Stopping — what another running form still needsDoneB2-R7
          • Stopping — the tunnel goes down lastDoneB2-R6
          • Stopping and starting — asking twice is not an errorDoneB2-R5
          • Forms — what one would start, said before it startsDoneB1-R3
          • Forms — one service failing degrades, it does not roll backDoneB1-R11
          • Forms — narrowing the active setDoneB1-R10
          • Form switcher — interactive picker with closure previewNot started
          • Logs — a container cannot rewrite the terminalDoneB4-R7
          • Logs — severity read rather than guessedDoneB4-R6
          • Logs — the evidence sits at the failureDoneB4-R5
          • Logs — one account of what happenedDoneB4-R1
          • Logs — read from the end, not from the beginningDoneB4-R13
          • A shortened value still says which one it isDoneG3-R10
          • One way out, and it renders for the terminal it hasDoneG3-R9
          • Colour is never the only thing saying itDoneG1-R10
          • Log viewer — exporting what is on screenDoneB4-R10
          • Log viewer — a service restarting mid-viewDoneB4-R11
          • Log viewer — filterable, with scrollbackDoneB4-R2
          • Doctor view — interactive re-run, remedies inlineNot started
          • Wizard in TUI — same state machine, richer presentationNot started
          • Full-screen dashboard — narrow terminals, and lists too long to showDoneB3-R9
          • Full-TUI layout polishNot startedB3-R10
        • M7

          Web surface & UX

          In progress

          0.9.0 and 0.10.0. A third surface — a web view over the same core — plus the cross-cutting UX (front door, health summary, error model, plain language, accessibility, privacy, web-security, support bundle). lemonfiber ui serves that surface: the read endpoints, the actions endpoint and the event stream, with the app served beside them where a build carries one. What the browser draws lives in lemonfiber-web. See the spec roadmap.

          106 of 107 recorded deliverables done

          What is recorded for it

          • Error model — remedies, grouping, retryDoneG4-R1
          • Error handling cannot cascadeDoneG4-R10
          • Never the operator's fault, swept over every messageDoneG4-R5
          • Health summary — one computation, from findingsDoneG7-R1..R13
          • Plain language — the words, and a report that explains its ownDoneG2-R2
          • Plain language — every surface explains its wordsDoneG2-R1
          • Explanations can be put away, and turned offDoneG2-R7
          • Numbers carry the consequence, where there is one to carryDoneG2-R4
          • Plain language — the shapes a false picture takesDoneG2-R8
          • Plain language — the rules that are now testsDoneG2-R3
          • Plain language — the words it had not explainedDoneG2-R13
          • A failure cannot drive the terminal, and explains its own wordsDoneG2-R10
          • Every answer a script asked for is one it can parseDoneG1-R7
          • Live state — the stream a browser holds openDoneARCH-R49
          • Redirected output — plain text, said onceDoneG3-R7
          • Nothing is reachable by mouse aloneDoneG3-R4
          • A question is asked without a clock on itDoneG3-R12
          • Severity is a word, and the colour is a second copy of itDoneG3-R14
          • A long wait says what it is waiting forDoneG3-R11
          • The third surface starts, says what it is, and stopsDoneG1-R5
          • The web API writes only what the command line canDoneARCH-R48
          • One envelope, and one rendering of itDoneARCH-R46
          • A refused read says which refusal it isDoneARCH-R74
          • What a request must carry before it is answeredDoneARCH-R52
          • The contract is generated, and a stale one fails the buildDoneARCH-R56
          • An SDK generates from the artefact and reaches nothing elseDoneARCH-R58
          • A client reads what it was handed, or refuses itDoneARCH-R55
          • The frontend is embedded, and serving it is not the core's businessDone
          • Every admin service answers this machine and nothing elseDoneC6-R1
          • What a cross-site request cannot sendDoneC6-R10
          • Nothing is carried through to a service's own interfaceDoneC6-R12
          • The password this surface asks for, kept as what proves itDoneC6-R8
          • The second way in, and what ends itDoneC6-R9
          • Guessing costs time, and is told how muchDoneC6-R11
          • Offered to a network, or refusedDoneC6-R4
          • The policy is one decision on both familiesDoneC6-R14
          • No certificate it made for itselfDoneC6-R7
          • What is actually listening, asked of the thing that is listeningDoneC6-R13
          • The household tier is reachable, and narrows through one settingDoneC6-R2
          • A rule you added does not decide, where it does notDoneC6-R16
          • An exposure somebody agreed to is theirs, and still reportedDoneC6-R15
          • An exit code says which kind of wrongDoneG1-R8
          • The dashboard on a terminal that offers nothingDoneG1-R9
          • One stack, one run at a time, whichever surface askedDoneG1-R12
          • Setup is completable from the browser tooDoneG1-R14
          • The third surface drives the same logic and adds none of its ownDoneG1-R2
          • Every action reachable from every surfaceDoneG1-R1
          • The web surface is swept at the level it owes, in every rendering a reader arrives inDoneG3-R3
          • A reader who asks for stillness gets it, and something checks every screenDoneG3-R5
          • Nothing repeats fast enough to flash, in either surfaceDoneG3-R6
          • The type follows the reader's own size, and the palette answers the systemDoneG3-R13
          • A bare run asks the screen, and says the whole of itDoneG1-R3
          • A repair asks nobody who is not thereDoneG1-R4
          • A narrow terminal is a terminal, not a smaller oneDoneG3-R8
          • Text this product did not write cannot drive the terminal it is shown onDoneG3-R15
          • Nothing reports on you, and something notices if it starts toDoneG8-R1
          • A bundle, a backup and a log stay on the machine that made themDoneG8-R6
          • The two references are generated, and a stale one fails the buildDoneARCH-R68
          • The household has one front door, and nothing stands in for itDoneG5-R1
          • The front door's address, read now rather than rememberedDoneG5-R8
          • What to do when it does not workDoneG6-R9
          • The address as something to point a camera atDoneG6-R4
          • Which app to watch on, and where the answer is to use something elseDoneG6-R1
          • Who has never arrived, on the list everybody else is onDoneG6-R13
          • What playback will struggle with, said before an app is chosenDoneG6-R6
          • What the household is waiting on, without being askedDoneD4-R8
          • Nobody in the house needs an account hereDoneD4-R9
          • The person who asked hears backDoneD4-R2
          • What the wiring graph still owesDoneD1-R18
          • The arrs that fulfil what the household asks forDoneD1-R17
          • Asking for part of a series, and only what you may ask forDoneD4-R11
          • Something already here, and something not out yetDoneD4-R5
          • Ready means the library has itDoneD4-R12
          • A request that keeps failing is not failing quietlyDoneD4-R14
          • Nothing at the quality you chose is its own answerDoneD4-R7
          • Asking for something already asked forDoneD4-R10
          • One way in, and it is the account somebody already hasDoneD4-R1
          • A door that is down and a door with no way to it are two answersDoneG5-R13
          • The door is chosen, and nothing beside it is a way inDoneG5-R10
          • The address reaches the third place it was owed, which is an invitationDoneG5-R6
          • An invitation is a link, and the same link as a codeDoneD6-R4
          • What an invitation does not hand overDoneD6-R2
          • The household list is a list of membersDoneD6-R7
          • An invitation outlives a request service that is downDoneD6-R12
          • Removal reaches both services, and says what it costs firstDoneD6-R8
          • A reset is an invitation againDoneD6-R10
          • An expired invitation keeps its accountDoneD6-R13
          • Access is chosen when somebody is invitedDoneD6-R5
          • A limit is said in the certificates the household already readsDoneD8-R1
          • One setting, and both halves of what it meansDoneD8-R2
          • What the media server does with a limit, once it has oneDoneD8-R8
          • Every outbound request is enumerable, and each is switched off on its ownDoneG8-R3
          • What is kept on this machine, and taking it offDoneG8-R7
          • What actually left, written down as it wentDoneG8-R14
          • Somebody being asked to join is told the operator can see what they watchDoneG8-R13
          • What the privacy stance still owesNot startedG8-R4
          • The app the browser is served, and the version it speaksDoneARCH-R54
          • The disk accounted for, and exhaustion seen comingDoneD5-R1
          • Removing a torrent that is still seedingDoneD5-R6
          • What a household may ask for, and what it may ask for nowDoneD7-R1
          • What a thing costs, said where somebody is deciding what to ask forDoneD7-R3
          • A limit stated before submission, and a period that says how it frees upDoneD7-R5
          • A refusal that requires a reason, and the reason reaching the person who askedDoneD7-R7
          • A reminder that is built, and an expiry the household agrees to in advanceDoneD7-R8
          • A full disk that stops the asking, and says so on the page they ask fromDoneD7-R13
          • The line accounted for, and the stack held to a share of itDoneD10-R1
          • The quiet half of the household's day, and what happens at a capDoneD10-R3
        • M8

          Household & content

          Not started

          0.11.0 and 0.12.0. The request flow, one-account identity, approval quotas, parental controls, disk-space and bandwidth management, client-app guidance. Not started. See the spec roadmap.

          Recorded in another repository

          • M9

            Lifecycle & maintenance

            In progress

            0.13.0, 0.14.0 and 0.15.0. Reconfiguration, migration, uninstall, notifications, remote control, autostart & boot persistence, stack and self updates, rollback, and the service catalogue. Notifications are in, and so is part of the change journal 0.14.0 locks — that is recorded in the M4 table above, beside the seeding it was built for. Uninstall is in, in the row below, and so now are the two families 0.13.0 carries: every setup answer is individually revisable, and a setup already on the machine can be surveyed, adopted, stood beside, stood in place of, or carried across. Of the rest, nothing is started: no command updates the stack or itself, rolls back, or lists a service catalogue, and the web surface is loopback-only by design rather than remote control. Autostart is the one to be careful about: setup asks the question and takes a --autostart flag, and the wizard says in as many words that the answer has no configuration home and is collected but not written. An answered question is not a boot-persistent stack, and nothing here brings the *stack* back after a reboot. This repository does now name launchd and systemd, and it is worth being exact about what for: they are reached to keep lemonfiber's own long-running commands going, which is B10 below and is a different subject from the stack coming back. Neither Docker Desktop's login item nor a container restart policy is touched by it. See the spec roadmap.

            15 of 15 recorded deliverables done

            What is recorded for it

            • The refresh loop as the driver — and in-app deliveryDoneB5-R4
            • Notifications & alertingDoneB5-R1
            • Critical alerts bypass quiet hoursDoneB5-R11
            • A read-only survey of what is already on this machineDoneA5-R1
            • What no migration carries across, and what an import left behindDoneA5-R8
            • An existing *arr database is never opened by an older binaryDoneA5-R6
            • Four modes, adopt already chosen and replace neverDoneA5-R3
            • A migration never touches the media it foundDoneA5-R4
            • An existing layout that breaks hardlinks, costed and left aloneDoneA5-R10
            • Clean uninstall — four removals, each explicitly chosenDoneA6-R1
            • One inventory of every credential, showing none of themDoneA7-R1
            • Rotation that validates before it destroys, and reports every consumerDoneA7-R4
            • Credential files owner-only, and a finding when they are notDoneA7-R8
            • The credential lemonfiber mints because the service offers nothing durableDoneA7-R14
            • Long-running commands hosted by the machineDoneB10-R1..R16
          • M10

            Release engineering

            In progress

            Signed, multi-platform release automation and the install paths a non-contributor follows. The cargo-dist pipeline runs on every tag and, from v0.3.0 onwards, each release carries attested archives for both macOS targets and both Linux targets, their checksums, and a shell installer. Windows and the tap are what the install paths still want.

            0 of 7 recorded deliverables done

            What is recorded for it

            • CI hardening — DCO, CODEOWNERS, SonarCloud gate, OpenSSFIn progress
            • cargo-dist release workflow — mac (arm64/x86_64), Linux (gnu/musl), WindowsIn progressL1-R1
            • homebrew-tap — auto-published by CINot startedL1-R3
            • Shell + PowerShell one-line installersIn progressL1-R4
            • Real Windows + Linux testing — beyond "it compiles"Not startedL1-R6
            • Docs site — generated from the specIn progressL1-R7
            • Epoch completeness — every v1 feature Accepted and done before 1.0.0Not startedOPS-R54

          Read from the revisions this site pins:lemonfiber/spec at 1d10402, 2026-09-09 · lemonfiber/lemonfiber at a2ac9bf, 2026-09-09