lemonfiber-web — the web surface
Lemonfiber — lemonfiber-web
The web surface: an operator console and a household view, drawn from the JSON
API the lemonfiber binary serves. A static application with no
server of its own.
Status: early. The component library is built, and the operator’s console is being assembled from it. This repo is milestone M7 on the roadmap. Full account in the spec:
30-repos/lemonfiber-web.md.
What it is
Section titled “What it is”A static application with no server of its own. It is built to a directory of
files, and on a version tag publish.yml pushes
that build output to a built-<tag> tag — the form the lemonfiber binary takes
it in, as a pinned submodule it embeds. v0.1.0 is cut, so built-v0.1.0 is what
lemonfiber pins at assets/web and embeds. The build declares the wire version it
speaks in app.json, and the binary refuses to compile against an app that speaks
one it does not serve.
lemonfiber’s
web API
is the only place this application may get data from, and it reaches nothing else.
The operator’s console asks it for what it draws, asks it to act on the forms
the stack declares, and redeems the name work outliving a request comes back
with; the household view is not built yet.
That constraint is the point rather than a limitation. G1-R2 says no surface may
implement behaviour independently, and an application whose only capability is to
ask the core and draw the answer cannot violate it.
The two surfaces
Section titled “The two surfaces”The application serves two audiences, and they are not the same interface with things hidden:
- The console — an operator’s view. State, checks, logs, services, setup, household administration.
- The household view — what everyone else gets. Asking for something, seeing whether it is ready, and nothing else.
Both are built from one component library and one set of tokens, so they cannot drift apart visually. What differs is which of them a given person is served.
Running it
Section titled “Running it”npm cinpm run devThat serves the application on Vite’s dev server, which has no back end of its own and no proxy to one. The page asks lemonfiber at its own address, which is the address the binary serves it from — so the dev server draws the shell and the empty states, and a console with figures in it means running a build the binary carries.
Requires Node 26 or newer, as declared in engines.
npm ci is also what turns on this repository’s pre-push hook, which refuses a
push that would leave a branch carrying no commit origin/main does not — what
pushing the trunk over a feature branch looks like. npm’s prepare script does
it, so npm install serves too. A clone nobody has installed into has no hook:
it is git config core.hooksPath .githooks, per clone, and git cannot read
.githooks/ on its own.
The gate
Section titled “The gate”Everything CI runs, in one command:
npm run ciThe individual steps are the scripts in package.json, and each
runs on its own while you work — npm test for the fast loop, npm run storybook
to build a component in isolation, and npm run a11y to sweep every built story in
a browser (after npm run storybook:build).
The Rust workspace’s standards apply here from the first commit, in their web
equivalents: 100% coverage across lines, statements, branches and functions;
strict TypeScript with any and non-null assertions banned; zero lint warnings
tolerated; architecture and file-size guards; every Svelte a11y_ warning
refused at the compile, and accessibility asserted in the component tests and
swept over every built story in a browser. The
spec page
maps each one to the workspace rule it mirrors.
What this repo must not do
Section titled “What this repo must not do”These are the load-bearing rules, and each has a spec requirement behind it:
| Rule | Why |
|---|---|
Reach anything but lemonfiber |
No CDN, no font host, no analytics, no telemetry. Every asset it draws is embedded at build time |
| Talk to the API directly | Transport, the event stream and version negotiation belong to @lemonfiber/sdk-ts; this repo renders what the SDK returns |
| Implement behaviour | If the answer is not in an envelope, the surface does not know it (G1-R2) |
| Invent an action | Everything it can do, the CLI can do (ARCH-R48) |
| Hardcode a colour, size or spacing | Those belong to @lemonfiber/brand and are consumed as data |
What it consumes
Section titled “What it consumes”| From | What |
|---|---|
@lemonfiber/sdk-ts |
State, actions and the event stream, over the web API; and the wire version the build declares |
@lemonfiber/brand |
Colour, type, spacing, radii, the logo — at build time |
Both are pinned in package.json; that file is the version of
record, so nothing here restates it.
The type is the one thing not embedded. src/app.css names Golos Text for the
interface, Bricolage Grotesque for the wordmark and DM Mono for figures, and no
@font-face ships here — @lemonfiber/brand owns type and gives this repo the
tokens rather than the files. So each of the three falls back to the platform’s
own stack wherever the face is not installed, and until the faces ship the
interface is identical across Linux, Windows and macOS in everything but its
lettering.
Contributing
Section titled “Contributing”The spec is canonical: every change cites a spec identifier that already
exists. Routine maintenance cites GOV-R12.
- Contributing guide · Support · Security · Code of conduct
- ADR-0011 — why the web surface is its own repo
- ADR-0012 — how it ships
- ADR-0013
— why it goes through an SDK rather than
fetch - G1 interface tiers · G3 accessibility
Licence
Section titled “Licence”Hippocratic License 3.0 — ethical-source, source-available, deliberately not OSI-approved. See the rationale.
This page lives in another repository Rendered from lemonfiber/lemonfiber-web at f2111fd, 2026-09-05. Read the source of this page