Documentation
Noctra is an autonomous Linear-to-PR agent. Point it at a trigger state or label and it implements tickets, opens pull requests, and sees them through — answering reviews and fixing CI — while you sleep.
Introduction
Noctra runs as an installable Go CLI. On a timer it polls Linear for issues in your trigger state or trigger label, dispatches Claude Code or OpenAI Codex to implement each one in an isolated git worktree, optionally runs a second-model review, opens a PR with the GitHub CLI, and moves the ticket to review. With auto-iterate enabled it then watches those PRs and pushes follow-up commits in response to review feedback and failing CI.
- Self-hosted & open source — runs on your machine or a Raspberry Pi.
- No extra API cost for coding — runs on your Claude Code or ChatGPT (Codex) subscription.
- Multi-repo — one instance serves many repos, routed per ticket.
Quick start
You'll need the GitHub CLI (gh, authenticated) and at least one supported agent CLI: Claude Code or OpenAI Codex. Install Noctra itself with the script, Homebrew on macOS (brew install ahmadAlMezaal/tap/noctra), go install, or a prebuilt binary from GitHub Releases.
# One-liner: install the latest release binary (no Go toolchain) curl -fsSL https://raw.githubusercontent.com/ahmadAlMezaal/noctra/main/scripts/install.sh | sh # macOS (Homebrew): brew install ahmadAlMezaal/tap/noctra # or: go install github.com/ahmadAlMezaal/noctra/cmd/noctra@latest claude # or: codex login — authenticate your agent once gh auth login # GitHub access for PRs noctra setup # guided ~/.noctra/ config (backend, Linear, Telegram) noctra doctor # preflight tokens, tools, repos, models noctra # start the poll loop
noctra: command not found? The install script drops the binary in ~/.local/bin — if that's not on your PATH, add it (the installer prints the exact line for your shell) or open a new shell. Then map each Linear project to a repo with a Repo: directive (see multi-repo routing), move a ticket into your trigger state or add the trigger label, and watch it become a PR.
Agent backends
Noctra implements each ticket with the coding agent you choose via AGENT_BACKEND — Claude Code (claude, the default) or OpenAI Codex (codex). You only need the CLI for the backend you select; noctra doctor checks for the right one. Authenticate it once on the host — claude or codex login using a subscription login (no extra per-token coding cost beyond that subscription), or an API key (ANTHROPIC_API_KEY / OPENAI_API_KEY) for headless Docker/cloud runs. Note that API-key auth is billed per token by the provider, separate from any subscription. Noctra never stores agent credentials — it inherits whatever the selected CLI is logged into.
Configuration
The setup wizard writes a .env under ~/.noctra/ with your backend, Linear key, and optional Gemini/Telegram settings — or copy .env.example and edit it by hand (see the environment reference). Repos aren't configured here; they're routed per ticket from each Linear project (see multi-repo routing).
The loop
Each tick: poll Linear by state or label → resolve the repo from the ticket's project → create a worktree → dispatch the selected agent backend → optional review gate → commit, push & open the PR → move the ticket to your review state and notify. Tickets run concurrently up to MAX_CONCURRENT, each in its own worktree.
Trigger modes
Noctra can pick up work from a state-based queue, usually the Next column, or from a label-based queue using the noctra label. State mode is useful when Linear workflow state is the source of truth; label mode lets you opt tickets in without moving them between states.
Multi-repo routing
The target repo is chosen per ticket from its Linear project — not a single global path — so one instance serves many repos with no config edits to switch between them. Repos are cloned on demand into ~/.noctra-repos/ and lock-guarded against concurrent clones.
The project Repo: directive
Add a Repo: line to the Linear project's description and every ticket in that project routes to that repo — no config file, no wizard, no redeploy; it lives entirely in Linear. It accepts owner/name (expanded to a GitHub HTTPS URL) or a full https:// / git@ git URL verbatim — so SSH, GitLab, and other non-GitHub hosts work too. An optional Branch: overrides the default branch (otherwise auto-detected from origin/HEAD).
# In the Linear project's description: Repo: your-org/your-repo Branch: main # optional — defaults to the repo's default branch
Resolution order at dispatch — the first match wins: (1) the project Repo: directive, (2) REPO_PATH from .env as a single-repo fallback if set. With neither, the ticket is skipped with a Linear comment. The host needs git access to each repo — an SSH key, or gh auth login / GH_TOKEN for HTTPS — and Noctra checks access with git ls-remote before cloning.
Worktrees & logs
Each ticket gets an isolated worktree at ~/.noctra-worktrees/<IDENTIFIER> on a noctra/<id> branch, so parallel tickets never share a working directory. Per-ticket logs live at .agent-logs/<IDENTIFIER>.log and append across attempts.
Agent Teams
Claude-only. Set USE_AGENT_TEAMS=true and a lead Claude agent delegates implementation, tests, and review to teammates running in parallel — stronger on complex tickets, at the cost of more tokens per run. Requires a recent claude CLI; it doesn't apply to the Codex backend. Off by default, where each ticket runs as a single agent session — fast, cheap, and light enough for a Raspberry Pi.
Auto-iterate: PR review feedback
Set AUTO_ITERATE_PRS=true and Noctra watches the PRs it opened. New conversation comments, reviews (changes-requested / non-empty comments), and inline review-thread comments trigger the selected agent backend to address them on the same branch and push a follow-up commit — inline comments are passed with their file:line so it knows exactly where each note applies.
Auto-iterate: CI failures
The same watcher reads each PR's check status. Once every check on the head commit has completed and at least one failed, Noctra fetches the failed-step logs and asks the selected agent backend to reproduce and fix them. CI is keyed by commit SHA, so it acts at most once per commit; review feedback and CI fixes share the MAX_PR_ITERATIONS budget and, when both are pending, are handled in one re-engagement.
Review gate
Provide GEMINI_API_KEY to enable an optional second-model review: before the PR opens, Gemini reviews the diff against the ticket. If it flags issues, the selected agent backend gets up to MAX_REVIEW_RETRIES fix passes. Leave the key empty to skip the gate entirely.
Safety guards
MAX_PR_ITERATIONS— per-PR re-engagement cap; on reaching it Noctra stops and pings you.AGENT_TIMEOUT_MINUTES— hard timeout per Claude run.MAX_DISPATCHES/MAX_RETRIES— bound work per run and per ticket.- Rate-limit detection — backs off instead of burning quota.
TRUSTED_REVIEWERS— bots are ignored unless explicitly allow-listed.
Telegram
Set TELEGRAM_ENABLED=true with a TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID for PR-ready pings, failure and rate-limit alerts, and a heads-up on every PR re-engagement. The same run loop also handles inbound Telegram commands through the command dispatcher — a two-way control channel for when you're away from your desk, with no separate process to start. Only your configured chat can issue commands. TELEGRAM_VERBOSE=true adds a notification on each ticket dispatch.
| Command | What it does |
|---|---|
| /status | Active runs + session stats. |
| /tickets [project] [state] | Ticket counts by state, or list one state. |
| /ticket ENG-42 | Show a ticket's details. |
| /search-tickets <text> | Search Linear tickets by text (alias /find). |
| /requeue ENG-42 [context] | Re-queue a blocked/failed ticket, optionally with extra context. |
| /kill ENG-42 | Stop a running ticket. |
| /help | List all commands. |
Trusted reviewers
Humans are always acted on. Bot reviewers are acted on only if their GitHub login is listed in TRUSTED_REVIEWERS (case-insensitive, comma-separated). The default is empty — humans only — so a confidently-wrong bot can't drive a bad change. Bot feedback is still logged.
Docker
A prebuilt image is published to GHCR with git, gh, and both agent CLIs baked in — it runs anywhere Docker does, from your laptop to a $4 VPS. Provide keys via .env; repos are routed per ticket from each Linear project's Repo: directive. In a container, use HTTPS URLs / owner/name in the directive so GH_TOKEN authenticates the clone (SSH would need a mounted key).
# keys via .env; repos routed per-ticket from each Linear # project's "Repo: owner/name" directive (see Multi-repo routing) docker run -d --name noctra --env-file .env -v "$PWD/data:/data" \ ghcr.io/ahmadalmezaal/noctra:latest docker logs -f noctra
Container auth uses API keys/tokens instead of interactive logins: LINEAR_API_KEY, AGENT_BACKEND, ANTHROPIC_API_KEY or OPENAI_API_KEY, and GH_TOKEN. Everything mutable — repo cache, worktrees, logs, PR cursor — lives under the mounted /data volume, so restarts keep their state. docker compose up -d works too.
Cloud
Always-on, no hardware. Templates for Fly.io, Render, Railway, and DigitalOcean each deploy the GHCR image with the same secrets as the Docker setup. Repos route per ticket via the project Repo: directive — nothing to mount or configure; use HTTPS URLs / owner/name so GH_TOKEN authenticates the clone.
All four persist /data (Fly volume / Render disk / Railway volume / droplet disk) so restarts keep state.
Running on a Raspberry Pi
Easiest: grab the prebuilt linux_arm64 (Pi 4 / 5, 64-bit OS) or linux_armv7 (Pi 3 / 32-bit OS) archive from Releases — no Go toolchain on the Pi needed. Prefer to cross-compile yourself? Noctra is a single static binary — build it and copy it over.
# Pi 4 / 5 (64-bit OS) GOOS=linux GOARCH=arm64 go build -o noctra ./cmd/noctra # Pi 3 / 32-bit OS GOOS=linux GOARCH=arm GOARM=7 go build -o noctra ./cmd/noctra
Then point your cron or service at the binary. No runtime dependencies.
Environment variables
| Variable | Purpose |
|---|---|
| LINEAR_API_KEY | Linear API key Noctra polls with. |
| LINEAR_TEAM_KEY | Team prefix (e.g. ENG) whose issues are watched. |
| AGENT_BACKEND | Coding agent that implements tickets: claude (default) or codex. |
| TRIGGER_MODE | Pick up work by state (column) or label. Default: state. |
| TRIGGER_STATE | State that marks a ticket ready to build (default: Next). |
| TRIGGER_LABEL | Label that marks a ticket ready to build when label mode is enabled. |
| IN_REVIEW_STATE | State set once the PR is opened (default: In Review). |
| REPO_PATH | Last-resort single-repo fallback when a project has no Repo: directive. |
| MAIN_BRANCH | Default base branch; overridden per-repo by a project Branch: directive. |
| MAX_CONCURRENT | How many tickets run in parallel. |
| POLL_INTERVAL | Seconds between Linear polls. |
| USE_AGENT_TEAMS | Claude-only: a lead agent delegates work to teammates in parallel. |
| MAX_DISPATCHES | Cap on tickets dispatched per run. |
| MAX_RETRIES | Implementation retries before giving up on a ticket. |
| AGENT_TIMEOUT_MINUTES | Hard timeout for a single agent run. |
| GEMINI_API_KEY | Enables the optional second-model review gate. |
| GEMINI_MODE | How the review gate reaches Gemini: api (default, uses the key) or cli (local gemini CLI / Google login). |
| GEMINI_MODEL | Gemini model for the review gate. |
| MAX_REVIEW_RETRIES | Fix passes the agent gets after Gemini flags issues. |
| TELEGRAM_ENABLED | Turn Telegram notifications on/off. |
| TELEGRAM_BOT_TOKEN | Bot token for notifications. |
| TELEGRAM_CHAT_ID | Chat the bot posts to. |
| TELEGRAM_VERBOSE | Also notify on every ticket dispatch. |
| AUTO_ITERATE_PRS | Watch opened PRs and re-engage on feedback + CI. |
| MAX_PR_ITERATIONS | Per-PR re-engagement cap (shared: review + CI). |
| PR_POLL_INTERVAL | Seconds between PR scans. |
| STATE_FILE | Path to the PR-cursor state file that tracks which PRs have been handled (default: ~/.noctra-state.json). |
| TRUSTED_REVIEWERS | CSV of bot logins to act on; empty = humans only. |
| ANTHROPIC_API_KEY | Claude auth when running headless (Docker/cloud), instead of an interactive login. |
| OPENAI_API_KEY | Codex auth when running headless, instead of codex login. |
| GH_TOKEN | PAT for gh PR creation + git push in containers (repo + PR scope). |
| GIT_USER_NAME | Commit author name (defaults to a Noctra bot). |
| GIT_USER_EMAIL | Commit author email (defaults to a Noctra bot). |
CLI commands
noctra— start the poll loop (default).noctra setup— interactive configuration wizard for~/.noctra/.noctra doctor— preflight tokens, local tools, repo routing, and model access (add--jsonfor machine-readable output).noctra update— self-update a release binary: download the latest, verify its checksum, and swap in place (--restartrestarts the service after).noctra install-service— write asystemd --userunit pointing at the installed binary (--startto enable, start, and keep it running after logout).noctra uninstall— remove the service and installed binary; add--purgeto also delete~/.noctra*state (config, cloned repos, worktrees, PR cursor), and--forceto skip the confirmation.noctra start/stop/restart/status— control thesystemd --userservice from the binary.noctra logs— tail the service logs (-fto follow).noctra cleanup— prune stale branches, worktrees, and old logs (add--forceto remove everything stale non-interactively).noctra completion bash|zsh— print a shell completion script.noctra version— print the build version.