Marrow

Live preview

The dev server — watch, rebuild, live reload, and a small JSON API.

marrow serve is the fast loop. It watches a file or a directory, recompiles on save, and pushes a reload over server-sent events. It serves exactly what build ships — a large topology gets the real multi-page site, not a simplified preview.

npm run dev                       # everything in input/ — the default
npm run dev input/branch.yaml     # a single file

Default bind is 127.0.0.1:5200.

npm run dev -- --port 5300
npm run dev:lan                   # reachable from the LAN

dev:lan (--host 0.0.0.0) exposes the server to your whole network. It is the right choice for showing a colleague a diagram on the same office LAN, and the wrong one on untrusted networks. There is no authentication.

How reloading works

How Live Reloading Works

Reactive filesystem watcher with memoized differential compilation

1. Developer Saves Fileinput/branch.yaml
fs write
2. File Watcher & DebounceBatches rapid IDE bursts (50ms window)
fs.watch
3. Schema Validation & Differential BuildStrict schema check + geometric memoization
marrow-core
Shape Unchanged

Metadata, IP, or title edit. Only recompiles the single zone or device page that changed.

⚡ ~12ms differential refresh
Shape Changed

New links, added devices, or modified tiers. Re-runs ELK placement and reloads all pages.

🔄 Full graph recalculation
4. Server-Sent Events (SSE) PushOpen browser viewport hot-reloads live
text/event-stream

Page builds are lazy and memoized, so a 5,000-device site doesn't recompile in full because you renamed one device. Editing a device inside one zone reloads that zone's page and the pages that reference it, not the entire site.

If the file you saved has errors, the server keeps serving the last good build and surfaces the diagnostics rather than blanking the page.

Switching files

When you serve a directory, the browser gets a file picker — every valid topology in that directory is selectable, and the server tracks which one is current. That's the mode to use when you're iterating on several diagrams at once.

The JSON API

The server exposes a small read-mostly API, mainly so agents and scripts can poll state without scraping HTML:

EndpointReturns
GET /api/statusCurrent file plus its diagnostics, as a JSON report
GET /api/validateValidation report for the current file
GET /api/sceneThe computed scene — boxes and polylines
GET /api/filesTopologies discovered in the served directory
GET /api/currentWhich file is currently selected
POST /api/currentSelect a different file
GET /api/eventsSSE stream of reload events

/api/scene is the interesting one if you're debugging layout: it's the exact geometry the renderers consume, after placement, routing, and the tidy pass.

When to prefer a snapshot

The server is for humans watching a browser. If you're an agent — or you just want a durable artifact to look at — render an image instead:

npm run snapshot -- input/branch.yaml -o /tmp/branch.png
npm run snapshot -- input/branch.yaml -o /tmp/x.png --focus core-sw-01
npm run snapshot -- input/branch.yaml -o /tmp/x.png --theme light

snapshot uses Playwright under the hood, so the first run may need npx playwright install chromium.

Only run one server per port. Check whether one is already listening before starting another — a stale server on 5200 serving an old directory is a classic five minutes lost.