Docs

Prerequisites

Before you start, make sure you have the following in place.

A host machine

Last Light runs as a single Node process (locally) or a single Docker container (in production). Each workflow phase runs inside its own isolated sandbox so the agent cannot touch your host filesystem, credentials, or git identity. The sandbox backend is selected by LASTLIGHT_SANDBOX: gondolin (default — QEMU micro-VM via agentic-pi, uses HVF on macOS and KVM on Linux, no Docker needed), docker (sibling Docker container per phase), or none (in-process, dev only).

  • Node.js 20+ — for local development (npm run dev).
  • Docker — required only when LASTLIGHT_SANDBOX=docker or for the bundled docker-compose deploy. The default gondolin sandbox does not need it.
  • KVM (Linux) or HVF (macOS) — required by gondolin for nested virtualisation. Bare-metal Linux and Apple Silicon Macs both have it; most managed container hosts do not.
  • Git — for cloning the repo you want to deploy.
Do not deploy Last Light onto core infrastructure. It's an autonomous agent with write access to your repos. Run it on an isolated host (a cheap VPS, a dedicated VM, a separate cloud project) where it cannot reach sensitive internal systems.

A GitHub App

Last Light authenticates to GitHub as its own GitHub App rather than as a personal access token. This keeps the bot's actions clearly attributed (last-light[bot]), lets you downscope permissions per workflow, and makes installation per-repo instead of per-user.

The Create a GitHub App page walks through the exact permissions and events to request. You'll end up with four values you need to paste into .env: App ID, Installation ID, webhook secret, and a .pem private key.

Model provider credentials

The agent runtime is agentic-pi — it runs on any model the pi coding agent supports (Claude, GPT, Gemini, Llama, and more — as openai/…, anthropic/… or openrouter/<vendor>/<model> strings). The default is anthropic/claude-sonnet-4-6. Set the matching API key in .env:

  • OPENAI_API_KEY — required if LASTLIGHT_MODEL resolves to an OpenAI model.
  • ANTHROPIC_API_KEY — required if LASTLIGHT_MODEL resolves to an Anthropic model.
  • OPENROUTER_API_KEY — required if LASTLIGHT_MODEL resolves to an OpenRouter model (openrouter/<vendor>/<model>). One key gives you access to Claude, GPT, Gemini, Llama, Mistral, DeepSeek, and most others through a single endpoint — at a small per-token markup over going direct. Sign up at openrouter.ai/keys.
  • You can set all three — phases routed via LASTLIGHT_MODELS JSON can mix providers freely.
No interactive login step. pi-ai reads provider API keys directly from the harness env — the same keys are forwarded into each sandbox so workflow runs can reach the API without a separate auth file.

A state database — SQLite or Postgres

Last Light keeps its own state — workflow runs, executions, approvals, cron history, chat sessions — in a relational database. You choose which at deploy time, and it is the only piece of infrastructure the harness needs beyond the host itself. lastlight server setup asks the question directly and writes the answer for you.

  • SQLite (default, recommended) — nothing to install and nothing to run. The database is a single file, lastlight.db, inside the agent's data volume. This is what production has run on since day one; if you have no reason to prefer otherwise, take it.
  • Postgres — an external or managed server you supply: self-hosted, RDS, Cloud SQL, Supabase, or serverless Neon. Set DATABASE_URL to a postgres:// URL and the harness runs its Postgres migrations against it at boot. The driver is picked from the host — *.neon.tech uses Neon's serverless WebSocket pool, everything else uses node-postgres — and you can force it with DATABASE_DRIVER=pg|neon.

Nothing else in the deployment changes between the two: the same compose stack, the same overlay, the same workflows. Pick Postgres when the state should outlive the host, when something else needs to read it, or when your tooling already assumes Postgres — not for throughput, which is not the constraint at this write volume. You can also start on SQLite and move later: lastlight server db migrate copies an existing database across, verified, in one command.

Postgres is a storage choice, not multi-instance HA. Last Light still runs as a single instance either way — the atomic operations behind the run lock and approval lifecycle use an in-process mutex, which a second process would not share. Full contract: State → Dialect posture.

Optional: a Slack workspace

Slack integration is optional but recommended — it's how you chat with the bot outside GitHub. You'll need permission to create a Slack app and install it in your workspace. See Slack integration.

Optional: a public domain

To receive GitHub webhooks in real-time (instead of relying on cron polling), the harness needs to be publicly reachable over HTTPS. In production this is handled by the bundled Caddy reverse proxy, which provisions TLS automatically — you just point a DNS record at your host. For local testing, use ngrok or Cloudflare Tunnel.