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, notrwxbits
CLI & UX
- [DEFERRED]
allmeta-operation — run all 6 (now 7) operations against a target in a single invocation - [DEFERRED]
--nssflag — shell out togetent passwd <name>for LDAP/SSSD/AD resolution instead of/etc/passwdparsing - [DEFERRED] Short operation aliases —
r,w,x,d,c,sfor the original 6
Privilege model
- [DEFERRED] Daemon + socket model — long-running privileged daemon, unprivileged CLI connects over Unix socket (like Docker). Eliminates per-invocation
sudoand 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]
faccessat2cross-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:Nis 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]
PrivateTmprewriting — when checking a systemd service that usesPrivateTmp=yes, map/tmpto 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/AppArmorStatetypes;SystemState.mac_state;gather_mac_state()pre-computes AVC decisions;check_selinux/check_apparmorrefactored to pure functions over pre-gathered state. Degraded messages updated to "SELinux state not gathered" / "AppArmor state not gathered". - [SHIPPED]
armv7-unknown-linux-musleabihfandriscv64gc-unknown-linux-muslbuild targets withjustfilebuild/test recipes - [SHIPPED] Live kernel CI — Proxmox VM templates for Fedora 40 (SELinux enforcing) and Ubuntu 24.04 (AppArmor enforcing)
- [SHIPPED]
ext4-tests/acl-testsfeature flags;selinux_tests.rs/apparmor_tests.rsunit 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_SGIDsemantics in cascade simulation
CLI
- [SHIPPED]
--new-mode,--new-uid,--new-gid,--xattr-keyflags;resolve_operation()routing;build_metadata_params() - [SHIPPED]
CAP_FSETID(bit 4) andCAP_SYS_ADMIN(bit 21) constants;operation_to_selinux_perm()promoted topub
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
--thenflag — 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
execvecredential 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:Nis 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 identityk8s:<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 support —
sandbox-execprofiles, TCC (Transparency, Consent, and Control), signed entitlements. Entirely different permission model; likely a separate binary or feature-gated build. - Remote mode —
whyno --host <ssh-target> nginx read /path— gather state over SSH, run checks locally - Continuous monitoring —
whyno 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.yamlfails CI if reality doesn't match - Package manager integration —
whyno 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