Setup, the testing philosophy, and TODO.md rendered whole
Come and build on it.
Local Reef is MIT-licensed, early, and built in the open. The milestones are done:
gateway, supervisor, canvas, ⌘K generation, live edit. What remains is written
down honestly in the ledger at the bottom of this page, unverified claims first,
because that is where a contribution lands hardest.
§Running it
# macOS, Node 20+
git clone https://github.com/everydev1618/localreef
cd localreef && npm install
npm start
# before you open a pull request
npm test
npm run lint
Launch from a terminal if you want ⌘K generation to work: npm start
inherits the shell environment and its ANTHROPIC_API_KEY; the Dock does
not. And read DESIGN.md before touching the gateway,
the supervisor or the window model. Several decisions in there look arbitrary and
are not.
§How this repo tests
The working style is test-driven: write the test, watch it fail, then implement.
But the part worth internalising before your first pull request is the split
between the plain suite and the Electron suites, because the Electron suites cover
what the plain suite structurally cannot, and two shipped bugs prove it.
npm test drives the gateway from Node, and a Node client is not a
browser. It sets the Cookie header directly, bypassing browser cookie
policy, which is exactly how an iframe 401 shipped green. It sets the auth header
on WebSocket handshakes by hand, which is how every app’s WebSocket shipped
silently broken while the Vite test stayed green. The Electron suites open a real
page, click with real input events, and ask a real cross-origin frame what
Permissions Policy granted it. They are not duplication; do not delete either side.
The pattern behind all of it: assert the effect a user sees, not the mechanism
you just wrote. Measure the bounding box, the HTTP status, the pixels. The
field notes are what this rule looks like learned the
hard way.
§The ledger
Below is TODO.md as it stands at the commit
this site was built from. It is the first place to look for something to pick up:
“unverified claims” are the highest-value contributions in the repo,
because they can invalidate a decision rather than just leave a feature unbuilt.
Everything known to be outstanding, in one place. Ordered by what would bite a
user first, not by how interesting it is to build.
Architecture and rationale live in DESIGN.md; this file is only
the ledger.
§Shipped
- M1 — the mechanic. Gateway, hostname routing, registry, supervisor.
- M2 — the canvas. Canvas, icons, draggable/resizable windows, dock.
- M3 — supervision. Lifecycle, readiness detection, log capture, crash panel
with real stderr.
- M4 — ⌘K. Generation from a description, plus Fix with AI on the crash panel.
- Linked apps. Any folder on disk, run in place.
- Settings. A scanned projects folder (opt-in per app via
reef.json) and
an API key stored in userData, so ⌘K works launched from Finder or the Dock. - Dock and window controls. Dock replaces canvas icons; minimize parks a
window without stopping the app, × still quits it.
- Backgrounds. Five wallpapers — two reef paintings and three CSS
gradients — with a picker in Settings. Each carries its own scrim, because
how much darkening a picture needs to stay legible under is a property of
the picture.
- Square app icons. One geometry, three contents — supplied art, an emoji on
a neutral tile, or a hue-tinted tile with initials. Closes the icon-design
open question in DESIGN.md §4.
- Any-language servers.
type: "server" is a shell command with $PORT, so
the "Python runtime" item from the original plan is closed — Python, Go, or a
binary all work today. - Vite verified end to end (
npm run test:vite): page proxied, HMR
WebSocket connected, live full-reload delivered after a file edit. Neither
feared risk materialised — no host-check rejection, and the HMR client's URL
survives proxying. Two unforeseen bugs did: Vite binds IPv6 loopback only,
and host was not threaded to the proxy. - M5 — edit live. A chat pane beside any ⌘K-built app (and only those —
provenance is the
generated flag, derived from the folder living in
userData/apps, enforced in main). Multi-turn: the conversation lives in
main, text turns only, and dies with the window; the files are the real
state. Every open static app's folder is watched (src/main/watcher.js) and
the frame reloads on change — chat edits and external-editor edits alike;
server apps keep their own HMR. Deferred: restart-on-edit for HMR-less
server apps (a manifest opt-in, someday); the titlebar name goes stale if a
turn renames the app (dock updates, reopen fixes the title).
§Unverified claims
Things asserted in the design that have not actually been demonstrated. These
matter more than the feature gaps below, because they could invalidate a
decision rather than just leave one unmade.
*(The long-standing "nobody has looked at the desktop" entry is closed —
npm run shot photographs the running app. It paid for itself immediately:
the first screenshot showed emoji icons invisible on dark tiles, and the
second showed the clock stuck on "disconnected — retrying", which turned out
to be every app's WebSocket failing.)*
- No screenshot runs in CI.
npm run shot is manual, so a visual
regression is still only caught by someone remembering to look.
§Papercuts
keepAlive is parsed but not enforced. Closing a window stops a server app
immediately; the warm-hold TTL in the manifest does nothing.
§Security
- Apps are served without their own CSP. A generated or linked app can still
load remote code. The desktop shell is locked down; the app frames are not.
This wants closing before generated apps become routine.
- Electron version currency is a dependency, not hygiene. The per-app-iframe
model means iframe-origin CVEs land directly on this design — the audit that
prompted the 34 → 43 upgrade included *"Permission Check Handler Receives Main
Frame Origin Instead of Requesting Iframe Origin"*. Treat
npm audit findings
against Electron as load-bearing. - Threat model is "apps I or my agent wrote." Not hostile third-party code.
If that changes, revisit iframes vs
WebContentsView (DESIGN.md §2).
§Not built yet
- The
reef.* SDK bridge. reef.storage, reef.ai, reef.window
— designed in DESIGN.md §7, no code yet. reef.ai is the notable one: it
lets generated apps call a model without the user pasting a key into each app. - Pop-out to native windows. Canvas-first was chosen with an escape hatch for
multi-monitor; the hatch does not exist.
- Intents and the shared store. The part that makes apps compose rather than
coexist, and the long-term reason this is a shell and not a launcher.
- Remote access (phone). Designed in REMOTE.md: the phone is
a client of the gateway's second face — LAN tier first (QR pairing, port-per-app
origins, device cookies), Tailscale as an optional accelerant, a first-party
relay as the eventual anywhere answer. R2 depends on making
keepAlive real
(see Papercuts).
§Housekeeping
- Machine-local state is unbacked-up.
links.json and generated apps live in
userData/, outside the repo — so a clone does not restore your desktop.
Linked projects already carry their own reef.json; the list itself does not. type: "node" is a legacy alias normalised in manifest.js. Harmless, but
it can go once nothing depends on it.npm test is 180 tests and growing. Three suites must be run separately
(test:electron, test:electron:ui, test:vite) and are easy to forget in
CI — they are also the ones that have caught the worst bugs.