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_BACKENDClaude 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 worktreedispatch 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.

CommandWhat it does
/statusActive runs + session stats.
/tickets [project] [state]Ticket counts by state, or list one state.
/ticket ENG-42Show 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-42Stop a running ticket.
/helpList 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

VariablePurpose
LINEAR_API_KEYLinear API key Noctra polls with.
LINEAR_TEAM_KEYTeam prefix (e.g. ENG) whose issues are watched.
AGENT_BACKENDCoding agent that implements tickets: claude (default) or codex.
TRIGGER_MODEPick up work by state (column) or label. Default: state.
TRIGGER_STATEState that marks a ticket ready to build (default: Next).
TRIGGER_LABELLabel that marks a ticket ready to build when label mode is enabled.
IN_REVIEW_STATEState set once the PR is opened (default: In Review).
REPO_PATHLast-resort single-repo fallback when a project has no Repo: directive.
MAIN_BRANCHDefault base branch; overridden per-repo by a project Branch: directive.
MAX_CONCURRENTHow many tickets run in parallel.
POLL_INTERVALSeconds between Linear polls.
USE_AGENT_TEAMSClaude-only: a lead agent delegates work to teammates in parallel.
MAX_DISPATCHESCap on tickets dispatched per run.
MAX_RETRIESImplementation retries before giving up on a ticket.
AGENT_TIMEOUT_MINUTESHard timeout for a single agent run.
GEMINI_API_KEYEnables the optional second-model review gate.
GEMINI_MODEHow the review gate reaches Gemini: api (default, uses the key) or cli (local gemini CLI / Google login).
GEMINI_MODELGemini model for the review gate.
MAX_REVIEW_RETRIESFix passes the agent gets after Gemini flags issues.
TELEGRAM_ENABLEDTurn Telegram notifications on/off.
TELEGRAM_BOT_TOKENBot token for notifications.
TELEGRAM_CHAT_IDChat the bot posts to.
TELEGRAM_VERBOSEAlso notify on every ticket dispatch.
AUTO_ITERATE_PRSWatch opened PRs and re-engage on feedback + CI.
MAX_PR_ITERATIONSPer-PR re-engagement cap (shared: review + CI).
PR_POLL_INTERVALSeconds between PR scans.
STATE_FILEPath to the PR-cursor state file that tracks which PRs have been handled (default: ~/.noctra-state.json).
TRUSTED_REVIEWERSCSV of bot logins to act on; empty = humans only.
ANTHROPIC_API_KEYClaude auth when running headless (Docker/cloud), instead of an interactive login.
OPENAI_API_KEYCodex auth when running headless, instead of codex login.
GH_TOKENPAT for gh PR creation + git push in containers (repo + PR scope).
GIT_USER_NAMECommit author name (defaults to a Noctra bot).
GIT_USER_EMAILCommit 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 --json for machine-readable output).
  • noctra update — self-update a release binary: download the latest, verify its checksum, and swap in place (--restart restarts the service after).
  • noctra install-service — write a systemd --user unit pointing at the installed binary (--start to enable, start, and keep it running after logout).
  • noctra uninstall — remove the service and installed binary; add --purge to also delete ~/.noctra* state (config, cloned repos, worktrees, PR cursor), and --force to skip the confirmation.
  • noctra start / stop / restart / status — control the systemd --user service from the binary.
  • noctra logs — tail the service logs (-f to follow).
  • noctra cleanup — prune stale branches, worktrees, and old logs (add --force to remove everything stale non-interactively).
  • noctra completion bash|zsh — print a shell completion script.
  • noctra version — print the build version.