From 11501ece781475fce4ec0e69671a37656145104f Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:05:34 +0700 Subject: [PATCH 01/39] image declare OpenSSH dependency --- build/usb/package-lists/rigos.list.chroot | 1 + 1 file changed, 1 insertion(+) diff --git a/build/usb/package-lists/rigos.list.chroot b/build/usb/package-lists/rigos.list.chroot index 89304e89..85b9c79b 100644 --- a/build/usb/package-lists/rigos.list.chroot +++ b/build/usb/package-lists/rigos.list.chroot @@ -7,6 +7,7 @@ live-boot live-config linux-image-amd64 network-manager +openssh-server python3 systemd-sysv sudo From fe61a3e06fb5a08966f62f6d75386e5327f39049 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:31:06 +0700 Subject: [PATCH 02/39] ci add alpha8 physical findings applicator --- .github/workflows/alpha8-apply.yml | 43 ++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/alpha8-apply.yml diff --git a/.github/workflows/alpha8-apply.yml b/.github/workflows/alpha8-apply.yml new file mode 100644 index 00000000..b6ee4849 --- /dev/null +++ b/.github/workflows/alpha8-apply.yml @@ -0,0 +1,43 @@ +name: Alpha8 Physical Findings + +on: + push: + branches: + - fix/alpha8-physical-findings + paths: + - .alpha8-patch-trigger + +permissions: + contents: write + +jobs: + apply-and-verify: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + steps: + - name: Check out branch + uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: true + + - name: Apply exact source patch + run: python3 scripts/apply-alpha8-physical-fixes.py + + - name: Install pinned Rust toolchain + uses: dtolnay/rust-toolchain@1.85.1 + with: + components: rustfmt, clippy + + - name: Verify source + run: ./scripts/verify.sh + + - name: Commit verified source + run: | + rm -f .alpha8-patch-trigger + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git diff --cached --check + git commit -m "alpha8 harden physical appliance findings" + git push origin HEAD:fix/alpha8-physical-findings From 0a0fe4f6a5ff67b9017008197e2080da22817e9f Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:33:09 +0700 Subject: [PATCH 03/39] tool add deterministic alpha8 source patch --- scripts/apply-alpha8-physical-fixes.py | 814 +++++++++++++++++++++++++ 1 file changed, 814 insertions(+) create mode 100644 scripts/apply-alpha8-physical-fixes.py diff --git a/scripts/apply-alpha8-physical-fixes.py b/scripts/apply-alpha8-physical-fixes.py new file mode 100644 index 00000000..5fa09d9d --- /dev/null +++ b/scripts/apply-alpha8-physical-fixes.py @@ -0,0 +1,814 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import os +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def read(path: str) -> str: + return (ROOT / path).read_text(encoding="utf-8") + + +def write(path: str, content: str, mode: int | None = None) -> None: + target = ROOT / path + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(content, encoding="utf-8", newline="\n") + if mode is not None: + target.chmod(mode) + + +def replace_once(path: str, old: str, new: str) -> None: + content = read(path) + count = content.count(old) + if count != 1: + raise SystemExit(f"{path}: expected one patch anchor, found {count}") + write(path, content.replace(old, new, 1)) + + +def create(path: str, content: str, mode: int | None = None) -> None: + target = ROOT / path + if target.exists(): + raise SystemExit(f"refusing to overwrite existing file: {path}") + write(path, content, mode) + + +# Exact RandomX thread intent. max-threads-hint remains a percentage hint. +replace_once( + "crates/rigos-config/src/lib.rs", + "pub fn build_runtime(\n", + '''fn exact_cpu_profile_name(algorithm: &str) -> Option<&str> { + if matches!(algorithm, "rx" | "rx/0") { + Some("rx") + } else if algorithm.starts_with("rx/") { + Some(algorithm) + } else { + None + } +} + +fn exact_cpu_profile(count: u16) -> Value { + Value::Array((0..count).map(|_| json!(-1)).collect()) +} + +pub fn build_runtime( +''', +) +replace_once( + "crates/rigos-config/src/lib.rs", + ''' match &sheet.cpu.threads { + Threads::Auto(value) if value == "auto" => {} + Threads::Count(value) if (1..=1024).contains(value) => {} + _ => { + return Err(error( + "RIGOS_FLIGHT_SHEET_INVALID", + Some(filename), + None, + Some("cpu.threads"), + "threads must be auto or 1 through 1024", + )); + } + } +''', + ''' match &sheet.cpu.threads { + Threads::Auto(value) if value == "auto" => {} + Threads::Count(value) if !(1..=1024).contains(value) => { + return Err(error( + "RIGOS_FLIGHT_SHEET_INVALID", + Some(filename), + None, + Some("cpu.threads"), + "threads must be auto or 1 through 1024", + )); + } + Threads::Count(_) if exact_cpu_profile_name(&sheet.algorithm).is_none() => { + return Err(error( + "RIGOS_FLIGHT_SHEET_INVALID", + Some(filename), + None, + Some("cpu.threads"), + "explicit threads currently require a RandomX algorithm", + )); + } + Threads::Count(_) => {} + _ => { + return Err(error( + "RIGOS_FLIGHT_SHEET_INVALID", + Some(filename), + None, + Some("cpu.threads"), + "threads must be auto or 1 through 1024", + )); + } + } +''', +) +replace_once( + "crates/rigos-config/src/lib.rs", + ''' let mut cpu = Map::from_iter([ + ("enabled".into(), Value::Bool(true)), + ("huge-pages".into(), Value::Bool(sheet.cpu.huge_pages)), + ]); + cpu.insert("max-threads-hint".into(), json!(sheet.cpu.max_threads_hint)); + if let Threads::Count(count) = sheet.cpu.threads { + cpu.insert("max-threads-hint".into(), json!(count)); + } + let xmrig = json!({"autosave":false,"background":false,"cpu":cpu,"pools":pools,"api":{"worker-id":worker},"http":{"enabled":false}}); +''', + ''' let mut cpu = Map::from_iter([ + ("enabled".into(), Value::Bool(true)), + ("huge-pages".into(), Value::Bool(sheet.cpu.huge_pages)), + ]); + cpu.insert("max-threads-hint".into(), json!(sheet.cpu.max_threads_hint)); + if let Threads::Count(count) = &sheet.cpu.threads { + let profile_name = exact_cpu_profile_name(&sheet.algorithm).ok_or_else(|| { + error( + "RIGOS_FLIGHT_SHEET_INVALID", + None, + None, + Some("cpu.threads"), + "explicit threads do not have a safe XMRig profile mapping", + ) + })?; + cpu.insert(profile_name.into(), exact_cpu_profile(*count)); + } + let xmrig = json!({"autosave":false,"background":false,"cpu":cpu,"pools":pools,"api":{"worker-id":worker},"http":{"enabled":false}}); +''', +) +replace_once( + "crates/rigos-config/src/lib.rs", + ''' #[test] + fn huge_pages_false_reaches_runtime_config() { +''', + ''' #[test] + fn explicit_randomx_threads_generate_exact_profile() { + let proposal = Proposal { + schema: "rigos.config-proposal/v1".into(), + profile: parse_rig_profile(&profile("native", "FLIGHT_REF=xmr-ssl\\n")).unwrap(), + flight_sheet: FlightSheet { + schema: "rigos.flight-sheet/v1".into(), + name: "xmr-ssl".into(), + coin: "XMR".into(), + backend: "xmrig".into(), + algorithm: "rx/0".into(), + pools: vec![Pool { + host: "pool.example".into(), + port: 443, + tls: true, + priority: 0, + }], + identity_ref: "main-xmr".into(), + worker_template: "{node_name}".into(), + cpu: CpuPolicy { + threads: Threads::Count(2), + huge_pages: true, + max_threads_hint: 100, + }, + }, + provenance: None, + source_sha256: "x".into(), + }; + let identity = IdentityRecord { + schema: "rigos.identity/v1".into(), + alias: "main-xmr".into(), + kind: "mining_identity".into(), + value: "private-value".into(), + created_locally: true, + }; + let (_, xmrig) = build_runtime(&proposal, &identity).unwrap(); + assert_eq!(xmrig["cpu"]["max-threads-hint"], 100); + assert_eq!(xmrig["cpu"]["rx"], json!([-1, -1])); + + let mut unsupported = proposal; + unsupported.flight_sheet.algorithm = "cn/r".into(); + assert!(build_runtime(&unsupported, &identity).is_err()); + } + + #[test] + fn huge_pages_false_reaches_runtime_config() { +''', +) + +# Unprivileged inspection must use a redacted derived config, never the wallet-bearing runtime file. +replace_once( + "crates/rigos-xmrig/src/lib.rs", + ''' let config_path = cmdline + .as_deref() + .and_then(extract_config_path) + .or_else(|| self.explicit_config.clone()); +''', + ''' let config_path = self.explicit_config.clone().or_else(|| { + cmdline + .as_deref() + .and_then(extract_config_path) + }); +''', +) +replace_once( + "crates/rigos-xmrig/src/lib.rs", + ''' algorithm: raw.get("algo").and_then(Value::as_str).map(str::to_owned), + huge_pages_requested: raw + .get("randomx") + .and_then(|v| v.get("huge-pages")) + .and_then(Value::as_bool), + thread_hint: raw.get("threads").and_then(Value::as_u64), +''', + ''' algorithm: raw + .get("algo") + .and_then(Value::as_str) + .or_else(|| { + raw.get("pools") + .and_then(Value::as_array) + .and_then(|pools| pools.first()) + .and_then(|pool| pool.get("algo")) + .and_then(Value::as_str) + }) + .map(str::to_owned), + huge_pages_requested: raw + .get("randomx") + .and_then(|v| v.get("huge-pages")) + .and_then(Value::as_bool) + .or_else(|| raw.pointer("/cpu/huge-pages").and_then(Value::as_bool)), + thread_hint: raw + .get("threads") + .and_then(Value::as_u64) + .or_else(|| { + raw.pointer("/cpu/rx") + .and_then(Value::as_array) + .and_then(|threads| u64::try_from(threads.len()).ok()) + }) + .or_else(|| raw.pointer("/cpu/max-threads-hint").and_then(Value::as_u64)), +''', +) +replace_once( + "crates/rigos-xmrig/src/lib.rs", + ''' #[test] + fn discovers_xmrig_from_synthetic_proc_without_mutation() { +''', + ''' #[test] + fn explicit_redacted_config_overrides_process_secret_path() { + let unique = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + let root = std::env::temp_dir().join(format!("rigos-xmrig-explicit-{unique}")); + let proc_root = root.join("proc"); + let pid_dir = proc_root.join("42"); + fs::create_dir_all(&pid_dir).unwrap(); + let secret = root.join("secret.json"); + let public = root.join("public.json"); + fs::write(&secret, r#"{"pools":[{"url":"secret-pool","user":"SENTINEL_SECRET"}]}"#).unwrap(); + fs::write(&public, r#"{"cpu":{"huge-pages":true,"rx":[-1,-1]},"pools":[{"url":"public-pool","algo":"rx/0"}],"http":{"enabled":false}}"#).unwrap(); + fs::write(pid_dir.join("comm"), "xmrig\\n").unwrap(); + fs::write(pid_dir.join("cmdline"), format!("xmrig\\0--config={}\\0", secret.display())).unwrap(); + fs::write(pid_dir.join("status"), "Name:\\txmrig\\nUid:\\t1000 1000 1000 1000\\n").unwrap(); + fs::write(pid_dir.join("cgroup"), "0::/system.slice/rigos-miner.service\\n").unwrap(); + fs::write(pid_dir.join("stat"), "42 (xmrig) S 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 100 0\\n").unwrap(); + fs::write(proc_root.join("uptime"), "100.0 0.0\\n").unwrap(); + let backend = XmrigBackend { + explicit_executable: None, + explicit_config: Some(public), + probe_version: false, + }; + let result = backend.discover(&MachineContext { proc_root, sys_root: root.join("sys") }); + let _ = fs::remove_dir_all(root); + let snapshot = result.value.unwrap(); + assert_eq!(snapshot.config.pools, vec!["public-pool"]); + assert_eq!(snapshot.config.algorithm.as_deref(), Some("rx/0")); + assert_eq!(snapshot.config.huge_pages_requested, Some(true)); + assert_eq!(snapshot.config.thread_hint, Some(2)); + assert!(!serde_json::to_string(&snapshot).unwrap().contains("SENTINEL_SECRET")); + } + + #[test] + fn discovers_xmrig_from_synthetic_proc_without_mutation() { +''', +) +replace_once( + "crates/rigosd/src/lib.rs", + ''' explicit_config: cli.xmrig_config, +''', + ''' explicit_config: cli + .xmrig_config + .or_else(|| Some(PathBuf::from("/run/rigos/xmrig-public.json"))), +''', +) + +# Appliance scripts and services. +create( + "build/usb/includes.chroot/usr/lib/rigos/rigos-miner-public-config", + r'''#!/usr/bin/python3 +import json +import os +import tempfile +from pathlib import Path + +SOURCE = Path("/var/lib/rigos/current/xmrig.json") +TARGET = Path("/run/rigos/xmrig-public.json") +MAX_BYTES = 2 * 1024 * 1024 + + +def fsync_directory(path: Path) -> None: + descriptor = os.open(path, os.O_RDONLY | os.O_DIRECTORY) + try: + os.fsync(descriptor) + finally: + os.close(descriptor) + + +def main() -> int: + raw = SOURCE.read_bytes() + if len(raw) > MAX_BYTES: + raise RuntimeError("XMRig configuration exceeds the public-view size limit") + value = json.loads(raw) + if not isinstance(value, dict): + raise RuntimeError("XMRig configuration is not an object") + for pool in value.get("pools", []): + if isinstance(pool, dict): + pool.pop("user", None) + pool.pop("pass", None) + http = value.get("http") + if isinstance(http, dict): + http.pop("access-token", None) + value["rigos-public-view"] = { + "schema": "rigos.xmrig-public-config/v1", + "source": "active_revision", + "identity_redacted": True, + } + TARGET.parent.mkdir(mode=0o755, parents=True, exist_ok=True) + with tempfile.NamedTemporaryFile( + mode="w", encoding="utf-8", dir=TARGET.parent, prefix=".xmrig-public-", delete=False + ) as stream: + json.dump(value, stream, sort_keys=True) + stream.write("\n") + stream.flush() + os.fsync(stream.fileno()) + temporary = Path(stream.name) + temporary.chmod(0o644) + os.replace(temporary, TARGET) + fsync_directory(TARGET.parent) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) +''', + 0o755, +) +create( + "build/usb/includes.chroot/usr/lib/rigos/rigos-ssh-identity", + r'''#!/usr/bin/python3 +import hashlib +import json +import os +import stat +import subprocess +import tempfile +from pathlib import Path + +RUNTIME = Path("/run/rigos") +STATE = Path("/var/lib/rigos") +SSH_DIRECTORY = Path("/etc/ssh") +BOOT_ID = Path("/proc/sys/kernel/random/boot_id") +STORE = STATE / "machine" / "ssh-host-keys" +STATUS = RUNTIME / "ssh-identity-status.json" +KEY_TYPES = ("rsa", "ecdsa", "ed25519") +MAX_KEY_BYTES = 1024 * 1024 + + +def key_paths(root: Path): + result = [] + for key_type in KEY_TYPES: + private = root / f"ssh_host_{key_type}_key" + result.append((private, 0o600)) + result.append((Path(f"{private}.pub"), 0o644)) + return result + + +def fsync_directory(path: Path) -> None: + descriptor = os.open(path, os.O_RDONLY | os.O_DIRECTORY) + try: + os.fsync(descriptor) + finally: + os.close(descriptor) + + +def ensure_directory(path: Path, mode: int) -> None: + path.mkdir(mode=mode, parents=True, exist_ok=True) + info = path.lstat() + if not stat.S_ISDIR(info.st_mode) or stat.S_ISLNK(info.st_mode): + raise RuntimeError(f"unsafe directory: {path}") + if info.st_uid != os.geteuid(): + raise RuntimeError(f"directory owner mismatch: {path}") + path.chmod(mode) + + +def valid_key(path: Path, mode: int) -> bool: + try: + info = path.lstat() + except FileNotFoundError: + return False + return ( + stat.S_ISREG(info.st_mode) + and not stat.S_ISLNK(info.st_mode) + and info.st_uid == os.geteuid() + and stat.S_IMODE(info.st_mode) == mode + and 0 < info.st_size <= MAX_KEY_BYTES + ) + + +def key_set_state(root: Path) -> str: + try: + info = root.lstat() + except FileNotFoundError: + return "absent" + if not stat.S_ISDIR(info.st_mode) or stat.S_ISLNK(info.st_mode) or info.st_uid != os.geteuid() or stat.S_IMODE(info.st_mode) != 0o700: + return "invalid" + expected = {path.name for path, _mode in key_paths(root)} + observed = {entry.name for entry in root.iterdir()} + if not observed: + return "absent" + if observed != expected: + return "invalid" + return "complete" if all(valid_key(path, mode) for path, mode in key_paths(root)) else "invalid" + + +def atomic_copy(source: Path, destination: Path, mode: int) -> None: + data = source.read_bytes() + if not data or len(data) > MAX_KEY_BYTES: + raise RuntimeError(f"invalid SSH host key size: {source}") + descriptor, name = tempfile.mkstemp(prefix=f".{destination.name}-", dir=destination.parent) + temporary = Path(name) + try: + os.fchmod(descriptor, mode) + with os.fdopen(descriptor, "wb", closefd=True) as stream: + stream.write(data) + stream.flush() + os.fsync(stream.fileno()) + os.replace(temporary, destination) + destination.chmod(mode) + fsync_directory(destination.parent) + finally: + temporary.unlink(missing_ok=True) + + +def live_keys_ready() -> bool: + return all(valid_key(path, mode) for path, mode in key_paths(SSH_DIRECTORY)) + + +def remove_live_keys() -> None: + for path, _mode in key_paths(SSH_DIRECTORY): + path.unlink(missing_ok=True) + + +def generate_live_keys() -> None: + SSH_DIRECTORY.mkdir(mode=0o755, parents=True, exist_ok=True) + remove_live_keys() + result = subprocess.run(["/usr/bin/ssh-keygen", "-A"], check=False) + if result.returncode != 0 or not live_keys_ready(): + raise RuntimeError("SSH host key generation failed") + + +def persist_live_keys() -> None: + ensure_directory(STORE, 0o700) + for source, mode in key_paths(SSH_DIRECTORY): + if not valid_key(source, mode): + raise RuntimeError(f"live SSH host key is invalid: {source}") + atomic_copy(source, STORE / source.name, mode) + if key_set_state(STORE) != "complete": + raise RuntimeError("persistent SSH host key store is incomplete") + + +def restore_live_keys() -> None: + if key_set_state(STORE) != "complete": + raise RuntimeError("persistent SSH host key store is unavailable") + SSH_DIRECTORY.mkdir(mode=0o755, parents=True, exist_ok=True) + remove_live_keys() + for source, mode in key_paths(STORE): + atomic_copy(source, SSH_DIRECTORY / source.name, mode) + if not live_keys_ready(): + raise RuntimeError("restored SSH host keys failed validation") + + +def write_status(value: dict) -> None: + RUNTIME.mkdir(mode=0o755, parents=True, exist_ok=True) + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", dir=RUNTIME, prefix=".ssh-identity-", delete=False) as stream: + json.dump(value, stream, sort_keys=True) + stream.write("\n") + stream.flush() + os.fsync(stream.fileno()) + temporary = Path(stream.name) + temporary.chmod(0o644) + os.replace(temporary, STATUS) + fsync_directory(RUNTIME) + + +def main() -> int: + boot_id = BOOT_ID.read_text(encoding="ascii").strip() + try: + state_status = json.loads((RUNTIME / "state-status.json").read_text(encoding="utf-8")) + if state_status.get("schema") != "rigos.state-status/v1" or state_status.get("boot_id") != boot_id or state_status.get("outcome") != "ready": + raise RuntimeError("persistent state is not ready for this boot") + persistent_state = key_set_state(STORE) + if persistent_state == "complete": + restore_live_keys() + action = "restored" + elif persistent_state == "absent": + generate_live_keys() + persist_live_keys() + action = "created" + else: + raise RuntimeError("persistent SSH host key store is incomplete or unsafe") + fingerprints = { + key_type: hashlib.sha256((SSH_DIRECTORY / f"ssh_host_{key_type}_key.pub").read_bytes()).hexdigest() + for key_type in KEY_TYPES + } + write_status({"schema":"rigos.ssh-identity-status/v1","boot_id":boot_id,"outcome":"ready","action":action,"persisted":True,"public_key_sha256":fingerprints,"reason":None}) + return 0 + except (OSError, ValueError, json.JSONDecodeError, RuntimeError) as error: + write_status({"schema":"rigos.ssh-identity-status/v1","boot_id":boot_id,"outcome":"error","action":None,"persisted":False,"public_key_sha256":{},"reason":str(error)}) + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) +''', + 0o755, +) +create( + "build/usb/includes.chroot/usr/lib/rigos/rigos-remote-access-probe", + r'''#!/usr/bin/python3 +import json +import os +import subprocess +import tempfile +from pathlib import Path + +RUNTIME = Path("/run/rigos") +BOOT_ID = Path("/proc/sys/kernel/random/boot_id") +STATUS = RUNTIME / "recovery-access-status.json" +IDENTITY_STATUS = RUNTIME / "ssh-identity-status.json" +SSH_PORT = 22 + + +def unit_state(action: str, name: str) -> bool: + return subprocess.run(["/usr/bin/systemctl", action, "--quiet", name], check=False).returncode == 0 + + +def tcp_listener(path: Path, port: int) -> bool: + try: + lines = path.read_text(encoding="ascii").splitlines()[1:] + except OSError: + return False + for line in lines: + fields = line.split() + if len(fields) < 4 or fields[3] != "0A": + continue + try: + observed_port = int(fields[1].rsplit(":", 1)[1], 16) + except (IndexError, ValueError): + continue + if observed_port == port: + return True + return False + + +def fsync_directory(path: Path) -> None: + descriptor = os.open(path, os.O_RDONLY | os.O_DIRECTORY) + try: + os.fsync(descriptor) + finally: + os.close(descriptor) + + +def main() -> int: + boot_id = BOOT_ID.read_text(encoding="ascii").strip() + status = json.loads(STATUS.read_text(encoding="utf-8")) + identity = json.loads(IDENTITY_STATUS.read_text(encoding="utf-8")) + if status.get("boot_id") != boot_id or identity.get("boot_id") != boot_id: + raise RuntimeError("remote access evidence belongs to another boot") + enabled = unit_state("is-enabled", "ssh.service") + active = unit_state("is-active", "ssh.service") + listener_ipv4 = tcp_listener(Path("/proc/net/tcp"), SSH_PORT) + listener_ipv6 = tcp_listener(Path("/proc/net/tcp6"), SSH_PORT) + listening = listener_ipv4 or listener_ipv6 + if enabled and active and listening: + remote_access = "active" + elif enabled and not listening: + remote_access = "enabled_no_listener" + elif listening and not active: + remote_access = "listener_without_service" + else: + remote_access = "inactive" + operational = status.get("state_outcome") == "ready" and status.get("local_console_access") is True and identity.get("outcome") == "ready" and identity.get("persisted") is True and remote_access == "active" + status.update({"mode":"operational" if operational else "recovery","remote_access":remote_access,"remote_protocol":"ssh","remote_port":SSH_PORT,"ssh_service_enabled":enabled,"ssh_service_active":active,"ssh_listener_ipv4":listener_ipv4,"ssh_listener_ipv6":listener_ipv6,"ssh_host_key_action":identity.get("action"),"ssh_host_keys_persisted":identity.get("persisted") is True}) + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", dir=RUNTIME, prefix=".recovery-access-", delete=False) as stream: + json.dump(status, stream, sort_keys=True) + stream.write("\n") + stream.flush() + os.fsync(stream.fileno()) + temporary = Path(stream.name) + temporary.chmod(0o644) + os.replace(temporary, STATUS) + fsync_directory(RUNTIME) + return 0 if operational else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) +''', + 0o755, +) + +create("build/usb/includes.chroot/etc/systemd/system/rigos-ssh-identity.service", '''[Unit] +Description=Establish persistent RIGOS SSH machine identity +After=rigos-state-ready.service +Requires=rigos-state-ready.service +Before=ssh.service + +[Service] +Type=oneshot +ExecStart=/usr/lib/rigos/rigos-ssh-identity +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +''') +create("build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-status.service", '''[Unit] +Description=Observe RIGOS remote access truth +After=network-online.target ssh.service rigos-ssh-identity.service +Wants=network-online.target +Requires=ssh.service rigos-ssh-identity.service + +[Service] +Type=oneshot +ExecStart=/usr/lib/rigos/rigos-remote-access-probe +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +''') +create("build/usb/includes.chroot/etc/systemd/system/rigos-miner-public-status.service", '''[Unit] +Description=Publish redacted RIGOS miner configuration truth +After=rigos-miner.service +Requires=rigos-state-ready.service +ConditionPathExists=/var/lib/rigos/xmrig.json + +[Service] +Type=oneshot +ExecStart=/usr/lib/rigos/rigos-miner-public-config +''') +create("build/usb/includes.chroot/etc/systemd/system/ssh.service.d/rigos.conf", '''[Unit] +Requires=rigos-ssh-identity.service +After=rigos-ssh-identity.service +''') +create("build/usb/includes.chroot/etc/ssh/sshd_config.d/rigos.conf", '''PasswordAuthentication yes +KbdInteractiveAuthentication no +PermitRootLogin no +AllowUsers rigosadmin +X11Forwarding no +AllowAgentForwarding no +AllowTcpForwarding no +PermitTunnel no +GatewayPorts no +''') + +replace_once( + "build/usb/hooks/010-rigos.chroot", + "install -d -m 0755 /usr/lib/rigos\n", + '''install -d -m 0755 /usr/lib/rigos /usr/local/bin +ln -sfn /usr/lib/rigos/rigosd /usr/local/bin/rigosd +ln -sfn /usr/lib/rigos/rigosctl /usr/local/bin/rigosctl +rm -f /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub +rm -f /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key.pub +rm -f /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key.pub +''', +) +replace_once( + "build/usb/hooks/010-rigos.chroot", + "/usr/lib/rigos/rigos-identity-seed /usr/lib/rigos/xmrig\n", + "/usr/lib/rigos/rigos-identity-seed /usr/lib/rigos/rigos-ssh-identity /usr/lib/rigos/rigos-remote-access-probe /usr/lib/rigos/rigos-miner-public-config /usr/lib/rigos/xmrig\n", +) +replace_once( + "build/usb/hooks/010-rigos.chroot", + "systemctl enable NetworkManager.service rigos-state.service rigos-recovery-access.service rigos-state-ready.service rigos-profile-apply.service rigos-hugepages.service rigos-firstboot.service rigos-miner.service tmp.mount\n", + "systemctl enable NetworkManager.service ssh.service rigos-state.service rigos-recovery-access.service rigos-state-ready.service rigos-profile-apply.service rigos-ssh-identity.service rigos-remote-access-status.service rigos-hugepages.service rigos-firstboot.service rigos-miner.service tmp.mount\nsystemctl disable ssh.socket 2>/dev/null || true\n", +) +replace_once( + "build/usb/includes.chroot/etc/systemd/system/rigos-miner.service", + "Wants=network-online.target\n", + "Wants=network-online.target rigos-miner-public-status.service\n", +) + +# Ordering and verification gates. +replace_once( + "scripts/verify-systemd-ordering.py", + ''' "rigos-state-ready.service", "rigos-profile-apply.service", + "rigos-firstboot.service", "rigos-hugepages.service", "rigos-miner.service", +''', + ''' "rigos-state-ready.service", "rigos-profile-apply.service", + "rigos-firstboot.service", "rigos-ssh-identity.service", + "rigos-remote-access-status.service", "rigos-miner-public-status.service", + "rigos-hugepages.service", "rigos-miner.service", +''', +) +replace_once( + "scripts/verify-systemd-ordering.py", + ' ready = units["rigos-state-ready.service"]\n', + ''' ssh_identity = units["rigos-ssh-identity.service"] + includes(ssh_identity.words("Unit", "After"), {"rigos-state-ready.service"}, "SSH identity must follow state readiness") + includes(ssh_identity.words("Unit", "Requires"), {"rigos-state-ready.service"}, "SSH identity must require state readiness") + includes(ssh_identity.words("Unit", "Before"), {"ssh.service"}, "SSH identity must precede sshd") + + remote_access = units["rigos-remote-access-status.service"] + includes(remote_access.words("Unit", "After"), {"ssh.service", "rigos-ssh-identity.service"}, "remote access truth ordering is incomplete") + includes(remote_access.words("Unit", "Requires"), {"ssh.service", "rigos-ssh-identity.service"}, "remote access truth dependencies are incomplete") + + public_status = units["rigos-miner-public-status.service"] + includes(public_status.words("Unit", "After"), {"rigos-miner.service"}, "public miner status must follow miner") + includes(public_status.words("Unit", "Requires"), {"rigos-state-ready.service"}, "public miner status must require ready state") + + ready = units["rigos-state-ready.service"] +''', +) +replace_once( + "scripts/verify.sh", + ''' build/usb/includes.chroot/usr/lib/rigos/rigos-miner-gate \\ + scripts/verify-systemd-ordering.py +''', + ''' build/usb/includes.chroot/usr/lib/rigos/rigos-miner-gate \\ + build/usb/includes.chroot/usr/lib/rigos/rigos-ssh-identity \\ + build/usb/includes.chroot/usr/lib/rigos/rigos-remote-access-probe \\ + build/usb/includes.chroot/usr/lib/rigos/rigos-miner-public-config \\ + scripts/verify-systemd-ordering.py +''', +) +replace_once( + "scripts/verify.sh", + "grep -Fq 'ExecCondition=/usr/lib/rigos/rigos-miner-gate' build/usb/includes.chroot/etc/systemd/system/rigos-miner.service\n", + '''grep -Fq 'ExecCondition=/usr/lib/rigos/rigos-miner-gate' build/usb/includes.chroot/etc/systemd/system/rigos-miner.service +grep -Fq 'rigos-miner-public-status.service' build/usb/includes.chroot/etc/systemd/system/rigos-miner.service +grep -Fq 'Before=ssh.service' build/usb/includes.chroot/etc/systemd/system/rigos-ssh-identity.service +grep -Fq 'Requires=rigos-ssh-identity.service' build/usb/includes.chroot/etc/systemd/system/ssh.service.d/rigos.conf +grep -Fq 'After=network-online.target ssh.service rigos-ssh-identity.service' build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-status.service +grep -Fq 'ln -sfn /usr/lib/rigos/rigosd /usr/local/bin/rigosd' build/usb/hooks/010-rigos.chroot +grep -Fq 'ln -sfn /usr/lib/rigos/rigosctl /usr/local/bin/rigosctl' build/usb/hooks/010-rigos.chroot +grep -Fq 'systemctl disable ssh.socket' build/usb/hooks/010-rigos.chroot +grep -Fqx 'AllowUsers rigosadmin' build/usb/includes.chroot/etc/ssh/sshd_config.d/rigos.conf +grep -Fqx 'PermitRootLogin no' build/usb/includes.chroot/etc/ssh/sshd_config.d/rigos.conf +''', +) + +# Keep the image verifier authoritative without requiring a physical boot. +replace_once( + "scripts/verify-usb-appliance.sh", + " etc/systemd/system/rigos-recovery-access.service \\\n", + " etc/systemd/system/rigos-recovery-access.service \\\n etc/systemd/system/rigos-ssh-identity.service \\\n etc/systemd/system/rigos-remote-access-status.service \\\n etc/systemd/system/rigos-miner-public-status.service \\\n etc/systemd/system/ssh.service.d/rigos.conf \\\n etc/ssh/sshd_config.d/rigos.conf \\\n", +) +replace_once( + "scripts/verify-usb-appliance.sh", + " usr/lib/rigos/rigosd usr/lib/rigos/rigosctl \\\n", + " usr/lib/rigos/rigosd usr/lib/rigos/rigosctl usr/local/bin/rigosd usr/local/bin/rigosctl \\\n", +) +replace_once( + "scripts/verify-usb-appliance.sh", + "usr/lib/rigos/rigos-miner-gate usr/lib/rigos/xmrig", + "usr/lib/rigos/rigos-miner-gate usr/lib/rigos/rigos-ssh-identity usr/lib/rigos/rigos-remote-access-probe usr/lib/rigos/rigos-miner-public-config usr/lib/rigos/xmrig", +) +replace_once( + "scripts/verify-usb-appliance.sh", + 'python3 -m py_compile "$temporary/root/usr/lib/rigos/rigos-miner-gate"\n', + '''python3 -m py_compile "$temporary/root/usr/lib/rigos/rigos-miner-gate" +python3 -m py_compile "$temporary/root/usr/lib/rigos/rigos-ssh-identity" +python3 -m py_compile "$temporary/root/usr/lib/rigos/rigos-remote-access-probe" +python3 -m py_compile "$temporary/root/usr/lib/rigos/rigos-miner-public-config" +''', +) +replace_once( + "scripts/verify-usb-appliance.sh", + '''rigosctl_path="$(PATH="$temporary/root/usr/local/sbin:$temporary/root/usr/bin" command -v rigosctl)" +[[ "$rigosctl_path" == "$temporary/root/usr/local/sbin/rigosctl" && -x "$rigosctl_path" ]] || die 'rigosctl is not executable in the appliance PATH' +''', + '''user_path="$temporary/root/usr/local/bin:$temporary/root/usr/bin:$temporary/root/bin" +rigosd_path="$(PATH="$user_path" command -v rigosd)" +rigosctl_path="$(PATH="$user_path" command -v rigosctl)" +[[ "$rigosd_path" == "$temporary/root/usr/local/bin/rigosd" && -x "$rigosd_path" ]] || die 'rigosd is not executable in the user appliance PATH' +[[ "$rigosctl_path" == "$temporary/root/usr/local/bin/rigosctl" && -x "$rigosctl_path" ]] || die 'rigosctl is not executable in the user appliance PATH' +''', +) +replace_once( + "scripts/verify-usb-appliance.sh", + 'unsquashfs -no-progress -d "$temporary/root" "$temporary/a/live/filesystem.squashfs" \\\n', + '''if unsquashfs -ll "$temporary/a/live/filesystem.squashfs" | grep -Eq '/etc/ssh/ssh_host_(rsa|ecdsa|ed25519)_key$'; then + die 'image contains a baked SSH private host key' +fi +unsquashfs -no-progress -d "$temporary/root" "$temporary/a/live/filesystem.squashfs" \\ +''', +) + +# Remove the one-shot patch mechanism from the resulting source commit. +(ROOT / "scripts/apply-alpha8-physical-fixes.py").unlink() +workflow = ROOT / ".github/workflows/alpha8-apply.yml" +workflow.unlink() +print("Alpha8 physical findings applied") From d9cd9e3d86953cd1813dcf4690599276ce5df053 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:33:47 +0700 Subject: [PATCH 04/39] ci trigger alpha8 source application --- .github/workflows/alpha8-apply.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/alpha8-apply.yml b/.github/workflows/alpha8-apply.yml index b6ee4849..dbc933ca 100644 --- a/.github/workflows/alpha8-apply.yml +++ b/.github/workflows/alpha8-apply.yml @@ -5,6 +5,7 @@ on: branches: - fix/alpha8-physical-findings paths: + - .github/workflows/alpha8-apply.yml - .alpha8-patch-trigger permissions: From 739d8a531fb66590085415d2c36977a4b080b133 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:40:29 +0700 Subject: [PATCH 05/39] runtime render exact miner policy --- .../usr/lib/rigos/rigos-runtime-render | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 build/usb/includes.chroot/usr/lib/rigos/rigos-runtime-render diff --git a/build/usb/includes.chroot/usr/lib/rigos/rigos-runtime-render b/build/usb/includes.chroot/usr/lib/rigos/rigos-runtime-render new file mode 100644 index 00000000..9e5a0fc2 --- /dev/null +++ b/build/usb/includes.chroot/usr/lib/rigos/rigos-runtime-render @@ -0,0 +1,146 @@ +#!/usr/bin/python3 +import copy +import grp +import hashlib +import json +import os +import tempfile +from pathlib import Path + +STATE = Path(os.environ.get("RIGOS_STATE_PATH", "/var/lib/rigos")) +RUNTIME = Path(os.environ.get("RIGOS_RUNTIME_PATH", "/run/rigos")) +PRIVATE_CONFIG = RUNTIME / "xmrig.json" +PUBLIC_CONFIG = RUNTIME / "xmrig-public.json" +STATUS = RUNTIME / "runtime-config-status.json" +MAX_JSON_BYTES = 2 * 1024 * 1024 + + +def read_object(path: Path) -> dict: + raw = path.read_bytes() + if len(raw) > MAX_JSON_BYTES: + raise RuntimeError(f"JSON exceeds size limit: {path}") + value = json.loads(raw) + if not isinstance(value, dict): + raise RuntimeError(f"JSON root is not an object: {path}") + return value + + +def fsync_directory(path: Path) -> None: + descriptor = os.open(path, os.O_RDONLY | os.O_DIRECTORY) + try: + os.fsync(descriptor) + finally: + os.close(descriptor) + + +def atomic_json(path: Path, value: dict, mode: int, gid: int | None = None) -> None: + path.parent.mkdir(mode=0o755, parents=True, exist_ok=True) + descriptor, name = tempfile.mkstemp(prefix=f".{path.name}-", dir=path.parent) + temporary = Path(name) + try: + os.fchmod(descriptor, mode) + if gid is not None: + os.fchown(descriptor, 0, gid) + with os.fdopen(descriptor, "w", encoding="utf-8", closefd=True) as stream: + json.dump(value, stream, sort_keys=True) + stream.write("\n") + stream.flush() + os.fsync(stream.fileno()) + os.replace(temporary, path) + path.chmod(mode) + fsync_directory(path.parent) + finally: + temporary.unlink(missing_ok=True) + + +def exact_profile_name(algorithm: str) -> str: + if algorithm in ("rx", "rx/0"): + return "rx" + if algorithm.startswith("rx/"): + return algorithm + raise RuntimeError("explicit threads require a RandomX algorithm") + + +def render() -> tuple[dict, dict, dict]: + current = (STATE / "current").resolve(strict=True) + if current.parent != (STATE / "revisions").resolve(strict=True): + raise RuntimeError("current revision is outside the revisions directory") + policy = read_object(current / "policy.json") + if policy.get("schema") != "rigos.policy/v1": + raise RuntimeError("policy schema mismatch") + sheet_name = policy.get("active_flight_sheet") + if not isinstance(sheet_name, str) or not sheet_name or "/" in sheet_name or ".." in sheet_name: + raise RuntimeError("active flight sheet name is invalid") + sheet = read_object(current / "flight-sheets" / f"{sheet_name}.json") + if sheet.get("schema") != "rigos.flight-sheet/v1" or sheet.get("backend") != "xmrig": + raise RuntimeError("flight sheet schema or backend mismatch") + committed = read_object(current / "xmrig.json") + runtime = copy.deepcopy(committed) + cpu = runtime.get("cpu") + if not isinstance(cpu, dict): + raise RuntimeError("committed XMRig CPU policy is missing") + algorithm = sheet.get("algorithm") + if not isinstance(algorithm, str) or not algorithm: + raise RuntimeError("flight sheet algorithm is invalid") + pools = runtime.get("pools") + if not isinstance(pools, list) or not pools: + raise RuntimeError("committed XMRig pools are missing") + if any(not isinstance(pool, dict) or pool.get("algo") != algorithm for pool in pools): + raise RuntimeError("committed pool algorithm disagrees with the flight sheet") + sheet_cpu = sheet.get("cpu") + if not isinstance(sheet_cpu, dict): + raise RuntimeError("flight sheet CPU policy is missing") + threads = sheet_cpu.get("threads") + profile = None + exact_threads = None + if isinstance(threads, int) and not isinstance(threads, bool): + if not 1 <= threads <= 1024: + raise RuntimeError("explicit thread count is out of range") + profile = exact_profile_name(algorithm) + cpu["max-threads-hint"] = sheet_cpu.get("max_threads_hint", 100) + cpu[profile] = [-1] * threads + exact_threads = threads + elif threads != "auto": + raise RuntimeError("threads must be auto or an integer") + public = copy.deepcopy(runtime) + for pool in public.get("pools", []): + if isinstance(pool, dict): + pool.pop("user", None) + pool.pop("pass", None) + http = public.get("http") + if isinstance(http, dict): + http.pop("access-token", None) + public["rigos-public-view"] = { + "schema": "rigos.xmrig-public-config/v1", + "identity_redacted": True, + "source_revision": current.name, + } + status = { + "schema": "rigos.runtime-config-status/v1", + "outcome": "ready", + "revision": current.name, + "algorithm": algorithm, + "thread_mode": "exact" if exact_threads is not None else "auto", + "exact_threads": exact_threads, + "profile": profile, + "private_sha256": hashlib.sha256( + (json.dumps(runtime, sort_keys=True) + "\n").encode("utf-8") + ).hexdigest(), + "identity_redacted_public_view": True, + } + return runtime, public, status + + +def main() -> int: + runtime, public, status = render() + gid = None + if os.environ.get("RIGOS_RENDER_SKIP_CHOWN") != "1": + gid = grp.getgrnam("rigos").gr_gid + atomic_json(PRIVATE_CONFIG, runtime, 0o640, gid) + atomic_json(PUBLIC_CONFIG, public, 0o644) + atomic_json(STATUS, status, 0o644) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From bed746a0eaa351ba5d37525e2ff99f521efa2fdd Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:40:49 +0700 Subject: [PATCH 06/39] runtime add miner render authority --- .../system/rigos-runtime-render.service | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 build/usb/includes.chroot/etc/systemd/system/rigos-runtime-render.service diff --git a/build/usb/includes.chroot/etc/systemd/system/rigos-runtime-render.service b/build/usb/includes.chroot/etc/systemd/system/rigos-runtime-render.service new file mode 100644 index 00000000..81b1bd55 --- /dev/null +++ b/build/usb/includes.chroot/etc/systemd/system/rigos-runtime-render.service @@ -0,0 +1,23 @@ +[Unit] +Description=Render RIGOS runtime miner configuration +After=rigos-state-ready.service rigos-profile-apply.service +Requires=rigos-state-ready.service rigos-profile-apply.service +Before=rigos-hugepages.service rigos-miner.service + +[Service] +Type=oneshot +ExecStart=/usr/lib/rigos/rigos-runtime-render +NoNewPrivileges=yes +PrivateTmp=yes +ProtectHome=yes +ProtectSystem=strict +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectControlGroups=yes +RestrictAddressFamilies=AF_UNIX +RestrictNamespaces=yes +LockPersonality=yes +ReadWritePaths=/run/rigos + +[Install] +WantedBy=multi-user.target From 493cc719522fcf215db52e12cd5f605e9ee75f14 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:41:23 +0700 Subject: [PATCH 07/39] miner override runtime render config --- .../system/rigos-miner.service.d/runtime-render.conf | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 build/usb/includes.chroot/etc/systemd/system/rigos-miner.service.d/runtime-render.conf diff --git a/build/usb/includes.chroot/etc/systemd/system/rigos-miner.service.d/runtime-render.conf b/build/usb/includes.chroot/etc/systemd/system/rigos-miner.service.d/runtime-render.conf new file mode 100644 index 00000000..3f2a44ee --- /dev/null +++ b/build/usb/includes.chroot/etc/systemd/system/rigos-miner.service.d/runtime-render.conf @@ -0,0 +1,10 @@ +[Unit] +After=rigos-runtime-render.service +Requires=rigos-runtime-render.service +ConditionPathExists= +ConditionPathExists=/run/rigos/xmrig.json + +[Service] +ExecCondition=/usr/lib/rigos/rigos-runtime-gate +ExecStart= +ExecStart=/usr/lib/rigos/xmrig --config=/run/rigos/xmrig.json From 3c4e19ad16d2bab164fd4e6e381dbd5bb9896590 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:41:58 +0700 Subject: [PATCH 08/39] runtime render wait for committed revision --- .../etc/systemd/system/rigos-runtime-render.service | 1 + 1 file changed, 1 insertion(+) diff --git a/build/usb/includes.chroot/etc/systemd/system/rigos-runtime-render.service b/build/usb/includes.chroot/etc/systemd/system/rigos-runtime-render.service index 81b1bd55..166dfc49 100644 --- a/build/usb/includes.chroot/etc/systemd/system/rigos-runtime-render.service +++ b/build/usb/includes.chroot/etc/systemd/system/rigos-runtime-render.service @@ -3,6 +3,7 @@ Description=Render RIGOS runtime miner configuration After=rigos-state-ready.service rigos-profile-apply.service Requires=rigos-state-ready.service rigos-profile-apply.service Before=rigos-hugepages.service rigos-miner.service +ConditionPathExists=/var/lib/rigos/current [Service] Type=oneshot From 8eef1954169eb03854c133a4e6329fb1f4d0f226 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:42:18 +0700 Subject: [PATCH 09/39] miner gate rendered runtime truth --- .../usr/lib/rigos/rigos-runtime-gate | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 build/usb/includes.chroot/usr/lib/rigos/rigos-runtime-gate diff --git a/build/usb/includes.chroot/usr/lib/rigos/rigos-runtime-gate b/build/usb/includes.chroot/usr/lib/rigos/rigos-runtime-gate new file mode 100644 index 00000000..c028d554 --- /dev/null +++ b/build/usb/includes.chroot/usr/lib/rigos/rigos-runtime-gate @@ -0,0 +1,66 @@ +#!/usr/bin/python3 +import argparse +import json +import sys +from pathlib import Path + +MAX_JSON_BYTES = 2 * 1024 * 1024 + + +def emit(outcome: str, reason: str | None = None) -> None: + value = { + "schema": "rigos.runtime-gate/v1", + "outcome": outcome, + "reason": reason, + } + print(json.dumps(value, sort_keys=True), file=sys.stdout if outcome == "allowed" else sys.stderr) + + +def read_object(path: Path) -> dict: + raw = path.read_bytes() + if len(raw) > MAX_JSON_BYTES: + raise ValueError("JSON exceeds size limit") + value = json.loads(raw) + if not isinstance(value, dict): + raise ValueError("JSON root is not an object") + return value + + +def deny(reason: str) -> int: + emit("denied", reason) + return 2 + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--state", type=Path, default=Path("/var/lib/rigos")) + parser.add_argument("--runtime", type=Path, default=Path("/run/rigos")) + args = parser.parse_args() + try: + revision = (args.state / "current").resolve(strict=True).name + status = read_object(args.runtime / "runtime-config-status.json") + config = read_object(args.runtime / "xmrig.json") + except (OSError, ValueError, json.JSONDecodeError): + return deny("runtime_truth_unreadable") + if status.get("schema") != "rigos.runtime-config-status/v1" or status.get("outcome") != "ready": + return deny("runtime_status_invalid") + if status.get("revision") != revision: + return deny("runtime_revision_stale") + pools = config.get("pools") + cpu = config.get("cpu") + if not isinstance(pools, list) or not pools or not isinstance(cpu, dict): + return deny("runtime_config_invalid") + exact_threads = status.get("exact_threads") + profile = status.get("profile") + if exact_threads is not None: + if not isinstance(exact_threads, int) or exact_threads < 1 or not isinstance(profile, str): + return deny("runtime_thread_truth_invalid") + entries = cpu.get(profile) + if not isinstance(entries, list) or len(entries) != exact_threads: + return deny("runtime_thread_profile_mismatch") + emit("allowed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From d8d50504f4889574966984554e8180ae3b4fb06d Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:43:36 +0700 Subject: [PATCH 10/39] image wire runtime authority and user CLI --- build/usb/hooks/010-rigos.chroot | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/build/usb/hooks/010-rigos.chroot b/build/usb/hooks/010-rigos.chroot index e29c5d1d..f7e37ac5 100644 --- a/build/usb/hooks/010-rigos.chroot +++ b/build/usb/hooks/010-rigos.chroot @@ -5,11 +5,14 @@ useradd --system --home-dir /var/lib/rigos --shell /usr/sbin/nologin --user-grou useradd --create-home --shell /bin/bash --groups sudo rigosadmin passwd --lock rigosadmin install -d -o rigos -g rigos -m 0750 /var/lib/rigos -install -d -m 0755 /usr/lib/rigos +install -d -m 0755 /usr/lib/rigos /usr/local/bin +ln -sfn /usr/lib/rigos/rigosd /usr/local/bin/rigosd +ln -sfn /usr/lib/rigos/rigosctl /usr/local/bin/rigosctl systemd-tmpfiles --create /usr/lib/tmpfiles.d/rigos.conf -chmod 0755 /usr/local/sbin/rigos-firstboot /usr/local/sbin/rigos-recovery-access /usr/local/sbin/rigos-state-orchestrate /usr/lib/rigos/rigos-miner-gate /usr/lib/rigos/rigos-state-init /usr/lib/rigos/rigos-state-ready /usr/lib/rigos/rigos-config /usr/lib/rigos/rigos-performance /usr/lib/rigos/rigos-lifecycle-cycles /usr/lib/rigos/rigos-identity-seed /usr/lib/rigos/xmrig -systemctl enable NetworkManager.service rigos-state.service rigos-recovery-access.service rigos-state-ready.service rigos-profile-apply.service rigos-hugepages.service rigos-firstboot.service rigos-miner.service tmp.mount +chmod 0755 /usr/local/sbin/rigos-firstboot /usr/local/sbin/rigos-recovery-access /usr/local/sbin/rigos-state-orchestrate /usr/lib/rigos/rigos-miner-gate /usr/lib/rigos/rigos-runtime-render /usr/lib/rigos/rigos-runtime-gate /usr/lib/rigos/rigos-state-init /usr/lib/rigos/rigos-state-ready /usr/lib/rigos/rigos-config /usr/lib/rigos/rigos-performance /usr/lib/rigos/rigos-lifecycle-cycles /usr/lib/rigos/rigos-identity-seed /usr/lib/rigos/xmrig +systemctl enable NetworkManager.service ssh.service rigos-state.service rigos-recovery-access.service rigos-state-ready.service rigos-profile-apply.service rigos-runtime-render.service rigos-hugepages.service rigos-firstboot.service rigos-miner.service tmp.mount +systemctl disable ssh.socket 2>/dev/null || true systemctl disable apt-daily.timer apt-daily-upgrade.timer logrotate.timer fstrim.timer 2>/dev/null || true systemctl disable systemd-journald-audit.socket 2>/dev/null || true From ab2baf5aed0ff5f828291073a33fa10087fb1c80 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:43:51 +0700 Subject: [PATCH 11/39] remote access report listener truth --- .../usr/lib/rigos/rigos-remote-access-probe | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 build/usb/includes.chroot/usr/lib/rigos/rigos-remote-access-probe diff --git a/build/usb/includes.chroot/usr/lib/rigos/rigos-remote-access-probe b/build/usb/includes.chroot/usr/lib/rigos/rigos-remote-access-probe new file mode 100644 index 00000000..b7b2d004 --- /dev/null +++ b/build/usb/includes.chroot/usr/lib/rigos/rigos-remote-access-probe @@ -0,0 +1,99 @@ +#!/usr/bin/python3 +import json +import os +import subprocess +import tempfile +from pathlib import Path + +RUNTIME = Path("/run/rigos") +STATUS = RUNTIME / "recovery-access-status.json" +BOOT_ID = Path("/proc/sys/kernel/random/boot_id") +SSH_PORT = 22 + + +def unit_state(action: str, unit: str) -> bool: + return subprocess.run( + ["/usr/bin/systemctl", action, "--quiet", unit], + check=False, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ).returncode == 0 + + +def has_listener(path: Path, port: int) -> bool: + try: + lines = path.read_text(encoding="ascii").splitlines()[1:] + except OSError: + return False + for line in lines: + fields = line.split() + if len(fields) < 4 or fields[3] != "0A": + continue + try: + observed = int(fields[1].rsplit(":", 1)[1], 16) + except (IndexError, ValueError): + continue + if observed == port: + return True + return False + + +def fsync_directory(path: Path) -> None: + descriptor = os.open(path, os.O_RDONLY | os.O_DIRECTORY) + try: + os.fsync(descriptor) + finally: + os.close(descriptor) + + +def main() -> int: + boot_id = BOOT_ID.read_text(encoding="ascii").strip() + status = json.loads(STATUS.read_text(encoding="utf-8")) + if status.get("boot_id") != boot_id: + raise RuntimeError("recovery access status belongs to another boot") + enabled = unit_state("is-enabled", "ssh.service") + active = unit_state("is-active", "ssh.service") + ipv4 = has_listener(Path("/proc/net/tcp"), SSH_PORT) + ipv6 = has_listener(Path("/proc/net/tcp6"), SSH_PORT) + listening = ipv4 or ipv6 + if enabled and active and listening: + remote_access = "active" + elif enabled and not listening: + remote_access = "enabled_no_listener" + elif listening and not active: + remote_access = "listener_without_service" + else: + remote_access = "inactive" + operational = ( + status.get("state_outcome") == "ready" + and status.get("local_console_access") is True + and remote_access == "active" + ) + status.update( + { + "mode": "operational" if operational else "recovery", + "remote_access": remote_access, + "remote_protocol": "ssh", + "remote_port": SSH_PORT, + "ssh_service_enabled": enabled, + "ssh_service_active": active, + "ssh_listener_ipv4": ipv4, + "ssh_listener_ipv6": ipv6, + } + ) + with tempfile.NamedTemporaryFile( + mode="w", encoding="utf-8", dir=RUNTIME, prefix=".recovery-access-", delete=False + ) as stream: + json.dump(status, stream, sort_keys=True) + stream.write("\n") + stream.flush() + os.fsync(stream.fileno()) + temporary = Path(stream.name) + temporary.chmod(0o644) + os.replace(temporary, STATUS) + fsync_directory(RUNTIME) + return 0 if operational else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) From 2a20d1537d646adc2899c8e084961fc4d7e52062 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:44:06 +0700 Subject: [PATCH 12/39] remote access add truth service --- .../system/rigos-remote-access-status.service | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-status.service diff --git a/build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-status.service b/build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-status.service new file mode 100644 index 00000000..9d876dfb --- /dev/null +++ b/build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-status.service @@ -0,0 +1,13 @@ +[Unit] +Description=Observe RIGOS remote access truth +After=network-online.target ssh.service rigos-recovery-access.service +Wants=network-online.target +Requires=ssh.service rigos-recovery-access.service + +[Service] +Type=oneshot +ExecStart=/usr/lib/rigos/rigos-remote-access-probe +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target From a6f491d552e2629b44799a2c55f33eaf9fdb9466 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:45:19 +0700 Subject: [PATCH 13/39] remote access add observer unit --- .../systemd/system/rigos-remote-access-observe.service | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-observe.service diff --git a/build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-observe.service b/build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-observe.service new file mode 100644 index 00000000..8f0afe23 --- /dev/null +++ b/build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-observe.service @@ -0,0 +1,10 @@ +[Unit] +Description=Observe RIGOS SSH listener truth +After=network-online.target ssh.service rigos-recovery-access.service +Wants=network-online.target +Requires=ssh.service rigos-recovery-access.service + +[Service] +Type=oneshot +ExecStart=/usr/bin/python3 /usr/lib/rigos/rigos-remote-access-probe +RemainAfterExit=yes From 1b097513f4eebe372392d7ca14340b3be5d02aea Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:45:38 +0700 Subject: [PATCH 14/39] ssh pull remote truth observer --- .../etc/systemd/system/ssh.service.d/rigos-observe.conf | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 build/usb/includes.chroot/etc/systemd/system/ssh.service.d/rigos-observe.conf diff --git a/build/usb/includes.chroot/etc/systemd/system/ssh.service.d/rigos-observe.conf b/build/usb/includes.chroot/etc/systemd/system/ssh.service.d/rigos-observe.conf new file mode 100644 index 00000000..d00954c6 --- /dev/null +++ b/build/usb/includes.chroot/etc/systemd/system/ssh.service.d/rigos-observe.conf @@ -0,0 +1,3 @@ +[Unit] +After=rigos-recovery-access.service +Wants=rigos-remote-access-observe.service From 80065d04d4f7da027fd25787665b53c3af33121d Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:47:28 +0700 Subject: [PATCH 15/39] remove unused workflow --- .github/workflows/alpha8-apply.yml | 44 ------------------------------ 1 file changed, 44 deletions(-) delete mode 100644 .github/workflows/alpha8-apply.yml diff --git a/.github/workflows/alpha8-apply.yml b/.github/workflows/alpha8-apply.yml deleted file mode 100644 index dbc933ca..00000000 --- a/.github/workflows/alpha8-apply.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Alpha8 Physical Findings - -on: - push: - branches: - - fix/alpha8-physical-findings - paths: - - .github/workflows/alpha8-apply.yml - - .alpha8-patch-trigger - -permissions: - contents: write - -jobs: - apply-and-verify: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - steps: - - name: Check out branch - uses: actions/checkout@v4 - with: - fetch-depth: 0 - persist-credentials: true - - - name: Apply exact source patch - run: python3 scripts/apply-alpha8-physical-fixes.py - - - name: Install pinned Rust toolchain - uses: dtolnay/rust-toolchain@1.85.1 - with: - components: rustfmt, clippy - - - name: Verify source - run: ./scripts/verify.sh - - - name: Commit verified source - run: | - rm -f .alpha8-patch-trigger - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - git diff --cached --check - git commit -m "alpha8 harden physical appliance findings" - git push origin HEAD:fix/alpha8-physical-findings From 73a3020eec34e860498a5ed0665b757ea07fe14b Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:47:47 +0700 Subject: [PATCH 16/39] remove unused source applicator --- scripts/apply-alpha8-physical-fixes.py | 814 ------------------------- 1 file changed, 814 deletions(-) delete mode 100644 scripts/apply-alpha8-physical-fixes.py diff --git a/scripts/apply-alpha8-physical-fixes.py b/scripts/apply-alpha8-physical-fixes.py deleted file mode 100644 index 5fa09d9d..00000000 --- a/scripts/apply-alpha8-physical-fixes.py +++ /dev/null @@ -1,814 +0,0 @@ -#!/usr/bin/env python3 -from __future__ import annotations - -import os -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[1] - - -def read(path: str) -> str: - return (ROOT / path).read_text(encoding="utf-8") - - -def write(path: str, content: str, mode: int | None = None) -> None: - target = ROOT / path - target.parent.mkdir(parents=True, exist_ok=True) - target.write_text(content, encoding="utf-8", newline="\n") - if mode is not None: - target.chmod(mode) - - -def replace_once(path: str, old: str, new: str) -> None: - content = read(path) - count = content.count(old) - if count != 1: - raise SystemExit(f"{path}: expected one patch anchor, found {count}") - write(path, content.replace(old, new, 1)) - - -def create(path: str, content: str, mode: int | None = None) -> None: - target = ROOT / path - if target.exists(): - raise SystemExit(f"refusing to overwrite existing file: {path}") - write(path, content, mode) - - -# Exact RandomX thread intent. max-threads-hint remains a percentage hint. -replace_once( - "crates/rigos-config/src/lib.rs", - "pub fn build_runtime(\n", - '''fn exact_cpu_profile_name(algorithm: &str) -> Option<&str> { - if matches!(algorithm, "rx" | "rx/0") { - Some("rx") - } else if algorithm.starts_with("rx/") { - Some(algorithm) - } else { - None - } -} - -fn exact_cpu_profile(count: u16) -> Value { - Value::Array((0..count).map(|_| json!(-1)).collect()) -} - -pub fn build_runtime( -''', -) -replace_once( - "crates/rigos-config/src/lib.rs", - ''' match &sheet.cpu.threads { - Threads::Auto(value) if value == "auto" => {} - Threads::Count(value) if (1..=1024).contains(value) => {} - _ => { - return Err(error( - "RIGOS_FLIGHT_SHEET_INVALID", - Some(filename), - None, - Some("cpu.threads"), - "threads must be auto or 1 through 1024", - )); - } - } -''', - ''' match &sheet.cpu.threads { - Threads::Auto(value) if value == "auto" => {} - Threads::Count(value) if !(1..=1024).contains(value) => { - return Err(error( - "RIGOS_FLIGHT_SHEET_INVALID", - Some(filename), - None, - Some("cpu.threads"), - "threads must be auto or 1 through 1024", - )); - } - Threads::Count(_) if exact_cpu_profile_name(&sheet.algorithm).is_none() => { - return Err(error( - "RIGOS_FLIGHT_SHEET_INVALID", - Some(filename), - None, - Some("cpu.threads"), - "explicit threads currently require a RandomX algorithm", - )); - } - Threads::Count(_) => {} - _ => { - return Err(error( - "RIGOS_FLIGHT_SHEET_INVALID", - Some(filename), - None, - Some("cpu.threads"), - "threads must be auto or 1 through 1024", - )); - } - } -''', -) -replace_once( - "crates/rigos-config/src/lib.rs", - ''' let mut cpu = Map::from_iter([ - ("enabled".into(), Value::Bool(true)), - ("huge-pages".into(), Value::Bool(sheet.cpu.huge_pages)), - ]); - cpu.insert("max-threads-hint".into(), json!(sheet.cpu.max_threads_hint)); - if let Threads::Count(count) = sheet.cpu.threads { - cpu.insert("max-threads-hint".into(), json!(count)); - } - let xmrig = json!({"autosave":false,"background":false,"cpu":cpu,"pools":pools,"api":{"worker-id":worker},"http":{"enabled":false}}); -''', - ''' let mut cpu = Map::from_iter([ - ("enabled".into(), Value::Bool(true)), - ("huge-pages".into(), Value::Bool(sheet.cpu.huge_pages)), - ]); - cpu.insert("max-threads-hint".into(), json!(sheet.cpu.max_threads_hint)); - if let Threads::Count(count) = &sheet.cpu.threads { - let profile_name = exact_cpu_profile_name(&sheet.algorithm).ok_or_else(|| { - error( - "RIGOS_FLIGHT_SHEET_INVALID", - None, - None, - Some("cpu.threads"), - "explicit threads do not have a safe XMRig profile mapping", - ) - })?; - cpu.insert(profile_name.into(), exact_cpu_profile(*count)); - } - let xmrig = json!({"autosave":false,"background":false,"cpu":cpu,"pools":pools,"api":{"worker-id":worker},"http":{"enabled":false}}); -''', -) -replace_once( - "crates/rigos-config/src/lib.rs", - ''' #[test] - fn huge_pages_false_reaches_runtime_config() { -''', - ''' #[test] - fn explicit_randomx_threads_generate_exact_profile() { - let proposal = Proposal { - schema: "rigos.config-proposal/v1".into(), - profile: parse_rig_profile(&profile("native", "FLIGHT_REF=xmr-ssl\\n")).unwrap(), - flight_sheet: FlightSheet { - schema: "rigos.flight-sheet/v1".into(), - name: "xmr-ssl".into(), - coin: "XMR".into(), - backend: "xmrig".into(), - algorithm: "rx/0".into(), - pools: vec![Pool { - host: "pool.example".into(), - port: 443, - tls: true, - priority: 0, - }], - identity_ref: "main-xmr".into(), - worker_template: "{node_name}".into(), - cpu: CpuPolicy { - threads: Threads::Count(2), - huge_pages: true, - max_threads_hint: 100, - }, - }, - provenance: None, - source_sha256: "x".into(), - }; - let identity = IdentityRecord { - schema: "rigos.identity/v1".into(), - alias: "main-xmr".into(), - kind: "mining_identity".into(), - value: "private-value".into(), - created_locally: true, - }; - let (_, xmrig) = build_runtime(&proposal, &identity).unwrap(); - assert_eq!(xmrig["cpu"]["max-threads-hint"], 100); - assert_eq!(xmrig["cpu"]["rx"], json!([-1, -1])); - - let mut unsupported = proposal; - unsupported.flight_sheet.algorithm = "cn/r".into(); - assert!(build_runtime(&unsupported, &identity).is_err()); - } - - #[test] - fn huge_pages_false_reaches_runtime_config() { -''', -) - -# Unprivileged inspection must use a redacted derived config, never the wallet-bearing runtime file. -replace_once( - "crates/rigos-xmrig/src/lib.rs", - ''' let config_path = cmdline - .as_deref() - .and_then(extract_config_path) - .or_else(|| self.explicit_config.clone()); -''', - ''' let config_path = self.explicit_config.clone().or_else(|| { - cmdline - .as_deref() - .and_then(extract_config_path) - }); -''', -) -replace_once( - "crates/rigos-xmrig/src/lib.rs", - ''' algorithm: raw.get("algo").and_then(Value::as_str).map(str::to_owned), - huge_pages_requested: raw - .get("randomx") - .and_then(|v| v.get("huge-pages")) - .and_then(Value::as_bool), - thread_hint: raw.get("threads").and_then(Value::as_u64), -''', - ''' algorithm: raw - .get("algo") - .and_then(Value::as_str) - .or_else(|| { - raw.get("pools") - .and_then(Value::as_array) - .and_then(|pools| pools.first()) - .and_then(|pool| pool.get("algo")) - .and_then(Value::as_str) - }) - .map(str::to_owned), - huge_pages_requested: raw - .get("randomx") - .and_then(|v| v.get("huge-pages")) - .and_then(Value::as_bool) - .or_else(|| raw.pointer("/cpu/huge-pages").and_then(Value::as_bool)), - thread_hint: raw - .get("threads") - .and_then(Value::as_u64) - .or_else(|| { - raw.pointer("/cpu/rx") - .and_then(Value::as_array) - .and_then(|threads| u64::try_from(threads.len()).ok()) - }) - .or_else(|| raw.pointer("/cpu/max-threads-hint").and_then(Value::as_u64)), -''', -) -replace_once( - "crates/rigos-xmrig/src/lib.rs", - ''' #[test] - fn discovers_xmrig_from_synthetic_proc_without_mutation() { -''', - ''' #[test] - fn explicit_redacted_config_overrides_process_secret_path() { - let unique = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_nanos(); - let root = std::env::temp_dir().join(format!("rigos-xmrig-explicit-{unique}")); - let proc_root = root.join("proc"); - let pid_dir = proc_root.join("42"); - fs::create_dir_all(&pid_dir).unwrap(); - let secret = root.join("secret.json"); - let public = root.join("public.json"); - fs::write(&secret, r#"{"pools":[{"url":"secret-pool","user":"SENTINEL_SECRET"}]}"#).unwrap(); - fs::write(&public, r#"{"cpu":{"huge-pages":true,"rx":[-1,-1]},"pools":[{"url":"public-pool","algo":"rx/0"}],"http":{"enabled":false}}"#).unwrap(); - fs::write(pid_dir.join("comm"), "xmrig\\n").unwrap(); - fs::write(pid_dir.join("cmdline"), format!("xmrig\\0--config={}\\0", secret.display())).unwrap(); - fs::write(pid_dir.join("status"), "Name:\\txmrig\\nUid:\\t1000 1000 1000 1000\\n").unwrap(); - fs::write(pid_dir.join("cgroup"), "0::/system.slice/rigos-miner.service\\n").unwrap(); - fs::write(pid_dir.join("stat"), "42 (xmrig) S 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 100 0\\n").unwrap(); - fs::write(proc_root.join("uptime"), "100.0 0.0\\n").unwrap(); - let backend = XmrigBackend { - explicit_executable: None, - explicit_config: Some(public), - probe_version: false, - }; - let result = backend.discover(&MachineContext { proc_root, sys_root: root.join("sys") }); - let _ = fs::remove_dir_all(root); - let snapshot = result.value.unwrap(); - assert_eq!(snapshot.config.pools, vec!["public-pool"]); - assert_eq!(snapshot.config.algorithm.as_deref(), Some("rx/0")); - assert_eq!(snapshot.config.huge_pages_requested, Some(true)); - assert_eq!(snapshot.config.thread_hint, Some(2)); - assert!(!serde_json::to_string(&snapshot).unwrap().contains("SENTINEL_SECRET")); - } - - #[test] - fn discovers_xmrig_from_synthetic_proc_without_mutation() { -''', -) -replace_once( - "crates/rigosd/src/lib.rs", - ''' explicit_config: cli.xmrig_config, -''', - ''' explicit_config: cli - .xmrig_config - .or_else(|| Some(PathBuf::from("/run/rigos/xmrig-public.json"))), -''', -) - -# Appliance scripts and services. -create( - "build/usb/includes.chroot/usr/lib/rigos/rigos-miner-public-config", - r'''#!/usr/bin/python3 -import json -import os -import tempfile -from pathlib import Path - -SOURCE = Path("/var/lib/rigos/current/xmrig.json") -TARGET = Path("/run/rigos/xmrig-public.json") -MAX_BYTES = 2 * 1024 * 1024 - - -def fsync_directory(path: Path) -> None: - descriptor = os.open(path, os.O_RDONLY | os.O_DIRECTORY) - try: - os.fsync(descriptor) - finally: - os.close(descriptor) - - -def main() -> int: - raw = SOURCE.read_bytes() - if len(raw) > MAX_BYTES: - raise RuntimeError("XMRig configuration exceeds the public-view size limit") - value = json.loads(raw) - if not isinstance(value, dict): - raise RuntimeError("XMRig configuration is not an object") - for pool in value.get("pools", []): - if isinstance(pool, dict): - pool.pop("user", None) - pool.pop("pass", None) - http = value.get("http") - if isinstance(http, dict): - http.pop("access-token", None) - value["rigos-public-view"] = { - "schema": "rigos.xmrig-public-config/v1", - "source": "active_revision", - "identity_redacted": True, - } - TARGET.parent.mkdir(mode=0o755, parents=True, exist_ok=True) - with tempfile.NamedTemporaryFile( - mode="w", encoding="utf-8", dir=TARGET.parent, prefix=".xmrig-public-", delete=False - ) as stream: - json.dump(value, stream, sort_keys=True) - stream.write("\n") - stream.flush() - os.fsync(stream.fileno()) - temporary = Path(stream.name) - temporary.chmod(0o644) - os.replace(temporary, TARGET) - fsync_directory(TARGET.parent) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) -''', - 0o755, -) -create( - "build/usb/includes.chroot/usr/lib/rigos/rigos-ssh-identity", - r'''#!/usr/bin/python3 -import hashlib -import json -import os -import stat -import subprocess -import tempfile -from pathlib import Path - -RUNTIME = Path("/run/rigos") -STATE = Path("/var/lib/rigos") -SSH_DIRECTORY = Path("/etc/ssh") -BOOT_ID = Path("/proc/sys/kernel/random/boot_id") -STORE = STATE / "machine" / "ssh-host-keys" -STATUS = RUNTIME / "ssh-identity-status.json" -KEY_TYPES = ("rsa", "ecdsa", "ed25519") -MAX_KEY_BYTES = 1024 * 1024 - - -def key_paths(root: Path): - result = [] - for key_type in KEY_TYPES: - private = root / f"ssh_host_{key_type}_key" - result.append((private, 0o600)) - result.append((Path(f"{private}.pub"), 0o644)) - return result - - -def fsync_directory(path: Path) -> None: - descriptor = os.open(path, os.O_RDONLY | os.O_DIRECTORY) - try: - os.fsync(descriptor) - finally: - os.close(descriptor) - - -def ensure_directory(path: Path, mode: int) -> None: - path.mkdir(mode=mode, parents=True, exist_ok=True) - info = path.lstat() - if not stat.S_ISDIR(info.st_mode) or stat.S_ISLNK(info.st_mode): - raise RuntimeError(f"unsafe directory: {path}") - if info.st_uid != os.geteuid(): - raise RuntimeError(f"directory owner mismatch: {path}") - path.chmod(mode) - - -def valid_key(path: Path, mode: int) -> bool: - try: - info = path.lstat() - except FileNotFoundError: - return False - return ( - stat.S_ISREG(info.st_mode) - and not stat.S_ISLNK(info.st_mode) - and info.st_uid == os.geteuid() - and stat.S_IMODE(info.st_mode) == mode - and 0 < info.st_size <= MAX_KEY_BYTES - ) - - -def key_set_state(root: Path) -> str: - try: - info = root.lstat() - except FileNotFoundError: - return "absent" - if not stat.S_ISDIR(info.st_mode) or stat.S_ISLNK(info.st_mode) or info.st_uid != os.geteuid() or stat.S_IMODE(info.st_mode) != 0o700: - return "invalid" - expected = {path.name for path, _mode in key_paths(root)} - observed = {entry.name for entry in root.iterdir()} - if not observed: - return "absent" - if observed != expected: - return "invalid" - return "complete" if all(valid_key(path, mode) for path, mode in key_paths(root)) else "invalid" - - -def atomic_copy(source: Path, destination: Path, mode: int) -> None: - data = source.read_bytes() - if not data or len(data) > MAX_KEY_BYTES: - raise RuntimeError(f"invalid SSH host key size: {source}") - descriptor, name = tempfile.mkstemp(prefix=f".{destination.name}-", dir=destination.parent) - temporary = Path(name) - try: - os.fchmod(descriptor, mode) - with os.fdopen(descriptor, "wb", closefd=True) as stream: - stream.write(data) - stream.flush() - os.fsync(stream.fileno()) - os.replace(temporary, destination) - destination.chmod(mode) - fsync_directory(destination.parent) - finally: - temporary.unlink(missing_ok=True) - - -def live_keys_ready() -> bool: - return all(valid_key(path, mode) for path, mode in key_paths(SSH_DIRECTORY)) - - -def remove_live_keys() -> None: - for path, _mode in key_paths(SSH_DIRECTORY): - path.unlink(missing_ok=True) - - -def generate_live_keys() -> None: - SSH_DIRECTORY.mkdir(mode=0o755, parents=True, exist_ok=True) - remove_live_keys() - result = subprocess.run(["/usr/bin/ssh-keygen", "-A"], check=False) - if result.returncode != 0 or not live_keys_ready(): - raise RuntimeError("SSH host key generation failed") - - -def persist_live_keys() -> None: - ensure_directory(STORE, 0o700) - for source, mode in key_paths(SSH_DIRECTORY): - if not valid_key(source, mode): - raise RuntimeError(f"live SSH host key is invalid: {source}") - atomic_copy(source, STORE / source.name, mode) - if key_set_state(STORE) != "complete": - raise RuntimeError("persistent SSH host key store is incomplete") - - -def restore_live_keys() -> None: - if key_set_state(STORE) != "complete": - raise RuntimeError("persistent SSH host key store is unavailable") - SSH_DIRECTORY.mkdir(mode=0o755, parents=True, exist_ok=True) - remove_live_keys() - for source, mode in key_paths(STORE): - atomic_copy(source, SSH_DIRECTORY / source.name, mode) - if not live_keys_ready(): - raise RuntimeError("restored SSH host keys failed validation") - - -def write_status(value: dict) -> None: - RUNTIME.mkdir(mode=0o755, parents=True, exist_ok=True) - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", dir=RUNTIME, prefix=".ssh-identity-", delete=False) as stream: - json.dump(value, stream, sort_keys=True) - stream.write("\n") - stream.flush() - os.fsync(stream.fileno()) - temporary = Path(stream.name) - temporary.chmod(0o644) - os.replace(temporary, STATUS) - fsync_directory(RUNTIME) - - -def main() -> int: - boot_id = BOOT_ID.read_text(encoding="ascii").strip() - try: - state_status = json.loads((RUNTIME / "state-status.json").read_text(encoding="utf-8")) - if state_status.get("schema") != "rigos.state-status/v1" or state_status.get("boot_id") != boot_id or state_status.get("outcome") != "ready": - raise RuntimeError("persistent state is not ready for this boot") - persistent_state = key_set_state(STORE) - if persistent_state == "complete": - restore_live_keys() - action = "restored" - elif persistent_state == "absent": - generate_live_keys() - persist_live_keys() - action = "created" - else: - raise RuntimeError("persistent SSH host key store is incomplete or unsafe") - fingerprints = { - key_type: hashlib.sha256((SSH_DIRECTORY / f"ssh_host_{key_type}_key.pub").read_bytes()).hexdigest() - for key_type in KEY_TYPES - } - write_status({"schema":"rigos.ssh-identity-status/v1","boot_id":boot_id,"outcome":"ready","action":action,"persisted":True,"public_key_sha256":fingerprints,"reason":None}) - return 0 - except (OSError, ValueError, json.JSONDecodeError, RuntimeError) as error: - write_status({"schema":"rigos.ssh-identity-status/v1","boot_id":boot_id,"outcome":"error","action":None,"persisted":False,"public_key_sha256":{},"reason":str(error)}) - return 1 - - -if __name__ == "__main__": - raise SystemExit(main()) -''', - 0o755, -) -create( - "build/usb/includes.chroot/usr/lib/rigos/rigos-remote-access-probe", - r'''#!/usr/bin/python3 -import json -import os -import subprocess -import tempfile -from pathlib import Path - -RUNTIME = Path("/run/rigos") -BOOT_ID = Path("/proc/sys/kernel/random/boot_id") -STATUS = RUNTIME / "recovery-access-status.json" -IDENTITY_STATUS = RUNTIME / "ssh-identity-status.json" -SSH_PORT = 22 - - -def unit_state(action: str, name: str) -> bool: - return subprocess.run(["/usr/bin/systemctl", action, "--quiet", name], check=False).returncode == 0 - - -def tcp_listener(path: Path, port: int) -> bool: - try: - lines = path.read_text(encoding="ascii").splitlines()[1:] - except OSError: - return False - for line in lines: - fields = line.split() - if len(fields) < 4 or fields[3] != "0A": - continue - try: - observed_port = int(fields[1].rsplit(":", 1)[1], 16) - except (IndexError, ValueError): - continue - if observed_port == port: - return True - return False - - -def fsync_directory(path: Path) -> None: - descriptor = os.open(path, os.O_RDONLY | os.O_DIRECTORY) - try: - os.fsync(descriptor) - finally: - os.close(descriptor) - - -def main() -> int: - boot_id = BOOT_ID.read_text(encoding="ascii").strip() - status = json.loads(STATUS.read_text(encoding="utf-8")) - identity = json.loads(IDENTITY_STATUS.read_text(encoding="utf-8")) - if status.get("boot_id") != boot_id or identity.get("boot_id") != boot_id: - raise RuntimeError("remote access evidence belongs to another boot") - enabled = unit_state("is-enabled", "ssh.service") - active = unit_state("is-active", "ssh.service") - listener_ipv4 = tcp_listener(Path("/proc/net/tcp"), SSH_PORT) - listener_ipv6 = tcp_listener(Path("/proc/net/tcp6"), SSH_PORT) - listening = listener_ipv4 or listener_ipv6 - if enabled and active and listening: - remote_access = "active" - elif enabled and not listening: - remote_access = "enabled_no_listener" - elif listening and not active: - remote_access = "listener_without_service" - else: - remote_access = "inactive" - operational = status.get("state_outcome") == "ready" and status.get("local_console_access") is True and identity.get("outcome") == "ready" and identity.get("persisted") is True and remote_access == "active" - status.update({"mode":"operational" if operational else "recovery","remote_access":remote_access,"remote_protocol":"ssh","remote_port":SSH_PORT,"ssh_service_enabled":enabled,"ssh_service_active":active,"ssh_listener_ipv4":listener_ipv4,"ssh_listener_ipv6":listener_ipv6,"ssh_host_key_action":identity.get("action"),"ssh_host_keys_persisted":identity.get("persisted") is True}) - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", dir=RUNTIME, prefix=".recovery-access-", delete=False) as stream: - json.dump(status, stream, sort_keys=True) - stream.write("\n") - stream.flush() - os.fsync(stream.fileno()) - temporary = Path(stream.name) - temporary.chmod(0o644) - os.replace(temporary, STATUS) - fsync_directory(RUNTIME) - return 0 if operational else 1 - - -if __name__ == "__main__": - raise SystemExit(main()) -''', - 0o755, -) - -create("build/usb/includes.chroot/etc/systemd/system/rigos-ssh-identity.service", '''[Unit] -Description=Establish persistent RIGOS SSH machine identity -After=rigos-state-ready.service -Requires=rigos-state-ready.service -Before=ssh.service - -[Service] -Type=oneshot -ExecStart=/usr/lib/rigos/rigos-ssh-identity -RemainAfterExit=yes - -[Install] -WantedBy=multi-user.target -''') -create("build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-status.service", '''[Unit] -Description=Observe RIGOS remote access truth -After=network-online.target ssh.service rigos-ssh-identity.service -Wants=network-online.target -Requires=ssh.service rigos-ssh-identity.service - -[Service] -Type=oneshot -ExecStart=/usr/lib/rigos/rigos-remote-access-probe -RemainAfterExit=yes - -[Install] -WantedBy=multi-user.target -''') -create("build/usb/includes.chroot/etc/systemd/system/rigos-miner-public-status.service", '''[Unit] -Description=Publish redacted RIGOS miner configuration truth -After=rigos-miner.service -Requires=rigos-state-ready.service -ConditionPathExists=/var/lib/rigos/xmrig.json - -[Service] -Type=oneshot -ExecStart=/usr/lib/rigos/rigos-miner-public-config -''') -create("build/usb/includes.chroot/etc/systemd/system/ssh.service.d/rigos.conf", '''[Unit] -Requires=rigos-ssh-identity.service -After=rigos-ssh-identity.service -''') -create("build/usb/includes.chroot/etc/ssh/sshd_config.d/rigos.conf", '''PasswordAuthentication yes -KbdInteractiveAuthentication no -PermitRootLogin no -AllowUsers rigosadmin -X11Forwarding no -AllowAgentForwarding no -AllowTcpForwarding no -PermitTunnel no -GatewayPorts no -''') - -replace_once( - "build/usb/hooks/010-rigos.chroot", - "install -d -m 0755 /usr/lib/rigos\n", - '''install -d -m 0755 /usr/lib/rigos /usr/local/bin -ln -sfn /usr/lib/rigos/rigosd /usr/local/bin/rigosd -ln -sfn /usr/lib/rigos/rigosctl /usr/local/bin/rigosctl -rm -f /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub -rm -f /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key.pub -rm -f /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key.pub -''', -) -replace_once( - "build/usb/hooks/010-rigos.chroot", - "/usr/lib/rigos/rigos-identity-seed /usr/lib/rigos/xmrig\n", - "/usr/lib/rigos/rigos-identity-seed /usr/lib/rigos/rigos-ssh-identity /usr/lib/rigos/rigos-remote-access-probe /usr/lib/rigos/rigos-miner-public-config /usr/lib/rigos/xmrig\n", -) -replace_once( - "build/usb/hooks/010-rigos.chroot", - "systemctl enable NetworkManager.service rigos-state.service rigos-recovery-access.service rigos-state-ready.service rigos-profile-apply.service rigos-hugepages.service rigos-firstboot.service rigos-miner.service tmp.mount\n", - "systemctl enable NetworkManager.service ssh.service rigos-state.service rigos-recovery-access.service rigos-state-ready.service rigos-profile-apply.service rigos-ssh-identity.service rigos-remote-access-status.service rigos-hugepages.service rigos-firstboot.service rigos-miner.service tmp.mount\nsystemctl disable ssh.socket 2>/dev/null || true\n", -) -replace_once( - "build/usb/includes.chroot/etc/systemd/system/rigos-miner.service", - "Wants=network-online.target\n", - "Wants=network-online.target rigos-miner-public-status.service\n", -) - -# Ordering and verification gates. -replace_once( - "scripts/verify-systemd-ordering.py", - ''' "rigos-state-ready.service", "rigos-profile-apply.service", - "rigos-firstboot.service", "rigos-hugepages.service", "rigos-miner.service", -''', - ''' "rigos-state-ready.service", "rigos-profile-apply.service", - "rigos-firstboot.service", "rigos-ssh-identity.service", - "rigos-remote-access-status.service", "rigos-miner-public-status.service", - "rigos-hugepages.service", "rigos-miner.service", -''', -) -replace_once( - "scripts/verify-systemd-ordering.py", - ' ready = units["rigos-state-ready.service"]\n', - ''' ssh_identity = units["rigos-ssh-identity.service"] - includes(ssh_identity.words("Unit", "After"), {"rigos-state-ready.service"}, "SSH identity must follow state readiness") - includes(ssh_identity.words("Unit", "Requires"), {"rigos-state-ready.service"}, "SSH identity must require state readiness") - includes(ssh_identity.words("Unit", "Before"), {"ssh.service"}, "SSH identity must precede sshd") - - remote_access = units["rigos-remote-access-status.service"] - includes(remote_access.words("Unit", "After"), {"ssh.service", "rigos-ssh-identity.service"}, "remote access truth ordering is incomplete") - includes(remote_access.words("Unit", "Requires"), {"ssh.service", "rigos-ssh-identity.service"}, "remote access truth dependencies are incomplete") - - public_status = units["rigos-miner-public-status.service"] - includes(public_status.words("Unit", "After"), {"rigos-miner.service"}, "public miner status must follow miner") - includes(public_status.words("Unit", "Requires"), {"rigos-state-ready.service"}, "public miner status must require ready state") - - ready = units["rigos-state-ready.service"] -''', -) -replace_once( - "scripts/verify.sh", - ''' build/usb/includes.chroot/usr/lib/rigos/rigos-miner-gate \\ - scripts/verify-systemd-ordering.py -''', - ''' build/usb/includes.chroot/usr/lib/rigos/rigos-miner-gate \\ - build/usb/includes.chroot/usr/lib/rigos/rigos-ssh-identity \\ - build/usb/includes.chroot/usr/lib/rigos/rigos-remote-access-probe \\ - build/usb/includes.chroot/usr/lib/rigos/rigos-miner-public-config \\ - scripts/verify-systemd-ordering.py -''', -) -replace_once( - "scripts/verify.sh", - "grep -Fq 'ExecCondition=/usr/lib/rigos/rigos-miner-gate' build/usb/includes.chroot/etc/systemd/system/rigos-miner.service\n", - '''grep -Fq 'ExecCondition=/usr/lib/rigos/rigos-miner-gate' build/usb/includes.chroot/etc/systemd/system/rigos-miner.service -grep -Fq 'rigos-miner-public-status.service' build/usb/includes.chroot/etc/systemd/system/rigos-miner.service -grep -Fq 'Before=ssh.service' build/usb/includes.chroot/etc/systemd/system/rigos-ssh-identity.service -grep -Fq 'Requires=rigos-ssh-identity.service' build/usb/includes.chroot/etc/systemd/system/ssh.service.d/rigos.conf -grep -Fq 'After=network-online.target ssh.service rigos-ssh-identity.service' build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-status.service -grep -Fq 'ln -sfn /usr/lib/rigos/rigosd /usr/local/bin/rigosd' build/usb/hooks/010-rigos.chroot -grep -Fq 'ln -sfn /usr/lib/rigos/rigosctl /usr/local/bin/rigosctl' build/usb/hooks/010-rigos.chroot -grep -Fq 'systemctl disable ssh.socket' build/usb/hooks/010-rigos.chroot -grep -Fqx 'AllowUsers rigosadmin' build/usb/includes.chroot/etc/ssh/sshd_config.d/rigos.conf -grep -Fqx 'PermitRootLogin no' build/usb/includes.chroot/etc/ssh/sshd_config.d/rigos.conf -''', -) - -# Keep the image verifier authoritative without requiring a physical boot. -replace_once( - "scripts/verify-usb-appliance.sh", - " etc/systemd/system/rigos-recovery-access.service \\\n", - " etc/systemd/system/rigos-recovery-access.service \\\n etc/systemd/system/rigos-ssh-identity.service \\\n etc/systemd/system/rigos-remote-access-status.service \\\n etc/systemd/system/rigos-miner-public-status.service \\\n etc/systemd/system/ssh.service.d/rigos.conf \\\n etc/ssh/sshd_config.d/rigos.conf \\\n", -) -replace_once( - "scripts/verify-usb-appliance.sh", - " usr/lib/rigos/rigosd usr/lib/rigos/rigosctl \\\n", - " usr/lib/rigos/rigosd usr/lib/rigos/rigosctl usr/local/bin/rigosd usr/local/bin/rigosctl \\\n", -) -replace_once( - "scripts/verify-usb-appliance.sh", - "usr/lib/rigos/rigos-miner-gate usr/lib/rigos/xmrig", - "usr/lib/rigos/rigos-miner-gate usr/lib/rigos/rigos-ssh-identity usr/lib/rigos/rigos-remote-access-probe usr/lib/rigos/rigos-miner-public-config usr/lib/rigos/xmrig", -) -replace_once( - "scripts/verify-usb-appliance.sh", - 'python3 -m py_compile "$temporary/root/usr/lib/rigos/rigos-miner-gate"\n', - '''python3 -m py_compile "$temporary/root/usr/lib/rigos/rigos-miner-gate" -python3 -m py_compile "$temporary/root/usr/lib/rigos/rigos-ssh-identity" -python3 -m py_compile "$temporary/root/usr/lib/rigos/rigos-remote-access-probe" -python3 -m py_compile "$temporary/root/usr/lib/rigos/rigos-miner-public-config" -''', -) -replace_once( - "scripts/verify-usb-appliance.sh", - '''rigosctl_path="$(PATH="$temporary/root/usr/local/sbin:$temporary/root/usr/bin" command -v rigosctl)" -[[ "$rigosctl_path" == "$temporary/root/usr/local/sbin/rigosctl" && -x "$rigosctl_path" ]] || die 'rigosctl is not executable in the appliance PATH' -''', - '''user_path="$temporary/root/usr/local/bin:$temporary/root/usr/bin:$temporary/root/bin" -rigosd_path="$(PATH="$user_path" command -v rigosd)" -rigosctl_path="$(PATH="$user_path" command -v rigosctl)" -[[ "$rigosd_path" == "$temporary/root/usr/local/bin/rigosd" && -x "$rigosd_path" ]] || die 'rigosd is not executable in the user appliance PATH' -[[ "$rigosctl_path" == "$temporary/root/usr/local/bin/rigosctl" && -x "$rigosctl_path" ]] || die 'rigosctl is not executable in the user appliance PATH' -''', -) -replace_once( - "scripts/verify-usb-appliance.sh", - 'unsquashfs -no-progress -d "$temporary/root" "$temporary/a/live/filesystem.squashfs" \\\n', - '''if unsquashfs -ll "$temporary/a/live/filesystem.squashfs" | grep -Eq '/etc/ssh/ssh_host_(rsa|ecdsa|ed25519)_key$'; then - die 'image contains a baked SSH private host key' -fi -unsquashfs -no-progress -d "$temporary/root" "$temporary/a/live/filesystem.squashfs" \\ -''', -) - -# Remove the one-shot patch mechanism from the resulting source commit. -(ROOT / "scripts/apply-alpha8-physical-fixes.py").unlink() -workflow = ROOT / ".github/workflows/alpha8-apply.yml" -workflow.unlink() -print("Alpha8 physical findings applied") From 3627b76874318a0b59f40ab90a102b046ed73d79 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:50:27 +0700 Subject: [PATCH 17/39] miner render before runtime gate --- .../systemd/system/rigos-miner.service.d/runtime-render.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/usb/includes.chroot/etc/systemd/system/rigos-miner.service.d/runtime-render.conf b/build/usb/includes.chroot/etc/systemd/system/rigos-miner.service.d/runtime-render.conf index 3f2a44ee..030eecfd 100644 --- a/build/usb/includes.chroot/etc/systemd/system/rigos-miner.service.d/runtime-render.conf +++ b/build/usb/includes.chroot/etc/systemd/system/rigos-miner.service.d/runtime-render.conf @@ -2,9 +2,10 @@ After=rigos-runtime-render.service Requires=rigos-runtime-render.service ConditionPathExists= -ConditionPathExists=/run/rigos/xmrig.json +ConditionPathExists=/var/lib/rigos/current [Service] +ExecCondition=+/usr/lib/rigos/rigos-runtime-render ExecCondition=/usr/lib/rigos/rigos-runtime-gate ExecStart= ExecStart=/usr/lib/rigos/xmrig --config=/run/rigos/xmrig.json From de4389db4d4b99720fb49f4621fc0fa95f1bba62 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:50:49 +0700 Subject: [PATCH 18/39] runtime render remain active --- .../systemd/system/rigos-runtime-render.service.d/remain.conf | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 build/usb/includes.chroot/etc/systemd/system/rigos-runtime-render.service.d/remain.conf diff --git a/build/usb/includes.chroot/etc/systemd/system/rigos-runtime-render.service.d/remain.conf b/build/usb/includes.chroot/etc/systemd/system/rigos-runtime-render.service.d/remain.conf new file mode 100644 index 00000000..75039e3a --- /dev/null +++ b/build/usb/includes.chroot/etc/systemd/system/rigos-runtime-render.service.d/remain.conf @@ -0,0 +1,2 @@ +[Service] +RemainAfterExit=yes From b5a60db02d2563b51f9dda0402cd91be4c63463f Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:56:30 +0700 Subject: [PATCH 19/39] test alpha8 runtime authority --- scripts/check-alpha8-runtime.py | 106 ++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 scripts/check-alpha8-runtime.py diff --git a/scripts/check-alpha8-runtime.py b/scripts/check-alpha8-runtime.py new file mode 100644 index 00000000..c8362f9f --- /dev/null +++ b/scripts/check-alpha8-runtime.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +import json +import os +import subprocess +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +RENDERER = ROOT / "build/usb/includes.chroot/usr/lib/rigos/rigos-runtime-render" +GATE = ROOT / "build/usb/includes.chroot/usr/lib/rigos/rigos-runtime-gate" + + +def write(path: Path, value: dict) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(value), encoding="utf-8") + + +def main() -> int: + with tempfile.TemporaryDirectory(prefix="rigos-alpha8-") as temporary: + root = Path(temporary) + state = root / "state" + runtime = root / "run" + revision = state / "revisions/r1" + (revision / "flight-sheets").mkdir(parents=True) + (state / "current").symlink_to(Path("revisions/r1")) + write( + revision / "policy.json", + {"schema": "rigos.policy/v1", "active_flight_sheet": "xmr"}, + ) + write( + revision / "flight-sheets/xmr.json", + { + "schema": "rigos.flight-sheet/v1", + "backend": "xmrig", + "algorithm": "rx/0", + "cpu": { + "threads": 2, + "huge_pages": True, + "max_threads_hint": 100, + }, + }, + ) + write( + revision / "xmrig.json", + { + "cpu": { + "enabled": True, + "huge-pages": True, + "max-threads-hint": 2, + }, + "pools": [{"url": "pool.test:1", "algo": "rx/0"}], + "http": {"enabled": False}, + }, + ) + environment = os.environ.copy() + environment.update( + { + "RIGOS_STATE_PATH": str(state), + "RIGOS_RUNTIME_PATH": str(runtime), + "RIGOS_RENDER_SKIP_CHOWN": "1", + } + ) + subprocess.run(["python3", str(RENDERER)], env=environment, check=True) + config = json.loads((runtime / "xmrig.json").read_text(encoding="utf-8")) + assert config["cpu"]["max-threads-hint"] == 100 + assert config["cpu"]["rx"] == [-1, -1] + status = json.loads( + (runtime / "runtime-config-status.json").read_text(encoding="utf-8") + ) + assert status["thread_mode"] == "exact" + assert status["exact_threads"] == 2 + assert status["profile"] == "rx" + allowed = subprocess.run( + [ + "python3", + str(GATE), + "--state", + str(state), + "--runtime", + str(runtime), + ], + check=False, + ) + assert allowed.returncode == 0 + config["cpu"]["rx"] = [-1] + write(runtime / "xmrig.json", config) + denied = subprocess.run( + [ + "python3", + str(GATE), + "--state", + str(state), + "--runtime", + str(runtime), + ], + check=False, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + assert denied.returncode == 2 + print("RIGOS Alpha8 runtime authority verification passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From efec3e76e8b30983f95bc60e548505e52054362b Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:57:25 +0700 Subject: [PATCH 20/39] test alpha8 runtime authority in workspace verify --- crates/rigos-config/tests/recovery_access.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/rigos-config/tests/recovery_access.rs b/crates/rigos-config/tests/recovery_access.rs index ea30a417..1e9ced83 100644 --- a/crates/rigos-config/tests/recovery_access.rs +++ b/crates/rigos-config/tests/recovery_access.rs @@ -8,6 +8,11 @@ fn recovery_path() -> PathBuf { .join("../../build/usb/includes.chroot/usr/local/sbin/rigos-recovery-access") } +fn alpha8_runtime_check_path() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../scripts/check-alpha8-runtime.py") +} + #[test] fn recovery_password_is_persisted_restored_and_redacted() { let root = std::env::temp_dir().join(format!("rigos-recovery-access-{}", Uuid::new_v4())); @@ -119,3 +124,12 @@ assert valid_hash not in json.dumps(json.loads((g['RUNTIME'] / 'recovery-access- let _ = fs::remove_dir_all(&root); assert!(result.success(), "recovery access fixture failed"); } + +#[test] +fn alpha8_runtime_authority_is_exact_and_fail_closed() { + let result = Command::new("python3") + .arg(alpha8_runtime_check_path()) + .status() + .expect("run Alpha8 runtime authority fixture"); + assert!(result.success(), "Alpha8 runtime authority fixture failed"); +} From 63580355bbd2c0ada1d5aa12ae6c210c0ac4db7b Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 14:57:56 +0700 Subject: [PATCH 21/39] test remote listener truth in alpha8 gate --- scripts/check-alpha8-runtime.py | 206 +++++++++++++++++++------------- 1 file changed, 125 insertions(+), 81 deletions(-) diff --git a/scripts/check-alpha8-runtime.py b/scripts/check-alpha8-runtime.py index c8362f9f..cd67fff7 100644 --- a/scripts/check-alpha8-runtime.py +++ b/scripts/check-alpha8-runtime.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import json import os +import runpy import subprocess import tempfile from pathlib import Path @@ -8,6 +9,7 @@ ROOT = Path(__file__).resolve().parents[1] RENDERER = ROOT / "build/usb/includes.chroot/usr/lib/rigos/rigos-runtime-render" GATE = ROOT / "build/usb/includes.chroot/usr/lib/rigos/rigos-runtime-gate" +REMOTE_PROBE = ROOT / "build/usb/includes.chroot/usr/lib/rigos/rigos-remote-access-probe" def write(path: Path, value: dict) -> None: @@ -15,90 +17,132 @@ def write(path: Path, value: dict) -> None: path.write_text(json.dumps(value), encoding="utf-8") +def verify_runtime_authority(root: Path) -> None: + state = root / "state" + runtime = root / "run" + revision = state / "revisions/r1" + (revision / "flight-sheets").mkdir(parents=True) + (state / "current").symlink_to(Path("revisions/r1")) + write( + revision / "policy.json", + {"schema": "rigos.policy/v1", "active_flight_sheet": "xmr"}, + ) + write( + revision / "flight-sheets/xmr.json", + { + "schema": "rigos.flight-sheet/v1", + "backend": "xmrig", + "algorithm": "rx/0", + "cpu": { + "threads": 2, + "huge_pages": True, + "max_threads_hint": 100, + }, + }, + ) + write( + revision / "xmrig.json", + { + "cpu": { + "enabled": True, + "huge-pages": True, + "max-threads-hint": 2, + }, + "pools": [{"url": "pool.test:1", "algo": "rx/0"}], + "http": {"enabled": False}, + }, + ) + environment = os.environ.copy() + environment.update( + { + "RIGOS_STATE_PATH": str(state), + "RIGOS_RUNTIME_PATH": str(runtime), + "RIGOS_RENDER_SKIP_CHOWN": "1", + } + ) + subprocess.run(["python3", str(RENDERER)], env=environment, check=True) + config = json.loads((runtime / "xmrig.json").read_text(encoding="utf-8")) + assert config["cpu"]["max-threads-hint"] == 100 + assert config["cpu"]["rx"] == [-1, -1] + status = json.loads( + (runtime / "runtime-config-status.json").read_text(encoding="utf-8") + ) + assert status["thread_mode"] == "exact" + assert status["exact_threads"] == 2 + assert status["profile"] == "rx" + allowed = subprocess.run( + [ + "python3", + str(GATE), + "--state", + str(state), + "--runtime", + str(runtime), + ], + check=False, + ) + assert allowed.returncode == 0 + config["cpu"]["rx"] = [-1] + write(runtime / "xmrig.json", config) + denied = subprocess.run( + [ + "python3", + str(GATE), + "--state", + str(state), + "--runtime", + str(runtime), + ], + check=False, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + assert denied.returncode == 2 + + +def verify_remote_truth(root: Path) -> None: + runtime = root / "remote-run" + runtime.mkdir() + boot_id = root / "remote-boot-id" + boot_id.write_text("boot-test\n", encoding="ascii") + status_path = runtime / "recovery-access-status.json" + write( + status_path, + { + "schema": "rigos.recovery-access-status/v1", + "boot_id": "boot-test", + "state_outcome": "ready", + "local_console_access": True, + }, + ) + namespace = runpy.run_path(str(REMOTE_PROBE), run_name="rigos_remote_probe_test") + globals_ = namespace["main"].__globals__ + globals_["RUNTIME"] = runtime + globals_["STATUS"] = status_path + globals_["BOOT_ID"] = boot_id + globals_["unit_state"] = lambda _action, _unit: True + globals_["has_listener"] = lambda path, _port: path.name == "tcp" + assert namespace["main"]() == 0 + observed = json.loads(status_path.read_text(encoding="utf-8")) + assert observed["mode"] == "operational" + assert observed["remote_access"] == "active" + assert observed["ssh_service_enabled"] is True + assert observed["ssh_service_active"] is True + assert observed["ssh_listener_ipv4"] is True + assert observed["ssh_listener_ipv6"] is False + globals_["has_listener"] = lambda _path, _port: False + assert namespace["main"]() == 1 + degraded = json.loads(status_path.read_text(encoding="utf-8")) + assert degraded["mode"] == "recovery" + assert degraded["remote_access"] == "enabled_no_listener" + + def main() -> int: with tempfile.TemporaryDirectory(prefix="rigos-alpha8-") as temporary: root = Path(temporary) - state = root / "state" - runtime = root / "run" - revision = state / "revisions/r1" - (revision / "flight-sheets").mkdir(parents=True) - (state / "current").symlink_to(Path("revisions/r1")) - write( - revision / "policy.json", - {"schema": "rigos.policy/v1", "active_flight_sheet": "xmr"}, - ) - write( - revision / "flight-sheets/xmr.json", - { - "schema": "rigos.flight-sheet/v1", - "backend": "xmrig", - "algorithm": "rx/0", - "cpu": { - "threads": 2, - "huge_pages": True, - "max_threads_hint": 100, - }, - }, - ) - write( - revision / "xmrig.json", - { - "cpu": { - "enabled": True, - "huge-pages": True, - "max-threads-hint": 2, - }, - "pools": [{"url": "pool.test:1", "algo": "rx/0"}], - "http": {"enabled": False}, - }, - ) - environment = os.environ.copy() - environment.update( - { - "RIGOS_STATE_PATH": str(state), - "RIGOS_RUNTIME_PATH": str(runtime), - "RIGOS_RENDER_SKIP_CHOWN": "1", - } - ) - subprocess.run(["python3", str(RENDERER)], env=environment, check=True) - config = json.loads((runtime / "xmrig.json").read_text(encoding="utf-8")) - assert config["cpu"]["max-threads-hint"] == 100 - assert config["cpu"]["rx"] == [-1, -1] - status = json.loads( - (runtime / "runtime-config-status.json").read_text(encoding="utf-8") - ) - assert status["thread_mode"] == "exact" - assert status["exact_threads"] == 2 - assert status["profile"] == "rx" - allowed = subprocess.run( - [ - "python3", - str(GATE), - "--state", - str(state), - "--runtime", - str(runtime), - ], - check=False, - ) - assert allowed.returncode == 0 - config["cpu"]["rx"] = [-1] - write(runtime / "xmrig.json", config) - denied = subprocess.run( - [ - "python3", - str(GATE), - "--state", - str(state), - "--runtime", - str(runtime), - ], - check=False, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - assert denied.returncode == 2 - print("RIGOS Alpha8 runtime authority verification passed") + verify_runtime_authority(root) + verify_remote_truth(root) + print("RIGOS Alpha8 runtime and remote truth verification passed") return 0 From 972b90ed3f9dfe4660c89d4df198b451633bbc17 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 15:00:16 +0700 Subject: [PATCH 22/39] test alpha8 appliance wiring --- crates/rigos-config/tests/recovery_access.rs | 32 ++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/crates/rigos-config/tests/recovery_access.rs b/crates/rigos-config/tests/recovery_access.rs index 1e9ced83..f17d9b3c 100644 --- a/crates/rigos-config/tests/recovery_access.rs +++ b/crates/rigos-config/tests/recovery_access.rs @@ -13,6 +13,12 @@ fn alpha8_runtime_check_path() -> PathBuf { .join("../../scripts/check-alpha8-runtime.py") } +fn repo_path(path: &str) -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../..") + .join(path) +} + #[test] fn recovery_password_is_persisted_restored_and_redacted() { let root = std::env::temp_dir().join(format!("rigos-recovery-access-{}", Uuid::new_v4())); @@ -133,3 +139,29 @@ fn alpha8_runtime_authority_is_exact_and_fail_closed() { .expect("run Alpha8 runtime authority fixture"); assert!(result.success(), "Alpha8 runtime authority fixture failed"); } + +#[test] +fn alpha8_appliance_wiring_is_explicit() { + let hook = fs::read_to_string(repo_path("build/usb/hooks/010-rigos.chroot")) + .expect("read appliance hook"); + assert!(hook.contains("ln -sfn /usr/lib/rigos/rigosd /usr/local/bin/rigosd")); + assert!(hook.contains("ln -sfn /usr/lib/rigos/rigosctl /usr/local/bin/rigosctl")); + assert!(hook.contains("rigos-runtime-render.service")); + assert!(hook.contains("systemctl disable ssh.socket")); + + let miner = fs::read_to_string(repo_path( + "build/usb/includes.chroot/etc/systemd/system/rigos-miner.service.d/runtime-render.conf", + )) + .expect("read miner runtime override"); + assert!(miner.contains("Requires=rigos-runtime-render.service")); + assert!(miner.contains("ConditionPathExists=/var/lib/rigos/current")); + assert!(miner.contains("ExecCondition=+/usr/lib/rigos/rigos-runtime-render")); + assert!(miner.contains("ExecCondition=/usr/lib/rigos/rigos-runtime-gate")); + assert!(miner.contains("--config=/run/rigos/xmrig.json")); + + let ssh = fs::read_to_string(repo_path( + "build/usb/includes.chroot/etc/systemd/system/ssh.service.d/rigos-observe.conf", + )) + .expect("read SSH observer override"); + assert!(ssh.contains("Wants=rigos-remote-access-observe.service")); +} From 24c4f0cf9cedd636055b27f3773935a3b1290e6a Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 15:05:48 +0700 Subject: [PATCH 23/39] format alpha8 runtime test --- crates/rigos-config/tests/recovery_access.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/rigos-config/tests/recovery_access.rs b/crates/rigos-config/tests/recovery_access.rs index f17d9b3c..4d6b5772 100644 --- a/crates/rigos-config/tests/recovery_access.rs +++ b/crates/rigos-config/tests/recovery_access.rs @@ -9,8 +9,7 @@ fn recovery_path() -> PathBuf { } fn alpha8_runtime_check_path() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../../scripts/check-alpha8-runtime.py") + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../scripts/check-alpha8-runtime.py") } fn repo_path(path: &str) -> PathBuf { From 4fee06180e2af3a1d6c539d31e96103fd2af3444 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 15:14:53 +0700 Subject: [PATCH 24/39] remove duplicate remote access unit --- .../system/rigos-remote-access-status.service | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-status.service diff --git a/build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-status.service b/build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-status.service deleted file mode 100644 index 9d876dfb..00000000 --- a/build/usb/includes.chroot/etc/systemd/system/rigos-remote-access-status.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Observe RIGOS remote access truth -After=network-online.target ssh.service rigos-recovery-access.service -Wants=network-online.target -Requires=ssh.service rigos-recovery-access.service - -[Service] -Type=oneshot -ExecStart=/usr/lib/rigos/rigos-remote-access-probe -RemainAfterExit=yes - -[Install] -WantedBy=multi-user.target From 627bef1d655460d7a1bf3fe4511ebef629c805a4 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 15:15:20 +0700 Subject: [PATCH 25/39] release stage alpha8 appliance candidate --- build/usb/version.env | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/usb/version.env b/build/usb/version.env index 041c82ab..9567e611 100644 --- a/build/usb/version.env +++ b/build/usb/version.env @@ -1,8 +1,8 @@ -RIGOS_PRODUCT_VERSION=0.0.4-alpha.7 -RIGOS_IMAGE_VERSION=0.0.4-alpha.7 +RIGOS_PRODUCT_VERSION=0.0.4-alpha.8 +RIGOS_IMAGE_VERSION=0.0.4-alpha.8 RIGOS_IMAGE_ID=rigos-usb-amd64 RIGOS_IMAGE_CHANNEL=alpha -RIGOS_BUILD_ORDINAL=7 +RIGOS_BUILD_ORDINAL=8 RIGOS_XMRIG_VERSION=6.26.0 RIGOS_XMRIG_ARCHIVE_SHA256=fc6f8ae5f64e4f17481f7e3be29a1c56949f216a998414188003eae1db20c9e5 RIGOS_XMRIG_BINARY_SHA256=b20f39fc00d242e706b6c30367ad811c676e0575050a4ec2f30104b696944b49 From c1ca5144d6fcd2721f99c73e8daee7988dba6e16 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 16:04:38 +0700 Subject: [PATCH 26/39] alpha8 hotfix enable SSH password login --- .../etc/ssh/sshd_config.d/00-rigos.conf | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 build/usb/includes.chroot/etc/ssh/sshd_config.d/00-rigos.conf diff --git a/build/usb/includes.chroot/etc/ssh/sshd_config.d/00-rigos.conf b/build/usb/includes.chroot/etc/ssh/sshd_config.d/00-rigos.conf new file mode 100644 index 00000000..720f859f --- /dev/null +++ b/build/usb/includes.chroot/etc/ssh/sshd_config.d/00-rigos.conf @@ -0,0 +1,12 @@ +PasswordAuthentication yes +KbdInteractiveAuthentication no +UsePAM yes +AuthenticationMethods any +PubkeyAuthentication yes +PermitRootLogin no +AllowUsers rigosadmin +X11Forwarding no +AllowAgentForwarding no +AllowTcpForwarding no +PermitTunnel no +GatewayPorts no From 105d391a9f538313666c27430f8aff6ed7911e47 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 16:05:28 +0700 Subject: [PATCH 27/39] verify alpha8 SSH hotfix policy --- scripts/check-alpha8-ssh-hotfix.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 scripts/check-alpha8-ssh-hotfix.py diff --git a/scripts/check-alpha8-ssh-hotfix.py b/scripts/check-alpha8-ssh-hotfix.py new file mode 100644 index 00000000..b303d350 --- /dev/null +++ b/scripts/check-alpha8-ssh-hotfix.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +import hashlib +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +POLICY = ROOT / "build/usb/includes.chroot/etc/ssh/sshd_config.d/00-rigos.conf" +PACKAGES = ROOT / "build/usb/package-lists/rigos.list.chroot" +HOOK = ROOT / "build/usb/hooks/010-rigos.chroot" +EXPECTED_POLICY_SHA256 = "d59b6bcc078a047d1f1cc90ef6ed9205476d91f874be809009bdd442ef66b8c3" + + +def main() -> int: + policy = POLICY.read_bytes() + observed = hashlib.sha256(policy).hexdigest() + if observed != EXPECTED_POLICY_SHA256: + raise RuntimeError( + f"Alpha8 SSH policy hash mismatch: expected={EXPECTED_POLICY_SHA256} observed={observed}" + ) + packages = PACKAGES.read_text(encoding="utf-8").splitlines() + if "openssh-server" not in packages: + raise RuntimeError("OpenSSH server package is missing") + hook = HOOK.read_text(encoding="utf-8") + if "ssh.service" not in hook or "systemctl disable ssh.socket" not in hook: + raise RuntimeError("deterministic SSH service wiring is missing") + print("RIGOS Alpha8 SSH hotfix verification passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 2a49c771a37eae706cb3d51490993d9b221f3b75 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 16:07:28 +0700 Subject: [PATCH 28/39] gate alpha8 rebuild on hotfix verification --- build/usb/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/usb/Dockerfile b/build/usb/Dockerfile index 9f773489..3a510058 100644 --- a/build/usb/Dockerfile +++ b/build/usb/Dockerfile @@ -8,4 +8,4 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* WORKDIR /source -ENTRYPOINT ["./scripts/build-usb-image.sh"] +ENTRYPOINT ["bash", "-lc", "python3 ./scripts/check-alpha8-ssh-hotfix.py && exec ./scripts/build-usb-image.sh"] From 83ecd6e7674f2907b73793550d7bdfccc0f8de71 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 18:55:00 +0700 Subject: [PATCH 29/39] alpha8 hotfix normalize SSH policy line endings --- scripts/check-alpha8-ssh-hotfix.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/check-alpha8-ssh-hotfix.py b/scripts/check-alpha8-ssh-hotfix.py index b303d350..5979c2cf 100644 --- a/scripts/check-alpha8-ssh-hotfix.py +++ b/scripts/check-alpha8-ssh-hotfix.py @@ -9,8 +9,15 @@ EXPECTED_POLICY_SHA256 = "d59b6bcc078a047d1f1cc90ef6ed9205476d91f874be809009bdd442ef66b8c3" +def normalized_lf_bytes(path: Path) -> bytes: + raw = path.read_bytes() + if raw.startswith(b"\xef\xbb\xbf"): + raise RuntimeError("Alpha8 SSH policy must be UTF-8 without BOM") + return raw.replace(b"\r\n", b"\n").replace(b"\r", b"\n") + + def main() -> int: - policy = POLICY.read_bytes() + policy = normalized_lf_bytes(POLICY) observed = hashlib.sha256(policy).hexdigest() if observed != EXPECTED_POLICY_SHA256: raise RuntimeError( From 98ed5bd1ee6e5048eb7141e38a3ccfae593675d3 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 19:00:39 +0700 Subject: [PATCH 30/39] alpha8 hotfix preserve Cargo path in builder --- build/usb/Dockerfile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/build/usb/Dockerfile b/build/usb/Dockerfile index 3a510058..baea6a79 100644 --- a/build/usb/Dockerfile +++ b/build/usb/Dockerfile @@ -1,11 +1,16 @@ FROM docker.io/library/rust:1.85.1-bookworm +ENV PATH="/usr/local/cargo/bin:/usr/local/rustup/bin:${PATH}" + RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ ca-certificates curl dosfstools e2fsprogs fdisk gdisk git grub-efi-amd64-bin \ grub-pc-bin grub2-common isolinux jq live-build mtools ovmf parted qemu-system-x86 \ ripgrep rsync squashfs-tools syslinux-common syslinux-utils udev uuid-runtime xorriso zstd \ - && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* \ + && command -v cargo \ + && cargo --version \ + && rustc --version WORKDIR /source -ENTRYPOINT ["bash", "-lc", "python3 ./scripts/check-alpha8-ssh-hotfix.py && exec ./scripts/build-usb-image.sh"] +ENTRYPOINT ["/bin/bash", "-c", "python3 ./scripts/check-alpha8-ssh-hotfix.py && exec ./scripts/build-usb-image.sh"] From e76734d22e040d44ef1f5a3038661ccc53eea8de Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 19:01:05 +0700 Subject: [PATCH 31/39] alpha8 hotfix verify builder Cargo path --- scripts/check-alpha8-ssh-hotfix.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/check-alpha8-ssh-hotfix.py b/scripts/check-alpha8-ssh-hotfix.py index 5979c2cf..e8b38990 100644 --- a/scripts/check-alpha8-ssh-hotfix.py +++ b/scripts/check-alpha8-ssh-hotfix.py @@ -6,6 +6,7 @@ POLICY = ROOT / "build/usb/includes.chroot/etc/ssh/sshd_config.d/00-rigos.conf" PACKAGES = ROOT / "build/usb/package-lists/rigos.list.chroot" HOOK = ROOT / "build/usb/hooks/010-rigos.chroot" +DOCKERFILE = ROOT / "build/usb/Dockerfile" EXPECTED_POLICY_SHA256 = "d59b6bcc078a047d1f1cc90ef6ed9205476d91f874be809009bdd442ef66b8c3" @@ -29,6 +30,15 @@ def main() -> int: hook = HOOK.read_text(encoding="utf-8") if "ssh.service" not in hook or "systemctl disable ssh.socket" not in hook: raise RuntimeError("deterministic SSH service wiring is missing") + dockerfile = DOCKERFILE.read_text(encoding="utf-8") + if 'ENV PATH="/usr/local/cargo/bin:/usr/local/rustup/bin:${PATH}"' not in dockerfile: + raise RuntimeError("builder Cargo PATH is not explicit") + if 'ENTRYPOINT ["/bin/bash", "-c",' not in dockerfile: + raise RuntimeError("builder entrypoint must use a non-login shell") + if '"bash", "-lc"' in dockerfile or '"/bin/bash", "-lc"' in dockerfile: + raise RuntimeError("builder entrypoint must not use a login shell") + if "cargo --version" not in dockerfile or "rustc --version" not in dockerfile: + raise RuntimeError("builder toolchain verification is missing") print("RIGOS Alpha8 SSH hotfix verification passed") return 0 From 8b6068c19f45f3d2c22695d9b7e3aca93b6f513f Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 19:01:41 +0700 Subject: [PATCH 32/39] alpha8 hotfix reuse builder package layer --- build/usb/Dockerfile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build/usb/Dockerfile b/build/usb/Dockerfile index baea6a79..be63222a 100644 --- a/build/usb/Dockerfile +++ b/build/usb/Dockerfile @@ -1,14 +1,15 @@ FROM docker.io/library/rust:1.85.1-bookworm -ENV PATH="/usr/local/cargo/bin:/usr/local/rustup/bin:${PATH}" - RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ ca-certificates curl dosfstools e2fsprogs fdisk gdisk git grub-efi-amd64-bin \ grub-pc-bin grub2-common isolinux jq live-build mtools ovmf parted qemu-system-x86 \ ripgrep rsync squashfs-tools syslinux-common syslinux-utils udev uuid-runtime xorriso zstd \ - && rm -rf /var/lib/apt/lists/* \ - && command -v cargo \ + && rm -rf /var/lib/apt/lists/* + +ENV PATH="/usr/local/cargo/bin:/usr/local/rustup/bin:${PATH}" + +RUN command -v cargo \ && cargo --version \ && rustc --version From 0b77a5f601be0c21a4a0c9eccd93500a5cc129cf Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 19:33:33 +0700 Subject: [PATCH 33/39] alpha8 hotfix verify recovery credential outcome --- .../lib/rigos/rigos-recovery-access-verify | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 build/usb/includes.chroot/usr/lib/rigos/rigos-recovery-access-verify diff --git a/build/usb/includes.chroot/usr/lib/rigos/rigos-recovery-access-verify b/build/usb/includes.chroot/usr/lib/rigos/rigos-recovery-access-verify new file mode 100644 index 00000000..c0e53c1b --- /dev/null +++ b/build/usb/includes.chroot/usr/lib/rigos/rigos-recovery-access-verify @@ -0,0 +1,54 @@ +#!/usr/bin/python3 +import json +import sys +from pathlib import Path + +RUNTIME = Path("/run/rigos") +BOOT_ID = Path("/proc/sys/kernel/random/boot_id") +STATUS = RUNTIME / "recovery-access-status.json" +MAX_STATUS_BYTES = 64 * 1024 + + +def emit(outcome: str, reason: str | None = None) -> None: + value = { + "schema": "rigos.recovery-access-gate/v1", + "outcome": outcome, + "reason": reason, + } + print(json.dumps(value, sort_keys=True), file=sys.stdout if outcome == "allowed" else sys.stderr) + + +def deny(reason: str) -> int: + emit("denied", reason) + return 2 + + +def main() -> int: + try: + boot_id = BOOT_ID.read_text(encoding="ascii").strip() + raw = STATUS.read_bytes() + if not boot_id or not raw or len(raw) > MAX_STATUS_BYTES: + return deny("recovery_status_unreadable") + status = json.loads(raw) + except (OSError, UnicodeError, json.JSONDecodeError): + return deny("recovery_status_unreadable") + + if not isinstance(status, dict): + return deny("recovery_status_invalid") + if status.get("schema") != "rigos.recovery-access-status/v1": + return deny("recovery_schema_mismatch") + if status.get("boot_id") != boot_id: + return deny("recovery_status_stale") + if status.get("local_console_access") is not True: + return deny("local_credential_unavailable") + if status.get("credential_persisted") is not True: + return deny("credential_not_persisted") + if status.get("credential_action") not in ("existing", "created", "restored"): + return deny("credential_action_invalid") + + emit("allowed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 3dd0db975f6ba3e773d8d83d2192917cfb08797e Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 19:33:45 +0700 Subject: [PATCH 34/39] alpha8 hotfix clear false recovery failure --- .../etc/systemd/system/rigos-recovery-access.service | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/usb/includes.chroot/etc/systemd/system/rigos-recovery-access.service b/build/usb/includes.chroot/etc/systemd/system/rigos-recovery-access.service index 228e2a24..ca00519e 100644 --- a/build/usb/includes.chroot/etc/systemd/system/rigos-recovery-access.service +++ b/build/usb/includes.chroot/etc/systemd/system/rigos-recovery-access.service @@ -2,11 +2,13 @@ Description=Establish local RIGOS recovery access After=rigos-state.service Wants=rigos-state.service -Before=rigos-state-ready.service rigos-firstboot.service getty@tty1.service +Before=rigos-state-ready.service rigos-firstboot.service getty@tty1.service ssh.service [Service] Type=oneshot ExecStart=/usr/local/sbin/rigos-recovery-access +SuccessExitStatus=1 +ExecStartPost=/usr/bin/python3 /usr/lib/rigos/rigos-recovery-access-verify StandardInput=tty StandardOutput=tty StandardError=tty From 827916fb37e6c358fdd27c7dacbebdbcccda285c Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 19:34:15 +0700 Subject: [PATCH 35/39] alpha8 hotfix make recovery gate testable --- .../usr/lib/rigos/rigos-recovery-access-verify | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/usb/includes.chroot/usr/lib/rigos/rigos-recovery-access-verify b/build/usb/includes.chroot/usr/lib/rigos/rigos-recovery-access-verify index c0e53c1b..ac3dd072 100644 --- a/build/usb/includes.chroot/usr/lib/rigos/rigos-recovery-access-verify +++ b/build/usb/includes.chroot/usr/lib/rigos/rigos-recovery-access-verify @@ -1,10 +1,11 @@ #!/usr/bin/python3 import json +import os import sys from pathlib import Path -RUNTIME = Path("/run/rigos") -BOOT_ID = Path("/proc/sys/kernel/random/boot_id") +RUNTIME = Path(os.environ.get("RIGOS_RUNTIME_PATH", "/run/rigos")) +BOOT_ID = Path(os.environ.get("RIGOS_BOOT_ID_PATH", "/proc/sys/kernel/random/boot_id")) STATUS = RUNTIME / "recovery-access-status.json" MAX_STATUS_BYTES = 64 * 1024 From 93de25eb6ef50f1e88ad824f09dbdfa0f27a75c7 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 19:34:32 +0700 Subject: [PATCH 36/39] test alpha8 recovery false-failure hotfix --- .../tests/alpha8_recovery_gate.rs | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 crates/rigos-config/tests/alpha8_recovery_gate.rs diff --git a/crates/rigos-config/tests/alpha8_recovery_gate.rs b/crates/rigos-config/tests/alpha8_recovery_gate.rs new file mode 100644 index 00000000..c1381b79 --- /dev/null +++ b/crates/rigos-config/tests/alpha8_recovery_gate.rs @@ -0,0 +1,79 @@ +use std::fs; +use std::path::PathBuf; +use std::process::Command; +use uuid::Uuid; + +fn repo_path(path: &str) -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../..") + .join(path) +} + +#[test] +fn recovery_gate_accepts_only_current_persisted_credential_truth() { + let root = std::env::temp_dir().join(format!("rigos-recovery-gate-{}", Uuid::new_v4())); + let runtime = root.join("run"); + let boot_id = root.join("boot-id"); + fs::create_dir_all(&runtime).unwrap(); + fs::write(&boot_id, "boot-test\n").unwrap(); + + let status = runtime.join("recovery-access-status.json"); + let valid = serde_json::json!({ + "schema": "rigos.recovery-access-status/v1", + "boot_id": "boot-test", + "local_console_access": true, + "credential_action": "created", + "credential_persisted": true + }); + fs::write(&status, serde_json::to_vec(&valid).unwrap()).unwrap(); + + let gate = repo_path( + "build/usb/includes.chroot/usr/lib/rigos/rigos-recovery-access-verify", + ); + let run = |runtime_path: &PathBuf, boot_path: &PathBuf| { + Command::new("python3") + .arg(&gate) + .env("RIGOS_RUNTIME_PATH", runtime_path) + .env("RIGOS_BOOT_ID_PATH", boot_path) + .status() + .unwrap() + }; + + assert!(run(&runtime, &boot_id).success()); + + let stale = serde_json::json!({ + "schema": "rigos.recovery-access-status/v1", + "boot_id": "old-boot", + "local_console_access": true, + "credential_action": "created", + "credential_persisted": true + }); + fs::write(&status, serde_json::to_vec(&stale).unwrap()).unwrap(); + assert_eq!(run(&runtime, &boot_id).code(), Some(2)); + + let not_persisted = serde_json::json!({ + "schema": "rigos.recovery-access-status/v1", + "boot_id": "boot-test", + "local_console_access": true, + "credential_action": "created", + "credential_persisted": false + }); + fs::write(&status, serde_json::to_vec(¬_persisted).unwrap()).unwrap(); + assert_eq!(run(&runtime, &boot_id).code(), Some(2)); + + let _ = fs::remove_dir_all(root); +} + +#[test] +fn recovery_service_accepts_legacy_exit_one_only_with_post_validation() { + let unit = fs::read_to_string(repo_path( + "build/usb/includes.chroot/etc/systemd/system/rigos-recovery-access.service", + )) + .unwrap(); + + assert!(unit.contains("Before=rigos-state-ready.service rigos-firstboot.service getty@tty1.service ssh.service")); + assert!(unit.contains("SuccessExitStatus=1")); + assert!(unit.contains( + "ExecStartPost=/usr/bin/python3 /usr/lib/rigos/rigos-recovery-access-verify" + )); +} From 5e00b35b3d704fa1d7c5173e62d44a84deabe67a Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 19:35:20 +0700 Subject: [PATCH 37/39] alpha8 hotfix verify recovery access outcome gate --- scripts/check-alpha8-ssh-hotfix.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/scripts/check-alpha8-ssh-hotfix.py b/scripts/check-alpha8-ssh-hotfix.py index e8b38990..17a154e3 100644 --- a/scripts/check-alpha8-ssh-hotfix.py +++ b/scripts/check-alpha8-ssh-hotfix.py @@ -7,6 +7,8 @@ PACKAGES = ROOT / "build/usb/package-lists/rigos.list.chroot" HOOK = ROOT / "build/usb/hooks/010-rigos.chroot" DOCKERFILE = ROOT / "build/usb/Dockerfile" +RECOVERY_UNIT = ROOT / "build/usb/includes.chroot/etc/systemd/system/rigos-recovery-access.service" +RECOVERY_GATE = ROOT / "build/usb/includes.chroot/usr/lib/rigos/rigos-recovery-access-verify" EXPECTED_POLICY_SHA256 = "d59b6bcc078a047d1f1cc90ef6ed9205476d91f874be809009bdd442ef66b8c3" @@ -39,7 +41,28 @@ def main() -> int: raise RuntimeError("builder entrypoint must not use a login shell") if "cargo --version" not in dockerfile or "rustc --version" not in dockerfile: raise RuntimeError("builder toolchain verification is missing") - print("RIGOS Alpha8 SSH hotfix verification passed") + + recovery_unit = RECOVERY_UNIT.read_text(encoding="utf-8") + required_unit_lines = ( + "Before=rigos-state-ready.service rigos-firstboot.service getty@tty1.service ssh.service", + "SuccessExitStatus=1", + "ExecStartPost=/usr/bin/python3 /usr/lib/rigos/rigos-recovery-access-verify", + ) + for required in required_unit_lines: + if required not in recovery_unit: + raise RuntimeError(f"recovery access hotfix wiring is missing: {required}") + + recovery_gate = RECOVERY_GATE.read_text(encoding="utf-8") + compile(recovery_gate, str(RECOVERY_GATE), "exec") + for required in ( + 'status.get("boot_id") != boot_id', + 'status.get("local_console_access") is not True', + 'status.get("credential_persisted") is not True', + ): + if required not in recovery_gate: + raise RuntimeError(f"recovery access validator contract is missing: {required}") + + print("RIGOS Alpha8 SSH and recovery hotfix verification passed") return 0 From a6dd9ecb6226a412aaa2a7ecb349bb9edbb0aa22 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 19:35:56 +0700 Subject: [PATCH 38/39] format alpha8 recovery gate test --- crates/rigos-config/tests/alpha8_recovery_gate.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/rigos-config/tests/alpha8_recovery_gate.rs b/crates/rigos-config/tests/alpha8_recovery_gate.rs index c1381b79..27309f05 100644 --- a/crates/rigos-config/tests/alpha8_recovery_gate.rs +++ b/crates/rigos-config/tests/alpha8_recovery_gate.rs @@ -71,7 +71,9 @@ fn recovery_service_accepts_legacy_exit_one_only_with_post_validation() { )) .unwrap(); - assert!(unit.contains("Before=rigos-state-ready.service rigos-firstboot.service getty@tty1.service ssh.service")); + assert!(unit.contains( + "Before=rigos-state-ready.service rigos-firstboot.service getty@tty1.service ssh.service" + )); assert!(unit.contains("SuccessExitStatus=1")); assert!(unit.contains( "ExecStartPost=/usr/bin/python3 /usr/lib/rigos/rigos-recovery-access-verify" From 3024868d7e6a0ba9f938371607302c67aa413e11 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Tue, 7 Jul 2026 19:57:24 +0700 Subject: [PATCH 39/39] format alpha8 recovery gate fixture exactly --- crates/rigos-config/tests/alpha8_recovery_gate.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/rigos-config/tests/alpha8_recovery_gate.rs b/crates/rigos-config/tests/alpha8_recovery_gate.rs index 27309f05..dce3b59b 100644 --- a/crates/rigos-config/tests/alpha8_recovery_gate.rs +++ b/crates/rigos-config/tests/alpha8_recovery_gate.rs @@ -27,9 +27,7 @@ fn recovery_gate_accepts_only_current_persisted_credential_truth() { }); fs::write(&status, serde_json::to_vec(&valid).unwrap()).unwrap(); - let gate = repo_path( - "build/usb/includes.chroot/usr/lib/rigos/rigos-recovery-access-verify", - ); + let gate = repo_path("build/usb/includes.chroot/usr/lib/rigos/rigos-recovery-access-verify"); let run = |runtime_path: &PathBuf, boot_path: &PathBuf| { Command::new("python3") .arg(&gate) @@ -75,7 +73,7 @@ fn recovery_service_accepts_legacy_exit_one_only_with_post_validation() { "Before=rigos-state-ready.service rigos-firstboot.service getty@tty1.service ssh.service" )); assert!(unit.contains("SuccessExitStatus=1")); - assert!(unit.contains( - "ExecStartPost=/usr/bin/python3 /usr/lib/rigos/rigos-recovery-access-verify" - )); + assert!( + unit.contains("ExecStartPost=/usr/bin/python3 /usr/lib/rigos/rigos-recovery-access-verify") + ); }