- TypeScript 98.5%
- Shell 1.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .husky | ||
| base | ||
| bin | ||
| src | ||
| tests | ||
| .gitignore | ||
| bun.lock | ||
| bunfig.toml | ||
| commitlint.config.ts | ||
| oxfmt.config.ts | ||
| oxlint.config.ts | ||
| package.json | ||
| README.md | ||
| regime.config.json | ||
| slammer.code-workspace | ||
| tsconfig.base.json | ||
| tsconfig.config.json | ||
| tsconfig.json | ||
| tsconfig.src.json | ||
| tsconfig.test.json | ||
Slammer
smolvm + tmux, per-project machines
Requirements
- bun
- smolvm
- bubblewrap (
bwrap) with unprivileged user namespaces enabled, when using additional mounts
Usage
From a project directory:
slammer start
slammer reset
start creates a machine on first use, runs .slammer/install.sh once if present, and opens tmux. Use exit or detach (Ctrl-b d) to stop the machine while preserving its disk and Docker images. Later starts reuse the same machine. The interactive session runs with your host UID/GID so files created in mounted directories retain host ownership; the optional installer runs as guest root.
Startup reports its current phase on stderr: checking the VM, preparing the foundation, creating and starting the VM, running the optional installer, and opening tmux. Only applicable phases are shown. Project installer output streams as it arrives rather than waiting for the command to finish; programs inside the installer may still buffer their own output.
reset deletes the stopped project's machine. Guest-only packages, containers, and Docker images are lost; mounted host files and the foundation pack remain. If no machine exists, reset does nothing. An active session blocks another start or reset for that project.
Mounts
The physical project directory is mounted read/write at its same absolute path inside the guest. Additional directory mounts can be shared across all projects in ~/.config/slammer/slammer.toml. When XDG_CONFIG_HOME is an absolute path, Slammer uses $XDG_CONFIG_HOME/slammer/slammer.toml instead; empty or relative values fall back to ~/.config.
For example, share a dedicated tmux configuration directory:
[[mounts]]
path = "~/.config/tmux"
guest_path = "/home/ubuntu/.config/tmux"
read_only = true
The host directory must exist and contain tmux.conf. Choose a guest destination matching the guest user's home; /home/ubuntu is an example, not a guaranteed home for every host UID. Individual files such as ~/.tmux.conf cannot be mounted: smolvm requires directory sources. Do not mount the entire host home directory as a workaround.
Add project-specific mounts in the project's .slammer.toml:
[[mounts]]
path = "../shared"
read_only = true
Both configuration files are optional. Slammer combines the project directory mount, global mounts, and project-specific mounts in that order. Project entries do not override global entries: duplicate resolved host paths or normalized guest destinations are errors, including conflicts with the project directory mount. Invalid configuration fails with the configuration filename.
path: an existing host directory.~/expands to the host home directory. Global relative paths resolve against the global configuration directory; project relative paths resolve against the physical project directory. Symlinks resolve to their physical paths.guest_path: an optional absolute path inside the guest. It defaults to the resolved absolute host path. Relative paths and~are not supported here.read_only: defaults tofalse; set it totrueto prevent guest writes to shared configuration.
Consolidated exports
Additional directories share one virtiofs export rather than consuming one virtual device per directory. The project stays on its own writable mount, so a VM uses at most two directory-mount devices regardless of the number of additional directories.
On each VM start, Slammer uses bwrap to assemble a private bind-mount tree. Each source is bound read-only or writable according to read_only; the export's parent directory is read-only. The VM process keeps this mount namespace alive after the launcher exits, and stopping the VM releases it. The host's normal mount namespace is unchanged. Start these VMs through slammer start, not directly through smolvm machine start.
Inside the guest, configured destinations are symlinks into the reserved /mnt/slammer export. Slammer creates these before the project installer and checks them on later starts. Matching links are preserved; missing links are recreated, including links on temporary filesystems. Existing files, directories, or different symlinks are never overwritten. Destination parents must not be symlinks. Destinations must not overlap the project path, each other, or /mnt/slammer (including its ancestors). Host sources must not contain this project's saved mount state.
Resolved mount definitions are saved under $XDG_STATE_HOME/slammer/projects/, falling back to ~/.local/state/slammer/projects/ when XDG_STATE_HOME is unset, empty, or relative. Each physical project has a separate layout and export directory. Keep this state alongside the VM: existing machines do not re-read mount definitions from TOML. Missing directories or source paths that have become symlinks cause startup to fail rather than exposing a different source.
Setup files and mount definitions are read only when the project machine is created. Change them and run slammer reset to rebuild that machine; reset deletes guest-only data and saved mount state, not mounted source files. Editing files inside an already mounted directory does not require a reset, though the application may need to reload its configuration.
VMs created before consolidated exports have no saved layout. This version refuses to start them until an explicit slammer reset recreates them. Preserve any needed guest-only data before resetting; Slammer never resets a VM automatically.
Slammer never mounts the whole host home directory or the host Docker socket. Configured mount paths that would expose either are rejected.
Validation
Run bun run test for configuration, saved-layout, guest-link, and lifecycle tests, bun run check for typechecking, and bun run lint for lint checks. Unit tests do not create real VMs.