Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/heimdall

docs/v2/node-floors.md

Node floors made by tools, one per network variant

Status: in progress, 2026-11-08. The numbers below are measured; what is not done yet is marked as upcoming.

What a node floor is

A Kubernetes node image (here: tunnr, Rusternetes on its own minimal OS) puts one seccomp filter on its PID 1 at boot. Every process on the node inherits it and can never remove it: the node's own programs, the network plugins, and every pod, privileged pods included. So the floor is the outer wall for the whole node. Each pod also gets its own, much tighter profile on top (64 to 96 calls for the bench pods); the floor is the ceiling above all of them.

Until now the floor was a hand-written list of 361 system calls, close to everything Linux has. It blocked nothing the node did, and protected little.

How the floor is made now: only from tools

  1. The profiler measures. The node runs in log mode under Heimdall's profiler (heimdall profile probe-log): every system call of every process, put on the process that made it, with no record lost. Runs cover boot, join, pods, network, network policy, DNS, drain, reboot, certificate rotation, log rotation, link failures and long soaks.
  2. The graphs check. Mímir's executable graphs of the node's own programs (Rust, with C start-up and thread start modelled) say which calls each program can reach at all, including paths no run took.
  3. The generator writes the floor. cargo xtask floor gen joins the profiler's evidence, the graphs, the kernel-implied base set and the pods' measured profiles. The policy is data (strict, or general with a listed margin of common workload calls). The evidence control refuses a floor the generator did not write ([hand-made-floor]) and records the sha256 of every input in the receipt.
  4. The enforce run proves it. The generated floor goes on, the whole suite runs, and no kill is allowed. A kill means missing evidence: more profiling, never a hand edit.

One floor per network variant

The node image is built in network variants, and Build Thing picks the variant when it builds the image. So each variant gets its own floor, generated from its own evidence and graphs, instead of one floor that must cover all of them:

The matrix: static filter and graph filter, per variant

Variant Profiled Calls seen Generated floor (strict) Enforce run, static filter Graph filter
bridge Yes: 7 runs + situations + a 4-hour soak (96/96 pods, 0 records lost) 174 197 Upcoming Upcoming: waits for the in-kernel follower
aya Yes: 1 run 137 182 Upcoming Upcoming: waits for the in-kernel follower
OVN Upcoming — 180, provisional: no OVN run yet, so not usable before it Upcoming Upcoming: waits for the in-kernel follower

Results so far