Docs

Run it locally

Local dev runs the harness on your host (so you get hot reload) but spawns every agent task in a real per-phase sandbox, exactly like production. This is deliberate: it's the only honest way to develop against the bot's actual execution model.

The default sandbox backend is gondolin — agentic-pi's QEMU micro-VM, using HVF on macOS or KVM on Linux. No Docker needed. Switch to LASTLIGHT_SANDBOX=docker if you'd rather run sibling Docker containers (useful for prod-like smoke testing), or none for in-process dev runs.

1. Clone and install

git clone https://github.com/nearform/lastlight.git
cd lastlight
npm install

2. Create .env

cp .env.example .env

Edit .env and fill in the four values you collected in the previous step:

GITHUB_APP_ID=123456
GITHUB_APP_PRIVATE_KEY_PATH=./your-app.private-key.pem
GITHUB_APP_INSTALLATION_ID=789012
WEBHOOK_SECRET=your-webhook-secret

Drop the .pem file into the project root (or wherever GITHUB_APP_PRIVATE_KEY_PATH points). It's already in .gitignore, so you won't accidentally commit it.

3. (Docker mode only) Build the sandbox image

docker compose --profile build-only build sandbox

Only needed if you set LASTLIGHT_SANDBOX=docker. The default gondolin sandbox runs from the harness directly and needs no image build. Rebuild when sandbox.Dockerfile changes.

4. Set a model API key

The agent runtime is agentic-pi — it runs on any model the pi coding agent supports (Claude, GPT, Gemini, Llama, and more). Drop the matching key into .env:

# Default model is anthropic/claude-sonnet-4-6
ANTHROPIC_API_KEY=sk-ant-...

# Or, if you'd rather route to OpenAI
OPENAI_API_KEY=sk-...
LASTLIGHT_MODEL=openai/gpt-5.5

# Or use OpenRouter — one key, many models
OPENROUTER_API_KEY=sk-or-v1-...
LASTLIGHT_MODEL=openrouter/anthropic/claude-sonnet-4.5

# Sandbox backend — gondolin (default), docker, or none
# LASTLIGHT_SANDBOX=gondolin

No interactive login step. pi-ai reads the API key from the harness env and forwards it into each sandbox.

5. Start the harness

# Server + dashboard, with hot reload
npm run dev

# Or individually
npm run dev:server
npm run dev:dashboard

Under the hood, npm run dev:server calls scripts/dev-local.sh, which:

  • When LASTLIGHT_SANDBOX=docker, verifies Docker is running and the sandbox image exists.
  • Copies your GITHUB_APP_PRIVATE_KEY_PATH into ./data/sandbox-data/secrets/app.pem (mode 600) so sandboxes can authenticate to GitHub.
  • Sets safe defaults: LASTLIGHT_LOCAL_DEV=1, STATE_DIR=./data, LASTLIGHT_SESSIONS_DIR=./data/agent-sessions.
  • Launches the harness with tsx watch src/index.ts.
LASTLIGHT_LOCAL_DEV=1 tells the harness not to touch your personal ~/.gitconfig or keychain. Everything sandbox-related stays inside ./data/. Safe to run on a dev laptop.

6. Trigger and debug via the CLI

The lastlight CLI is a thin HTTP client — it POSTs triggers and reads the instance's admin API. In dev use npm run cli -- <args> (or npx tsx src/cli.ts <args>); installed globally it's just lastlight. Open a second terminal:

# Authenticate once — saves the instance URL + token to ~/.lastlight
lastlight login http://localhost:8644          # browser handoff (password / Slack / GitHub)
lastlight login http://localhost:8644 --password   # headless fallback
lastlight status                               # instance, server health, token validity

# Cheap defaults — single agent invocation
lastlight <github-url>                          # default: triage the issue
lastlight owner/repo#N                          # shorthand: triage the issue
lastlight https://github.com/owner/repo/pull/99 # review one PR
lastlight triage owner/repo                     # scan repo for new issues
lastlight review owner/repo                     # scan repo for PRs to review
lastlight health owner/repo                     # weekly-style health report

# Expensive, opt-in — full Architect → Executor → Reviewer → PR cycle
lastlight build owner/repo#42

# Debug a running instance instead of SSH (all accept --json)
lastlight workflow list --status failed         # recent failed runs
lastlight workflow log <run-id> --follow         # phases + live tail
lastlight session list                          # recent sandbox sessions
lastlight session log <session-id> --follow     # live transcript (SSE)
lastlight logs search "ECONNREFUSED"            # search errors / transcripts
lastlight approvals list                        # pending approval gates
lastlight cron list                             # scheduled jobs + next/last run
lastlight cron trigger <name>                    # fire a cron now (testing)
lastlight stats --daily 14                      # cost / token rollups

The default action for a single-issue or single-PR shorthand is the cheap one (triage / review). Build cycles are always opt-in via the explicit build subcommand. The debug commands read the same data the dashboard shows, so you can investigate a remote instance from your terminal without SSHing in. See the CLI reference for the full command list and global flags.

7. Open the dashboard

The admin dashboard is served at http://localhost:8644/admin. It has four tabs: Home, Workflows, Sandbox Sessions, and Chat Sessions. You can watch sessions stream live, drill into any run, and resolve approval gates.

If you set ADMIN_PASSWORD in .env, the dashboard will prompt for it. Otherwise it's open.

Optional: receive real webhooks

To receive real GitHub webhooks in local dev, expose port 8644 with ngrok or cloudflared and point your GitHub App's webhook URL at the public tunnel. Without webhooks you can still trigger every workflow via the CLI.