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:
- Looks up the real binary in PATH (with the shim dir stripped).
- Posts a
cli_invocation_startevent to the sensor's UDS / named pipe. - Forks the real binary, inherits stdin/stdout/stderr unchanged, forwards signals.
- Waits for the child, then posts
cli_invocation_endwith the exit code. - 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
| Metric | Budget | Measured (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 ms | confirmed 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.