@getzopu/machine
v0.1.5
Published
The Machine CLI: boot-time bootstrap for credential-free zopu session Machines.
Readme
@getzopu/machine — the Machine CLI
The only product code baked into a session Machine image (see the repo's CONTEXT.md for the Machine vocabulary). Machines are credential-free: bootstrap turns boot env into a running sshd and then — via execve(2) — the process becomes sshd. Nothing of the CLI lingers.
Commands
zopu-machine bootstrap
- Reads
SSH_AUTHORIZED_KEYfrom env (passed by the provisioning layer — ticket 5's Boxenv), writes it to~/.ssh/authorized_keys(0600). ssh-keygen -A— fresh host keys per Machine (the image bakes none).exec sshd -D -e— foreground sshd, logs to stderr.
Requires node ≥ 24 (process.execve).
The image (image/Dockerfile)
Debian slim + openssh-server (hardened via sshd_config.d drop-in: internal-sftp, no password auth, root only via key) + bash, git, ripgrep, node, and this CLI globally installed. No other product code.
Local fake Machine (scripts/dev-machine.sh)
One command runs the session image as a plain local Docker container with SSH on 127.0.0.1:2222 — the target the server's MACHINE_DEV=local dev profile provisions against instead of calling zopu-infra. The script mints a fixed dev keypair under .dev/, prefers the digest-pinned image from packages/api/src/machines/provisioning.ts (falls back to building image/ locally when the Fly registry pull fails), and writes the MACHINE_DEV* env into apps/server/.env.local. Re-running replaces the container (fresh host keys, like a real Machine); MACHINE_DEV_IMAGE=<ref> overrides the image choice.
Publishing
Two artifacts, one version, in order:
- CLI → public npm (it holds no secrets):
scripts/publish-cli.shfrom this directory. It drives the real npm (bypassing the local npm→bun intercept shim, which cannot prompt for 2FA), checks login and org membership, previews the tarball, and publishes with an interactive OTP prompt. Manual equivalent:npm publish --access public. - Image → Fly's registry:
../infra/scripts/publish-machine-image.sh(from this directory; the CLI script offers to chain into it). It lives in packages/infra — the one home for registry/deploy concerns — and authenticates with the Fly token only (no flyctl). It reads the version frompackage.json, builds multi-arch (amd64 + arm64) withMACHINE_CLI_VERSIONpinned to that version, pushes toregistry.fly.io/zopu-codepi-machine:v<version>, and prints the digest-pinned ref.
Versioning policy
- Image tag ↔ CLI version, one-to-one: image
v0.1.0bakes@getzopu/[email protected](the build arg enforces the pairing). - Tags are immutable in practice: never re-push an existing tag; cut a new version instead.
- Deployments pin by digest (the script prints it) — Fly's registry garbage-collects unreferenced tags, and zopu-infra's Catalog (ADR-0015) records the digest as the durable identity anyway.
After a push
zopu-infra must run an imageSync for repository zopu-codepi-machine before boxCreate accepts the new tag — the Catalog gate. catalogOverride exists as the escape hatch; the sync is the policy.
