Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Releases before `0.2.5` predate the public launch; their notes live in the

### Changed

- Make Debian stable releases 12 and later eligible, while refusing an unknown
version and releases below the security-support floor. Debian eligibility is
separate from live-VM validation; Ubuntu-only actions remain excluded (#238).
- Separate Ubuntu identity requirements from Debian-family mechanisms and
planner defaults. Canonical services, PPAs and the reboot sentinel require
Ubuntu itself; portable tools are no longer refused merely for being another
Expand Down
7 changes: 5 additions & 2 deletions apps/sysknife-cli/src/distro_routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@ mod tests {
"AptUpdate",
] {
for distro in [
DistroId::Debian { version: Some(13) },
DistroId::Debian { version: Some(11) },
DistroId::Debian { version: None },
DistroId::Ubuntu {
major: 18,
minor: 4,
Expand All @@ -229,13 +230,15 @@ mod tests {
);
}
for distro in [
DistroId::Debian { version: Some(12) },
DistroId::Debian { version: Some(13) },
DistroId::Ubuntu {
major: 24,
minor: 4,
},
DistroId::FedoraSilverblue { version: 41 },
] {
if action != "AptUpdate" {
if action != "AptUpdate" || matches!(distro, DistroId::Debian { .. }) {
assert!(
check_action_distro(action, Some(&distro)).is_ok(),
"portable {action} must remain usable on {distro}"
Expand Down
4 changes: 2 additions & 2 deletions crates/sysknife-core/src/action_family.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,8 +358,8 @@ mod tests {
assert!(action_matches_distro(action, &debian), "{action}");
}
assert!(
!debian.is_supported(),
"classification must not enable Debian"
!derivative.is_supported(),
"Debian-family membership must not enable an unrecognised derivative"
);
}
}
54 changes: 48 additions & 6 deletions crates/sysknife-core/src/distro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ pub const OLDEST_ELIGIBLE_FEDORA_ATOMIC: u32 = 41;
/// every release in a year, interim ones included.
pub const OLDEST_SUPPORTED_UBUNTU_MAJOR: u32 = 20;

/// Oldest eligible Debian stable release. Debian 11 LTS ended in August 2026;
/// Debian 12 remains in LTS through June 2028.
pub const OLDEST_SUPPORTED_DEBIAN: u32 = 12;

// ---------------------------------------------------------------------------
// Error types
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -231,6 +235,10 @@ impl DistroId {
/// Ubuntu is validation effort, not support, because Ubuntu has far more
/// users. See `docs/distro-support.md`.
///
/// - Debian stable releases 12 and later are eligible. An absent version
/// (including testing/sid) is refused: security support cannot be inferred
/// from Debian identity alone.
///
/// # Eligibility is not validation coverage
///
/// This predicate answers "may SysKnife act on this host at all" — the
Expand All @@ -249,7 +257,8 @@ impl DistroId {
Self::Fedora { .. } => false,
Self::FedoraSilverblue { version } => *version >= OLDEST_ELIGIBLE_FEDORA_ATOMIC,
Self::Ubuntu { major, .. } => *major >= OLDEST_SUPPORTED_UBUNTU_MAJOR,
Self::UbuntuCore { .. } | Self::Debian { .. } | Self::Other { .. } => false,
Self::Debian { version } => version.is_some_and(|v| v >= OLDEST_SUPPORTED_DEBIAN),
Self::UbuntuCore { .. } | Self::Other { .. } => false,
}
}
}
Expand Down Expand Up @@ -649,6 +658,20 @@ ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
"#;

const DEBIAN_13: &str = r#"PRETTY_NAME="Debian GNU/Linux 13 (trixie)"
NAME="Debian GNU/Linux"
VERSION_ID="13"
VERSION="13 (trixie)"
VERSION_CODENAME=trixie
ID=debian
"#;

const DEBIAN_TESTING: &str = r#"PRETTY_NAME="Debian GNU/Linux forky/sid"
NAME="Debian GNU/Linux"
VERSION_CODENAME=forky
ID=debian
"#;

/// Linux Mint 22 (ID=linuxmint, ID_LIKE="ubuntu debian").
Expand Down Expand Up @@ -955,9 +978,17 @@ SUPPORT_END="2028-03-15"
}

#[test]
fn detect_debian_12() {
let r = parse_os_release(DEBIAN_12).unwrap();
assert_eq!(detect_distro(&r), DistroId::Debian { version: Some(12) });
fn detect_debian_stable_and_testing() {
for (fixture, version, eligible) in [
(DEBIAN_12, Some(12), true),
(DEBIAN_13, Some(13), true),
(DEBIAN_TESTING, None, false),
] {
let r = parse_os_release(fixture).unwrap();
let distro = detect_distro(&r);
assert_eq!(distro, DistroId::Debian { version });
assert_eq!(distro.is_supported(), eligible);
}
}

#[test]
Expand Down Expand Up @@ -1091,8 +1122,19 @@ SUPPORT_END="2028-03-15"
}

#[test]
fn unsupported_debian() {
assert!(!DistroId::Debian { version: Some(12) }.is_supported());
fn debian_eligibility_requires_a_supported_known_version() {
for (version, expected) in [
(Some(11), false),
(Some(12), true),
(Some(13), true),
(None, false),
] {
assert_eq!(
DistroId::Debian { version }.is_supported(),
expected,
"{version:?}"
);
}
}

#[test]
Expand Down
10 changes: 9 additions & 1 deletion crates/sysknife-daemon/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5082,7 +5082,7 @@ mod tests {
// why the platform gate does not exempt reads at all.
let dir = tempdir().unwrap();
let mut state = test_state(&dir);
state.host_distro = Some(sysknife_core::distro::DistroId::Debian { version: Some(12) });
state.host_distro = Some(sysknife_core::distro::DistroId::Debian { version: Some(11) });
assert!(
!state.host_distro.as_ref().unwrap().is_supported(),
"this test needs an ineligible host"
Expand All @@ -5096,6 +5096,14 @@ mod tests {
validate_action_platform(&state, "AptUpdate").is_err(),
"a privileged mutation must stay refused on an ineligible host"
);
for version in [None, Some(11), Some(12), Some(13)] {
state.host_distro = Some(sysknife_core::distro::DistroId::Debian { version });
assert_eq!(
validate_action_platform(&state, "AptUpdate").is_ok(),
matches!(version, Some(12 | 13))
);
assert!(validate_action_platform(&state, "AddPpa").is_err());
}
}
#[test]
fn raising_a_read_only_action_via_risk_overrides_does_not_arm_the_platform_fence() {
Expand Down
13 changes: 11 additions & 2 deletions docs/distro-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ the bug this section now documents:
- **Eligibility** — will the daemon act on this host at all? That is
`DistroId::is_supported()` in `crates/sysknife-core/src/distro.rs`, and the
daemon refuses every *mutating* action when it is false. It is true for all
Ubuntu releases from 20.04 up, and for Fedora Atomic 41 and later.
Ubuntu releases from 20.04 up, Fedora Atomic 41 and later, and Debian stable
releases with a known version of 12 or later.
- **Validation tier** — has the full story suite been run on that release?
That is the table below, and it will always be narrower than eligibility.

Expand All @@ -31,6 +32,13 @@ ineligible because they no longer receive Ubuntu security updates, and Ubuntu
Core stays ineligible for a structural reason rather than an age one — it has no
apt and a read-only root, so the Debian-family action set cannot apply.

Debian 11 is below the floor because its LTS ended on 2026-08-31. Debian 12
remains eligible during LTS; Debian 13 is eligible as well. Testing/sid images
without `VERSION_ID` remain ineligible: Debian identity alone does not establish
security support. This enables the existing Debian mechanisms, not Ubuntu-only
services, PPAs or reboot-sentinel assumptions. Firewall backend coverage remains
tracked separately in [#239](https://github.com/lacs-project/sysknife/issues/239).

## Status definitions

| Tier | Meaning |
Expand All @@ -52,6 +60,7 @@ apt and a read-only root, so the Debian-family action set cannot apply.
| **Fedora Silverblue 44** | rpm-ostree, Flatpak, toolbox, firewalld, systemd, containers | Harness and fixture coverage; no live-VM run on this release | **Blocked** (`make install` does not complete on rpm-ostree, [#301](https://github.com/lacs-project/sysknife/issues/301)) |
| **Other Fedora Atomic 41+ variants** | rpm-ostree family | Detection and shared action tests, plus a live VM run on Fedora 43 Silverblue (2026-08-24) that provisioned and then stopped at `make install` | **Blocked** (`make install` does not complete on rpm-ostree, [#301](https://github.com/lacs-project/sysknife/issues/301)) |
| **Fedora Workstation / Server** | `dnf` family incomplete | Detection tests only | **Experimental** |
| **Debian stable 12+** | Existing apt/dpkg and portable actions; Ubuntu-only actions excluded | Version and routing fixtures; no live Debian story-suite run | **Current validation required** |

## Fedora Atomic cannot be installed yet

Expand Down Expand Up @@ -117,7 +126,7 @@ Fedora-family systems receive a warning rather than a false support claim.

| Distro | State |
|---|---|
| Debian stable/testing | Planned after Ubuntu hardening |
| Debian testing / sid without `VERSION_ID` | Ineligible; no known supported stable version |
| Arch / EndeavourOS | Planned; requires a `pacman` action family |
| openSUSE Leap / Tumbleweed | Planned; requires `zypper` and transactional-update design |
| NixOS | Out of scope; configuration evaluation does not fit per-action mutation |
Expand Down
Loading