Adds table-driven store tests: - projects: drawing_name auto-default, explicit-name accept, empty-name reject, duplicate-name conflict, ordered list, GetProject not-found, partial PATCH semantics, blank-drawing-name re-default on PATCH, ?confirm=<name> guardrail (wrong / empty / correct), snapshot returns the 5 globally-seeded cable_types - cable_types: 5 seeded with the legend colours, global UNIQUE(name), rename + recolour, RESTRICT-blocked delete when a cable references the type (with count surfaced via CountCablesUsingType), unused delete succeeds, project deletion does NOT cascade into cable_types go test -race ./... passes. Updates README.md with run instructions, env vars, the slice-1 API surface, and the slice roadmap.
mCables
Cable-management framework for m's setup — visual web editor backed by a single Go binary + SQLite, generating Excalidraw drawings via mExDraw.
Each cable-managed environment (LOFT, OFFICE, …) is a separate mCables
project; each project is backed by exactly one .excalidraw drawing on
mxdrw.msbls.de.
Status
Slice 1 — bootstrap shipped. Projects + global cable types are end-to-end; the SVG canvas is intentionally empty until slice 2.
| Slice | What's in it | Status |
|---|---|---|
| 1 | Project CRUD, global cable types, empty SVG canvas, project picker | ✅ |
| 2 | Frames + devices, drag-to-position | pending |
| 3 | Ports + cables (click-port → click-port) | pending |
| 4 | IO markers + cable-type editing | pending |
| 5 | Export to mxdrw.msbls.de | pending |
Run it
go run ./cmd/mcables
# open http://localhost:7777
Or built:
make build
./bin/mcables
The binary serves the frontend from an embedded web/static/ and the
JSON API under /api/. SQLite lives at ./data/mcables.db by default.
Environment
| Var | Default | Notes |
|---|---|---|
MCABLES_ADDR |
0.0.0.0:7777 |
Listen address. |
MCABLES_DB |
./data/mcables.db |
SQLite path. Parent dir is created on boot. |
MEXDRAW_BASE_URL |
(unset) | Used by slice 5 export — not consumed yet. |
MEXDRAW_TOKEN |
(unset) | Bearer for the mExDraw export. Not consumed yet. |
Tests
make test # go test -race ./...
Store-level tests cover projects + cable-types CRUD, the
drawing_name auto-default, the ?confirm=<name> guardrail on
DELETE /api/projects/:pid, and the ON DELETE RESTRICT on a
referenced cable type.
API (slice 1)
GET /api/healthz → 200 {"status":"ok"}
GET /api/projects → [Project, …]
POST /api/projects ← {name, drawing_name?, description?}
drawing_name defaults to "<name>.excalidraw"
GET /api/projects/:pid → {project, cable_types, frames, devices, …}
PATCH /api/projects/:pid ← partial
DELETE /api/projects/:pid?confirm=<name> ← confirm must equal current name
GET /api/cable-types → [CableType, …] (global)
POST /api/cable-types ← {name, color}
PATCH /api/cable-types/:id ← partial — affects every project
DELETE /api/cable-types/:id ← 409 in_use if any cable references it
Design + project conventions
docs/design.md— full v3 design (schema, API, importer/export conventions, slices, mDock deploy notes).CLAUDE.md— project instructions for mai workers.
Architecture
| Layer | Tech |
|---|---|
| DB | SQLite via modernc.org/sqlite (cgo-free), WAL, FKs on |
| Backend | Go 1.22+ net/http ServeMux pattern routing, single binary |
| Frontend | Vanilla ES modules + SVG, no build step, embedded via embed.FS |
| Export (slice 5) | mExDraw HTTP API on mxdrw.msbls.de |
LAN-trusted, no auth.