This is the multi-page printable view of this section. Click here to print.
Container
1 - Base Image
Base Image
The base image is the foundation for all aibox container flavors. It provides a complete, opinionated development environment built on Debian Trixie Slim.
Installed Tools
| Tool | Version / Source | Purpose |
|---|---|---|
| tmux | Debian package | Terminal multiplexer |
| Yazi | 25.4.8 (prebuilt binary from GitHub releases) | Terminal file manager |
| Vim | Debian package (vim + vim-runtime) | Editor |
| Git | Debian package | Version control |
| Claude CLI | Official install script | AI assistant |
ripgrep (rg) | Debian package | Fast recursive search (grep replacement) |
| fd | Debian package | Fast file finder (find replacement) |
| bat | Debian package | Syntax-highlighting cat replacement |
| eza | Debian package | Modern ls replacement with git integration |
| zoxide | Debian package | Smarter cd that learns your habits |
| fzf | Debian package | Fuzzy finder for files, history, and more |
| delta | Debian package | Syntax-highlighting diff viewer (used by git) |
| starship | Prebuilt binary | Minimal, fast shell prompt with context |
| curl | Debian package | HTTP client |
| jq | Debian package | JSON processor |
| less | Debian package | Pager |
| unzip | Debian package | Archive extraction |
| iproute2 | Debian package | Network route/interface inspection for status segments |
| iputils-ping | Debian package | ICMP latency checks for status segments |
| bash-completion | Debian package | Shell completions |
| ca-certificates | Debian package | TLS root certificates |
| locales | Debian package | Locale support (en_US.UTF-8) |
| tzdata | Debian package | Timezone data |
GitHub CLI (gh) and lazygit are provided by the optional
git-ui addon, not by the base image. Add
[addons.git-ui.tools] to aibox.toml when a project needs those tools.
Audio tools are provided by the internal audio-voice recipe, which is selected
automatically when [audio] enabled = true and install = true. File-preview and archive helpers
such as chafa, timg, poppler-utils, mupdf-tools, entr, and
resvg are provided by the optional preview-archive addon.
Build Architecture
The Dockerfile keeps the runtime stage on Debian Trixie Slim and installs tmux from the distro package set. That keeps the terminal multiplexer on the same security update path as the rest of the base image and avoids a separate prebuilt-binary fetch stage.
tmux Configuration
Plugin Policy
aibox-managed tmux plugins are preinstalled and pinned by the generated runtime and image build. TPM is documented only as a user convenience layer for adding personal tmux plugins after initialization; aibox does not rely on TPM to install or update managed plugins.
tmux-resurrect and tmux-continuum are installed and available in the image,
but disabled by default until the workspace persistence policy is decided. Users
can opt into them in local tmux config, but generated layouts should not assume
session resurrection is active.
Key Bindings
All bindings use Ctrl+g as a leader key — press Ctrl+g, release, then press the action key. This avoids conflicts with macOS Option key (which produces special characters like @, €, |) and with Vim/bash Ctrl bindings.
| Key | Action |
|---|---|
Ctrl+g then h/j/k/l | Navigate panes (vim-style) |
Ctrl+g then n | New pane |
Ctrl+g then d | Split down |
Ctrl+g then r | Split right |
Ctrl+g then x | Close focused pane |
Ctrl+g then f | Toggle fullscreen |
Ctrl+g then z | Toggle pane frames |
Ctrl+g then e | Toggle embed/floating |
Ctrl+g then = / - | Resize pane (increase / decrease) |
Ctrl+g then t | New window |
Ctrl+g then w | Close window |
Ctrl+g then [ / ] | Previous / next window |
Ctrl+g then 1-5 | Jump to window N |
Ctrl+g then i / o | Move window left / right |
Ctrl+g then s | Session chooser |
Ctrl+g then m | Session manager |
Ctrl+g then u | Enter scroll mode |
Ctrl+g then / | Search scrollback |
Ctrl+q | Quit tmux |
Press Escape or Ctrl+g again to cancel the leader and return to normal mode.
Layouts
aibox ships four tmux layouts. Select one with aibox up --layout <name> (the default is dev). Layouts include harness windows based on the enabled entries in [ai].harnesses and [ai].harness_order; they include the lazygit window only when the git-ui addon selects lazygit.
dev (default)
Window work has Yazi and the 1st harness stacked on the left, with shell on the right. Further harnesses use the ai window; lazygit and shell get their own windows.
focus – one tool per window, fullscreen
Each tool gets the entire screen in its own window. Switch with Ctrl+g [/] or Ctrl+g 1-5.
Windows: files (yazi) | one window per harness | optional lazygit | shell
cowork – side-by-side coding with AI
Window work has Yazi on the left and shell on the right. The ai window contains all harnesses split evenly across full-height panes; the lazygit window is generated when enabled.
Opening Files from Yazi
Enter– opens file in vim in-place (suspends Yazi,:qreturns to Yazi). Works in all layouts.e– opens file in a full-screen vim popup and returns to Yazi when vim exits.
Ctrl+g then s opens the tmux session chooser. The sidebar file manager in
all layouts is Yazi, an external terminal file manager with richer features
(preview, bulk operations, async I/O).
Theme
Gruvbox dark, defined in themes/gruvbox.conf.
Vim Configuration
Notable settings baked into the image:
- Leader key: Space
- Line numbers: Relative + absolute (hybrid)
- Indentation: 4 spaces default, 2 spaces for YAML, JSON, KDL, HTML, CSS, JavaScript
- Undo: Persistent undo files stored in
/home/aibox/.vim/undo - No swap files – clean container environment
- Color column at 88 (Black/PEP8 default)
- Grep program: ripgrep if available (
rg --vimgrep --smart-case) - Netrw: Tree mode, no banner, 25% width
- Colorscheme:
desert(ships with vim-runtime, no plugins needed)
Git Configuration
Git config lives at /home/aibox/.config/git/config (XDG path, not ~/.gitconfig). The environment variable GIT_CONFIG_GLOBAL is set in the generated docker-compose.yml to point to this location.
Using a directory mount (rather than a single-file mount) allows a credentials file to coexist alongside config.
AI Coding Agents
AI coding agents (Claude, Codex, Aider, Gemini, and others) are not
pre-installed in the base image. They are installed per-project when you select
them in the ordered [ai].harnesses list, for example
{ harness = "claude", enable = true, install = true }.
Audio Support
Set [audio] enabled = true to enable audio bridging. aibox then selects the
internal audio-voice recipe and configures the PulseAudio environment for the
container. See Audio Support for setup details.
Configuration Persistence
All user configuration is persisted on the host under .aibox-home/ and bind-mounted into the container:
| Host Path | Container Path | Contents |
|---|---|---|
.aibox-home/.ssh/ | /home/aibox/.ssh (read-only) | SSH keys |
.aibox-home/.vim/ | /home/aibox/.vim | Vim config and undo history |
.aibox-home/.config/ | /home/aibox/.config | Git, tmux, Yazi, prompt, and tool config |
.aibox-home/.cache/ | /home/aibox/.cache | Runtime caches |
.aibox-home/.local/ | /home/aibox/.local | Helper scripts, state, and local data |
.aibox-home/.tmux/ | /home/aibox/.tmux | tmux plugin and socket state |
The Dockerfile bakes identical defaults into the image as a fallback. If no mounts are present, the container still works out of the box.
On first aibox init or aibox up, the .aibox-home/ directory is auto-seeded
from built-in templates. User-owned files are retained. Files explicitly
managed by aibox may be refreshed on aibox apply when their upstream template
changes.
Tool credentials saved below /home/aibox/.config, including a GitHub CLI
login stored in /home/aibox/.config/gh, therefore survive container
replacement and image rebuilds. They remain local secret-bearing files rather
than encrypted storage. See GitHub authentication
for the security tradeoffs and the recommended scoped-PAT alternative.
File Preview
The base image ships the Yazi configuration and preview plugins. Install the
optional preview-archive addon for raster/SVG/PDF/archive helper binaries and
preview-enhanced for Markdown, EPS, video, and Ghostscript
support. PDF and SVG also support watch-mode preview when the required
preview tools are selected.
See the dedicated File Preview page for full documentation, including format coverage, standalone tools (chafa, timg), and the PDF/SVG watch-mode patterns.
Container Entrypoint
CMD ["sleep", "infinity"]
The container stays alive and idle. Both VS Code and aibox up exec into it. tmux is never the container entrypoint – it is launched on attach.
2 - Container Configuration
Container Configuration
The [container] section in aibox.toml controls per-project container settings.
Container Identity
[container]
name = "my-project" # Container name (used by compose)
hostname = "my-project" # Container hostname
user = "aibox" # Container user (default: aibox)
The user field determines the non-root user inside the container. The default aibox user (UID 1000) is recommended. Set user = "root" only if needed for specific tools.
aibox apply generates a Compose file with an explicit top-level project name,
an explicit service image, and container_name = [container].name. Docker
Desktop, OrbStack, and Compose UIs therefore group aibox projects by their
project/container names instead of under a generic devcontainer identity.
The generated main service also sets Compose init: true. Compose starts a
small init process as PID 1 so orphaned child processes are reaped correctly
while the service still runs command: sleep infinity as its long-lived
container command. This prevents zombie buildup from tools that spawn helper
processes, including bubblewrap-based sandbox helpers.
init: true is part of the Compose Specification. Docker Compose and modern
Compose-spec providers support it. podman compose delegates to an external
Compose provider, so support depends on the configured provider; if an older
provider rejects the key, upgrade the provider rather than removing the reaper
from generated projects.
Use the configured container name when writing Compose overrides:
services:
my-project:
ports:
- "8080:80"
Post-Create Command
Run a command after the container is first created:
[container]
post_create_command = "npm install"
This maps to devcontainer.json’s postCreateCommand.
Network Keepalive
Prevent OrbStack/VM NAT from dropping idle connections:
[container]
keepalive = true
This sends a lightweight DNS lookup every 2 minutes via the devcontainer postStartCommand.
Custom Packages, Ports, Volumes, and Environment Variables
Container customizations such as extra packages, port forwarding, volume mounts, and environment variables are handled through standard Docker mechanisms rather than aibox.toml.
Extra Packages — Dockerfile.local
Install additional apt packages by adding them to .devcontainer/Dockerfile.local, which is appended to the generated Dockerfile at build time:
RUN apt-get update && apt-get install -y --no-install-recommends \
universal-ctags graphviz postgresql-client \
&& rm -rf /var/lib/apt/lists/*
Ports, Volumes, and Environment Variables — docker-compose.override.yml
Use .devcontainer/docker-compose.override.yml to add port mappings, volume mounts, and environment variables:
services:
my-project: # must match [container] name in aibox.toml
ports:
- "8080:80"
- "5432:5432"
volumes:
- /host/data:/container/data:ro
environment:
DATABASE_URL: "postgres://localhost/mydb"
NODE_ENV: "development"
Both Dockerfile.local and docker-compose.override.yml are scaffolded by aibox init and are never overwritten by aibox apply.
Compose Override
For project-specific services (databases, sidecars, test companions), use Docker Compose’s standard override mechanism. During aibox init, an empty .devcontainer/docker-compose.override.yml is scaffolded with example usage.
Docker Compose automatically merges the override file with the generated docker-compose.yml using a strategic merge — maps (services, environment) are deep-merged by key, lists (ports, volumes) are appended, and scalars (image, command) are replaced.
When aibox apply detects the override file, it wires both files into devcontainer.json so VS Code picks them up.
Example — add a PostgreSQL sidecar:
services:
postgres:
image: postgres:16
environment:
POSTGRES_PASSWORD: dev
ports:
- "5432:5432"
Example — add depends_on to the main service:
services:
my-project: # must match [container] name in aibox.toml
depends_on:
- postgres
The override file is never overwritten by aibox apply — you own it, just like Dockerfile.local.
3 - Audio Support
Audio Support
aibox can enable audio support for voice-capable tools. Audio is bridged from
the container to the host via PulseAudio over TCP, and the required container
packages live in the optional audio-voice addon.
Why Audio Matters
Claude Code supports voice interaction. For this to work inside a container, audio output (and optionally input) must be forwarded to the host’s sound system. aibox handles this by installing PulseAudio client utilities in the container and connecting them to a PulseAudio server running on the host.
Architecture
Container Host
┌─────────────────────┐ ┌─────────────────────┐
│ Claude Code │ │ PulseAudio Server │
│ │ │ │ │ │
│ pulseaudio-utils │────>│ TCP :4714 │
│ sox │ │ │ │
│ .asoundrc │ │ Speakers / Mic │
└─────────────────────┘ └─────────────────────┘
The container sets PULSE_SERVER to point at the host’s PulseAudio TCP module. Audio data flows over the network socket.
Configuration in aibox.toml
[audio]
enabled = true
backend = "pulseaudio"
install = true
pulse_server = "tcp:host.docker.internal:4714"
| Field | Default | Description |
|---|---|---|
enabled | false | Whether to set up audio environment variables in the container |
backend | pulseaudio | Audio bridge backend. Only PulseAudio is currently supported |
install | true | Whether to install the internal audio-voice tool recipe when audio is enabled |
pulse_server | tcp:host.docker.internal:4714 | PulseAudio server address |
When enabled = true, the generated docker-compose.yml sets PULSE_SERVER in the container environment.
When install = true, it also selects the internal audio-voice recipe during
aibox apply, which installs Sox, PulseAudio client utilities, and ALSA
PulseAudio plugins in the container.
Host Setup
The fastest way to set up audio on your host is the built-in CLI command:
# Check if your host is ready
aibox doctor audio
# Automatic setup (macOS: installs PulseAudio, configures TCP, creates launchd agent)
aibox apply audio
aibox apply audio handles:
- Installing PulseAudio via Homebrew (macOS) if not present
- Configuring
~/.config/pulse/default.pawith the TCP module on port 4714 - Creating a launchd agent with
KeepAliveso PulseAudio auto-starts and restarts on crash (macOS) - Loading the TCP module immediately
aibox doctor audio diagnoses: PulseAudio installation, daemon status, TCP module, persistence config, port listening, launchd agent (macOS), and connectivity.
Both commands accept --port to override the default port (4714).
Manual setup
If you prefer manual configuration:
macOS
Install PulseAudio:
brew install pulseaudioEnable the TCP module. Add to
~/.config/pulse/default.pa:load-module module-native-protocol-tcp port=4714 auth-anonymous=1Start PulseAudio:
pulseaudio --startVerify it is listening:
lsof -i :4714
Docker Desktop and OrbStack provide host.docker.internal automatically. For Podman, check your machine’s network configuration — you may need to use the host IP directly:
[audio]
enabled = true
pulse_server = "tcp:192.168.64.1:4714"
Linux
PulseAudio is likely already running. Enable the TCP module:
pactl load-module module-native-protocol-tcp port=4714 auth-ip-acl=127.0.0.1;172.16.0.0/12;10.0.0.0/8;192.168.0.0/16To make this persistent, add to
~/.config/pulse/default.pa:load-module module-native-protocol-tcp port=4714 auth-ip-acl=127.0.0.1;172.16.0.0/12;10.0.0.0/8;192.168.0.0/16Use
host.docker.internal(Docker 20.10+) or the Docker bridge IP:[audio] enabled = true pulse_server = "tcp:host.docker.internal:4714"
Ensure port 4714 is accessible from the container network. On systems with strict firewalls, you may need to allow traffic from the Docker/Podman bridge interface.
Claude Code OAuth in Containers
When running claude auth inside a container with bridge networking (OrbStack, Docker Desktop), the OAuth callback may fail. Claude Code starts a temporary HTTP server on a random ephemeral port to receive the callback, but that port isn’t forwarded to the host browser.
Workaround: Use claude setup-token to authenticate manually, or authenticate on the host first. aibox bind-mounts Claude’s .claude/, .claude.json, and XDG cache/config/state locations, so credentials survive container rebuilds.
This is tracked at anthropics/claude-code#14528. A fix on the Claude Code side (e.g., configurable callback port) would resolve this properly without compromising container network isolation.
The .asoundrc File
The seeded home configuration includes an .asoundrc file at
/home/aibox/.asoundrc. This configures ALSA to route through PulseAudio, so
applications that use ALSA rather than PulseAudio directly also get audio output
when the audio-voice addon is installed.
Troubleshooting
No sound output
Verify PulseAudio is running on the host:
pulseaudio --check && echo "running" || echo "not running"Verify the TCP module is loaded:
pactl list modules | grep module-native-protocol-tcpTest from inside the container:
paplay /usr/share/sounds/freedesktop/stereo/bell.ogaIf the file does not exist, use
soxto generate a test tone:play -n synth 0.5 sine 440
Connection refused
The PULSE_SERVER address is not reachable from the container.
- Check that the PulseAudio TCP module is listening on the correct port
- Check that
host.docker.internalresolves from inside the container:# From inside the container getent hosts host.docker.internal - Try using the host’s explicit IP address instead
Audio works but is choppy
This is usually a network or resource issue. PulseAudio over TCP adds latency. Ensure the container has sufficient CPU resources and the host is not under heavy load.
Disabling audio
If you do not need audio, set enabled = false in aibox.toml:
[audio]
enabled = false
This removes the PULSE_SERVER environment variable from the container. The audio packages (sox, pulseaudio-utils) remain installed in the base image but are inert without a server to connect to.
4 - File Preview
File Preview
aibox containers ship with Yazi preview configuration and can install TUI-native preview tools for raster images, vector graphics, PDF, archives, and video through optional addons. Several formats also support watch-mode preview, where the rendered output updates automatically whenever the source file changes.
Overview
There are two independent preview mechanisms:
| Mechanism | When to use |
|---|---|
| Yazi file preview | Browsing files — preview appears automatically in the right panel as you navigate |
| Standalone TUI viewers | Viewing a specific file in a pane, or piping output from a build tool |
Watch-mode (live-updating preview) is available via the standalone tools — see Watch-Mode Preview.
Yazi File Preview
When you open Yazi (Ctrl+g s from the file manager pane, or via the layout sidebar), files are previewed automatically in the right panel as you navigate. No manual invocation needed.
Supported formats
| Format | Extensions | Previewer | Requirement |
|---|---|---|---|
| JPEG / PNG | .jpg .jpeg .png | image (built-in) | chafa |
| GIF (incl. animated) | .gif | image (built-in) | chafa |
| WebP | .webp | image (built-in) | chafa |
| BMP | .bmp | image (built-in) | chafa |
| TIFF | .tiff .tif | image (built-in) | chafa |
| SVG | .svg | svg.yazi plugin | resvg or rsvg-convert |
| EPS | .eps | eps.yazi plugin | ghostscript (addon) |
.pdf | pdf (built-in) | poppler-utils | |
| Markdown | .md .markdown | rich-preview.yazi plugin | preview-enhanced addon |
| SQLite | .sqlite .sqlite3 .db | sqlite-preview.yazi plugin | data-preview addon |
| CSV / TSV | .csv .tsv | tabular-preview.yazi plugin | data-preview addon |
| Excel | .xls .xlsx | tabular-preview.yazi plugin | data-preview addon |
| Video | .mp4 .mkv .webm .avi | video (built-in) | ffmpeg (addon) |
| Text / code | most text formats | code (built-in) | — |
Raster image, SVG, PDF, archive, and standalone terminal viewers require the
preview-archive addon. SQLite, CSV/TSV, and Excel previews require the
data-preview addon. Markdown rendering, EPS, and video thumbnails require
the preview-enhanced addon, which depends on preview-archive:
aibox set addon preview-archive enabled --apply # adds chafa, timg, poppler, mutool, entr, p7zip, resvg
aibox set addon data-preview enabled --apply # adds sqlite3 and csvkit for data previews
aibox set addon preview-enhanced enabled --apply # adds python3-rich, ffmpeg, ghostscript
How previewer dispatch works
Yazi matches files against a list of prepend_previewers in ~/.config/yazi/yazi.toml. The first matching entry wins:
[plugin]
prepend_previewers = [
{ url = "*.svg", run = "svg" },
{ url = "*.eps", run = "eps" },
{ url = "*.md", run = "rich-preview" },
{ url = "*.jpg", run = "image" },
{ url = "*.jpeg", run = "image" },
{ url = "*.png", run = "image" },
{ url = "*.gif", run = "image" },
{ url = "*.webp", run = "image" },
{ url = "*.bmp", run = "image" },
{ url = "*.tiff", run = "image" },
{ url = "*.tif", run = "image" },
]
Custom plugins (svg.yazi, eps.yazi) live at ~/.config/yazi/plugins/<name>.yazi/init.lua. They are seeded into .aibox-home/.config/yazi/ on first aibox init.
Format notes
SVG — converted to PNG by resvg, a fast standalone Rust-based SVG renderer bundled as a static binary in /usr/local/bin/resvg. The rendered PNG is cached under Yazi’s cache directory. If resvg is absent from PATH, the plugin fails gracefully and Yazi falls back to the text previewer.
EPS — rendered to PNG at 150 DPI by gs (Ghostscript), then displayed as an image. Result is cached.
Markdown — when the preview-enhanced addon is enabled, .md and .markdown files are rendered through rich-preview.yazi, which uses Python Rich for terminal-native Markdown rendering. Without the addon, Yazi falls back to its built-in text/code preview.
PDF — page 1 is rendered by pdftoppm (from poppler-utils). Navigate multi-page documents with Yazi’s built-in PDF plugin controls.
Wide text previews — the seeded Yazi keymap includes a pager shortcut for the selected file using less -R -S. -R preserves ANSI color from rich/code output; -S disables wrapping so long lines can be inspected with horizontal scrolling.
SQLite — sqlite-preview.yazi opens databases read-only through sqlite3 and shows schema objects plus table/view columns. It is enabled only when the data-preview addon is configured.
CSV / TSV / Excel — tabular-preview.yazi formats CSV/TSV with csvlook; .xls and .xlsx are converted with in2csv before formatting. It is enabled only when the data-preview addon is configured.
.excalidraw files — Excalidraw’s native format is JSON. A graphical preview is not possible in a TUI environment. Yazi falls back to the text previewer showing the raw JSON. This is a known limitation — Excalidraw requires a browser to render.
Image preview support depends on the host terminal and tmux passthrough settings. The generated Yazi config favors terminal-safe fallbacks inside tmux; use half-block mode when you need predictable rendering across terminals.
Standalone TUI Viewers
These tools are available directly in the shell for viewing a specific file or
integrating into a pipeline when the preview-archive addon is enabled.
chafa — universal image renderer
chafa converts images to terminal graphics using Sixel, Kitty protocol, half-block Unicode, or plain ASCII, auto-detecting the best mode for the current terminal.
# View any raster image
chafa photo.jpg
# Force half-block mode (safe inside tmux)
chafa --format=halfblock diagram.png
# SVG via librsvg (if chafa was compiled with librsvg support)
chafa --format=halfblock logo.svg
# Constrain to a specific cell size
chafa -s 80x40 banner.png
Supported formats: JPEG, PNG, GIF (animated), WebP, BMP, TIFF, AVIF, and more. SVG support depends on whether chafa was built with librsvg — run chafa --version and check for SVG: yes.
timg — terminal image and document viewer
timg renders images, animated GIFs, videos, and PDFs (page by page) directly in the terminal.
# View an image
timg photo.jpg
# View a PDF — renders all pages sequentially
timg document.pdf
# View a specific PDF page (page 2)
timg -p2 document.pdf
# Constrain output size
timg -g 120x40 wide-image.png
# Clear previous output before rendering (useful in watch loops)
timg --clear output.pdf
Supported formats: JPEG, PNG, GIF (animated), WebP, BMP, TIFF, PDF (via MuPDF), video (via ffmpeg).
Watch-Mode Preview
Watch-mode preview automatically re-renders a file whenever it changes on disk. This is particularly useful for LaTeX, Typst, and other document workflows where you write source in one pane and see the rendered output update in real time in another.
The pattern uses entr (an inotify-based file watcher) combined with a rasteriser and timg --clear:
source file changes → entr triggers → rasteriser produces PNG → timg renders PNG in terminal
Install the preview-archive addon to get the watch-mode tools (entr,
mupdf-tools, resvg, timg).
PDF watch preview ⟳
Run this in a dedicated pane while editing your LaTeX or Typst source:
pdf-watch output.pdf
pdf-watch wraps the underlying entr + mutool draw + timg --clear pipeline and re-renders page 1 whenever the PDF changes:
ls output.pdf | entr -s 'mutool draw -o /tmp/p.png output.pdf 1 && timg --clear /tmp/p.png'
| Part | Role |
|---|---|
entr | Watches output.pdf for changes (inotify, near-zero CPU at idle) |
mutool draw | MuPDF rasteriser — renders a PDF page to PNG (fast, no X11 needed) |
-o /tmp/p.png output.pdf 1 | Output file, input file, page number |
timg --clear | Renders the PNG inline, clearing the previous frame first |
Yazi also seeds a PDF live-watch binding for selected .pdf files. It invokes pdf-watch so you can start the same live preview directly from the file manager.
Tips:
- Change the final
1to preview a different page number. - To watch all pages:
mutool draw -o /tmp/p-%d.png output.pdf(produces/tmp/p-1.png,/tmp/p-2.png, …); thentimg --clear /tmp/p-*.png. - Add
-r 150tomutool drawfor higher resolution (default is 72 DPI). - Use
timg -g 120x40to constrain the rendered size to fit a specific pane.
SVG watch preview ⟳
ls diagram.svg | entr -s 'resvg diagram.svg /tmp/d.png && timg --clear /tmp/d.png'
resvg converts the SVG to a high-fidelity PNG. Unlike Inkscape or rsvg-convert, resvg is a static binary with no runtime dependencies and typically renders in under 100 ms for typical diagrams.
General pattern
The same entr + rasteriser + timg pattern works for any file format that has a headless rasteriser:
# Watch a file and re-render on change
ls <file> | entr -s '<rasterise-command> && timg --clear <output.png>'
| Source format | Rasteriser command |
|---|---|
| PDF (page 1) | mutool draw -o /tmp/p.png file.pdf 1 |
| SVG | resvg file.svg /tmp/p.png |
| EPS | gs -dBATCH -dNOPAUSE -sDEVICE=png16m -r150 -sOutputFile=/tmp/p.png file.eps |
In the dev or cowork layouts, open a new horizontal pane below the editor (Ctrl+g d) and run the watch command there. The preview refreshes in that pane every time you save. Use Ctrl+g = to resize the pane to taste.
Format Coverage Summary
| Format | Yazi preview | chafa | timg | Watch-mode |
|---|---|---|---|---|
| JPEG / PNG | ✓ | ✓ | ✓ | — |
| GIF (animated) | ✓ | ✓ | ✓ | — |
| WebP | ✓ | ✓ | ✓ | — |
| BMP | ✓ | ✓ | ✓ | — |
| TIFF | ✓ | ✓ | ✓ | — |
| SVG | ✓ (resvg) | ✓ (librsvg) | — | ✓ (resvg) |
| EPS | ✓ (ghostscript) | — | — | ✓ (ghostscript) |
| ✓ (poppler) | — | ✓ | ✓ (mutool) | |
| Video | ✓ (ffmpeg) | — | ✓ | — |
.excalidraw | text fallback | — | — | — |
5 - Runtime Operations
Runtime Operations
The generated devcontainer is standard Compose output, but the normal workflow should go through aibox so generated files, runtime home state, and diagnostics stay aligned.
Start and Attach
aibox up
aibox up --layout focus
aibox up --apply
aibox up creates or starts the container and attaches through tmux. Use
--layout for a one-session layout override. Use --apply when you want aibox
to reconcile configuration before starting.
Stop or Remove
aibox down
aibox delete runtime
down stops the Compose project. delete runtime removes the container while
preserving project files and .aibox-home/.
Rebuild
aibox apply
aibox apply --no-cache
aibox apply --config-only
Use --no-cache after base-image, addon, or package-cache issues. --rebuild
is kept as a visible alias for the same behavior. Use --config-only when you
only want to regenerate files, and in processkit mode refresh processkit
content, without building the image. In harness-only projects,
--config-only regenerates config/runtime surfaces without processkit content
work.
Inspect Runtime State
aibox get runtime
aibox get runtime --resources
aibox get runtime --resources -o json
aibox describe runtime
The resource snapshot is designed for low-dependency environments. It reads
cgroupfs and procfs directly instead of relying on tools such as ps or free
being installed.
Key fields:
| Field | What it tells you |
|---|---|
memory_current_bytes | current cgroup memory usage |
memory_max | memory limit or unlimited |
oom_kill_count | whether the kernel has killed a process in the cgroup |
total_process_count | total visible processes |
processkit_mcp_python_process_count | live Python processkit MCP server processes |
An oom_kill_count above zero is strong evidence that a missing agent or
terminated tool was killed by the operating system rather than by the CLI.
With current processkit releases, [ai.mcp.gateway].mode = "auto" registers a
processkit-gateway stdio proxy for MCP-capable harnesses. The proxy starts the
local gateway on demand when no listener exists, so generated devcontainer
startup no longer has to supervise one Python process per skill in the default mode.
Use separate only when a harness needs the older one-server-per-skill layout.
Harness-only projects do not register the processkit gateway.
Resource Thresholds
Configure warning thresholds in aibox.toml:
[container.resource_thresholds]
memory_mib_warn = 4096
process_count_warn = 400
processkit_mcp_python_warn = 50
aibox doctor uses these values when reporting runtime pressure.
Compose Identity
Generated Compose output includes:
- a top-level project name derived from
[container].name - an explicit image name
- a main service named after the project
container_name = [container].nameinit: truefor PID 1 process reaping
This keeps Docker Desktop, OrbStack, and Compose UIs from grouping unrelated
aibox projects under a generic devcontainer identity.
Existing Containers After Generator Changes
aibox apply rewrites generated files, but it does not magically replace a
running container that was created from older Compose output. Recreate the
runtime when a change affects process model, mounts, image labels, init
behavior, or service identity:
aibox down
aibox apply
aibox up
If a container has already accumulated zombie processes, the fix is still a
runtime recreate. A new generated init: true service can reap future orphaned
children, but it cannot change PID 1 in an already-created container.
Common Symptoms
| Symptom | First checks |
|---|---|
| AI process disappears | aibox get runtime --resources, then inspect oom_kill_count |
| OrbStack groups projects oddly | rerun aibox apply, then recreate the container |
| Network drops after idle time | set [container] keepalive = true |
| Build keeps using stale layers | run aibox apply --no-cache |
| Runtime starts but tools are missing | check selected addons with aibox describe workspace-manifest |
Podman Notes
Podman support depends on the Compose provider behind podman compose.
Generated files follow the Compose Specification, including init: true.
If an older provider rejects a spec key, upgrade the provider instead of
removing the generated setting.