Custom Images
Capsem images are defined by signed Profile V2 payloads. Organizations create
profiles with their own packages, tools, MCP servers, VM assets, enforcement packs,
and detection packs, then use capsem-admin to derive build plans, verify
assets, generate manifests, and sign the catalog.
Quick Start
Section titled “Quick Start”python -m pip install capsemcapsem-admin profile init corp-dev --out profiles/corp-dev.profile.tomlcapsem-admin profile validate profiles/corp-dev.profile.toml --jsoncapsem-admin image build profiles/corp-dev.profile.toml --arch all --jsoncapsem-admin image verify profiles/corp-dev.profile.toml --assets-dir assets/ --jsoncapsem-admin manifest generate --profiles profiles/ --base-url https://profiles.example.com/catalog/ --out manifest.jsonThe generated build workspace still contains TOML files consumed by the Docker templates, but those files are derived artifacts. The profile is the source of truth.
Generated Build Workspace
Section titled “Generated Build Workspace”build/corp-dev-image/ config/ build.toml Architectures, compression, base images ai/ anthropic.toml Provider: API key, domains, CLI install, config files google.toml openai.toml packages/ apt.toml System packages python.toml Python packages + PyPI registry mcp/ capsem.toml MCP server definitions security/ controls.toml Developer seed controls for built-in profiles vm/ resources.toml CPU, RAM, disk, session limits environment.toml Shell, bashrc, TLS config kernel/ defconfig.arm64 Kernel config per architecture defconfig.x86_64 artifacts/ capsem-init PID 1 init script capsem-bashrc Shell configuration banner.txt Login banner diagnostics/ In-VM test suiteConfiguration Reference
Section titled “Configuration Reference”AI Providers
Section titled “AI Providers”Each file in config/ai/ defines one provider. The filename is the provider identifier.
[anthropic]name = "Anthropic"description = "Claude Code AI agent"enabled = true
[anthropic.api_key]name = "Anthropic API Key"env_vars = ["ANTHROPIC_API_KEY"]prefix = "sk-ant-"docs_url = "https://console.anthropic.com/settings/keys"
[anthropic.network]domains = ["*.anthropic.com", "*.claude.com"]allow_get = trueallow_post = true
[anthropic.install]manager = "curl"packages = ["https://claude.ai/install.sh"]
[anthropic.files.settings_json]path = "/root/.claude/settings.json"content = '{"permissions":{"defaultMode":"bypassPermissions"}}'Add a custom provider by editing the profile package/tool/provider contract, then validate the profile:
capsem-admin profile validate profiles/corp-dev.profile.toml --jsoncapsem-admin image plan profiles/corp-dev.profile.toml --jsonPackage Sets
Section titled “Package Sets”Each file in config/packages/ defines packages for one manager.
[apt]name = "System Packages"manager = "apt"packages = [ "coreutils", "util-linux", "git", "curl", "python3", "python3-pip", "python3-venv",][python]name = "Python Packages"manager = "uv"install_cmd = "uv pip install --system --break-system-packages"packages = ["numpy", "pandas", "requests", "pytest"]
[python.network]name = "PyPI"domains = ["pypi.org", "files.pythonhosted.org"]allow_get = trueMCP Servers
Section titled “MCP Servers”[capsem]name = "Capsem"description = "Built-in file and snapshot tools"transport = "stdio"command = "/run/capsem-mcp-server"builtin = trueenabled = trueSecurity Controls
Section titled “Security Controls”Profile V2 enforcement and detection packs control network access and findings inside the VM. Developer image TOML can still seed built-in profile generation, but corp/operator releases should author controls in the profile.
[security.rules.http.allow_github]on = "http.request"if = 'http.request.host == "github.com" || http.request.host.endsWith(".githubusercontent.com")'decision = "allow"priority = 10
[security.rules.http.block_unknown_writes]on = "http.request"if = 'http.request.method in ["POST", "PUT", "PATCH", "DELETE"]'decision = "block"priority = 1000Build Configuration
Section titled “Build Configuration”config/build.toml defines per-architecture build parameters. Each architecture is self-contained.
[build]compression = "zstd"compression_level = 15
[build.architectures.arm64]base_image = "debian:bookworm-slim"docker_platform = "linux/arm64"rust_target = "aarch64-unknown-linux-musl"kernel_branch = "6.6"kernel_image = "arch/arm64/boot/Image"defconfig = "kernel/defconfig.arm64"node_major = 24
[build.architectures.x86_64]base_image = "debian:bookworm-slim"docker_platform = "linux/amd64"rust_target = "x86_64-unknown-linux-musl"kernel_branch = "6.6"kernel_image = "arch/x86_64/boot/bzImage"defconfig = "kernel/defconfig.x86_64"node_major = 24VM Resources
Section titled “VM Resources”[resources]cpu_count = 4ram_gb = 4scratch_disk_size_gb = 16retention_days = 30max_sessions = 100VM Environment
Section titled “VM Environment”[environment.shell]term = "xterm-256color"home = "/root"path = "/opt/ai-clis/bin:/root/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
[environment.shell.bashrc]path = "/root/.bashrc"content = '''PS1='\[\033[1;32m\]capsem\[\033[0m\]:\[\033[1;34m\]\w\[\033[0m\]\$ 'alias pip='uv pip'alias claude='claude --dangerously-skip-permissions'alias gemini='gemini --yolo''''
[environment.tls]ca_bundle = "/etc/ssl/certs/ca-certificates.crt"The PATH is set by the host at boot via the settings registry — do not set PATH in the bashrc (it creates duplicates and hides bugs). The aliases enable auto-approve modes for AI CLIs since the VM is already sandboxed.
CLI Reference
Section titled “CLI Reference”| Command | What it does |
|---|---|
capsem-admin profile init <id> --out <profile> | Create a valid Profile V2 draft |
capsem-admin profile validate <profile> --json | Validate profile JSON/TOML |
capsem-admin image build <profile> | Build all architectures from a Profile V2 payload |
capsem-admin image build <profile> --arch arm64 | Single architecture |
capsem-admin image build <profile> --dry-run --json | Preview without building |
capsem-admin image verify <profile> --assets-dir assets/ --json | Verify local assets, hashes, and package/tool inventory |
capsem-admin image sbom <profile> --assets-dir assets/ --out-dir sboms/ | Emit guest-image SPDX SBOMs |
capsem-admin manifest generate --profiles profiles/ --out manifest.json | Build a profile catalog manifest |
capsem-admin manifest check manifest.json --download --pubkey profile-sign.pub --json | Download and verify profile/assets/signatures |
capsem-admin enforcement validate <enforcement-pack> --json | Validate enforcement packs |
capsem-admin detection compile <detection-pack> --out detection.ir.json --json | Validate Sigma with pySigma and compile Detection IR |
Manifest
Section titled “Manifest”Every build produces assets/manifest.json (format 2) — a single top-level file covering every arch. It records BLAKE3 hashes and file sizes for each asset and ties asset versions to compatible binary versions:
{ "format": 2, "assets": { "current": "2026.0421.30", "releases": { "2026.0421.30": { "date": "2026-04-21", "deprecated": false, "min_binary": "1.0.0", "arches": { "arm64": { "vmlinuz": {"hash": "<64-char blake3>", "size": 7797248}, "initrd.img": {"hash": "<64-char blake3>", "size": 2314963}, "rootfs.squashfs": {"hash": "<64-char blake3>", "size": 454230016} } } } } }, "binaries": { "current": "1.0.1776688771", "releases": { "1.0.1776688771": { "date": "2026-04-21", "deprecated": false, "min_assets": "2026.0421.30" } } }}The runtime boots only when the asset hashes match. min_binary/min_assets gate which binary and asset versions are compatible with each other.
Corporate Deployment
Section titled “Corporate Deployment”Workflow
Section titled “Workflow”capsem-admin profile init corp-image --out profiles/corp-image.profile.toml— create a typed draft.- Remove unwanted providers, MCP servers, packages, enforcement packs, or detection packs from the profile.
- Add internal providers and package/tool requirements to the profile.
- Validate:
capsem-admin profile validate profiles/corp-image.profile.toml --json. - Build:
capsem-admin image build profiles/corp-image.profile.toml --arch all --json. - Verify:
capsem-admin image verify profiles/corp-image.profile.toml --assets-dir assets/ --json. - Generate and sign the profile catalog manifest.
Lockdown Example
Section titled “Lockdown Example”Create a corp profile draft, then keep only the approved providers and security packs:
capsem-admin profile init corp-image --out profiles/corp-image.profile.tomlcapsem-admin profile validate profiles/corp-image.profile.toml --jsoncapsem-admin enforcement validate corp-enforcement.toml --jsoncapsem-admin detection compile corp-detections.yml --out detection.ir.json --jsonEnforcement packs carry blocking rules:
[security.rules.http.allow_internal]on = "http.request"if = 'http.request.host.endsWith(".internal.corp.com")'decision = "allow"priority = -100
[security.rules.http.block_google]on = "http.request"if = 'http.request.host.contains("google")'decision = "block"priority = -90Install Methods
Section titled “Install Methods”AI providers support two install methods via the [provider.install] section:
npm (default for most CLIs)
Section titled “npm (default for most CLIs)”[provider.install]manager = "npm"prefix = "/opt/ai-clis"packages = ["@google/gemini-cli"]All npm packages across providers are batched into a single npm install -g --prefix /opt/ai-clis command. The prefix directory is writable at runtime via the overlayfs upper layer, allowing CLIs to self-update.
curl (native binary installers)
Section titled “curl (native binary installers)”[provider.install]manager = "curl"packages = ["https://claude.ai/install.sh"]Each URL gets its own RUN curl -fsSL <url> | bash step. Binaries are automatically copied from ~/.local/bin/ to /usr/local/bin/ (chmod 555) because /root is a tmpfs at runtime.
Troubleshooting
Section titled “Troubleshooting”| Diagnostic | Cause | Fix |
|---|---|---|
error[E001] missing required field | TOML config missing a schema field | Check file:line in error, compare against examples above |
error[E304] defconfig missing | Kernel config for declared arch doesn’t exist | Add config/kernel/defconfig.{arch} |
warn[W001] no npm registry | npm packages declared but no profile rule permits registry access | Add an enforcement rule or package contract entry for the registry |
warn[W005] API key in config | Hardcoded key in TOML | Use credential references in Service Settings V2/Profile V2 |
| Build fails: “container runtime not found” | No Docker | Install Docker (brew install colima docker on macOS, sudo apt install docker.io on Linux) |
| Build fails: exit 137 (OOM) or exit 143 (SIGTERM mid-build) | Container runtime VM out of memory — Tauri install-test cold build needs >12GB | Bump Colima to 16GB: colima stop && colima start --vm-type vz --vz-rosetta --memory 16 --cpu 8 |
| Build fails: “Release file not valid yet” | Container VM clock drift | Builder handles this automatically via Acquire::Check-Valid-Until=false |
| CLI not found at runtime | Installer put binary in /root/ which is tmpfs | Copy binary to /usr/local/bin/ in the Dockerfile template |