@hugpy/vm-mgr
v0.2.0
Published
Host-side LXD VM fleet manager — the backend companion to @hugpy/console: VM lifecycle CLIs (vm-new/clone/snapshot/backup/restore), fleet web console + API, keeper persistence layer, vsock bridge.
Maintainers
Readme
@hugpy/vm-mgr
Host-side LXD VM fleet manager — the backend companion to
@hugpy/console. The console is
the UI; this package is everything it talks to on the host. In the operator's
framing: @hugpy/console is the window, @hugpy/vm-mgr is the host's
sandbox — the VM lifecycle CLIs, the fleet web console and its API, the
keeper persistence layer, and the host↔VM bridge that make LXD instances
console-usable.
Node is only the delivery vehicle — the tooling itself is bash, Python, and systemd. Nothing in the package runs on Node at runtime.
Requirements
- A Linux host (the package is marked
os: ["linux"]). - LXD installed and initialised, with your
user in the
lxdgroup. node/npmto install the package;sudoto runinstall.sh(it writes systemd units, a cron manifest, and — on first run — an LXD storage pool).- The installer additionally checks for
python3(3.10+),tmux, andbtrfs-progsat run time and tells you what's missing.
Quickstart
npm install -g @hugpy/vm-mgr
vm-mgr-install # no args: prints the guided two-step pathThat guided path:
cp "$(npm root -g)/@hugpy/vm-mgr/install.conf.example" ./install.conf
${EDITOR:-nano} ./install.conf # see Configure, below
sudo vm-mgr-install ./install.conf # copies your conf in, then runs install.shThere is no postinstall hook, by design. Installing host tooling — an LXD
storage pool, systemd units, the console service — is an explicit, root,
opt-in action, never a side effect of npm install. vm-mgr-install with no
arguments only prints instructions; it changes nothing.
You can also drive install.sh directly from a checkout (equivalent to the
above, and how the flags below are usually exercised):
cp install.conf.example install.conf && ${EDITOR:-nano} install.conf
sudo bash install.sh --dry-run # print the full plan, touch nothing
sudo bash install.sh --yes # apply it, no promptsUseful flags (see install.sh -h for the full list): --skip-golden,
--skip-console, --skip-cron skip individual steps; --json-result <path>
writes a machine-readable {ok, ts, steps: [...], services: {...}} result
file alongside normal stdout logging (works with --dry-run too, adding
"dry_run": true); --stack-manifest <versions.json> [--allow-drift] refuses
to proceed on a @hugpy/vm-mgr version mismatch against a pinned stack
manifest. Every step is idempotent — safe to re-run; each one checks whether
it's already satisfied before acting.
Health check: vm-mgr-doctor
A read-only probe for an existing install — never writes anything (no lxc
create/config/exec-that-mutates, no file writes, no service restarts):
vm-mgr-doctor [--json] [--conf <install.conf>]Checks LXD reachability, storage-pool health, the console service + port, and per-VM keeper readiness. Exit code is the contract:
| Exit | Meaning |
|---|---|
| 0 | every check passed |
| 1 | at least one check failed |
| 2 | couldn't even probe (no lxc / LXD unreachable) — every other check would be meaningless, so it stops there rather than printing a wall of misleading failures |
Configure
Copy install.conf.example to install.conf and set the values you need.
Every field has a safe default (shown in the file's comments); anything left
blank uses it. The main knobs:
VM_MGR_ROOT— install prefix; blank derives it from the checkout location.- Storage pool —
POOL_DISK(a dedicated drive, not/),POOL_SIZE,POOL_NAME, andBACKUP_ROOT(keep backups off the LXD pool). - Networking —
LAN_PARENTfor an optional macvlan parent NIC; blank is NAT-only. LAN presence stays opt-in per VM. - Golden image —
GOLDEN_NAME/GOLDEN_TARBALL/BASE_IMAGEcontrol what new VMs clone from. - Console service —
CONSOLE_USER,CONSOLE_HOST,CONSOLE_PORT,CONSOLE_DOMAIN(cert CN),ADMIN_PASSWORD. - In-VM dev user —
STATION_UID/STATION_GID/STATION_HOME. - Fleet policy —
KEEPER_PROTECTED(comma-separated VM names shielded from stop/restart),KEEPER_GROUP, an optional local-model gateway URL for the console's to-do assist, and the source CIDRs allowed to provision VMs from the console (localhost-only by default, fail-closed). HUGPY_KEEPER_SRC— host dir with the hugpy keeper'skeeper.py/central.py; unset skips hugpy-keeper provisioning cleanly (loudly, not silently — see "Per-VM onboarding" below).
Per-VM onboarding
install.sh builds the golden image and stands up the host services, but a
freshly cloned VM is not yet console-complete on its own: the console's
per-VM features (search, the to-do board, push cadence) read/write files that
provisioning alone doesn't lay down. bin/vm-onboard closes that gap and is
idempotent — safe to re-run against an already-onboarded VM:
bin/vm-onboard <instance> --dry-run # print the full plan, touch nothing
bin/vm-onboard <instance> # apply itIt seeds ~/todo.json / ~/wireframe.json (never overwriting an existing
board), installs the todo and finder CLIs, and touches ~/.todo.lock.
bin/vm-hugpy-keeper — called automatically during golden-image build and by
vm-new, and runnable standalone — provisions the hugpy/local-LLM keeper
option into an instance, gated on HUGPY_KEEPER_SRC in install.conf. It
also lays down hugpy-dispatch (p14) — /usr/local/bin/hugpy-dispatch,
a one-command wrapper any keeper (claude or hugpy) can run to dispatch a
task to hugpy-agent using the VM's provisioned config. Both never enable
or restart a running service on their own.
What gets installed
| Piece | What it is |
|---|---|
| Console service | station-console.service (systemd unit, rendered from console/station-console.service.in) — the terminal console, TLS + auth, enabled and started by install.sh unless --skip-console. |
| Web console + API | web-console/console-api.service.in and friends — the fleet management API and single-page UI (VM control, per-VM to-do boards, keeper attach); staged, not auto-enabled. |
| bugreport sidecar | host/bugreport-api/ — a small API + nginx-locations.conf.in fragment for surfacing bug reports through the same reverse proxy as the console. |
| nginx templates | .conf.in fragments (bugreport, console) rendered with @VM_MGR_ROOT@/@CONSOLE_USER@ substitution, same idiom as the systemd .service.in templates — you cp the ones you want into place. |
| Cron manifest | host/cron/vm-mgr.cron.in → /etc/cron.d/vm-mgr (backups, housekeeping); skip with --skip-cron. |
| keeper-notify | A daemon that watches board changes and notifies the operator (keeper-notify/). |
| vsock bridge | Host↔VM vsock bridge units (bridge/) for the in-VM agent / host HTTP API. |
None of the above is enabled or started except the console service (and only
when --skip-console isn't passed) — everything else is rendered/staged for
the operator to systemctl enable --now deliberately.
hugpy setup integration
@hugpy/vm-mgr is a driveable install target for hugpy setup --profile
vm-host (owned by the hugpy keeper) via the --json-result / --stack-manifest
flags and vm-mgr-doctor documented above.
Internals / design notes
For per-tree architecture notes, the console/web-console feature-parity
tracker, and other engineering-detail docs, see docs/ and
web-console/PARITY.md.
License
hugpy Source-Available (non-commercial, no redistribution). See LICENSE.
