Skip to content

Evidence

The Evidence page is the forensic record of everything Churchill observed and decided across your fleet: every execution allowed or denied, every integrity check, every kill and lockdown. Each event links to the one before it in a hash chain, so records cannot be altered or removed without detection. Each record is kept in two places, appended on the host and mirrored to the control plane, so neither side can quietly rewrite history. See the glossary.

This page shows the fleet-wide log. For a single host’s events, the host’s detail page carries a host-scoped evidence tab. See Host detail.

The page loads a bounded window of events, set by the range chip and the server-side filters. The load walks forward to the tail of that range and keeps the newest 5,000 rows. The loaded rows therefore always reach the present. When something must be dropped, it is the oldest part of the range, never the newest.

Two warnings can appear under the header after a load:

  • “window trimmed to the newest {n} events — the oldest part of this range is not loaded”. The range matched more rows than the page retains. Narrow the range, raise the minimum severity, or filter by host to reach further back.
  • “walk stopped at the page guard before reaching the end of this range — the newest events may be missing; raise the minimum severity or filter by host”. The walk hit its safety limit before reaching the tail. Tighten the filters as the message advises.

A pill at the top right reports the chain’s linkage for whatever is loaded. It shows one of four messages:

  • chain — {x} of {y} links verified · {n} events: every adjacent pair of loaded events that could be compared linked correctly. Because the filters make the loaded sequence numbers sparse, only consecutive-seq pairs are comparable. The pill reports how many pairs it checked instead of claiming coverage it does not have.
  • chain link broken in loaded window: two consecutive events do not link. This deserves immediate attention. Open the events around the break and compare their recorded hashes.
  • chain unverified — no adjacent events in view: events are loaded, but no two of them are adjacent in sequence, so no link could be checked.
  • chain — no events loaded: the current window contains no events.

“Verified” here means each loaded event’s previous-hash field equals its predecessor’s recorded hash. This is a consistency check over loaded events, not a cryptographic re-verification of the whole chain.

The page opens on the last 24 hours at minimum severity critical. Chips in the header switch the window between 1h, 24h, 7d, and 30d. Changing the range reloads the log. Two counters next to the chips show Loaded (events loaded for the window) and Critical+ loaded (loaded events at critical or fatal severity).

While the indicator reads live tail (green dot), the page polls about every 5 seconds for events newer than the newest loaded row, under the same filters, and appends them. The poll looks back a short wall-clock margin as well, so fresh rows from a clock-skewed host are not lost. Select Freeze to stop polling (the indicator changes to frozen), and Resume to restart it. If a poll fails, the header shows “live tail interrupted” with a short reason. Polling keeps retrying, and the notice clears on the next successful check.

The filters split into two kinds. Knowing which is which tells you whether changing one reloads the window:

  • Applied by the control plane (changing one reloads the window): the minimum severity, an event type picked by a saved view, and the host scope. Because these narrow the query itself, the 5,000-row retention applies to matching rows only.
  • Applied in the browser (instant, over the loaded window): the category chips, the hash-mismatch toggle, and the free text.

The controls:

  • min severity: chips for all, fatal, critical, warning, info. This is a ranked floor, not an exact match: critical loads critical and fatal. Defaults to critical.
  • category: chips for all sources, Host sentinel (execution decisions, process activity, and kernel-layer denials observed from the host), In-process sentinel (integrity checks reported from inside the protected application), and Lifecycle (Churchill’s own actions and state changes: kill sequences, lockdown entry and exit, compromise reports). The category names the producer, not the severity: kernel denials are filed under the host sentinel because that is what drained them.
  • hash mismatch: a toggle that keeps only events whose hash verification recorded a mismatch.
  • host scope: when you arrive through a host link, a removable chip pins the query to that host, named where the fleet has a name for it. Clearing the chip widens the window again.
  • Free text: matches against the reason, the process path, the host ID, the host’s name, and the uid.

A rail on the left offers seven built-in presets. Selecting one sets the range and filters in one move and clears the search box:

  • Active criticals · 24h: critical events across all sources, last 24 hours. This is the default view.
  • Bundle stranger execs: stranger-binary executions, last 30 days.
  • Drift on prod databases: in-process spatial integrity events, last 30 days.
  • Allowed sshd exec: execution events observed by the host sentinel, last 30 days.
  • Compromise reports: compromise reports, last 30 days.
  • LSM denials: kernel-layer denial counts, last 30 days. These are the wall holding, not a compromise, which is why they carry warning severity rather than critical.
  • LSM probes (identity): one record per denied probe, carrying the identity of the process that was refused, last 30 days. Use this when the denial counts tell you something is being blocked and you need to know what.

These are presets, not saved state. There is no way to save your current filter combination as a new view.

Events appear newest first. Selecting a row opens the event detail modal. The columns are:

Column Meaning
SEQ The event’s sequence number in the chain. Higher means newer.
TIME The time of day the event occurred. Hover to see the full date and time.
HOST The reporting host’s name, falling back to its host ID when the fleet has no name for it. Hover it to read the host ID either way.
EVENT The event type.
SEV Severity: FATAL, CRIT, WARN, or INFO.
DEC The enforcement decision. deny and kill show in red, allow in green.
PROCESS The name of the binary involved. Hover to see its full path.
REASON A one-line explanation of why the event was recorded.
HASH The hash verification outcome: a green match or red diff pill, or n/a when the event carries no hash verification.

When no loaded events match your filters, the table shows a single “No events matching filters” row.

The detail modal breaks one event into five sections:

  • Summary: timestamp, host, category, decision, and reason.
  • Process tree: the pid, ppid, and uid, plus the full binary path.
  • Verification: the expected and actual hashes and whether they matched. Shown only when the event carries hash verification.
  • Chain link: the event’s prev_hash and this_hash, with a pill reading linked, broken, or not verifiable. “Not verifiable” means the preceding event is not in the loaded window. Widen the range if you need to check it.
  • Related events: the events within five sequence numbers on either side, so you can see what happened around this one.

At the bottom of the modal:

  • Copy event ID: copies the sequence number.
  • Copy hashes: copies the event’s prev_hash and this_hash.
  • Export this event: downloads the single event as NDJSON.
  • Replay session: appears only when a sealed session recording is anchored to this event. It opens the recording with the player loaded and ready. See Recordings.

Two kinds of links land here from elsewhere in the console, and each adjusts the window so its target can actually load:

  • A link to a specific event (/evidence?hostId=…&evidenceSeq=…), for example from a recording, opens on the 30d range with severity all, then opens that event’s detail. If the event still is not in the loaded window, a notice says so and repeats the advice: widen the range, lower the minimum severity, or, under trimming, tighten the filters to reach further back. The notice offers a dismiss chip.
  • A host link (/evidence?hostId=…), for example from a governance decision, scopes the query to that host, sets severity to all, and seeds the free-text box with the host ID.

Export filtered in the header downloads exactly the rows currently matching your filters as NDJSON: one JSON object per line, containing each event’s full fields including both chain hashes. The button is disabled when nothing matches. For a single event, use Export this event in the detail modal instead.

While loading, the table area shows “Loading evidence…”. If the console cannot read the log at all, the table is replaced with “Evidence log unavailable” and a short reason, such as “control plane unavailable”. As long as the tail is live, the page keeps retrying in the background and recovers on its own once a read succeeds.