Experiment formal installation acceptance run 01 - kmosoti/FabricO11y GitHub Wiki
Historical report imported 2026-10-10 from
docs/experiments/formal/installation-acceptance-run-01.md. Original SHA-256:a2c8c871a0080b8cc77a5eb4d1292ba29718a9bb1b4ec5cec07fd213921bac56. Measurements and outcomes retain their stated revisions. Links and local environment labels are normalized;$FABRIC_DATAis the mounted experiment drive. See evidence policy.
Status: the registered installation acceptance ran on 2026-09-28. The result is Inconclusive: 16 of the 17 checks ran and passed, and each of the three negative-control mutations failed its named checks. One check did not run: A8h, MemoryHigh enforcement, because the host has only a legacy cgroup hierarchy.
This is the first running installation of the package. It is not a pass: the contract assumes that systemd owns the unified hierarchy, and that was not available.
-
Host. A Firecracker VM with 4 CPUs and 15 GiB RAM, running kernel
6.18.44-fc-v37and Docker (cgroupfs driver). The host has no KVM, so a nested VM was not possible. -
Cgroups.
/sys/fs/cgroupis a tmpfs of v1 hierarchies. The platform that runs this VM keeps its own processes in the v1memoryandpidshierarchies (process_api,sbx-telemetry). Switching the VM to the unified hierarchy would have meant dismantling those controls, so it was not done. -
Container. The container runs Debian 13.7 with systemd 257 (257.13-1~deb13u1) as PID 1, booted in forced legacy mode.
systemctl is-system-runningreporteddegraded: onlysystemd-modules-load.servicefailed, because a container cannot load kernel modules.
The package was built with RUSTUP_TOOLCHAIN=1.98.0 packaging/build-deb.sh target/alpha-install-deb at the registration commit, bbd2dd5. The pinned rustc 1.98.0 was used, and SOURCE_DATE_EPOCH 1790623428 is that commit's time. The package's SHA-256 is 59bfd745a9f24e4ef9033e1de22c1b68c0a365248e6d48997d9866597ab8344b. The four invocations below ran from 19:24:12 to 19:28:38 UTC:
tools/qualification/install/run.sh target/alpha-install-deb/fabrico11y_0.1.0~alpha.1_amd64.deb <OUT>/clean
tools/qualification/install/run.sh <same package> <OUT>/<mutation> --mutate <mutation> # root-user, no-collision-check, no-memory-maxThe output of each invocation, and the unmutated run's service journal, are in data/linux-installation (local evidence omitted from stage).
Unmutated package: exit 3. No check failed, and one could not run.
| Check | Result | Evidence |
|---|---|---|
| A1 sysusers | passed | The dry run wrote nothing. Two applies to a temporary root gave fabricolly:x:999:999:Fabric O11y service:/:/usr/sbin/nologin
|
| A2a human-account collision | passed | With UID 1500 present, dpkg -i refused and unpacked nothing |
| A2b non-system-group collision | passed | With GID 1600 present, dpkg -i refused and unpacked nothing |
| A3 repeated install | passed | Two installs gave the same account, fabricolly:x:996:996:…:/usr/sbin/nologin
|
A3b /etc/fabrico11y
|
passed | 750 root:fabricolly |
A4 systemd-analyze verify
|
passed | exit 0 |
| A5 TLS | passed | An untrusted client got exit 60; a CA-verified client got 200 |
| A6 identity | passed | Both services ran with UID/GID 996, CapEff 0 and NoNewPrivs 1 |
| A7 managed directories | passed | All four directories were 700 fabricolly:fabricolly
|
| A8 placement and limits | passed | Both units sat in /system.slice/system-fabrico11y.slice/<unit>. Effective limits were 128M/256M/128 (node) and 2560M/3072M/512 (server). The kernel limits memory.limit_in_bytes and pids.max were equal to them; the slice's were 3328M and 640. Accounting values were set |
A8h MemoryHigh |
not run | The legacy hierarchy has no memory.high; systemd reports the value only |
| A9 metrics and the authorized log | passed |
system.memory.available points arrived, and so did the ACL-authorized line, both over TLS |
| A10 denied log | passed | The gap read log source unavailable /var/log/fabric-accept/denied.log: Permission denied (os error 13); the log's content was never delivered |
| A11 restart with a retained Spool | passed | The server stopped with Result=success. The node restarted with Result=success, holding 5 Spool files. The outage line arrived after the restart, and the earlier line was kept |
| A12 pressure containment | passed | The hog under the service limits peaked at 268,435,456 bytes and was SIGKILLed. The hog under the slice limit peaked at 3,477,590,016 bytes and was SIGKILLed. The task tree peaked at TasksCurrent=128 with fork failures logged. Both services stayed active with NRestarts unchanged, and delivery continued |
| A13a remove | passed | The binaries were gone and the services inactive. State, configuration and the account were kept |
| A13b purge | passed |
/var/lib/fabrico11y and /etc/fabrico11y were removed |
Negative controls: every mutation exited 1 and failed at least its named check.
| Mutation | Checks failed | Named check |
|---|---|---|
root-user |
A6 (node UID 0), A7 (node directories owned by root), A10 (no denied-log gap appeared, as expected when root can read the file) | A6 ✓ |
no-collision-check |
A2a and A2b (installed over the colliding account and group), A3 (UID 1600, not a system UID) | A2a, A2b ✓ |
no-memory-max |
A8 (the node's effective memory limit became the slice's 3,489,660,928 bytes) | A8 ✓ |
The root-user result is worth noting. It shows concretely that the non-root identity is what makes the denied-log check meaningful: under root, the unauthorized log no longer produces a gap.
On Debian 13 with systemd 257, the package does what the contract says:
- it installs, refuses collisions and verifies;
- it runs unprivileged in the slice with its limits in the kernel;
- it collects inside the sandbox, keeps its Spool across restarts, and contains memory and task pressure;
- it uninstalls without losing data unless purged.
Two things are not shown:
-
MemoryHighthrottling; - behavior under a unified hierarchy that systemd owns, which the contract assumes.
A run on a unified-hierarchy host decides the gate.
-
Environment.
- The container shares the host kernel. It is not a separate machine.
- OOM kills are inferred from SIGKILL, because systemd cannot observe OOM events on v1.
- I/O accounting (
IOReadBytes) is not available on this hierarchy; it is reported and never counted.
- Coverage. Upgrades from an earlier package version, reboots and power loss are not exercised.