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.