Configuration

A config is a JSON document passed with --cfg <file>. It has two top-level objects: norm (the policy to validate against) and an optional collect (which domains to gather at runtime). A baseline file (see Baseline capture) is just a generated norm, so it can be fed straight back via --cfg.

{
  "collect": { "processes": true, "ports": true, "packages": true,
               "netif": true, "trace": true, "trace_window_ms": 1000,
               "trace_args": false },
  "norm": { "...": "..." }
}

The collect section

Selects domains without command-line flags (OR’d with the CLI flags), so a deployed config can drive a device. Keys: processes, ports, packages, netif, trace (plus trace_window_ms and trace_args). Mounts are always collected; fingerprints need --fingerprint <dir>.

The norm section

Each block is optional; only configured blocks are enforced. An other_* flag, where present, controls whether items not listed in the norm are allowed.

Note

With no policy configured for a domain (e.g. no processes or ports block, or no --cfg at all) that domain’s check is inventory-only: the items are collected and reported but nothing is flagged. The allow-list semantics (other_*_present: false ⇒ unlisted is a violation) apply only once you actually provide a list — typically a generated baseline. The policy-free threat heuristics (integrity, IOC, persistence, accounts, resolvers) always run regardless, so an unconfigured run still surfaces real compromise, just without inventory noise.

Processes

"other_processes_present": false,
"processes": [ { "name": "sshd", "cpu_min": 0, "cpu_max": 100 } ]

A process is matched by name; cpu_min/cpu_max bound its CPU load. With other_processes_present: false any unlisted process is a violation — but only when the processes list is non-empty; an absent/empty list leaves the process check inventory-only (see the note above).

Ports

"other_ports_present": false,
"ports": [
  { "port": 22, "protocol": "tcp", "state": "LISTEN", "process": "sshd" },
  { "port_min": 49152, "port_max": 65535 }
]

protocol (tcp/udp/tcp6/udp6) and state (LISTEN/ESTABLISHED/…) are optional filters; process pins the owner; a range uses port_min / port_max.

Files

Expected fingerprints (typically generated via --baseline --fingerprint):

"other_files_present": false,
"files": [
  { "path": "/bin/su", "csum": "...", "mode": "4755", "uid": 0, "gid": 0 },
  { "path": "/bin/ping", "csum": "...", "caps": "0100000200000000" }
]

mode (octal, incl. setuid/setgid/sticky), uid/gid and caps (Linux file capabilities) are optional and enforced only when present. Paths are image-relative (any --root prefix is stripped). See Baseline capture.

Packages

"other_packages_present": false,
"packages": [ { "name": "openssl", "version": "1.1.1n" } ]

SBOM drift; version is optional (any version allowed if omitted). CVE matching is separate — see Packages & vulnerabilities.

Network

"other_netifs_present": true,
"netifs": [ { "name": "eth0", "promisc": false, "mac": "...", "mtu": 1500 } ],
"other_routes_present": false,
"routes": [ { "dest": "0.0.0.0", "mask": "0.0.0.0", "gateway": "192.168.1.1" } ],
"other_neighbors_present": true,
"neighbors": [ { "ip": "192.168.1.1", "mac": "aa:bb:cc:dd:ee:ff" } ]

See Network for the detection semantics (PROMISC, gateway redirect, ARP spoofing) and recommended flag settings.

Filesystems / mounts

"file_systems": {
  "restrictions": { "file_mounts": true, "bind_mounts": { "files": true, "dirs": false } },
  "paths": { "allow": ["/", "/proc", "/tmp"], "deny": ["/etc/shadow"] },
  "verify_filesystems": [
    { "fstype": "ext4", "root": "/" },
    { "fstype": "tmpfs", "root": "/tmp", "options": { "require": ["nosuid","nodev"], "forbid": ["exec"] } }
  ]
}

Restrict file/bind mounts, allow/deny mount targets, and verify filesystem types + mount-option flags against verify_filesystems rules. Offline, mount data comes from the image’s /etc/fstab (see Offline image analysis).

Kernel memory (SLAB/SLUB)

"memory": {
  "min_available_pct": 10,
  "max_slab_pct": 40,
  "max_unreclaim_pct": 25,
  "min_swap_free_pct": 20,
  "max_cache_mb": 512,
  "require_slabinfo_restricted": true,
  "require_freelist_hardened": true,
  "require_freelist_random": true
}

Thresholds are checked against /proc/meminfo and /proc/slabinfo; a percentage knob left out (or set to 0) is not enforced, so a device can pin only what matters for it. max_cache_mb bounds any single slab cache, which is the shape a kernel object leak takes long before total memory exhaustion. The three require_* flags check hardening rather than volume: that /proc/slabinfo is root-only, and that the running kernel was built with CONFIG_SLAB_FREELIST_HARDENED / CONFIG_SLAB_FREELIST_RANDOM (read from /proc/config, /boot/config-<release> or /lib/modules/<release>/…; when no config is readable the option is reported as unknown and never flagged).

Without this block the domain is inventory-only, with one exception: the meminfo-vs-slabinfo cross-check always runs. See Threat detection.

Syscalls (behavior)

"syscalls": {
  "forbidden": ["init_module", "finit_module", "kexec_load", "bpf"],
  "forbidden_args": ["path=/etc/shadow", "prot=rwx"],
  "deny_classes": ["module", "kexec", "ptrace"],
  "max_rate": 200000,
  "max_process_rate": 50000
}

Checked against the --trace sample. forbidden names syscalls no process may issue; deny_classes names whole behavior classes (exec, ptrace, module, bpf, kexec, mount, namespace, privilege, network, memexec, keyring, fsperm), which keeps a policy meaningful as the kernel grows new syscalls. An unknown class name is ignored, so a newer policy still loads on an older agent. forbidden_args matches against the decoded argument text (--trace-args) as a substring, which lets one rule pin a path, an address range or a flag combination. The two rate ceilings are syscalls per second, host-wide and per process; a knob left out is not enforced, and neither is evaluated over an observation window shorter than 200 ms. Without this block the domain is inventory-only. See Syscall behavior tracing.