Setup
This walkthrough assumes you already have a runner CLI installed and logged in (Claude Code is the default; Codex, OpenCode, Antigravity, and Hermes also work — see Runners).
Prerequisites
Section titled “Prerequisites”- Node 22+ (uses the experimental SQLite module).
- A Linear workspace you can administer (you’ll create a webhook).
- A runner CLI logged in:
claude(default), or one ofcodex/opencode/agy/hermes. - Webhook ingress — Cloudflare Tunnel is recommended; ngrok and Tailscale Funnel work too.
1. Clone and install
Section titled “1. Clone and install”git clone https://github.com/juneclaw/june.gitcd junenpm install2. Get a Linear API key
Section titled “2. Get a Linear API key”In Linear: Settings → API → Personal API keys → Create key. Copy the value; you can’t retrieve it later. June reads it from JUNE_LINEAR_API_KEY (or LINEAR_CLAUDE_LINEAR_API_KEY as a legacy alias).
export JUNE_LINEAR_API_KEY=lin_api_…The token needs read + write on issues and comments in the team you’ll point it at. A personal key with full scope is fine for self-hosting.
3. Pick a webhook ingress
Section titled “3. Pick a webhook ingress”The orchestrator listens on :8787 (override with PORT). Linear needs to reach it over HTTPS.
Cloudflare Tunnel (recommended)
Section titled “Cloudflare Tunnel (recommended)”Stable URL, free, no rotating token. From a machine with cloudflared installed and authenticated:
cloudflared tunnel create junecloudflared tunnel route dns june june.<your-domain>cloudflared tunnel run --url http://localhost:8787 junengrok http 8787The free tier URL rotates every restart — fine for development, painful for long-running setups.
Tailscale Funnel
Section titled “Tailscale Funnel”If your Linear team has Tailscale ACLs, expose :8787 via Funnel and use the *.ts.net URL.
4. Register the Linear webhook
Section titled “4. Register the Linear webhook”In Linear: Settings → API → Webhooks → New webhook. Point it at https://<your-ingress>/webhook/linear. Subscribe to Issue and Comment events. Copy the signing secret.
export JUNE_LINEAR_WEBHOOK_SECRET=lws_…(LINEAR_CLAUDE_WEBHOOK_SECRET is honored as a legacy alias.)
If you’d rather have June register the webhook for you, set LINEAR_CLAUDE_PUBLIC_WEBHOOK_URL=https://<your-ingress>/webhook/linear and npm run setup will create or update the webhook via the API.
5. Configure labels and states
Section titled “5. Configure labels and states”June expects a small set of Linear labels and workflow states. Run:
npm run ensure-labels # creates: codex, opencode, gemini, hermes, xhigh, fast, goalnpm run ensure-states # ensures: Todo, In Progress, Ready for Review, Blocked, WaitingBoth are idempotent. Re-run after any Linear UI change without worry.
6. Start the service
Section titled “6. Start the service”npm startThe service runs with Node’s experimental SQLite module under the hood:
node --experimental-sqlite src/server.mjsHealth checks live at GET /health. Logs stream to logs/agent.log and stdout.
7. Verify end to end
Section titled “7. Verify end to end”Create a fresh Linear issue in the team you configured, in any active state (Todo, In Progress, etc.). Within a few seconds the orchestrator should:
- Receive the webhook.
- Move the issue to In Progress.
- Post the agent’s first reply as a comment.
- Land in Ready for Review on a clean exit, or Blocked with a triage summary on failure.
Add a comment to the same issue — the agent resumes the same session and replies.
- Concepts — sessions, steering, debounce, the state machine.
- Configuration — every env var, grouped.
- Troubleshooting — predictable failure modes and their fixes.