provefab

Last updated

Run Claude Code and Codex on a headless Linux server, always on

Support update, 14 October 2026: Native enforcement and login modes are restricted. Linux enforcement remains unverified; all Claude stages temporarily refuse. Select Codex or Pi. See current versions, prerequisites and protection limits.

Install both CLIs as your own user, then sign in with the flows made for machines without a browser: Claude Code gives you a link to open elsewhere and takes the code you paste back, and Codex shows a device code. Keep any API key in an environment file with mode 600, run your agent process as a systemd user service, and turn on lingering so it keeps running after you log out. Provefab does the same with one install line and a few commands, and adds what to run: labelled issues that become pull requests.

What you need

Install the CLIs

Claude Code’s setup page gives one native installer for macOS, Linux and WSL:

Terminal window
curl -fsSL https://claude.ai/install.sh | bash
claude --version

On Linux it installs the launcher at ~/.local/bin/claude, under your home directory. The same page offers apt, dnf and apk repositories (those need sudo) and an npm package, with a plain warning: “Do NOT use sudo npm install -g”.

Codex has a standalone installer for macOS and Linux on its CLI page:

Terminal window
curl -fsSL https://chatgpt.com/codex/install.sh | sh

Run both as the account the agents will use, so their files and logins belong to it.

Sign in without a browser

Claude Code. Run claude. The authentication page covers the headless case: if the browser does not open, press c to copy the login URL and open it on another machine. When the browser shows a login code instead of redirecting back, “paste it into the terminal at the Paste code here if prompted prompt”, which the page says “is common in WSL2, SSH sessions, and containers.” For scripts, claude setup-token prints a one-year token to set as CLAUDE_CODE_OAUTH_TOKEN; the page recommends it “for CI pipelines and scripts where browser login isn’t available.” With an API key instead, set ANTHROPIC_API_KEY.

Codex. The Codex authentication page recommends device code authentication, in beta, for “a remote or headless environment”:

Terminal window
codex login --device-auth

Open the link on any machine with a browser, sign in, and enter the one-time code. Device code login may need enabling first, in your ChatGPT security settings for a personal account or in the workspace permissions for a workspace admin. For an API key, pipe it in: printenv OPENAI_API_KEY | codex login --with-api-key. Codex then keeps the login “in a plaintext file at ~/.codex/auth.json or in your OS-specific credential store”.

Keep keys in a file only you can read

The service reads its secrets from an environment file. Create it with a mode that leaves group and others out:

Terminal window
mkdir -p ~/.config/agent-runner
( umask 077; touch ~/.config/agent-runner/env )
chmod 600 ~/.config/agent-runner/env
"${EDITOR:-nano}" ~/.config/agent-runner/env

Put one NAME=value per line, for example ANTHROPIC_API_KEY=... or CLAUDE_CODE_OAUTH_TOKEN=.... Be clear about what this protects. The file is protected by its permissions, not encrypted: anyone with root, and any process running as your account, can read it. That includes the agents themselves.

Run it as a systemd user service

Neither CLI watches a queue by itself, so the service runs a script you write, here ~/bin/agent-runner: it decides what to work on and calls the CLIs non-interactively. Claude Code runs a single prompt with claude -p "<prompt>" (headless mode), and Codex with codex exec "<prompt>" (non-interactive mode). A user service does not read your shell profile, and the systemd.exec page says the user manager’s PATH “may be configured by the distribution”, so call the CLIs by full path or set PATH in the environment file.

Write the unit to ~/.config/systemd/user/agent-runner.service:

[Unit]
Description=Coding agent runner
[Service]
ExecStart=%h/bin/agent-runner
EnvironmentFile=%h/.config/agent-runner/env
Restart=on-failure
RestartSec=30
[Install]
WantedBy=default.target

%h is your home directory (systemd.unit). EnvironmentFile= reads “newline-separated variable assignments” from the file. Restart=on-failure restarts the script when it “exits with a non-zero exit code” or is killed by a signal, and RestartSec=30 waits 30 seconds first (systemd.service). default.target is “the main target of the user service manager.” Load and start it:

Terminal window
systemctl --user daemon-reload
systemctl --user enable --now agent-runner
systemctl --user status agent-runner

Keep it running after you log out

A user service belongs to your user manager, which normally stops when your last session ends. Lingering changes that: according to loginctl, “a user manager is spawned for the user at boot and kept around after logouts.” Turn it on once, with administrator rights:

Terminal window
sudo loginctl enable-linger "$USER"
loginctl show-user "$USER" --property=Linger

Without it, the service stops when you close your SSH session and does not start at boot.

Read the logs

systemd keeps what your script prints to stdout and stderr in the journal:

Terminal window
journalctl --user -u agent-runner # everything
journalctl --user -u agent-runner -f # follow live

--user shows “messages from service of current user”, and -f keeps printing new entries (journalctl). Logs your script writes to its own files stay there.

One common trap: after su - <user>, systemctl --user fails with Failed to connect to bus, because that shell has no user systemd session. Log in over SSH as that user instead, or set XDG_RUNTIME_DIR=/run/user/$(id -u).

The same setup with Provefab

Provefab packages these steps, and adds the part you would otherwise write in agent-runner. Install it into ~/.local/bin, without sudo:

Terminal window
curl -fsSL https://provefab.com/install.sh | sh

A static .tar.gz for x86_64 or ARM64 is also on the GitHub Releases page. On Linux, HTTPS needs the ca-certificates package. Then:

Terminal window
provefab login claude # shows a link, takes the code you paste
provefab login codex # shows a device code to enter on another machine
provefab init
provefab repos add your-account/your-repo
# Provision SRT and offline dependencies; declare protected validation paths.
# Confirm runtime/login support before starting tasks.
provefab doctor
provefab run --dry-run
provefab service install --workers 1

Both logins live in Provefab’s own config directories (~/.provefab/claude, ~/.provefab/codex), kept apart from your own sessions. Codex also supports --api-key, using ~/.provefab/codex-api. Claude API-key profiles can be configured in ~/.provefab/claude-api, but native stages refuse them. On Linux, the Anthropic key, the Jev key and the Jira and Linear credentials go to ~/.provefab/credentials.toml, created with mode 600 and refused when group or others can read it. Like your own env file, it is protected by its permissions, not encrypted (Configuration).

provefab service install writes ~/.config/systemd/user/provefab.service, then runs systemctl --user daemon-reload, enable and restart. When lingering is off, it prints sudo loginctl enable-linger <user> for you to run: Provefab never runs sudo itself, and provefab doctor shows a lingering line. Logs go to ~/.provefab/logs/run.log; service install rotates it to run.log.1 once it passes 10 MB, and journalctl --user -u provefab shows systemd’s own messages. The service keeps the PATH of the terminal you install it from, so run service install again after you install or move a tool (Operations).

On top of the service, Provefab decides what to run: it picks up GitHub issues you label provefab (Jira Cloud and Linear in beta), filters every agent tool call through a guard, runs your repository’s checks, has a model from another provider review the change when your catalog has a model from another provider, and opens a pull request that waits for a person. To let a coding agent do the configuration, point it at Set up with a coding agent; it leaves the sign-ins, keys and service to you. What the pull request carries is in How to verify AI-generated pull requests. To start work from Jira Cloud or Linear instead of GitHub issues, see Jira or Linear ticket to pull request.

Limits

Try it: the source-available core on GitHub, orProvefab Pro.