Skip to main content
Nightshift is currently in its infancy and is subject to change and incompleteness. If you find a bug, or have an idea to improve Nightshift, please raise an issue on Github and join our slack for updates.
Nightshift provides two layers of isolation for running coding agents safely:
  1. Prefix Isolation - All tools installed in a self-contained directory
  2. Sandbox Mode - Optional process-level filesystem protection

Prefix Installation

When you run nightshift install --prefix <prefix>, Nightshift creates a self-contained environment with:
  • OpenCode - The coding agent harness
  • uv - Python toolchain manager (handles Python installation, packages, and virtual environments)
  • ripgrep - Fast code search
Tools are installed following the XDG Base Directory Specification:
<prefix>/
├── bin/                # Symlinked binaries (uv, rg, opencode)
├── cache/              # XDG_CACHE_HOME - caches for tools
├── config/             # XDG_CONFIG_HOME - configuration files
├── python/             # uv-managed Python installations
├── share/              # XDG_DATA_HOME - application data
├── state/              # XDG_STATE_HOME - state/logs
├── tools/              # Downloaded tool archives
├── uv-tools/           # CLI tools installed via `uv tool`
└── workspace/          # The uv workspace the agent maintains

Environment Variables

The run() function in src/index.ts:968 configures the environment when launching the agent:
  • PATH includes workspace/.venv/bin, uv-tools/bin, and prefix/bin
  • XDG variables (XDG_CONFIG_HOME, XDG_CACHE_HOME, etc.) point to prefix subdirectories via buildXdgEnv()
  • uv variables (UV_PYTHON_INSTALL_DIR, UV_PYTHON_PREFERENCE) via buildUvEnv()
  • HOME, USER, TERM, and LANG are passed through from your shell

How Python Imports Work

The workspace contains a Python package at src/<library>/. When uv sync runs, it installs this package in editable mode into the virtual environment’s site-packages. This means from agent_lib.utils import hello works automatically. Since PATH places .venv/bin first, both python script.py and uv run python script.py use the same interpreter with the editable install—no PYTHONPATH manipulation needed.

Python Management with uv

Nightshift delegates all Python management to uv, Astral’s fast Python toolchain. This provides complete isolation from any system Python installation.

How It Works

The syncWorkspace() function in src/index.ts:553 runs uv sync during installation. When executed, uv automatically:
  1. Downloads Python from python-build-standalone if no suitable version exists
  2. Creates a virtual environment at workspace/.venv
  3. Installs dependencies from pyproject.toml
  4. Locks versions in uv.lock for reproducibility

Environment Configuration

The buildUvEnv() helper in src/index.ts:181 configures uv with two environment variables:
function buildUvEnv(prefix: string): Record<string, string> {
  return {
    UV_PYTHON_INSTALL_DIR: join(prefix, "python"),
    UV_PYTHON_PREFERENCE: "only-managed",
  };
}
VariableValuePurpose
UV_PYTHON_INSTALL_DIR<prefix>/pythonStore Python installations inside the prefix
UV_PYTHON_PREFERENCEonly-managedIgnore system Python, only use uv-managed versions
This ensures agents always use an isolated Python, even if the host has Python installed.

Python Version

The generateRootPyproject() function in src/index.ts:336 generates a pyproject.toml with:
requires-python = ">=3.13"
When uv syncs, it downloads the latest Python 3.13.x to <prefix>/python/. The virtual environment at workspace/.venv/bin/python links to this installation.

Package Management

Agents are instructued to manage dependencies using uv commands:
# Add a package
uv add requests

# Add a dev dependency
uv add --dev pytest

# Sync environment (install all dependencies)
uv sync

# Run a command in the virtual environment
uv run python script.py
uv run pytest

CLI Tools

The installUvTools() function in src/index.ts:572 uses uv tool to install standalone CLI tools:
const proc = Bun.spawn([uv, "tool", "install", "ty"], {
  env: {
    ...process.env,
    ...buildUvEnv(prefix),
    UV_TOOL_DIR: toolDir,
    UV_TOOL_BIN_DIR: toolBinDir,
  },
});
Tools are installed to <prefix>/uv-tools/ to keep them separate from the workspace. Currently, Nightshift installs ty (the type checker) this way. This also ensures that Opencode will have an LSP to use when running regardless of if there is one installed on the host system.

Workspace Structure

The workspace is a uv-managed Python project that the agent maintains:
workspace/
├── .opencode/              # OpenCode configuration and skills
│   └── skills/             # Agent-created skill definitions (generated at boot)
│       └── <skill>/
│           └── SKILL.md
├── .venv/                  # Python virtual environment (managed by uv)
├── src/<library>/          # Library code the agent writes
│   ├── __init__.py
│   └── utils.py
├── tests/                  # Test suite
│   └── test_utils.py
├── AGENTS.md               # Agent instructions (generated at boot)
├── opencode.json           # OpenCode permission configuration
├── pyproject.toml          # Python project metadata
├── README.md               # Project documentation
└── uv.lock                 # Dependency lock file

Bootstrap Process

During installation, Nightshift runs a bootstrap Agent Routine process where the agent:
  1. Reads a user-provided BOOT.md file (if present) describing the intended use case
  2. Interviews the user about their requirements
  3. Installs appropriate packages via uv add
  4. Generates AGENTS.md with project-specific instructions
  5. Creates skills in .opencode/skills/
This allows the agent to self-configure based on the user’s needs.

Sandbox Mode

For additional security, enable sandbox mode with the --sandbox flag:
nightshift run --sandbox
Sandbox mode provides filesystem write protection - the host filesystem becomes read-only while the agent can only write to:
  • The workspace directory
  • The prefix directory (for caches and state)
  • Temporary directories (/tmp, /var/tmp)

Platform Support

macOS uses the built-in sandbox-exec with a custom profile:
  • No additional installation required
  • Uses Apple’s sandbox technology
Linux uses Bubblewrap (bwrap):
  • Install with: apt install bubblewrap (Debian/Ubuntu) or dnf install bubblewrap (Fedora)
  • Creates a containerized environment with bind mounts
Sandbox mode is optional because:
  • Some legitimate workflows need broader filesystem access
  • The prefix isolation already provides meaningful separation