Skip to main content

CLI Shim Deployment

The Pavri CLI shims wrap Claude Code, Gemini CLI, and Aider with a thin PATH-injected binary that emits start/end events to the local pavri-endpointd sensor. They are a single signed multi-call binary (pavri-shim) installed by the sensor at startup.

How it works

The sensor extracts pavri-shim to a shim directory and creates three hardlinks/copies named claude-code, gemini, aider. Your shell PATH is extended (via /etc/paths.d/pavri if the sensor runs as root, or via a delimited block in ~/.zshrc/~/.bashrc/~/.config/fish/config.fish/PowerShell $PROFILE if not) so the shim runs first when you invoke any of the wrapped CLIs. The shim:

  1. Looks up the real binary in PATH (with the shim dir stripped).
  2. Posts a cli_invocation_start event to the sensor's UDS / named pipe.
  3. Forks the real binary, inherits stdin/stdout/stderr unchanged, forwards signals.
  4. Waits for the child, then posts cli_invocation_end with the exit code.
  5. Exits with the same code as the child.

The real CLI's behavior is unchanged. The shim adds <6 ms to startup wall time (measured ~5.7 ms p50 on M-series macOS) and a small constant RSS while parented.

What if the sensor is offline?

The shim fails open. It logs once to stderr (suppressed if PAVRI_SHIM_QUIET=1), then execs the real binary anyway. Your CLI works whether the sensor is running or not.

Per-OS install paths

macOS / Linux (root sensor)

  • Shim dir: /usr/local/lib/pavri/shims/
  • PATH injection: /etc/paths.d/pavri (macOS) or /etc/profile.d/pavri-shims.sh (Linux)

macOS / Linux (user-mode sensor)

  • Shim dir: ~/.pavri/shims/
  • PATH injection: marker block in ~/.zshrc, ~/.bashrc, ~/.config/fish/config.fish

Windows (admin sensor)

  • Shim dir: C:\ProgramData\Pavri\shims
  • PATH injection: HKLM Path entry (machine-wide)

Windows (user-mode sensor)

  • Shim dir: %LOCALAPPDATA%\Pavri\shims
  • PATH injection: marker block in PowerShell $PROFILE

Verifying the install

# POSIX
which claude-code # should resolve to your shim dir
ls -la ~/.pavri/shims # claude-code, gemini, aider all link to pavri-shim
claude-code --version # the real version, unchanged
# Windows
Get-Command claude-code

Uninstalling

The sensor uninstall script removes shim binaries and PATH injection automatically. For manual uninstall:

sudo /opt/pavri-endpointd/bin/endpointctl uninstall-shims --root
# or, for user-mode installs:
~/.pavri/bin/endpointctl uninstall-shims --home "$HOME"

Troubleshooting

which claude-code doesn't resolve to the shim

  • Open a fresh shell — your current shell may have a stale PATH.
  • If your dotfiles re-export PATH after sourcing rcfiles, the shim's marker block (which prepends to PATH) gets overridden. Move the dotfile PATH manipulation before the PAVRI marker block, or move the shim dir injection to a system-wide path (run sensor as root).

Shim binary signature invalid (shim_install_status=signature_invalid)

The embedded shim binary's hash didn't match the expected hash baked into the sensor binary. This is a defense against on-disk tampering. Reinstall the sensor from a trusted source.

macOS Gatekeeper blocks shim execution

The sensor's installer clears the com.apple.quarantine xattr on extraction. If you see a Gatekeeper prompt, verify the sensor binary itself is properly notarized:

spctl --assess --type execute /opt/pavri-endpointd/bin/pavri-endpointd

Performance

MetricBudgetMeasured (W5 bench)
Startup overhead (avg)< 5 ms~5.7 ms (M-series macOS; first-call slower due to resolve cache miss)
Parent RSS while waiting< 5 MB< 1 KB measured (test runs the shim against an exiting child; see ResourceMonitor for live)
Signal forwarding latency< 1 msconfirmed via TestShim_SIGINT_Forwarded; SIGINT reaches child within the 200 ms test wait window

Verified against code at commit ac271ce706b6 on 2026-05-08. See docs/superpowers/specs/2026-05-08-cap-2.3-design.md for full design.