CLI and JSON, not a REST API
Agent operations use the local CLI and JSONL/CSV exports. The licensed 0.4.1 release adds an HTTPS licensing API for activation and renewal only; there is no public HTTP API for incidents, remote shell execution, or a monitoring dashboard.
Operational commands require a valid signed local lease. Licensing commands, config-show, blocks, and emergency unblocking remain available when protection is inactive.
bhim-protection --help
bhim-protection --config /etc/bhim-protection/config.toml health --helpLicensing API endpoints
The compiled agent signs activation and renewal requests using its private device key. Use the shipped license CLI rather than embedding private device keys into a browser.
License command reference
License commands run before configuration loading. Activation and renewal require root; check and status can validate the locally readable signed lease. A missing or expired license returns nonzero.
sudo bhimkvh license activate
sudo bhimkvh license status
sudo bhimkvh license check
sudo bhimkvh license renew
sudo bhimkvh license fingerprintComplete command reference
| Command | Arguments / options | Purpose |
|---|---|---|
| --help / --version | Global flags | Show usage/version without daemon execution. |
| run | No command-specific flags | Start the Linux daemon; normally systemd-managed. |
| response-serve | No command-specific flags | Privileged local helper; managed by systemd. |
| health | --strict | JSON health; strict fails on degraded checks. |
| metrics | None | JSONL name/value counters plus spool depth. |
| incidents | --limit N (default 100) | JSONL incident summaries, newest last_seen first. |
| timeline | INCIDENT_ID | Event JSONL for the incident, chronological. |
| export | --format json|csv; --limit N (default 100) | Newest events first; JSON output is newline-delimited. |
| config-show | None | Parse/validate then print the TOML file. |
| config-set | KEY VALUE | Change an allowlisted scalar, validate, write atomically. |
| baseline | --replace | Capture trusted integrity hashes; replace explicitly resets existing entries. |
| verify | None | Print integrity findings as event JSONL. |
| blocks | None | List recorded block state. |
| block | IP --scope ssh|http --reason TEXT | Request a scoped block; scope defaults ssh, reason defaults operator. |
| unblock | IP --scope ssh|http | Remove a reviewed scope; scope defaults ssh. |
| snapshot | ABSOLUTE_PATH | Capture an explicit recovery snapshot. |
| snapshot-show | ID | Print snapshot manifest JSON. |
| restore | ID --expected-current-sha256 HASH|absent | Restore only against reviewed current state. |
| quarantine | ABSOLUTE_PATH --expected-sha256 HASH | Isolate a reviewed non-critical file. |
| graph | PROCESS_KEY | Query observed process provenance. |
| evidence-verify | None | Verify the local retained-payload evidence chain. |
| update | --version VERSION; --yes | Disabled; use the signed binary installer. |
Health output and exit status
Successful health checks print one JSON object. A degraded report may still exit successfully without --strict. With --strict, degraded checks return a nonzero status after the JSON report. Other parse, permission, configuration, or storage failures can also return nonzero; do not assume every error includes health JSON.
sudo -u bhim bhim-protection health --strictExample health response
Illustrative healthy output; the heartbeat timestamp changes on your host.
{
"status": "healthy",
"database": "ok",
"reasons": [],
"heartbeat": "2026-11-14T10:30:00+00:00"
}JSONL and CSV exports
The json export format produces one event object per line, not a JSON array. Use a JSONL consumer or jq -s to gather an array. CSV includes event_id, incident_id, timestamp, event_type, severity, source, and description; it omits nested evidence.
sudo -u bhim bhim-protection export --format json --limit 100 > events.jsonl
sudo -u bhim bhim-protection export --format csv --limit 100 > events.csv
# jq is an optional integration dependency, not part of BHIM.
jq -s . events.jsonl > events.json
jq -c 'select(.severity == "high" or .severity == "critical")' events.jsonlEvent schema
| Field | Type | Meaning |
|---|---|---|
| event_id | string | evt_ followed by UUID for generated events. |
| incident_id | string | null | Correlation ID when assigned. |
| timestamp | RFC3339 UTC string | Event timestamp. |
| event_type | string | Detector or operation-specific event name. |
| severity | string | For example info, medium, high, critical. |
| source | string | Collector or operator source. |
| confidence | string | Observed evidence classification. |
| description | string | Human-readable finding. |
| evidence | JSON value | Event-specific fields; not a fixed universal object schema. |
Example event
This sample illustrates the event shape. A recorded observation is not proof that the system was compromised.
{
"event_id": "evt_example",
"incident_id": null,
"timestamp": "2026-11-14T10:30:00Z",
"event_type": "network_new_listener",
"severity": "medium",
"source": "proc_net_poll",
"confidence": "observed",
"description": "New socket observed",
"evidence": {
"state_hex": "0A",
"limitation": "Polling may miss transient sockets"
}
}Metrics, incidents, and timelines
Metrics output is JSONL with name/value fields. It includes spool_pending_batches and spool_pending_bytes alongside stored counters. Incidents are JSONL with incident_id, opened_at, last_seen, event_count, and correlation_key. Use a returned incident_id with timeline.
sudo -u bhim bhim-protection metrics
sudo -u bhim bhim-protection incidents --limit 20
sudo -u bhim bhim-protection timeline INCIDENT_ID
sudo -u bhim bhim-protection graph BOOT_ID:PID:START_NSExample local automation
This Python example consumes a local JSONL export without depending on a network API. Run it as a user authorised to read the BHIM state and configuration.
import json
import subprocess
result = subprocess.run(
["bhim-protection", "export", "--format", "json", "--limit", "100"],
check=True, capture_output=True, text=True, timeout=30,
)
for line in result.stdout.splitlines():
if line.strip():
event = json.loads(line)
print(event["event_type"], event["severity"])Local helper authentication
The split executor listens on a Unix-domain socket, validates Linux peer UID/GID, and reads root-owned protected configuration. It accepts constrained firewall operations and digests for configured critical files. It has no general shell-command or file-content endpoint. Use the supported CLI rather than depending on an internal wire protocol.
Updater behaviour
The unsigned legacy updater is disabled in licensed 0.4.1. Use the Ed25519-verified installer to obtain an updated binary-only archive.