Skills cache
Materialise SKILL.md bundles on every machine so claude / codex can reference them by path without baking the content into agent definitions.
Why#
A skill is a SKILL.md document plus optional companion files (shell scripts, helper configs, etc.). The dashboard stores the source of truth in Postgres; the daemon caches each version locally so the harness can be pointed at a real on-disk directory rather than streaming bytes per session start.
On-disk layout#
text
~/.controlum/skills/
└── <skill-id>/
└── v<version>/
├── SKILL.md
├── deploy.sh # whatever the "files" jsonb contained
├── helpers/util.sh
└── .checksum # sha256 the daemon computed; matches backend's<skill-id> is the backend uuid; <version> is the user-supplied semver string. Multiple versions coexist — claude pointed at v1.0.0 keeps working while a fresh session opens v1.1.0.
Sync flow#
- Dashboard /skills → row → Sync icon → pick a machine.
POST /skills/{id}/reload-on-machinewith{ "machine_id": "<uuid>" }. Returns202if the daemon is online,503if it isn't.- Backend looks up the skill bundle (SKILL.md + files + checksum) and pushes a
sync_skillWS message to the chosen machine's daemon. - Daemon writes the bundle to a sibling
.tmp-*directory, computes the sha256 overskill_md + "\n" + files-json, and verifies it matches the backend's checksum. - On match:
os.Renamethe tmp dir into place (atomic; readers of the previous version aren't disturbed). On mismatch:ErrChecksumMismatch— tmp dir is cleaned and the on-disk version stays untouched. - Daemon emits
skill_synced(success) orskill_sync_failed(mismatch / IO error) back over WS. Future iterations will surface this as a per-skill badge on the dashboard.
Safety#
- Path traversal rejected. Entries whose path resolves out of the version dir (
../escape, absolute paths) are refused before any write happens. - Atomic. A partial bundle (process killed mid-write, disk full) leaves the previous version untouched.
- Per-user. The HTTP handler verifies the user owns BOTH the skill and the machine; daemons only ever receive bundles from
ws.Hub.SendToDaemon(the user's WS connection).
Limitations (v1)#
- No automatic GC of old versions. Disk pressure for skills is tiny; downgrades stay possible.
- No retry on the backend if the WS push fails — the user re-clicks Sync. WS-level retries are on the roadmap.
- No per-machine sync status in the table yet — only a toast on dispatch.