Talk this topic through with an AI

You get an answer from the documentation and the page it comes from.

Installation & operation

Installing the poison0 agent: complete reference

The poison0 agent is a small daemon on each device that watches your agents' memory files locally. This page documents the installer end to end: the quick-install commands, every environment variable, the data directory per operating system, the service that runs the daemon, and how to roll it out unattended.

~9 min read · Installation & operation

Overview

What the installer does, in order:

  • Downloads the matching daemon binary from https://poisonzero.com/dl/latest (Linux/macOS/Windows, amd64 or arm64).
  • Verifies its SHA-256 checksum against the published SHA256SUMS; aborts on any mismatch.
  • Installs a privileged service and, optionally, enrolls the device using an install code from the console.
Service install needs elevated rights: root (sudo) on Linux/macOS, an Administrator PowerShell on Windows. Use PZ_FETCH_ONLY=1 to only download and verify.

Requirements, checked before install:

  • x86-64: AVX2 (Intel Haswell/2013+, AMD Zen+).
  • ARM64: i8mm (Apple M2+; Apple M1 not supported).
  • Windows: data directory on an NTFS volume.

Quick install

Create an install code in the console (or use a legacy per-device code), then run the command for your platform:

bashTerminal
curl -fsSL https://poisonzero.com/install.sh | sudo \
  PZ_ENROLL_CODE="your-code" sh
powershellPowerShell
$env:PZ_ENROLL_CODE="your-code"
irm https://poisonzero.com/install.ps1 | iex
  • No code pre-set: the installer prompts for the install code (plus acceleration/tier and headless choices on a real terminal); an app ID is only asked for the legacy per-device pairing.
  • An install code is multi-use up to its redemption cap; a legacy per-device code stays single-use.
  • Already enrolled, re-run without a new code = upgrade: swaps the binary, restarts the service, keeps your credentials.
Updates run automatically. Install once per device; re-run only to change enrollment or force a binary swap.

Install codes

Create an install code in the console, under Install keys. It is the recommended way to enroll devices:

  • Multi-use: one code enrolls devices up to the redemption cap you set when you create it; each enrollment counts against that cap.
  • Shown once: copy it when you create it - the console does not display it again.
  • Revocable: revoke it in the console to stop further redemptions immediately; already-enrolled devices keep working.
  • No app ID needed: the backend assigns each device its identity at redemption.
The legacy flow, creating a device in the console to get a matching app ID and a single-use code, still works unchanged. Use it for older installers or integrations already built around it.

Environment variables

Pin any option through an environment variable to run the install non-interactively:

VariableValuesDefaultEffect
PZ_ENROLL_CODEinstall codenoneThe install code from the console. Required for a fresh enrollment; on its own it is enough, since the backend assigns the device identity. Passing a fresh one again on an enrolled device forces a deliberate re-enroll.
PZ_APP_IDapp idnoneLegacy and optional: only needed together with an older per-device code minted before install codes. Ignored on its own.
PZ_HEADLESSnone, standard, strictnonePins the user confirmation-prompt handling on a machine with no desktop user to answer prompts. none auto-detects from the logged-in user; standard/strict write a headlessmode file the daemon reads.
PZ_BACKENDcpu, gpucpuAcceleration backend. CPU-only is the recommended, most secure default. GPU is opt-in and prints a security disclaimer; the GPU engine is not released yet.
PZ_TIERenterprise, ultraenterpriseProduct tier. ultra implies GPU and is fail-closed with no CPU fallback; enterprise with GPU falls back to a working CPU-only install.
PZ_FETCH_ONLY1unsetDownload and verify the binary only, then exit. No service is installed, so no elevated rights are needed.
PZ_FETCH_DESTpath./poisonzero (.\poisonzero.exe on Windows)Destination path for the verified binary when PZ_FETCH_ONLY=1.
PZ_SETUP_AGENTScomma list, e.g. claude,codexunsetWires the local /poisonzero stats command into the listed agents (Claude Code, Codex, Gemini, Cursor). Fully local, opt-in.
PZ_SETUP_STATUSLINE1unsetAdds the poison0 agent's status line to Claude Code (~/.claude/settings.json). An existing status line is never replaced.
PZ_DRY_RUN1unsetPrints the resolved mode (fresh/re-enroll/upgrade) and exits before any side effect. Useful for testing automation.
POISONZERO_DATA_DIRpathper-OS default (see below)Overrides the data directory the daemon and installer use.
Linux/macOS also accept a --headless none|standard|strict flag (wins over PZ_HEADLESS); the Windows deploy script takes -Headless.

Data directory and files

Binary: /usr/local/bin/poisonzero (Linux/macOS), <ProgramFiles>\PoisonZero\poisonzero.exe (Windows). Data directory per OS:

OSData directoryOwner
Linux/var/lib/poisonzero (enroll file at /etc/poisonzero/enroll)poisonzero:poisonzero
macOS/Library/Application Support/PoisonZeroroot:wheel
Windows<ProgramData>\PoisonZeroSYSTEM / Administrators

Files inside the data directory:

  • credentials - stored device token; its presence marks the device as enrolled and is never overwritten on upgrade.
  • enroll - one-time enrollment input (the install code, plus appId for the legacy pairing) consumed by poisonzero enroll on first start.
  • headlessmode - present only when standard/strict was pinned; absent or empty = auto-detect.
  • vault/ - quarantine store for reverted content.

Service / daemon

Runs as a privileged background service, one mechanism per OS:

OSTypeName & status command
Linuxsystemd unit (root)poisonzero.service · systemctl status poisonzero
macOSlaunchd LaunchDaemoncom.poisonzero.daemon · launchctl print system/com.poisonzero.daemon
WindowsScheduled Task (SYSTEM, at boot)PoisonZero · Get-ScheduledTask -TaskName PoisonZero
  • Windows uses a Scheduled Task, not a Windows Service: the Go binary loads its model on first start and would miss the Service Control Manager start timeout.
  • A desktop notifier runs in each user session for the user confirmation prompt: XDG autostart (Linux), per-user LaunchAgent (macOS), Run-key VBS wrapper (Windows).

Unattended install (Intune / SCCM)

Set the install code in the environment, then run the same one-liner; with the code present and no terminal, it runs prompt-free:

bashUnattended
PZ_ENROLL_CODE="your-code" \
  sh -c "$(curl -fsSL https://poisonzero.com/install.sh)"
powershellUnattended
$env:PZ_ENROLL_CODE="your-code"
irm https://poisonzero.com/install.ps1 | iex
Add PZ_DRY_RUN=1 to print the resolved mode (fresh/re-enroll/upgrade) and exit before any change.

Download and verify only

PZ_FETCH_ONLY=1 downloads, verifies the SHA-256 checksum and writes the binary without installing a service (no elevated rights):

bashFetch only
curl -fsSL https://poisonzero.com/install.sh | PZ_FETCH_ONLY=1 sh
# writes ./poisonzero (override with PZ_FETCH_DEST)
powershellFetch only
$env:PZ_FETCH_ONLY="1"; irm https://poisonzero.com/install.ps1 | iex
# writes .\poisonzero.exe (override with $env:PZ_FETCH_DEST)
Checksum verification is mandatory on every path; a tampered or truncated download is never installed.

Verify the device

  1. Confirm the service is running with the status command for your OS (table above).
  2. Open the console: the enrolled device appears in your device list with its status.
  3. To move a device to a fresh install code, re-run the installer with a new PZ_ENROLL_CODE - it re-enrolls cleanly.

Uninstall

One command removes everything: first the exec0 file managed-settings.d/50-poisonzero-exec0.json and the Claude Code hooks of every user, then the service, the program and the data directory. If the exec0 file cannot be removed, the uninstall stops and the program stays installed. Run it as administrator (root). Never delete the program by hand: hooks that point to a missing program block every tool call in Claude Code.

bashUninstall
curl -fsSL https://poisonzero.com/install.sh | sudo sh -s -- --uninstall
powershellUninstall
& ([scriptblock]::Create((irm https://poisonzero.com/install.ps1))) -Uninstall
bashUninstall
# .deb / .rpm
sudo apt remove poisonzero   # or: sudo dnf remove poisonzero
# Homebrew
poison0 remove-agent-integration && brew uninstall poisonzero
# Scoop
scoop uninstall poisonzero
To pause instead of uninstall: the protection is fail-closed by design, so removing the service is the clean way to stop it entirely.

exec0 hooks in Claude Code

exec0 hooks into Claude Code with three hooks: UserPromptSubmit, PreToolUse and PostToolUse. Where they are registered depends on the type of installation:

InstallationLocation of the exec0 hooks
System installation: install.sh, install.ps1, .deb/.rpm (program in a folder only administrators can change)Claude Code's managed settings, separate file managed-settings.d/50-poisonzero-exec0.json
User installation: Homebrew under a user-owned prefix, Scoop in the user profileThe user's ~/.claude/settings.json
OSManaged settings folder
macOS/Library/Application Support/ClaudeCode/
Linux, WSL/etc/claude-code/
WindowsC:\Program Files\ClaudeCode\
  • PoisonZero never changes your managed-settings.json and never changes other files in managed-settings.d/.
  • Folders PoisonZero creates are writable only by administrators and readable by everyone.

Effect

CaseResult
A user or agent removes the entry or sets "disableAllHooks": true in user or project settings or via --settingsThe exec0 hooks keep running.
You set allowManagedHooksOnlyThe exec0 hooks keep running; hooks from user and project settings do not.
You set "disableAllHooks": true in the managed settingsThe exec0 hooks are off as well; poison0 doctor reports it.
Device with an older installationThe entry in ~/.claude/settings.json stays as a fallback until the PoisonZero service sees Claude Code call the managed entry for that user. Then the service removes the user entry. If both run until then, each tool call is still checked only once.

Policies already delivered centrally

If you already deliver Claude Code policies through one of the following sources, Claude Code by default applies only the highest source and ignores 50-poisonzero-exec0.json:

SourceLocation
Server-managed settingsclaude.ai admin console
MDM profile (macOS)Domain com.anthropic.claudecode
Group Policy or Intune (Windows)HKLM\SOFTWARE\Policies\ClaudeCode
FixHow
Merge the sources (Claude Code 2.1.242 or later)Set "managedSourcesBehavior": "merge" in the highest source.
Deliver through your own sourceCopy the content of managed-settings.d/50-poisonzero-exec0.json from an installed device into that source.
Checkpoison0 doctor, poison0 exec0 status or the PoisonZero console show whether Claude Code really calls the hook, and name the source.

Turn exec0 off and on for Claude Code

Requires administrator rights. On Windows, run it in a terminal as administrator.

bashexec0 for Claude Code
# turn off
sudo poison0 setup-claude --toolgate-off
# turn on
sudo poison0 setup-claude --toolgate
powershellexec0 for Claude Code
# turn off
poison0 setup-claude --toolgate-off
# turn on
poison0 setup-claude --toolgate