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
19 changes: 19 additions & 0 deletions crates/sysknife-brain/src/planning_tools/propose_plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,11 @@ reports live interface state"),
fn available_on(action: &str, hint: Option<&sysknife_types::DistroHint>) -> bool {
let family = hint.map(|hint| hint.family);
if let Some(hint) = hint {
// Ubuntu Core is immutable and is not an eligible Debian host. Do not
// offer apt (or other host-policy actions) merely from its family tag.
if hint.id == "ubuntu-core" {
return !action_requires_supported_host(action);
}
if hint.id != "ubuntu" && UBUNTU_ONLY_ACTIONS.contains(&action) {
return false;
}
Expand Down Expand Up @@ -735,6 +740,20 @@ mod tests {

#[test]
fn debian_host_does_not_inherit_ubuntu_tools_or_preferences() {
let core = sysknife_types::DistroHint {
id: "ubuntu-core".into(),
family: DISTRO_FAMILY_DEBIAN,
version: Some("24".into()),
};
let core_def = propose_plan_tool_def(Some(&core));
let core_actions = offered_actions(&core_def);
for (action, _) in KNOWN_ACTIONS {
assert_eq!(
core_actions.contains(&action.to_string()),
!action_requires_supported_host(action),
"Ubuntu Core: {action}"
);
}
let hint = sysknife_types::DistroHint {
id: "debian".into(),
family: DISTRO_FAMILY_DEBIAN,
Expand Down
3 changes: 2 additions & 1 deletion crates/sysknife-daemon/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5079,7 +5079,8 @@ mod tests {
// `AptUpdate` is RiskLevel::Low, so min_role_for_action puts it at
// Observer — yet it runs `sudo apt-get update`. Any exemption keyed on
// the RBAC role therefore lets a privileged mutation through, which is
// why the platform gate does not exempt reads at all.
// why the platform gate does not exempt hard-fenced reads. Portable
// Observer reads still return early without requiring distro detection.
let dir = tempdir().unwrap();
let mut state = test_state(&dir);
state.host_distro = Some(sysknife_core::distro::DistroId::Debian { version: Some(11) });
Expand Down
5 changes: 5 additions & 0 deletions docs/action-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ longer require distro detection after leaving the hard lists. This widens
read-only inspection; mutations still require an eligible host, and the
Low-risk mutating `AptUpdate` remains hard-fenced.

Ubuntu Core is not an eligible host. Its Debian family tag does not make apt
available: the planner limits its catalogue to actions outside the shared
host-policy predicate, as it does for an unknown family. This does not claim
Ubuntu Core execution support.

The MCP surface uses the same routing checks and withholds hard-restricted actions when
detection fails. The planner receives an explicit distribution ID alongside the
family; display text never grants Ubuntu capabilities.
Expand Down
Loading