diff --git a/SECURITY.md b/SECURITY.md
index 19fb4ebc..3f19803c 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -326,9 +326,11 @@ isolation. Source review and socket metadata—not reads of private credentials
- All nine desktop wrappers use shared nixpak policies: cleared environment, filtered session D-Bus
and selected mounts. The [permission inventory](hardening/README.md#app-permissions) lists each
app's file, network, credential and media grants.
-- Only Firefox/Chromium share host networking. Other apps isolate loopback with pasta but can still
- reach the LAN/Internet. Browser media and browserpass are enabled only for
- Firefox/Chromium/Chrome; browserpass exposes password-store/GPG data and agent operations.
+- Only Firefox/Chromium share host networking. Chromium is an intentional local-development
+ exception; all other apps isolate loopback with pasta but can still reach the LAN/Internet.
+ Browser media and browserpass are enabled only for Firefox/Chromium/Chrome. Brave Origin and Edge
+ are backup browsers with no media grants. browserpass exposes password-store/GPG data and agent
+ operations.
- Nixpak supplements Chromium's renderer sandbox, which remains enabled. Zoom/WeChat retain their
FHS runtimes inside the outer boundary; inner mounts cannot recover hidden host resources.
- Desktop access has limits: GPU drivers remain exposed, PulseAudio permits recording, read-only
@@ -339,9 +341,11 @@ isolation. Source review and socket metadata—not reads of private credentials
browser receives broad GNOME Keyring authority. Firefox uses its own NSS storage.
- **Rollout note (WA-045):** v12 is verified on a disposable profile, but if the portal is
unavailable Chromium may fall back to weaker v10 storage; portal initialization is monitored.
-- Runtime evidence is partial: Brave/Edge headless export times out; interactive login, media, IME
- and file workflows require acceptance. See the
- [validation records](hardening/README.md#validation).
+- Policy/build passes for all nine wrappers. Interactive validation passed for Firefox, Chrome,
+ Chromium, Brave Origin, Edge, QQ, Telegram and Zoom; the Chromium smoke test also validates
+ renderer sandboxing, environment clearing, bind scope and profile persistence. WeChat's
+ interactive Wayland check remains pending. See the
+ [runtime validation](hardening/tests/runtime-validation.md).
#### Agent-first diagnostics by host role
diff --git a/WORKAROUNDS.md b/WORKAROUNDS.md
index 46354240..37533691 100644
--- a/WORKAROUNDS.md
+++ b/WORKAROUNDS.md
@@ -83,12 +83,12 @@ A known limitation with no fix yet.
An unfinished feature or gap.
-| ID | What & where | Why | Removal condition | Added | Revisit | Status |
-| ------ | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------------------------ | ---------- |
-| WA-013 | NixOS tests — `outputs/README.md` | full-host tests still rebuild custom packages (no private cache) and need the host ssh key to decrypt agenix; the shared-firewall security-exporters VM test now runs in CI (KVM); `outputs/x86_64-linux/nixos-tests/idols-ruby.nix` is exposed as `packages.x86_64-linux.ruby-nixos-tests` but is not in `checks` or CI | Resolve when a private cache exists and tests can run per-service | 2024-03-13 | quarterly | active |
-| WA-027 | NFS golden store `no_root_squash` — `hosts/12kingdoms-youko/nfs-golden.nix` | the existing PVC subdirectories are root-owned, so enabling `root_squash` would break running workloads until they are migrated; the CSI node plugin is hostNetwork and sources from the allowlisted node IPs | Drop when the PVC subdirectories are migrated and `root_squash` is enabled | 2026-10-07 | next maintenance window | active |
-| WA-044 | transmission AppArmor lock permission — `hosts/12kingdoms-youko/homelab-services/transmission.nix` | nixpkgs' transmission 4.1.4 profile grants `rw` on the daemon's config dir but not `k`, while 4.1.4 takes an fcntl lock on `~/.config/transmission-daemon/lock`; the daemon exits with "Another process is already using". A `local/bin.transmission-daemon` include adds `rwk` for that lock file only | Drop once the nixpkgs profile includes the lock permission | 2026-10-31 | on the next nixpkgs bump | active |
-| WA-045 | Chromium cookie crypto — `hardening/nixpaks/modules/chromium-browser.nix` | Chromium-family browsers use the app-scoped Secret portal and write v12 cookies. If the portal is unavailable, Chromium may fall back to weak v10 storage. Brave/Edge headless export also times out. | Drop once the v10 fallback is eliminated and the Brave/Edge headless checks pass; monitor portal initialization and interactive browser checks | 2026-10-31 | on Chromium updates | active gap |
+| ID | What & where | Why | Removal condition | Added | Revisit | Status |
+| ------ | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ------------------------ | ---------- |
+| WA-013 | NixOS tests — `outputs/README.md` | full-host tests still rebuild custom packages (no private cache) and need the host ssh key to decrypt agenix; the shared-firewall security-exporters VM test now runs in CI (KVM); `outputs/x86_64-linux/nixos-tests/idols-ruby.nix` is exposed as `packages.x86_64-linux.ruby-nixos-tests` but is not in `checks` or CI | Resolve when a private cache exists and tests can run per-service | 2024-03-13 | quarterly | active |
+| WA-027 | NFS golden store `no_root_squash` — `hosts/12kingdoms-youko/nfs-golden.nix` | the existing PVC subdirectories are root-owned, so enabling `root_squash` would break running workloads until they are migrated; the CSI node plugin is hostNetwork and sources from the allowlisted node IPs | Drop when the PVC subdirectories are migrated and `root_squash` is enabled | 2026-10-07 | next maintenance window | active |
+| WA-044 | transmission AppArmor lock permission — `hosts/12kingdoms-youko/homelab-services/transmission.nix` | nixpkgs' transmission 4.1.4 profile grants `rw` on the daemon's config dir but not `k`, while 4.1.4 takes an fcntl lock on `~/.config/transmission-daemon/lock`; the daemon exits with "Another process is already using". A `local/bin.transmission-daemon` include adds `rwk` for that lock file only | Drop once the nixpkgs profile includes the lock permission | 2026-10-31 | on the next nixpkgs bump | active |
+| WA-045 | Chromium cookie crypto — `hardening/nixpaks/modules/chromium-browser.nix` | Chromium-family browsers use the app-scoped Secret portal and write v12 cookies. If the portal is unavailable, Chromium may fall back to weak v10 storage. | Drop once the v10 fallback is eliminated; monitor portal initialization and the `Local State.os_crypt.portal.prev_init_success` flag | 2026-10-31 | on Chromium updates | active gap |
## Related
diff --git a/hardening/README.md b/hardening/README.md
index 8f17edac..9e9ee2ba 100644
--- a/hardening/README.md
+++ b/hardening/README.md
@@ -1,6 +1,6 @@
# Linux Hardening
-Deployed and interactively validated on `ai` (2026-10-31). Policy and limits:
+Deployed and interactively validated on the Linux desktop host (2026-10-31). Policy and limits:
[SECURITY.md](../SECURITY.md).
## Shared boundaries
@@ -10,7 +10,9 @@ Deployed and interactively validated on `ai` (2026-10-31). Policy and limits:
- Read-only `/nix/store`; GPU and display; fonts and themes; LAN and Internet.
- Portals for file chooser, URI opening, notifications, input methods and document exports.
- No whole host home, system D-Bus, SSH agent, USB/serial, raw camera or raw PipeWire socket.
-- Host loopback only for Firefox/Chromium; all other apps use pasta-isolated loopback.
+- Host loopback only for Firefox/Chromium; all other apps use pasta-isolated loopback. Chromium is
+ an intentional development exception. Brave Origin and Edge are backup browsers and receive no
+ media grants.
## App permissions
@@ -50,21 +52,21 @@ All browsers get read-only NSS certificates, printing, native notifications and
capture.
- PulseAudio grants recording authority. Read-only GPG sockets still permit signing and decryption.
- pasta isolates loopback, not LAN access. Chromecast discovery is unverified.
-- Brave/Edge renderer and permission checks pass, but headless page export still times out.
+- Brave Origin and Edge intentionally have no PulseAudio, Camera, ScreenCast or MPRIS grants. They
+ are backup browsers, not media browsers.
## Validation
-- Passed: `just fmt`, `just test`, `ai-niri` build and `shoukei-niri` eval.
-- Deployed on `ai`; interactive startup and tray checks passed for Chrome, Firefox, QQ, Telegram and
- Zoom.
-- WeChat uses the native deb package from nixpkgs-patched, with nixpak as the only bwrap layer. The
- build passes; interactive Wayland validation is pending.
-- Chromium/Chrome: renderer sandbox, mounts, rendering and disposable-profile persistence passed.
- Chromium reaches host loopback; Chrome cannot.
-- Firefox: disposable headless screenshot passed.
-- Brave/Edge: renderer and permission checks passed; headless page export timed out.
-- Evidence: [browsers](tests/browser-runtime.md),
- [Firefox/QQ/Telegram](tests/unified-app-runtime.md), [Zoom](tests/zoom-runtime.md).
+- Passed: `just fmt`, `just test`, `ai-niri` build, `shoukei-niri` eval and all wrapper builds.
+- Interactive validation passed for Firefox, Chrome, Chromium, Brave Origin, Edge, QQ, Telegram and
+ Zoom. Chromium's host-loopback/personal-file exception and the Brave/Edge no-media policy are
+ intentional.
+- Chromium smoke test passed: renderer sandbox, environment clearing, bind scope, disposable-profile
+ persistence and loopback policy.
+- WeChat uses the native nixpkgs-patched package with nixpak as the only bwrap layer. Build/policy
+ pass; interactive Wayland validation is still pending.
+- Evidence and repeat commands: [runtime validation](tests/runtime-validation.md),
+ [policy test](tests/policy.nix).
## Code map
diff --git a/hardening/tests/browser-runtime.md b/hardening/tests/browser-runtime.md
deleted file mode 100644
index f6f5ea39..00000000
--- a/hardening/tests/browser-runtime.md
+++ /dev/null
@@ -1,126 +0,0 @@
-# Browser runtime verification
-
-- Run from the repository root as the Linux desktop user, with user D-Bus; no sudo or activation.
-- Tests use disposable profiles only, synthetic environment canaries, and no environment dumps.
-- **Keep HOME under `/var/tmp`:** the sandbox's private `/tmp` masks a HOME created there. Rendering
- reads a host-prepared HTML file through the profile bind; persistence requires a nonempty
- `Local State` file in the disposable host HOME.
-
-## Reproduce
-
-Build against the locked flake inputs:
-
-```bash
-mkdir -p browser-validation
-for app in chromium google-chrome brave-origin microsoft-edge; do
- APP="$app" nix build --impure --out-link "browser-validation/$app" --expr '
- let
- flake = builtins.getFlake (toString ./.);
- pkgs = import flake.inputs.nixpkgs {
- system = builtins.currentSystem;
- config.allowUnfree = true;
- };
- in pkgs.callPackage
- (./hardening/nixpaks + "/${builtins.getEnv "APP"}.nix")
- {
- mkNixPak = flake.inputs.nixpak.lib.nixpak {
- inherit pkgs;
- inherit (pkgs) lib;
- };
- }
- '
-done
-```
-
-Run each test separately so a failing browser does not hide the other results:
-
-```bash
-python3 hardening/tests/verify-chromium.py browser-validation/chromium/bin/chromium
-python3 hardening/tests/verify-chromium.py browser-validation/google-chrome/bin/google-chrome
-python3 hardening/tests/verify-chromium.py browser-validation/brave-origin/bin/brave-origin
-python3 hardening/tests/verify-chromium.py browser-validation/microsoft-edge/bin/microsoft-edge
-```
-
-- Each launch has a 35-second limit; failures exit nonzero. Temporary HOME is cleaned up.
-- Intermediate JSON records process checks; final JSON appears only after all checks pass.
-
-## Current measured results
-
-Host: `ai`, x86_64 Linux. All four rebuilt after the shared policy/media opt-in changes and tested
-with `/var/tmp` HOME, explicit Fontconfig configuration, CJK fonts, and narrow NVIDIA mappings.
-
-| Browser | Process/security checks | HTML rendering | Profile persistence | Host loopback | Overall |
-| ---------------------- | ----------------------- | --------------- | ------------------- | ------------- | -------- |
-| Chromium 156.0.8078.73 | Pass | Pass | Pass | Pass: allowed | **Pass** |
-| Chrome 156.0.8078.73 | Pass | Pass | Pass | Pass: blocked | **Pass** |
-| Brave Origin 1.98.60 | Pass | **35s timeout** | Not reached | Not reached | **Fail** |
-| Edge 156.0.4314.42 | Pass | **35s timeout** | Not reached | Not reached | **Fail** |
-
-Measured evidence:
-
-- All four build and have live renderers with `Seccomp=2`, `NoNewPrivs=1`; no sandbox-disabling
- flags are present.
-- Actual bwrap arguments pass user/PID namespace, effective network flag, clear environment, new
- session, parent-death, and private `/dev/shm` checks. No broad HOME/`/home`/`/dev`, SSH,
- system-bus, raw video, X11, GVfs, or direct PipeWire binds. Brave/Edge also have no browserpass
- credential or PulseAudio binds.
-- All nine canaries are absent: `NIXPAK_VALIDATION_CANARY`, `SSH_AUTH_SOCK`, `GPG_AGENT_INFO`, and
- upper/lowercase `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`.
-- Chromium/Chrome render the prepared file without Fontconfig errors and persist `Local State`. Host
- HTTP control succeeds; Chromium fetches its marker. Chrome reports `ERR_CONNECTION_REFUSED`,
- returns no marker, and sends zero requests to the host listener.
-- Brave/Edge stall on `--headless=new --dump-dom` despite `--timeout=10000`. Cause unresolved;
- system D-Bus/UPower warnings also occur in passing browsers. No upstream-package control was run,
- so this does not establish a wrapper regression.
-
-## Current permission checks
-
-- **Firefox/Chromium/Chrome:** evaluated media opt-in enables PulseAudio, camera/screencast portals,
- MPRIS, and inhibition. **Brave/Edge:** all disabled; live socket-bind checks pass.
-- Chromium/Chrome main processes enable `WaylandWindowDecorations,WebRtcPipeWireCamera`. Brave/Edge
- retain Wayland decorations and have **no camera feature**; all flag checks pass.
-- Firefox/Chromium share host networking and allow personal files; Chrome/Brave/Edge use pasta
- isolation without personal-file access. Firefox parity is evaluated, not runtime-tested here.
-- GPU nodes remain available for webpage rendering. Interactive audio/camera/capture, hardware
- acceleration, CJK/IME, portals, downloads, and browserpass remain unverified by this headless
- test.
-- Inventory: [hardening README](../README.md). Security limits: [SECURITY.md](../../SECURITY.md).
-
-## Cookie crypto
-
-- Chromium-family wrappers enable `SecretPortalKeyProviderUseForEncryption` and receive only
- `org.freedesktop.portal.Secret.RetrieveSecret`; they do **not** get
- `--talk=org.freedesktop.secrets`.
-- A disposable-profile probe under `/var/tmp` served an HTTP `Set-Cookie`, then inspected the
- profile DB: the cookie prefix was `v12` (`763132`) and
- `Local State.os_crypt.portal.prev_init_success` was true.
-- If the portal is unavailable, Chromium can fall back to v10. That is weak; monitor portal startup
- Portal startup is a monitored degradation, not a fail-closed guarantee.
-- Firefox uses NSS (`key4.db`/`logins.json`) and `cookies.sqlite`, not Chrome OSCrypt. Set a Firefox
- Primary Password if saved-login at-rest protection matters.
-- Sources:
- [feature defaults](https://github.com/chromium/chromium/blob/156.0.8078.73/chrome/browser/browser_features.cc),
- [v12 provider](https://github.com/chromium/chromium/blob/156.0.8078.73/components/os_crypt/async/browser/secret_portal_key_provider.cc),
- [prefix decryption](https://github.com/chromium/chromium/blob/156.0.8078.73/components/os_crypt/async/common/encryptor.cc).
-
-## Check these files
-
-```bash
-ruff format --check hardening/tests/verify-chromium.py
-ruff check hardening/tests/verify-chromium.py
-prettier --check hardening/tests/browser-runtime.md
-git diff --check
-```
-
-## Manual Chrome media smoke test
-
-Use the official WebRTC samples. They require no account and do not upload the stream; the preview
-stays local. Chrome uses pasta-isolated loopback, so a test server on the host is intentionally not
-reachable.
-
-- Camera + microphone:
- - Click **Start**, accept Chrome and portal prompts, verify local video preview and audio level.
-- Screen sharing:
- - Click **Start**, choose a window/screen, verify preview, then **Stop**.
-- Files: use any site's upload control, then download the same file; confirm the file chooser and
- Downloads work.
diff --git a/hardening/tests/runtime-validation.md b/hardening/tests/runtime-validation.md
new file mode 100644
index 00000000..b332fc3c
--- /dev/null
+++ b/hardening/tests/runtime-validation.md
@@ -0,0 +1,81 @@
+# Runtime validation
+
+Nixpak wrappers are accepted in three layers:
+
+1. **Policy/build:** `just test` evaluates every wrapper and [`policy.nix`](policy.nix) rejects
+ broad mounts, system D-Bus, keyring access and device grants outside each app's declared scope.
+2. **Disposable smoke test:** [`verify-chromium.py`](verify-chromium.py) launches a Chromium-family
+ wrapper with a temporary HOME and validates renderer sandboxing, environment clearing, bind
+ scope, profile persistence and loopback policy.
+3. **Interactive desktop check:** launch the real app, use its normal file/network/device workflows,
+ and confirm the sandbox does not break required integration.
+
+## Current status
+
+| App | Policy/build | Runtime |
+| ---------------------- | ------------ | ----------------------------------------------------- |
+| Firefox | Pass | Interactive pass |
+| Chromium | Pass | Automated smoke + interactive pass |
+| Chrome | Pass | Interactive pass |
+| Brave Origin | Pass | Interactive pass; backup browser without media grants |
+| Edge | Pass | Interactive pass; backup browser without media grants |
+| Zoom | Pass | Interactive pass |
+| QQ / Telegram | Pass | Interactive startup/tray pass |
+| WeChat (native nixpak) | Pass | Interactive Wayland validation pending |
+
+Intentional exceptions:
+
+- **Chromium** keeps host loopback and personal files for local development. Firefox has the same
+ exception. Chrome, Brave Origin and Edge use pasta-isolated loopback.
+- **Brave Origin and Edge are backup browsers.** They intentionally have no PulseAudio, Camera,
+ ScreenCast or MPRIS grants.
+
+## Browser smoke test
+
+Build a wrapper from the locked inputs:
+
+```bash
+APP=chromium nix build --impure --out-link "browser-validation/$APP" --expr '
+ let
+ flake = builtins.getFlake (toString ./.);
+ pkgs = import flake.inputs.nixpkgs {
+ system = builtins.currentSystem;
+ config.allowUnfree = true;
+ };
+ in
+ pkgs.callPackage (./hardening/nixpaks + "/${builtins.getEnv "APP"}.nix") {
+ mkNixPak = flake.inputs.nixpak.lib.nixpak { inherit pkgs; inherit (pkgs) lib; };
+ }
+'
+python3 hardening/tests/verify-chromium.py "browser-validation/$APP/bin/$APP"
+```
+
+The script writes its disposable HOME under `/var/tmp`; the sandbox's private `/tmp` would otherwise
+hide profile binds. Pasta-wrapped browsers also require a usable `/dev/net/tun` on the host. The
+script checks live renderer `Seccomp=2` and `NoNewPrivs=1`, absence of `--no-sandbox`, canary-free
+environments, effective bwrap arguments, profile persistence and loopback reachability.
+
+Chromium-family browsers use the app-scoped Secret portal for v12 cookie encryption; they do not
+receive `org.freedesktop.secrets`. If the portal is unavailable, Chromium can fall back to weaker
+v10 storage; monitor `Local State.os_crypt.portal.prev_init_success`. Firefox uses NSS storage
+instead. Browserpass separately exposes the password store and GPG-agent socket to
+Firefox/Chromium/Chrome only.
+
+## Zoom
+
+Zoom keeps its upstream FHS runtime inside the outer nixpak boundary. The nested sandbox propagates
+`/.flatpak-info`, uses pasta-isolated networking, and binds only `DISPLAY`, `XAUTHORITY`,
+`/dev/video0`, `/dev/video1`, DRM sysfs and the declared app directories. X11 remains a
+shared-display trust boundary: ScreenCast portal permission does not prevent capture through the X11
+connection. Interactive login, audio, camera, screen sharing and file workflows passed.
+
+## Commands
+
+```bash
+just fmt
+just test
+git diff --check
+ruff format --check hardening/tests/verify-chromium.py
+ruff check hardening/tests/verify-chromium.py
+prettier --check hardening/tests/runtime-validation.md
+```
diff --git a/hardening/tests/unified-app-runtime.md b/hardening/tests/unified-app-runtime.md
deleted file mode 100644
index f5ca4072..00000000
--- a/hardening/tests/unified-app-runtime.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Unified desktop-policy validation
-
-Validation on the Linux `ai` host, using the pinned nixpkgs and nixpak inputs. These checks use
-disposable profiles and do not read existing browser or chat accounts.
-
-| Check | Result | Scope |
-| --------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Firefox, QQ, Telegram derivations | Pass | All three wrappers evaluated and built after conversion to the shared desktop policy. |
-| Browser policy regression checks | Pass | `policy.nix` returned `true`; covers Firefox/Chromium capability and portal-call parity, host loopback, Chrome isolated networking and media grants, browserpass scope, and forbidden service/device access. |
-| Firefox headless screenshot | Pass | Exit status 0; `about:blank` produced a PNG with a fresh profile and HOME under `/var/tmp`. |
-| QQ startup | Partial | With a disposable HOME and `--ozone-platform=headless`, initialized private QQ data and stayed running for 12 seconds until explicitly terminated. Headless EGL/GPU initialization errors occurred. No account login or media function was tested. |
-| Telegram runtime | Not run | Evaluation and build only. |
-
-The Firefox invocation used the built wrapper with
-`--headless --no-remote --profile "$HOME/.mozilla/test" --screenshot "$HOME/Downloads/smoke.png" about:blank`.
-The disposable environment retained only the launcher's PATH, USER, locale, Wayland display and
-session-bus/runtime-directory variables, with HOME and all XDG storage paths redirected to the
-temporary tree. Existing account profiles were not mounted. The temporary tree was removed after the
-process exited.
-
-Use `/var/tmp`, not `/tmp`, for disposable host profiles. Nixpak emits the sandbox's private `/tmp`
-mount after filesystem binds; a profile prepared under host `/tmp` is hidden by that mount. An
-application that creates a fresh profile inside the resulting tmpfs can appear to work without
-testing persistence.
-
-The common policy replaces the earlier direct camera-device and PipeWire-socket grants with explicit
-Camera and ScreenCast portal methods. QQ and Telegram retain their private XDG data and existing
-personal-file mappings, and opt into notifications, tray, MPRIS and accessibility. Firefox retains
-its `.mozilla` profile and MPRIS artwork mounts and shares browser capability definitions with
-Chromium.
-
-Interactive validation remains necessary for camera capture, microphone input, video playback,
-desktop/window sharing, file chooser/save operations, IME, tray, notifications and media controls.
-The QQ headless startup is not evidence that those functions work. Browser encrypted-login behavior
-is covered in `browser-runtime.md`.
diff --git a/hardening/tests/verify-chromium.py b/hardening/tests/verify-chromium.py
index 56f3cb88..4bbf8b67 100755
--- a/hardening/tests/verify-chromium.py
+++ b/hardening/tests/verify-chromium.py
@@ -2,7 +2,8 @@
"""Runtime smoke test: verify a built nixpak Chromium without existing profiles.
Usage: python3 hardening/tests/verify-chromium.py /nix/store/.../bin/chromium
-Requires Linux procfs and an active user D-Bus session. Uses only stdlib.
+Requires Linux procfs and an active user D-Bus session. Pasta-wrapped browsers also
+need /dev/net/tun. Uses a disposable HOME under /var/tmp and only the standard library.
"""
import argparse
diff --git a/hardening/tests/zoom-runtime.md b/hardening/tests/zoom-runtime.md
deleted file mode 100644
index 9a430454..00000000
--- a/hardening/tests/zoom-runtime.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# Zoom nested runtime verification
-
-The Zoom 7.2.1.5760 candidate built and opened two `Zoom Workplace` X11 windows on the NVIDIA
-desktop. The final tested output was
-`/nix/store/yy6sxsgn5rqafvvgs3k53akwqjcfa2r7-nixpak-zoom-7.2.1.5760`.
-
-The live main process had `NoNewPrivs: 1`, a network namespace different from the host, and
-`/.flatpak-info` with `name=us.zoom.Zoom`. Its ancestry included ZoomLauncher and the nested
-bubblewrap processes. Its `/dev` contained only standard synthetic devices, `dri`, `nvidia0`,
-`nvidiactl`, `nvidia-modeset`, and `nvidia-uvm`; no camera, USB or block devices were exposed.
-Zoom's main process reported `Seccomp: 0`; this test does not establish a renderer seccomp
-guarantee.
-
-The upstream FHS runtime remains intact. Its automatic root mounts propagate directories, so the
-wrapper explicitly propagates the outer `/.flatpak-info` file for portal identification. Its `/dev`
-bind inherits the outer sandbox's filtered devices. The outer network uses pasta isolate mode,
-without host loopback sharing. X11 remains a display trust boundary: explicit ScreenCast portal
-permission does not prevent access through the X11 connection.
-
-Initial display testing found that `/tmp` tmpfs shadowed the X11 socket and that NVIDIA OpenGL
-needed the four device mappings now in `desktop-base.nix`. Zoom therefore keeps the sandbox's
-private root `/tmp` instead of mounting another tmpfs over the socket, and explicitly passes
-`DISPLAY` and `XAUTHORITY`.
-
-## Repeat without touching the normal Zoom profile
-
-Build the candidate without activating a host, then substitute its output path below. The temporary
-home must be outside `/tmp` when testing policies that mount a `/tmp` tmpfs. Keep the current
-display and session bus environment.
-
-```bash
-zoom_probe_dir=$(mktemp -d "$HOME/.cache/zoom-nixpak-probe.XXXXXX")
-mkdir -p "$zoom_probe_dir"/{.config,.cache,.local/share,.local/state,Documents,Downloads}
-HOME="$zoom_probe_dir" \
-XDG_CONFIG_HOME="$zoom_probe_dir/.config" \
-XDG_CACHE_HOME="$zoom_probe_dir/.cache" \
-XDG_DATA_HOME="$zoom_probe_dir/.local/share" \
-XDG_STATE_HOME="$zoom_probe_dir/.local/state" \
-timeout --kill-after=3 30 /nix/store/OUTPUT/bin/zoom
-```
-
-While it runs, inspect the exact test process through `/proc/PID/root/dev`,
-`/proc/PID/root/.flatpak-info`, `/proc/PID/ns/net`, `/proc/PID/status`, and its parent processes. Do
-not sign in or join a meeting during this smoke test. The timeout terminates the owned launcher and
-its sandbox. Confirm the test windows and processes are gone afterward.
-
-The disposable run created `zoomus.conf` under the private application config directory. Existing
-host `zoomus.conf` and `zoom.conf` are individually mounted when present. Application caches use the
-private application cache directory.
-
-Still pending: login and meeting join, microphone and speaker routing, camera portal behavior on
-supported hardware, screencast selection and PipeWire FD handoff, recording/export paths, file
-chooser access, URL opening and IME. No successful camera, audio or screencast session is claimed by
-this smoke test.