chainsaw doctor
Diagnose local package-manager wiring and server-install health
Diagnose local package-manager wiring and server-install health
| Subcommand | What it does |
|---|---|
chainsaw doctor verify-hook | Drive a synthetic install through a wired manager and confirm the proxy saw it |
chainsaw doctor [flags]
Enumerate every supported package manager and report whether its binary is on PATH and whether the chainsaw-managed block is present in its user config file.
With –strict, also check project-scope config overrides, registry- pointing env vars (NPM_CONFIG_REGISTRY, PIP_INDEX_URL, GOPROXY, …), lockfiles for hardcoded public-registry URLs, and direct-egress reachability to public registries. Exits non-zero when any of those drift signals fire, so CI can wire –strict as a preflight gate. Exit codes: 0 compliant · 1 egress probe inconclusive (network could not be confirmed blocked; –no-egress-probe reports “skipped” and never trips this) · 10 drift · 30 direct egress to a public registry reachable · 40 installed package manager without an enforcer. The –upgrade-check ladder below is different.
With –attest, additionally POST the strict report to the configured Chainsaw server at /api/attestations so the org compliance dashboard sees this endpoint. –bundle-id=<id> implies –attest and reports the server’s answer for that hardening bundle: whether it is recorded as applied on this machine, and attestation_seen_at. The exit code stays the strict compliance code; a failed or unmatched attestation is reported, not turned into an exit code.
With –upgrade-check, diagnose the local chainsaw-proxy server install before upgrading. Scope: self-hosted chainsaw-proxy server preflight; not needed for CLI-only installs (see MIGRATIONS.md). It checks env vars, config YAML parse, data-dir perms, port availability, upstream-registry reachability, TLS cert validity, docker-compose version drift, and — critically — any removed flags (e.g. –embedded-ui) or deprecated env defaults (e.g. CHAINSAW_STRICT_JWT) that would brick a systemd unit on boot. Exit 0 = safe to upgrade, 1 = warnings worth acknowledging, 2 = breaking changes present. See MIGRATIONS.md for the manual upgrade path when breaking changes land.
With –fix, apply auto-fixable remediations surfaced by –upgrade-check (today: chmod 0400 on stale generated_password / generated_jwt_secret files). Breaking findings are never auto-fixed — operator must acknowledge.
Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--attest | bool | — | POST the strict report to /api/attestations on the configured server. Implies –strict. |
--bundle-id | string | — | W11 phone-home channel: report that this hardening bundle is applied on this machine. Implies –attest (and so –strict): the attest POST body includes bundle_id, the proxy stamps applied_at on the matching hardening_bundles row, and doctor prints the server’s answer (applied / attestation_seen_at). MDM-rendered install scripts pre-fill this from the bundle emitted by the admin hardening wizard at /admin/hardening (POST /api/hardening/bundle). |
--bypass-check | bool | — | Compare host package-manager config files (.npmrc, pip.conf, ~/.gemrc, cargo config) against the configured chainsaw URL. Reports drift; exits 0 even when a config is missing. |
--config | string | — | Path to chainsaw-proxy YAML config (for –upgrade-check). Defaults to $CHAINSAW_CONFIG. |
--data-dir | string | — | Path to chainsaw data directory (for –upgrade-check). Defaults to $CHAINSAW_DATA_DIR or /etc/chainsaw/data. |
--device-id | string | — | Override the derived device identifier (default: hostname/USER). MDM provisioning scripts use this to assign stable device IDs. |
--docker-compose-path | string | — | Path to docker-compose.yml for version-drift check (for –upgrade-check). Empty disables the check. |
--fix | bool | — | Apply auto-fixable remediations from –upgrade-check (e.g. chmod 0400 on generated_* files, generate JWT secret). Breaking findings are never auto-fixed. |
--no-egress-probe | bool | — | With –strict: skip the direct-egress reachability probe and report it as ‘skipped’ (a distinct sentinel from ‘unknown’ that never soft-fails). For air-gapped CI where outbound is known-blocked, so the run doesn’t eat the ~9s probe timeout. |
--offline | bool | — | Air-gap diagnostics (W4): walk every intelligence condition and report whether it runs offline (✓), is degraded (⚠), or requires a refreshed bundle (✗). The matrix prints an ASCII alphabet instead on consoles that cannot render these; its legend names whichever set it used. Reads CHAINSAW_INTEL_BUNDLE_PATH and CHAINSAW_OFFLINE_FAIL_MODE. |
--skip-network | bool | — | Skip upstream-registry reachability probes (for –upgrade-check). Use in air-gapped environments. |
--strict | bool | — | Fail (non-zero exit) on any drift: project configs, env overrides, lockfile hits, direct egress reachable. |
--upgrade-check | bool | — | Run server-upgrade-safety diagnostics: compare running schema, flag deprecated flags, check data-dir/TLS/ports. Exit 0=safe, 1=warn, 2=breaking. See MIGRATIONS.md. |
The global flags apply here too.
chainsaw doctor verify-hook
Drive a synthetic install through a wired manager and confirm the proxy saw it
chainsaw doctor verify-hook <manager> [flags]
Catch client-side bypasses (OBSERVABILITY_AUDIT gap 2).
install-hook writes the manager’s config block, but never proves the wire actually works. Multiple Wave AG bugs were INVISIBLE to chainsaw because the client tool (bun, docker, swift) decided the proxy was broken or the refspec invalid and fell back to upstream directly — bypassing the proxy entirely.
verify-hook drives a synthetic install through the configured manager using a sentinel package coordinate (chainsaw-verify-<hex>-<ts>), waits for the request to land in the audit log, and reports PASS / FAIL / DEGRADED:
PASS proxy saw the sentinel — the hook is wired and routing. FAIL proxy did NOT see the sentinel — the client tool bypassed chainsaw. Output includes a manager-specific remediation hint. DEGRADED could not reach /api/events to confirm receipt. Verification ran but cannot prove the result. A one-liner is printed so you can grep the proxy logs yourself.
The synthetic install is expected to fail upstream (the sentinel is not a real package). What we’re verifying is that the proxy received the attempt — not that an install succeeded.
v1 managers: npm, bun, docker, pip, cargo. Deferred: swift, yarn, maven, gradle, sbt, nuget, go.
Examples: chainsaw doctor verify-hook bun chainsaw doctor verify-hook docker –timeout 60s chainsaw –server https://chain305.com/chainproxy doctor verify-hook npm –json
Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--timeout | duration | 30s | Maximum time to wait for the sentinel to appear in the audit log. |
--verbose | bool | — | Print the executed manager command and its raw output. |
The global flags apply here too.