Skip to content

popey/lincubate

Repository files navigation

lincubate (lb)

Run AI coding agents in sandboxed LXD containers. Your project directory is bind-mounted in, so the agent sees your files but runs fully isolated from your host.

Use terminal agents like Claude Code, Codex, and Aider from the host — or launch GUI agents like Cursor directly inside the container with X11/Wayland forwarding. Either way, your code stays on your machine and the agent stays in its sandbox.

Quick start

# Build from source
git clone https://github.com/popey/lincubate
cd lincubate
just build
# Copy ./lb somewhere on your PATH

# Launch Claude Code in a container for the current project
cd ~/Projects/MyApp
lb claude

# Drop into a bare shell inside the container
lb

# Launch Cursor IDE inside the container (GUI)
lb cursor-gui

On first run, lb builds a base image with Node.js, common packages, and all agents pre-installed. This takes a few minutes — subsequent launches are fast.

How it works

  • One container per project. Container names are derived from your working directory, so lb from the same directory always targets the same container.
  • Bind-mounted files. Your project directory is mounted into the container at /home/ubuntu/project with UID mapping, so file ownership just works.
  • Credential forwarding. API keys are forwarded via environment variables. Auth tokens (OAuth, config files) can be optionally copied or mounted read-only — all off by default.
  • GUI support. Pass --gui (or use cursor-gui which enables it automatically) to forward X11/Wayland and GPU access into the container.

Supported agents

Agent Type Command
Claude Code Terminal lb claude
OpenAI Codex Terminal lb codex
Aider Terminal lb aider
Gemini CLI Terminal lb gemini
GitHub Copilot Terminal lb copilot
OpenCode Terminal lb opencode
Cursor (CLI) Terminal lb cursor
Cursor (IDE) GUI lb cursor-gui

You can also just run lb with no agent to get a shell inside the container.

Requirements

  • Linux with LXD installed and running
  • Go 1.26+ (building from source)

Configuration

lb works with zero configuration — sensible defaults are built in. To customise things, generate a config file and edit it:

lb generate-config
# Writes ~/.config/lincubate/lb.toml — edit this file to change behaviour

The generated file is fully commented and includes all available options. Here are some common things you might want to change:

Add packages to the base image

The base image comes with curl, git, build-essential, and unzip. If your project needs extra tools, add them:

apt_packages = ["curl", "git", "build-essential", "unzip", "postgresql-client", "redis-tools", "jq"]

After changing this, rebuild the base image: lb base-destroy then run lb again.

Control which agents are pre-installed

By default all agents are installed in the base image. If you only use a couple, you can speed up the initial build:

base_agents = ["claude", "aider"]
# Or disable all: base_agents = "none"

Forward additional environment variables

API keys and tokens are forwarded from your host into the container. The defaults cover the main providers, but you can add your own:

credential_env = [
    "ANTHROPIC_API_KEY",
    "OPENAI_API_KEY",
    "GEMINI_API_KEY",
    "GITHUB_TOKEN",
    "AWS_ACCESS_KEY_ID",
    "AWS_SECRET_ACCESS_KEY",
    "DATABASE_URL",
]

Any variable in this list that is set on your host will be available inside the container.

Share agent credentials

Credential sharing is off by default. If you want an agent to reuse your host login (so you don't have to re-authenticate inside the container), enable it:

# IMPORTANT: [sections] must come last in TOML — all top-level keys above.
[claude]
allow_auth = true    # Copy OAuth credentials into container

[cursor]
allow_auth = true    # Copy Cursor auth into container

Or use CLI flags for one-off sessions without changing the config:

lb claude --allow-claude-auth

Run commands on every container launch

If you want something to happen every time a container starts (e.g. pull latest dotfiles, update tools), use updaters:

updaters = ["cd ~/dotfiles && git pull -q", "pip install --upgrade mytools"]

Config file resolution

Config is loaded from the first match:

  1. -c FILE flag
  2. lb.toml next to the binary (handy for per-project configs)
  3. $XDG_CONFIG_HOME/lincubate/lb.toml (default: ~/.config/lincubate/lb.toml)
  4. Built-in defaults

Commands

Command Description
lb [agent] Launch agent or shell in the project container
lb stop Stop the current project's container
lb all-stop Stop all lincubate containers
lb upgrade Run upgrade commands in the current container
lb base-upgrade Upgrade the base image in-place
lb snapshot Stop, publish as base image, restart
lb destroy Delete the current project's container
lb base-destroy Delete the base image (next run rebuilds)
lb generate-config Write default config file

Building

just build    # Build the binary
just test     # Run tests
just release  # Cross-compile linux/amd64 + linux/arm64

License

MIT

About

Run AI coding agents in sandboxed LXD containers

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages