roadmap

Planned features and scope targets beyond v0.1. Versions are scope buckets, not calendar commitments — items shift based on complexity discovered during implementation.

v0.2 — MAC Layers & Capability Awareness

Note (post-ship): This section is the pre-implementation plan recorded before v0.2.0 was built. Legend: [SHIPPED] = landed in v0.2.0 · [DEFERRED] = not shipped, see future versions.

Theme: Move beyond DAC. Handle the two most common MAC frameworks and stop assuming UID 0 = full caps.

New permission layers

Layer Scope Complexity Status
SELinux Parse policy, evaluate type enforcement rules against subject context and target label High — requires seinfo/sesearch equivalent or raw policy binary parsing [SHIPPED]--features selinux; queries in-kernel AVC via selinux_check_access()
AppArmor Parse loaded profiles, match path rules against target Medium — profile format is simpler than SELinux policy [SHIPPED]--features apparmor; reads profile mode from securityfs
Linux capabilities Read effective/permitted/bounding sets from /proc/<pid>/status; model CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_FOWNER impact on existing layers Medium — mostly extends existing DAC logic [SHIPPED]pid:N/svc: subjects probe CapEff; --with-cap flag for hypothetical queries

New operations

  • [DEFERRED] Metadata change (chmod, chown, setxattr) — gated on ownership and capabilities, not rwx bits

CLI & UX

  • [DEFERRED] all meta-operation — run all 6 (now 7) operations against a target in a single invocation
  • [DEFERRED] --nss flag — shell out to getent passwd <name> for LDAP/SSSD/AD resolution instead of /etc/passwd parsing
  • [DEFERRED] Short operation aliasesr, w, x, d, c, s for the original 6

Privilege model

  • [DEFERRED] Daemon + socket model — long-running privileged daemon, unprivileged CLI connects over Unix socket (like Docker). Eliminates per-invocation sudo and repeated capability checks.

Fix engine

  • [DEFERRED] Context-aware fix scoring — factor in group membership count when choosing between group-based and ACL-based fixes (currently uses a heuristic; v0.2 makes it data-driven)

Testing & CI

  • [DEFERRED] Multi-distro CI matrix — Ubuntu, Alpine, Fedora
  • [DEFERRED] SELinux test environment — Fedora with targeted policy
  • [DEFERRED] AppArmor test environment — Ubuntu with enforced profiles

Build

  • [RESOLVED] Evaluate whether SELinux policy parsing requires a new crate — selinux = "0.3" crate adopted

Also shipped in v0.2.0 (not in original plan)

  • [SHIPPED] faccessat2 cross-check (--self-test) — landed in v0.1.3 (pulled forward)
  • [SHIPPED] VFS capability management (whyno caps install/check/uninstall) — landed in v0.1.0 (pulled forward)
  • [SHIPPED] cascade fix simulation — landed in v0.1.3 (pulled forward)
  • [SHIPPED] statvfs-based mount flag gathering — landed in v0.1.4
  • [SHIPPED] user namespace detection — PIDs in non-initial user namespaces annotated as Probe::Inaccessible

v0.3 — MAC State Architecture & Distribution

Note (post-ship): This section is the pre-implementation plan recorded before v0.3.0 was built. Legend: [SHIPPED] = landed in v0.3.0 · [DEFERRED] = not shipped, see future versions.

Theme: Cover the containment layers that trip up services inside systemd units and lightweight containers.

New permission layers

Layer Scope Complexity Status
systemd sandboxing Parse active directives: ProtectSystem, ProtectHome, ReadOnlyPaths, InaccessiblePaths, PrivateTmp, NoNewPrivileges, etc. High — requires D-Bus queries or systemctl show parsing for the full directive set [DEFERRED]
User namespaces Resolve UID/GID mappings from /proc/<pid>/uid_map and /proc/<pid>/gid_map; translate subject identity into the namespace context before running DAC checks High — mapping resolution is container-runtime-dependent (Docker, Podman, LXC all differ) [DEFERRED]

Subject resolution

  • [DEFERRED] Container-aware PID resolution — when pid:N is inside a user namespace, resolve the mapped UID/GID, not the host-side values
  • [DEFERRED] ctr:<name> subject format (stretch) — resolve a container name → PID → namespace-mapped identity

Path handling

  • [DEFERRED] Bind mount awareness — detect when a path crosses a bind mount boundary and show the source path in --explain
  • [DEFERRED] PrivateTmp rewriting — when checking a systemd service that uses PrivateTmp=yes, map /tmp to the service's private tmp directory

Output

  • [DEFERRED] Layer grouping in checklist — group layers into "filesystem" (mount, flags), "DAC" (traversal, permissions, ACLs), "MAC" (SELinux, AppArmor), "sandboxing" (systemd, namespaces) with collapsible sections in --explain

Also shipped in v0.3.0 (not in original plan)

  • [SHIPPED] MAC state architecture refactor — MacState/SeLinuxState/AppArmorState types; SystemState.mac_state; gather_mac_state() pre-computes AVC decisions; check_selinux/check_apparmor refactored to pure functions over pre-gathered state. Degraded messages updated to "SELinux state not gathered" / "AppArmor state not gathered".
  • [SHIPPED] armv7-unknown-linux-musleabihf and riscv64gc-unknown-linux-musl build targets with justfile build/test recipes
  • [SHIPPED] Live kernel CI — Proxmox VM templates for Fedora 40 (SELinux enforcing) and Ubuntu 24.04 (AppArmor enforcing)
  • [SHIPPED] ext4-tests / acl-tests feature flags; selinux_tests.rs / apparmor_tests.rs unit test suites

v0.4 — Metadata Permission Operations

Note (post-ship): This section is the pre-implementation plan recorded before v0.4.0 was built. Legend: [SHIPPED] = landed in v0.4.0 · [DEFERRED] = not shipped, see future versions.

Theme: Close the gap on metadata-change operations deferred from v0.2. Ownership and capability checks for chmod, chown, and setxattr.

New permission layers

Layer Scope Complexity Status
Metadata chmod, chown-uid, chown-gid, setxattr — ownership and capability checks per operation; implements setattr_prepare semantics Medium — well-specified kernel source, but 4 distinct sub-checks with different capability requirements [SHIPPED]CoreLayer::Metadata; check_metadata(); MetadataParams; XattrNamespace

Fix engine

  • [SHIPPED] FixAction::GrantCap — least-privilege fix suggestions for metadata-op failures. apply_grant_cap() cascade.
  • [SHIPPED] apply_chown()ATTR_KILL_SUID/ATTR_KILL_SGID semantics in cascade simulation

CLI

  • [SHIPPED] --new-mode, --new-uid, --new-gid, --xattr-key flags; resolve_operation() routing; build_metadata_params()
  • [SHIPPED] CAP_FSETID (bit 4) and CAP_SYS_ADMIN (bit 21) constants; operation_to_selinux_perm() promoted to pub

Testing

  • [SHIPPED] Docker and Proxmox VM integration tests for all 4 metadata operations

v0.5+ — Sandboxing, Namespaces & Deep Inspection

Theme: The remaining containment layers and BPF introspection.

New permission layers

Layer Scope Complexity
systemd sandboxing Parse active directives: ProtectSystem, ProtectHome, ReadOnlyPaths, InaccessiblePaths, PrivateTmp, NoNewPrivileges, etc. High — requires D-Bus queries or systemctl show parsing for the full directive set
User namespaces Resolve UID/GID mappings from /proc/<pid>/uid_map and /proc/<pid>/gid_map; translate subject identity into the namespace context before running DAC checks High — mapping resolution is container-runtime-dependent (Docker, Podman, LXC all differ)
seccomp Inspect BPF filters attached to a process; determine if the target syscall (open, execve, etc.) would be allowed, killed, or trapped Very high — requires BPF program introspection, runtime-specific profiles (Docker default, custom filters)

Simulation

  • --then flag — post-exec context simulation: "if this binary is executed, what can the resulting process do?" Resolves setuid/setgid bits, file capabilities, and the resulting effective UID/GID/caps, then re-runs all checks from that new identity.
  • Setuid/setgid + file capability resolution — model the full execve credential transformation (real/effective/saved UID, capability bounding set, ambient set)

Fix engine

  • Interactive fix mode (stretch) — prompt the user to apply fixes directly (whyno --fix), with confirmation and rollback support
  • Ansible/Chef/Puppet output — render fix plans as configuration management snippets instead of raw shell commands

Subject resolution

  • Container-aware PID resolution — when pid:N is inside a user namespace, resolve the mapped UID/GID, not the host-side values
  • ctr:<name> subject format (stretch) — resolve a container name → PID → namespace-mapped identity
  • k8s:<pod> subject format (stretch) — resolve a Kubernetes pod → container PID → namespace-mapped identity + seccomp profile + AppArmor profile

Future / Unversioned Ideas

Items without a clear version target. May be promoted as the project evolves.

  • macOS supportsandbox-exec profiles, TCC (Transparency, Consent, and Control), signed entitlements. Entirely different permission model; likely a separate binary or feature-gated build.
  • Remote modewhyno --host <ssh-target> nginx read /path — gather state over SSH, run checks locally
  • Continuous monitoringwhyno watch svc:nginx read /path — re-check on file/permission change events (inotify)
  • Web UI / TUI dashboard — visual permission map for a path showing all layers, all subjects
  • Policy-as-code integration — define expected permissions in a config file, whyno audit --policy permissions.yaml fails CI if reality doesn't match
  • Package manager integrationwhyno why-broke <package> — after a system update, identify which permission changes broke a service

Version dependency graph

graph TD
    V01["v0.1
DAC + ACL + mount + flags
5 layers, static binary"]
    V02["v0.2
SELinux + AppArmor + caps
MAC layers, capability-aware DAC"]
    V03["v0.3
MAC state architecture
4 build targets, live kernel CI"]
    V04["v0.4
metadata ops (chmod/chown/setxattr)
6 layers, GrantCap fix"]
    V05["v0.5+
systemd sandbox + namespaces
seccomp, post-exec sim"]

    V01 --> V02
    V02 --> V03
    V03 --> V04
    V04 --> V05

    style V01 fill:#2d6a4f,color:#fff
    style V02 fill:#40916c,color:#fff
    style V03 fill:#52b788,color:#fff
    style V04 fill:#74c69d,color:#1b4332
    style V05 fill:#95d5b2,color:#1b4332