V6-06: the protected helper copy is a new right, so an installed PC takes it once

Rights are by id and a changed script re-asks nothing, so without this a 2.0.1 install would have kept its task on the
LOCALAPPDATA exe for ever. power-helper-task@protected is helper-takeable: an update by job takes it through the
running helper's reregister (the copy under the signed manifest's engine hash, no click); otherwise the next
interactive start asks once.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-08 20:14:25 +00:00
parent 295711ba29
commit ec35d31c50

View file

@ -28,6 +28,10 @@ pub const RIGHTS: &[(&str, &str)] = &[
// 0.3.24: the same task registered to run whether or not a user is logged on (S4U); a new id, so an install that holds the
// interactive-only form takes it again once at the update (main, 7 October 2026, PC 1)
("power-helper-task@unattended", "the Igneum Power Helper task in its unattended form: it runs whether or not a user is logged on, so a job's or the engine's own start is accepted (src/powertask.rs)"),
// V6-06 (8 October 2026): the same task pointed at the protected copy in %ProgramData%\Igneum\helper; a new id, so an
// install that holds the LOCALAPPDATA form takes it again once (by the running helper's own reregister where the
// signed manifest names the engine hash, else one prompt at the next interactive start)
("power-helper-task@protected", "the Igneum Power Helper task run from a protected copy of the engine that only administrators can write (src/powertask.rs)"),
("boot-task", "the Igneum Miner (boot) task: the engine starts at boot with nobody logged on (src/boot.rs)"),
("firewall-node", "the inbound firewall rule for igneumd.exe (other nodes can dial in)"),
("firewall-miner", "the inbound firewall rule for igneum-miner.exe (a pool's stratum port)"),
@ -278,7 +282,7 @@ pub fn webview2_script(install_dir: &Path) -> String {
/// verb (the helper re-registers its own task from the running build's script: src/powertask.rs). 0.3.24: the task's
/// unattended form is such a right, so an update by job on a PC with nobody at the desk (PC 2, 7 October 2026) takes it
/// without a click, where the deferred rule alone would have left it to "the next interactive start".
pub const HELPER_TAKEABLE: &[&str] = &["power-helper-task@unattended"];
pub const HELPER_TAKEABLE: &[&str] = &["power-helper-task@unattended", "power-helper-task@protected"];
pub fn helper_can_take(missing: &[String], task_registered: bool) -> bool {
task_registered && !missing.is_empty() && missing.iter().all(|m| HELPER_TAKEABLE.contains(&m.as_str()))
@ -419,7 +423,7 @@ mod tests {
assert!(RIGHTS.iter().any(|(id, _)| *id == WEBVIEW2_RIGHT));
// absent runtime: one prompt at install; present at or above the minimum: the right still has to be taken once
// (the manifest records it), but the script installs nothing (its own check); a raised minimum is a new id
let old = m(&["power-helper-task", "power-helper-task@unattended", "boot-task", "firewall-node", "firewall-miner", "webview2-runtime@100.0.0.0", "driver-install-task", WSL2_RIGHT]);
let old = m(&["power-helper-task", "power-helper-task@unattended", "power-helper-task@protected", "boot-task", "firewall-node", "firewall-miner", "webview2-runtime@100.0.0.0", "driver-install-task", WSL2_RIGHT]);
assert_eq!(missing(Some(&old), RIGHTS), vec![WEBVIEW2_RIGHT.to_string()], "a raised minimum is exactly one new right");
assert_eq!(prompts(Event::Install, Some(&old), RIGHTS), 1);
let now = m(&RIGHTS.iter().map(|(i, _)| *i).collect::<Vec<_>>());
@ -467,19 +471,33 @@ mod tests {
#[test]
fn the_unattended_task_form_is_one_new_right_for_a_0_3_23_install() {
let r0323 = m(&["power-helper-task", "boot-task", "firewall-node", "firewall-miner", WEBVIEW2_RIGHT, "driver-install-task"]);
assert_eq!(missing(Some(&r0323), RIGHTS), vec!["power-helper-task@unattended".to_string(), WSL2_RIGHT.to_string()], "2.0: the WSL feature is the second right a 0.3.23 install lacks");
assert_eq!(missing(Some(&r0323), RIGHTS), vec!["power-helper-task@unattended".to_string(), "power-helper-task@protected".to_string(), WSL2_RIGHT.to_string()], "2.0: the WSL feature and the protected copy are the other rights a 0.3.23 install lacks");
assert_eq!(prompts(Event::Install, Some(&r0323), RIGHTS), 1);
let s = script(Path::new("C:\\p\\Igneum Miner\\igneum-app.exe"), Path::new("C:\\p\\Igneum Miner"), Path::new("C:\\u\\AppData\\Local\\igneum"));
assert!(s.contains("-TaskName 'Igneum Power Helper'") && s.contains("-LogonType S4U -RunLevel Highest"), "the rights step registers the unattended form: {s}");
}
/// V6-06, known-failed first (8 October 2026): a changed script re-asks nothing (rights are by id), so an installed
/// 2.0.1 PC would have kept its task on the LOCALAPPDATA exe for ever. The protected copy is a new id; a 2.0.1
/// install lacks exactly it; the running helper may take it itself (its reregister makes the copy, under the
/// signed manifest's engine hash) so an update by job asks no click, and an interactive start asks once otherwise.
#[test]
fn the_protected_helper_copy_is_one_new_right_for_a_2_0_1_install() {
assert!(RIGHTS.iter().any(|(id, _)| *id == "power-helper-task@protected"));
let r201 = m(&["power-helper-task", "power-helper-task@unattended", "boot-task", "firewall-node", "firewall-miner", WEBVIEW2_RIGHT, "driver-install-task", WSL2_RIGHT]);
assert_eq!(missing(Some(&r201), RIGHTS), vec!["power-helper-task@protected".to_string()]);
assert_eq!(prompts(Event::Install, Some(&r201), RIGHTS), 1);
assert!(helper_can_take(&["power-helper-task@protected".to_string()], true));
assert!(!helper_can_take(&["power-helper-task@protected".to_string()], false), "no task registered: the one prompt");
}
/// Igneum 2.0, known-failed first (main, 8 October 2026): the node refused to start on Windows without igneum-prove-host
/// and nothing installed it. The WSL feature is a right the installer's elevated step takes (no distribution, no
/// window); a 0.3.26 install lacks exactly it and asks once at the update.
#[test]
fn the_wsl2_feature_is_a_right_the_installer_takes_once() {
assert!(RIGHTS.iter().any(|(id, _)| *id == WSL2_RIGHT));
let r0326 = m(&["power-helper-task", "power-helper-task@unattended", "boot-task", "firewall-node", "firewall-miner", WEBVIEW2_RIGHT, "driver-install-task"]);
let r0326 = m(&["power-helper-task", "power-helper-task@unattended", "power-helper-task@protected", "boot-task", "firewall-node", "firewall-miner", WEBVIEW2_RIGHT, "driver-install-task"]);
assert_eq!(missing(Some(&r0326), RIGHTS), vec![WSL2_RIGHT.to_string()]);
assert_eq!(prompts(Event::Install, Some(&r0326), RIGHTS), 1);
let s = script(Path::new("C:\\p\\Igneum Miner\\igneum-app.exe"), Path::new("C:\\p\\Igneum Miner"), Path::new("C:\\u\\AppData\\Local\\igneum"));