Repo: lemonfiber
Status: Accepted
The lemonfiber binary — CLI, TUI and web UI over one core. Rust, Hippocratic 3.0.
Implements: the operator-facing half of 10-functional, under the structure fixed in 20-architecture/component-model.
What this repo is
Section titled “What this repo is”One binary, three surfaces, one core. It sets the stack up, runs it, and proves it’s working. It is not a general Docker manager (vision non-goals) — its value is that it knows about this stack.
Layout
Section titled “Layout”lemonfiber/├── Cargo.toml workspace├── crates/│ ├── lemonfiber/ bin — chooses a surface and runs it│ │ ├── cli.rs clap: subcommands, flags, exit codes│ │ ├── dashboard.rs ratatui: the dashboard and the log viewer│ │ ├── ui.rs binds the socket the web surface is served on│ │ └── main.rs surface selection│ ├── lemonfiber-api/ lib — axum: the JSON endpoints, and the frontend served│ ├── lemonfiber-core/ lib — all logic, no UI│ ├── lemonfiber-ports/ lib — the boundary and its vocabulary│ ├── lemonfiber-fixtures/ lib — the fakes for those traits│ └── lemonfiber-manifest/ lib — stack.toml├── assets/media-stack/ git submodule, embedded at build├── build.rs validates submodule schema_version├── .docs/ repo-local technical docs└── tests/ integration + golden filesCrate responsibilities are fixed in component-model; this doc covers what’s specific to building the repo.
.docs/ — repo-local documentation
Section titled “.docs/ — repo-local documentation”Per the three-layer model,
lemonfiber carries its own .docs/ tree for Rust-specific technical detail that is
not a product decision (those live in this spec) and not a code comment.
lemonfiber/.docs/├── 00-index.md├── architecture/ how subsystems are built — render loop, docker split,│ the vpn-port-forwarding push mechanism, …├── adr/ repo-local decisions (crate choices, not product ones)├── conventions/ code-comments.md (copied from spec), naming, error style├── cicd/ pipeline detail├── runbooks/ release cutting, submodule bumping└── features/ subsystem notesCode links here (Q-R5); these pages cite spec requirement IDs. This is the
middle layer that keeps requirement IDs out of code comments (GOV-R6) while
still connecting code to the spec.
Surface selection
Section titled “Surface selection”flowchart TD start[lemonfiber invoked] --> args{subcommand?} args -->|"up/down/doctor/…"| cli[CLI path] args -->|"ui"| web[Start web server] args -->|none| tty{stdout a TTY?} tty -->|yes| conf{configured?} tty -->|no| help[Print help, exit] conf -->|yes| dash[TUI dashboard] conf -->|no| wiz[Offer setup]This realises G1-R3 and G1-R4: bare invocation opens the TUI at a terminal and prints help when piped, never blocking on stdin.
Key dependencies
Section titled “Key dependencies”| Crate | Role | Why |
|---|---|---|
clap |
CLI parsing | Derive; generates help and completions |
ratatui + crossterm |
TUI | ADR-0003; crossterm for real Windows support |
axum |
Web server | Minimal, tokio-native; only in the web module |
bollard |
Docker API | Reads only; only in lemonfiber-core::docker |
tokio |
Async | Shallow — component-model |
reqwest |
Service HTTP | Seed clients |
serde + toml |
Manifest, config | |
etcetera |
Platform paths | XDG / AppData / ~/Library; permissive dependencies throughout |
include_dir |
Embed stack + web assets | ADR-0005 |
thiserror |
Library errors | lemonfiber-core |
color-eyre |
Binary errors | The lemonfiber crate only |
unsafe is denied crate-wide (see code-standards).
The submodule
Section titled “The submodule”assets/media-stack is a git submodule pinned to a lemonfiber-media-stack tag.
build.rs:
- Fails clearly if the submodule is empty (
git submodule update --init). - Parses
stack.tomland fails the build ifschema_versionis unsupported (ARCH-R6).
So an incompatible stack/binary pairing cannot compile, let alone ship.
Build-time work
Section titled “Build-time work”| Step | Produces |
|---|---|
build.rs |
Schema validation; embedded stack |
cargo build |
The binary |
The app is built in lemonfiber-web’s own CI and tagged
there; this repo carries the result as a pinned submodule and embeds it with
include_dir! (ADR-0012).
No Node toolchain enters the Rust build or the operator’s machine (ARCH-R19).
Configuration on disk
Section titled “Configuration on disk”| Path (Linux shown) | Holds |
|---|---|
~/.config/lemonfiber/ |
.env, expected-state baseline, journal |
~/.local/share/lemonfiber/stack/ |
Materialised stack files |
~/.local/share/lemonfiber/config/ |
Per-service config (the /config mounts) |
~/.local/share/lemonfiber/backups/ |
Backup archives |
Resolved via etcetera, so macOS and Windows get their conventional
locations. --stack-dir overrides the stack path (F1-R3).
Testing posture
Section titled “Testing posture”Most of lemonfiber-core is testable without Docker or a terminal, by design
(ARCH-R11). Detail in testing-strategy; the
load-bearing point:
- Command construction is pure → golden-file tested across every form × every platform, no daemon.
- Docker access is behind a trait → mockable.
- Platform is one component → faked, so all four environments run from one
machine (
ARCH-R35).
What lives here vs. in the spec
Section titled “What lives here vs. in the spec”Here (lemonfiber) |
Spec |
|---|---|
| How the render loop is built | That it must not block (B3-R4) |
| Which crate parses TOML | The manifest contract |
| The exact port-push shell mechanism | That the port must be re-pushed (C2-R5) |
| Rust module structure | That core cannot render (ARCH-R11) |
Rule of thumb: what and why are the spec’s; how, in Rust is this
repo’s .docs/.
Related
Section titled “Related”- lemonfiber-tui.md — screen-by-screen TUI spec
- lemonfiber-reference.md — every subcommand and flag
- component-model · platform-matrix
- 40-quality — how the code is written
This page lives in another repository Rendered from lemonfiber/spec at 1d10402, 2026-09-09. Read the source of this page