Skip to content

lemonfiber-web — the web surface

lemonfiber

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 Licence


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.

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 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.

Terminal window
npm ci
npm run dev

That 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.

Everything CI runs, in one command:

Terminal window
npm run ci

The 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.

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
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.

The spec is canonical: every change cites a spec identifier that already exists. Routine maintenance cites GOV-R12.

Hippocratic License 3.0 — ethical-source, source-available, deliberately not OSI-approved. See the rationale.


NightWorks.io  · Discord

This page lives in another repository Rendered from lemonfiber/lemonfiber-web at f2111fd, 2026-09-05. Read the source of this page