Skip to main content

Coding Agent Protection — Sensor Deployment

This page covers building and running pavri-endpointd and configuring it via environment variables. Linux .deb and .rpm packages are available from the Linux packaging tree, Windows MVP deployment uses the Intune Win32 path documented below, and macOS uses the signed .pkg release path when signing credentials are present.

MDM rollout? If you are deploying pavri-endpointd via an MDM, start with the MDM rollout overview and your vendor's runbook (Jamf / Intune / Kandji / Workspace ONE). The reference below is for env-var and command-line use.


Per-OS install commands

macOS

go build -o /usr/local/bin/pavri-endpointd \
./services/endpointd/cmd/endpointd

Requires Go 1.22+. The binary links against CGO for the Endpoint Security framework bindings; Xcode Command Line Tools must be installed.

Apple Endpoint Security entitlement: Production-grade process and file observation on macOS requires an Apple Endpoint Security (ES) entitlement. This entitlement must be provisioned in your Developer ID certificate by Apple. Obtaining and configuring this entitlement is deferred to CAP-1.7, which will deliver a notarized, signed package.

Without the entitlement the macOS observer reports StatusUnavailable at startup and the sensor degrades to basename-only attribution: it can still attribute processes by binary path and basename from the working directory, but cannot inspect the full process ancestry via ES authorization events. The sensor continues to run and send heartbeats; posture remains healthy.

Linux

go build -o /usr/local/bin/pavri-endpointd \
./services/endpointd/cmd/endpointd

For package installs, build the native package first:

./services/endpointd/packaging/linux/build-binaries.sh
./services/endpointd/packaging/linux/build-deb.sh # Debian/Ubuntu
./services/endpointd/packaging/linux/build-rpm.sh # Fedora/RHEL-family

Then install the artifact and edit /etc/pavri-endpoint/endpointd.env:

sudo dpkg -i dist/linux/pavri-endpointd_<version>_amd64.deb
sudo rpm -Uvh dist/linux/pavri-endpointd-<version>-1.x86_64.rpm
sudo systemctl enable --now pavri-endpointd

The packages seed /etc/pavri-endpoint/endpointd.env with mode 0600, install the systemd unit, and do not auto-enable the service before the gateway URL and one-time enrollment token are set. Ubuntu .deb install/enroll/observe has been live-verified. RPM packages have PR-safe structural checks plus a nightly/manual live Fedora and RHEL-family userspace harness under tests/e2e/endpoint-linux-rpm/; do not claim Fedora/RHEL production validation until that live job or an equivalent VM run is green. The hosted workflow uses privileged systemd containers, which are valid RPM userspace/scriptlet smoke targets but not RHEL kernel/eBPF validation.

The eBPF probes require either root or the following Linux capabilities on the sensor process:

  • CAP_BPF — load and run BPF programs.
  • CAP_PERFMON — attach to perf events and tracepoints (required alongside CAP_BPF on kernel 5.8+).

Alternatively, run the sensor as root (sudo). Kernel 6.x or higher is recommended for LSM hook support; the sensor degrades gracefully to tracepoints-only on older kernels.

For live eBPF integration tests on a macOS workstation, use the Lima VM. See docs/lima-setup.md in the repository root.

make lima-up           # provision Ubuntu 24.04 VM
make lima-test-ebpf # run eBPF tests inside the VM

Environment variables

All configuration is read from the process environment. No config file is required.

VariableDefaultPurpose
PAVRI_GATEWAY_URL(required)Base URL of the Pavri gateway service (e.g., https://gateway.example.com). Used for enrollment, heartbeat, and telemetry uploads.
PAVRI_ENROLLMENT_TOKEN(required for enrollment)One-time enrollment credential. Mint this in the Pavri dashboard under Settings > Endpoints > Add Endpoint.
PAVRI_ENDPOINT_STATE_DIR/var/lib/pavri-endpointDirectory where the sensor stores its enrollment state (TLS certificate, private key, endpoint_id). Disk persistence ships in CAP-1.7; in CAP-1.2 this is in-memory only.
PAVRI_ENDPOINT_HEARTBEAT_INTERVAL_S60Seconds between heartbeat POSTs to the gateway. Decrease for faster posture updates; increase to reduce gateway load.
PAVRI_ENDPOINT_SENSOR_VERSION0.1.0Reported sensor version. Overridden by the build system when packaging a release build.
PAVRI_OBSERVER_ENABLEDtrueSet to false to run in heartbeat-only mode. No observer pipeline is started; no coding events are emitted. Useful for enrollment-only rollouts or low-privilege environments.
PAVRI_OBSERVER_CONCURRENCY4Number of attribution worker goroutines. Increase on high-event-rate endpoints (e.g., build servers).
PAVRI_OBSERVER_MAILBOX_CAPACITY1024Per-observer bounded mailbox depth. Increase if the sensor logs drop warnings under normal load.
PAVRI_TELEMETRY_BATCH_SIZE50Number of event envelopes that triggers an immediate flush to the gateway.
PAVRI_TELEMETRY_FLUSH_INTERVAL_S5Maximum seconds between telemetry flushes regardless of batch fill.

macOS Apple Endpoint Security

Full process and file observation on macOS requires a Developer ID certificate provisioned with the com.apple.developer.endpoint-security.client entitlement. Apple grants this entitlement through a review process; the application is separate from standard Developer ID provisioning.

Until CAP-1.7 delivers the signed, entitlement-bearing package:

  • The macOS process observer starts and immediately returns ErrNotEntitled.
  • The supervisor logs observer unavailable: observer: macOS Endpoint Security entitlement required and marks the observer StatusUnavailable.
  • The sensor continues to run with heartbeat and basename-only attribution from working-directory inspection.
  • Posture is reported as healthy; no alert is raised.

Operators can verify the degraded state by querying the gateway's endpoint heartbeat records for observer_status: unavailable.


Linux eBPF capabilities

The Linux process observer (process_observer_linux.go) and file observer (file_observer_linux.go) load eBPF programs at startup. The required Linux capabilities are:

CapabilityRequired for
CAP_BPFLoading BPF programs and maps.
CAP_PERFMONAttaching to tracepoints and perf events. Introduced in kernel 5.8; before 5.8 these operations required CAP_SYS_ADMIN.
CAP_SYS_ADMINAlternative to CAP_BPF + CAP_PERFMON on kernels < 5.8. Also required for LSM hook attachment on some distributions.

If these capabilities are absent, the observers return ErrInsufficientCapabilities and the supervisor degrades gracefully (same pattern as the macOS entitlement case).

Recommended approach: run the sensor with sudo during development and via the packaged systemd unit in production. The unit grants CAP_BPF CAP_PERFMON CAP_NET_ADMIN CAP_SYS_ADMIN CAP_DAC_READ_SEARCH CAP_KILL, uses ProtectSystem=full and ProtectHome=read-only, and limits writable paths to the endpoint state/runtime directories, /tmp, and /usr/local/lib/pavri.

Kernel requirement: 6.x or higher for LSM hook support (security_file_open / vfs_write). The sensor falls back to tracepoints on older kernels. Ubuntu 24.04 ships kernel 6.8 and is the reference platform for eBPF testing.


Local MCP Broker

The MCP broker is the sensor subsystem that intercepts MCP tool-call traffic between assistants (Claude Desktop, Cursor, VS Code, JetBrains, Gemini CLI, Windsurf) and their configured MCP servers. It rewrites each assistant's MCP config to point at a sensor-managed endpoint, transparently spawns or proxies the original upstream, and evaluates each tools/call against the active policy snapshot before forwarding.

For the broker to function, the sensor needs:

RequirementReason
Filesystem write access to the assistant config filesTo rewrite (and back up) claude_desktop_config.json, ~/.cursor/mcp.json, .vscode/mcp.json, JetBrains mcp.xml, ~/.config/gemini/settings.json, and Windsurf's mcp.json.
UDS support for STDIO upstreamsmacOS / Linux: bind a UDS under the sensor's state directory. Windows STDIO support is gated to CAP-2.1; the rest of the broker compiles cleanly on Windows.
Loopback ports for HTTP+SSE-legacy / Streamable HTTP upstreamsDefault range 127.0.0.1:7878-7888.
Read access to the assistant's environment for upstream spawnThe broker captures env key names at rewrite time and re-reads values at upstream-spawn time, so secrets never persist on the manifest.

Drift handling

If a rewritten config changes on disk, the sensor's fsnotify watcher reads the new content, queries the CAP-1.2 file observer for the most recent attributed writer, and emits a ConfigDriftEvent with one of these drift_kind values:

drift_kindWhen it firesSensor response
user_editA non-assistant process (or no observed writer within the attribution window) wrote the change.Emit only. The broker does not auto-clobber a user's manual edit in CAP-1.3; re-rewrite-on-revert lands in CAP-1.5.
assistant_self_modificationThe writer chain matches a registered assistant binary.No clobber. Emit and preserve the on-disk content. This is D-17 evidence: a coding agent rewriting its own MCP server list, plausibly to escape the broker chokepoint.
schema_unknownThe new content does not parse against the rewriter's schema.Emit only.
restore_verification_failedA Restore attempt produced content whose SHA does not match the manifest's OriginalContentSHA256.Emit only.

This ensures the sensor never silently undoes a legitimate user change while still catching the prompt-injection / supply-chain case where an assistant rewrites its own MCP server list to escape the broker chokepoint.

Live integration tests

The sensor ships a Lima-based live test harness that drives the broker against a real @modelcontextprotocol/server-filesystem upstream and asserts a runtime policy hot-swap takes effect on the next tools/call within 2 seconds:

make lima-up-mcp        # provision Ubuntu 24.04 + Node 20 + Python (first run ~5 min)
make lima-test-mcp # install fixtures host-side, run mcplive-tagged tests in VM
make lima-down-mcp # destroy the VM

See MCP Broker for the full operator reference and Policy authoring (raw) for the policy bundle JSON shape.

Windows (CAP-2.1+)

Installation

The sensor ships as pavri-endpointd.exe plus Intune/Win32 staging artifacts under services/endpointd/packaging/windows. This is the accepted MVP Windows installer deliverable; a full MSI is not required for MVP. The installer stages pavri-endpointd.exe and endpointctl.exe under C:\Program Files\Pavri\pavri-endpointd and registers PavriEndpointd as an automatic-start Windows service. Service account: LocalSystem (required for ETW kernel providers and SeSystemProfilePrivilege).

Enrollment values are delivered by MDM to HKLM\SOFTWARE\Pavri; the Intune install script writes them to machine-level environment variables before starting the service and also writes the service-specific HKLM\SYSTEM\CurrentControlSet\Services\PavriEndpointd\Environment REG_MULTI_SZ block before Start-Service. PAVRI_GATEWAY_URL and PAVRI_ENROLLMENT_TOKEN drive real gateway enrollment and endpoint health heartbeats; endpoint state is persisted under C:\ProgramData\Pavri\Endpoint.

.\services\endpointd\packaging\windows\build-binaries.ps1 -Arches amd64
IntuneWinAppUtil.exe -c dist\windows\pavri-endpoint-windows-amd64 -s install.ps1 -o dist\windows

Observer coverage

ObserverMechanismStatus in CAP-2.1
ProcessETW Microsoft-Windows-Kernel-Process event 1At parity with macOS+Linux
NetETW Microsoft-Windows-Kernel-Network events 12 (TCPv4 connect), 28 (TCPv6 connect)At parity
FileETW Microsoft-Windows-Kernel-File events 12/15/16Best-effort, read-only. No enforcement. The minifilter driver path that would enable deny-write semantics is deferred to CAP-4.8 and requires WHQL signing.

File observer gap (deferred to CAP-4.8)

CAP-2.1 ships ETW file observation only. ETW gives us visibilityread, write, and create events are recorded and emitted as CodingActionEvent with kind = KIND_FILE_READ / KIND_FILE_WRITE. ETW does not give us enforcement — there is no kernel-level path to deny a write before it reaches the disk.

The enforcement path requires a minifilter driver (Filter Manager callbacks at IRP_MJ_CREATE / IRP_MJ_WRITE). A minifilter is a kernel-mode component, so it needs:

  1. A code-signing certificate accepted by the Windows Hardware Certification.
  2. WHQL "attest signing" via the Microsoft Hardware Dev Center Dashboard (free for an EV cert holder; turnaround ~1 business day).
  3. A .cat file embedded in the driver package (signed by Microsoft).

The minifilter or equivalent supported chokepoint is tracked for CAP-4.8. The CAP-2.1 WindowsFileObserver emits read/write events with the same envelope shape as the Linux/macOS file observers, so detection-side rules function correctly — they observe and warn but do not deny. Operators who require deny-on-Windows during CAP-2.1 should pair the sensor with an existing EDR product or accept the gap.

Signing the user-mode service binary (Authenticode)

The user-mode binary pavri-endpointd.exe needs an Authenticode signature for SmartScreen reputation and to satisfy enterprise software allowlisting policies. The Win32 package should contain that signed binary; Intune manages package delivery and integrity.

Required: an EV (Extended Validation) Code Signing Certificate from a CA accepted by the Microsoft Trusted Root Program (DigiCert, Sectigo, GlobalSign, etc.). EV is required to seed SmartScreen reputation in a reasonable time window — non-EV certs accumulate reputation slowly.

Build-side signing pipeline:

signtool.exe sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 /a /v pavri-endpointd.exe

The signing pipeline runs in a separate, certificate-bearing CI job; public-PR CI jobs run unsigned (the cross-compile gate in .github/workflows/cap-2-1-windows.yml does not sign).

Privilege requirements

The sensor's Windows service must run as LocalSystem (or a service account with SeSystemProfilePrivilege and SeAuditPrivilege). ETW kernel providers reject session creation otherwise. The Intune Win32 installer configures this; operators running the binary interactively for development must open an elevated command prompt (Run as Administrator).


CAP-2.3: JetBrains plugin and CLI shims

CAP-2.3 ships two enrichment clients of the sensor:

  • JetBrains plugin — see jetbrains-plugin.md. Captures chat-vs-user edit attribution from JetBrains AI Assistant inside any IntelliJ Platform IDE.
  • CLI shims — see cli-shim-deployment.md. PATH-injected wrapper binary covers Claude Code, Gemini CLI, and Aider on macOS / Linux / Windows.

Both clients post to the sensor's local UDS / named pipe; neither talks to backend services directly.