Baseline drift for Linux fleets

Approve the baseline.
Keep every host honest.

driftkeep watches the files, daemons, ports, packages, local accounts, and runtime security posture that matter on a Linux host. It compares them to the baseline you explicitly accepted — and shows you only what changed.

One static binary No kernel module No rules DSL Pre-1.0
driftkeep.local / fleet live
Fleet overview
All hosts
Fleet policy rev 12 · 128 hosts
30s refresh
Hosts128126 online
Pending drift72 high
Security14updates
Exposure3critical
HostStateDriftSecurity
web-01AlmaLinux 10 · 192.168.30.62 online 2 5
web-02Debian 12 · 192.168.30.174 online clean 2
db-01Rocky 9 · 192.168.30.88 stale 1 clean
edge-07Ubuntu 24.04 · 10.0.4.17 online clean clean
package_verified /usr/bin/sshd matches rpm · no alert
policy applied rev 12 128 / 128 hosts current
Built for the hosts you already run
RHELRockyAlmaLinuxDebianUbuntu systemdSQLiteTLSREST API
The noise problem

Most tools tell you everything.
driftkeep tells you what changed.

Rules multiply. Upgrades look like attacks. Per-host scripts drift apart. The result is a queue nobody trusts — and the one change that matters gets lost in it.

Rules

Rule sets become their own project

A DSL, a tuning pass, an exception file, and a second exception file. Every new host is another chance for the policy to mean something different.

Upgrades

Package updates look like tampering

A binary hash changes on every normal upgrade. Without package context, the tool either pages you constantly or you turn the alert off.

Fleet

Every host is a one-off

Per-host scripts, ad-hoc baselines, and no shared view. You cannot tell whether a host is different because it should be, or because nobody looked.

The model

One idea. Four moves.

Everything in driftkeep is a diff against a baseline the operator explicitly accepted. Events add timing and attribution; a full reconcile scan is the source of truth.

  1. 01

    Learn

    Observe the real host. Auto-accept only what is demonstrably boring: package-owned processes, loopback listeners, ordinary config, stable across cycles.

  2. 02

    Review

    Risky items stay in the queue. Nothing in /tmp, nothing unowned, nothing critical, nothing high-severity gets accepted silently.

  3. 03

    Accept

    The baseline records the exact state, who accepted it, and how. Every item has provenance, not just a timestamp.

  4. 04

    Reconcile

    Every cycle compares the full host to the baseline. Restarts, missed inotify events, and overloaded queues cannot hide a change.

Policy applied

RHEL baseline

rev 12 · 128 hosts current

Files & config1,204
Packages862
Services94
Users18
Ports27
last reconcile 34s ago
Product tour

See the whole host. Not a pile of alerts.

Pick a surface. Every view is designed to answer one question: is this different from what we approved, and why does it matter?

Files & config

Know which config changed, when, and what moved it.

Security-relevant paths, content hashes, ownership, and package provenance. Exclude the noise at the policy level, not with a pile of host-local exceptions.

  • Content hash, mode, owner, and package owner
  • Policy-level excludes and fleet-wide exceptions
  • Path-level history and change reasons
web-01 · driftkeep diffbaseline rev 412
› driftkeep diff

hub policy RHEL baseline rev 4 · baseline rev 412 · 5 changes

HIGH  1 change · 1 area
FILE  /etc/ssh/sshd_config
      sha256 336857dd → 5c99f9fb · +2 lines
      reason: content changed outside package manager

MED   2 changes · 2 areas
PROC  /tmp/.x/payload added
PORT  tcp:0.0.0.0:4444 added

LOW   2 changes · 2 areas
PKG   openssl.x86_64 3.0.7-2 → 3.0.7-3
Processes & ports

Catch the instance, not just the binary.

driftkeep scores process identity, not just path. A known binary running from a new cgroup, a deleted executable, an unowned process, or a reverse-shell shape can all be different.

  • Onowned, drop-zone, deleted, and suspicious shapes
  • Listening ports with service labels and owner
  • Loopback → public exposure changes
/tmp/.x/payloadno package owner · deleted inode · spawned from web cgroup
critical
tcp:0.0.0.0:4444new listener · wildcard bind · no service label
high
loopback → publicservice moved from 127.0.0.1 to 0.0.0.0
med
/usr/sbin/nginxpackage-owned · known cgroup · unchanged
verified
Packages & integrity

An upgrade should not page you. Tampering should.

Package inventory catches additions, removals, and downgrades. Scheduled integrity checks ask rpm -V or dpkg --verify whether the files still match the package.

  • Package-aware binary hash changes
  • Critical-package removal and downgrade signals
  • Low-priority verification windows with resume
Package changesrev 412
+
nginx-1.26.2added · web repo
low
↑
openssl.x86_643.0.7-2 → 3.0.7-3 · security repo
low
↓
sudo.x86_641.9.15 → 1.9.13 · version moved backwards
high
package_verifiedhash change explained by rpm · no alert
quiet
Security & exposure

Patch visibility and vulnerability context, without another agent.

Read cached repository metadata for available security updates. Optionally query OSV, the Debian tracker, or Ubuntu OVAL for known vulnerabilities with fix and no-fix status.

  • Security updates by severity and advisory
  • Evidence-backed vulnerability findings
  • Per-host risk acceptance with audit trail
critical3 packages
kernel

5.14.0-427.el9 → 5.14.0-427.28.1.el9_4

RHSA-2026:1234 · CVE-2026-1111
high2 packages
openssl

3.0.7-2 → 3.0.7-3

ALSA-2026:5678 · CVE-2026-2222
no fix1 finding
qemu

component not in use

accepted risk · reason recorded · audit entry
Fleet & policy

One policy, many hosts, no mystery drift.

Assign policies by tag. Roll out revisions. See which hosts applied them, which are stale, and which have an error. The dashboard talks to the hub API; it never needs the admin token.

  • Manual pins, dynamic selectors, default policy
  • Revision history, diff, and rollback
  • Role-based dashboard, audit, and TLS bundles
AssignmentRHEL baseline rev 12
105 current15 pending8 error
web-01manualcurrent
db-01selectorpending
edge-07defaultcurrent
The fleet view

Designed for the operator who has to explain it later.

Every alert carries the item, the signal, the score, the policy, the first-seen time, and the reason. Accept, ignore, or investigate from one place — then keep the audit trail.

Why, not just whatPolicy context, provenance, and signal weights on every change.
Review without guessworkApprove one item, a host, or a fleet-wide decision with a bounded severity.
History that staysEvents, commands, accepted risks, and audit entries are queryable.
highfile · /etc/ssh/sshd_config2m ago

sshd_config content changed

Hash changed outside a package transaction. 2 lines added. The package owner still matches.

content_changedpackage_verifiedpolicy: RHEL baseline
medprocess · /tmp/.x/payloadjust now

New process with no package owner

Deleted inode, spawned from the web service cgroup, listening on a public port.

Built like infrastructure

Small surface. Serious coverage.

One binary, one state directory, one SQLite hub. No kernel module, no runtime dependency, no Java, no agent framework.

Baseline-first

Everything is a diff against the state you approved. Events add timing; the reconcile scan is authoritative.

Learning mode

Auto-accepts only demonstrably boring items. Never drop-zone executables, fileless processes, or critical paths.

Files & config

/etc, SSH keys, cron, systemd units, setuid sweeps, and drop-zone executables — hashed where it matters.

Processes & ports

Instance identity, cgroups, package owner, reverse-shell shapes, and loopback-to-public changes.

Packages & integrity

Inventory, version changes, critical-package removal, and scheduled rpm/dpkg verification.

Security & exposure

Native security updates and optional vulnerability lookups with fix/no-fix context and accepted risks.

Fleet hub

Policies, selectors, revisions, rollout status, TLS bundles, host commands, and audit.

Alerts that wait

Settle windows, suppression, batching, and delivery retries. No alert for a process that was already gone.

Automation ready

JSON output, stable exit codes, a documented REST API, and an OpenAPI contract.

Get started

From zero to a baseline in one command.

Install the static binary, run the learning window, and review what driftkeep found. The agent is a single file; the hub is optional.

One static binary No host dependencies Self-hosted hub
Read the docs
install
# RHEL / Rocky / Alma
sudo dnf install ./driftkeep-*.rpm

# Debian / Ubuntu
sudo apt install ./driftkeep_*.deb

# write a documented config and start learning
sudo driftkeep init
sudo driftkeep agent --learning 24h
sudo systemctl enable --now driftkeep
Questions

The honest answers.

driftkeep is pre-1.0. The model and the core are ready; the rough edges are being filed down in public.

Is this an EDR?

No. driftkeep is a visibility tool. It does not block execution and cannot defend against a kernel rootkit. The threat model is explicit about that.

Does it need a kernel module or an agent framework?

No. It is one static binary with no runtime dependencies. Events are optional; the reconcile scan is the source of truth.

What does it watch?

Security-relevant files and config, local users, processes and listening ports, systemd units, installed packages, package file integrity, native security updates, and optional vulnerability feeds.

Does it send data anywhere?

Only if you enable the optional exposure lookup. The default providers are local on Debian and RPM-family hosts. The hub is self-hosted.

Why is the baseline model better than rules?

Rules describe what you think the host is. A baseline records what you approved. New hosts start from a real observation, and changes are reviewed against that record instead of a growing pile of exceptions.

What is the license?

AGPL-3.0. The source, docs, and threat model are public.

Stop reviewing noise

Start reviewing drift.

Approve once. Reconcile everywhere. Know exactly what changed, and why it matters.