feat: rootless podman, session hardening, boot stability, sidebar fix
Rootless podman migration (TASK-11): - Remove sudo from all podman calls in PodmanClient + 8 backend files - Remove sudo from all podman/docker calls in deploy script - Restore full systemd security hardening: NoNewPrivileges, RestrictAddressFamilies, MemoryDenyWriteExecute, RestrictRealtime, RestrictNamespaces, RestrictSUIDSGID, SystemCallFilter, ProtectSystem=strict - Enable loginctl linger for rootless container persistence - Remove Ollama from auto-deploy (marketplace-only) Session & auth hardening: - Increase MAX_CONCURRENT_SESSIONS 20→50 (prevents eviction storms) - Debounced 401 redirect in rpc-client.ts (prevents redirect storms) Boot stability: - optimize-debian.sh: adds chrony, swap, removes policy-rc.d - deploy script: pre-restart chrony + swap setup - ISO build: chrony package, swap file creation - BootScreen: no longer clears localStorage (prevents splash replay) - RootRedirect: sole owner of localStorage clearing on server ready UI fixes: - Sidebar opacity default changed from 0→visible (fixes missing sidebar after page-persistence login without entrance animation) - Console.log/error wrapped in import.meta.env.DEV guards - Remove unused route import from RootRedirect Beta tracking: - CLAUDE.md: beta freeze protocol added - MASTER_PLAN.md: TASK-11, TASK-17, phase structure - BETA-PROGRESS.md: initial tracking doc - Tagged v1.2.0-alpha.1 as pre-rootless baseline Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
b89808862c
commit
39c7ac1924
@@ -140,9 +140,9 @@ impl RpcHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: list containers directly via sudo podman (for bundled apps)
|
||||
let output = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "ps", "-a", "--format", "json"])
|
||||
// Fallback: list containers directly via podman (for bundled apps)
|
||||
let output = tokio::process::Command::new("podman")
|
||||
.args(["ps", "-a", "--format", "json"])
|
||||
.output()
|
||||
.await
|
||||
.context("Failed to list containers via podman")?;
|
||||
|
||||
@@ -45,8 +45,8 @@ impl RpcHandler {
|
||||
// Dependency checks: verify required services are running before install
|
||||
let has_lnd;
|
||||
{
|
||||
let dep_check = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "ps", "--format", "{{.Names}}"])
|
||||
let dep_check = tokio::process::Command::new("podman")
|
||||
.args(["ps", "--format", "{{.Names}}"])
|
||||
.output()
|
||||
.await
|
||||
.context("Failed to check running containers")?;
|
||||
@@ -101,8 +101,8 @@ impl RpcHandler {
|
||||
}
|
||||
|
||||
// Check if container already exists
|
||||
let check_output = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "ps", "-a", "--format", "{{.Names}}", "--filter", &format!("name=^{}$", package_id)])
|
||||
let check_output = tokio::process::Command::new("podman")
|
||||
.args(["ps", "-a", "--format", "{{.Names}}", "--filter", &format!("name=^{}$", package_id)])
|
||||
.output()
|
||||
.await
|
||||
.context("Failed to check existing containers")?;
|
||||
@@ -117,8 +117,8 @@ impl RpcHandler {
|
||||
let is_local_image = docker_image.starts_with("localhost/");
|
||||
let has_local_fallback = if !is_local_image {
|
||||
let local_tag = format!("localhost/{}:latest", package_id);
|
||||
let check = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "images", "-q", &local_tag])
|
||||
let check = tokio::process::Command::new("podman")
|
||||
.args(["images", "-q", &local_tag])
|
||||
.output().await.ok();
|
||||
check.map_or(false, |o| !String::from_utf8_lossy(&o.stdout).trim().is_empty())
|
||||
} else { false };
|
||||
@@ -129,8 +129,8 @@ impl RpcHandler {
|
||||
self.set_install_progress(package_id, 0, 0).await;
|
||||
|
||||
// Stream pull progress via piped stderr
|
||||
let mut child = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "pull", docker_image])
|
||||
let mut child = tokio::process::Command::new("podman")
|
||||
.args(["pull", docker_image])
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.spawn()
|
||||
@@ -165,8 +165,8 @@ impl RpcHandler {
|
||||
debug!("Using local build for {} (skipping registry pull)", package_id);
|
||||
} else {
|
||||
// Verify local image exists
|
||||
let images_output = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "images", "-q", docker_image])
|
||||
let images_output = tokio::process::Command::new("podman")
|
||||
.args(["images", "-q", docker_image])
|
||||
.output()
|
||||
.await
|
||||
.context("Failed to check local image")?;
|
||||
@@ -187,7 +187,7 @@ impl RpcHandler {
|
||||
|
||||
// Create and start container with security constraints
|
||||
let mut run_args = vec![
|
||||
"podman", "run",
|
||||
"run",
|
||||
"-d", // Detached
|
||||
"--name", container_name,
|
||||
"--restart=unless-stopped", // Auto-restart policy
|
||||
@@ -252,8 +252,8 @@ impl RpcHandler {
|
||||
run_args.push("--cap-add=NET_RAW");
|
||||
run_args.push("--device=/dev/net/tun");
|
||||
} else if needs_archy_net {
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "network", "create", "archy-net"])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["network", "create", "archy-net"])
|
||||
.output()
|
||||
.await;
|
||||
run_args.push("--network=archy-net");
|
||||
@@ -367,7 +367,7 @@ printtoconsole=1\n", rpc_pass);
|
||||
debug!("Running container with args: {:?}", run_args);
|
||||
|
||||
// Build command with optional custom command
|
||||
let mut cmd = tokio::process::Command::new("sudo");
|
||||
let mut cmd = tokio::process::Command::new("podman");
|
||||
cmd.args(&run_args);
|
||||
|
||||
// Add custom command/args if specified
|
||||
@@ -397,9 +397,9 @@ printtoconsole=1\n", rpc_pass);
|
||||
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
|
||||
for domain_idx in 1..=2u8 {
|
||||
let value = if domain_idx == 1 { host_ip.as_str() } else { "localhost" };
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args([
|
||||
"podman", "exec", "-u", "33", "nextcloud",
|
||||
"exec", "-u", "33", "nextcloud",
|
||||
"php", "occ", "config:system:set",
|
||||
"trusted_domains", &domain_idx.to_string(),
|
||||
"--value", value,
|
||||
@@ -415,17 +415,17 @@ printtoconsole=1\n", rpc_pass);
|
||||
if matches!(package_id, "bitcoin" | "bitcoin-core" | "bitcoin-knots") {
|
||||
tokio::spawn(async move {
|
||||
let ui_dir = "/opt/archipelago/docker/bitcoin-ui";
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "build", "-t", "localhost/bitcoin-ui", ui_dir])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["build", "-t", "localhost/bitcoin-ui", ui_dir])
|
||||
.output()
|
||||
.await;
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "rm", "-f", "bitcoin-ui"])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["rm", "-f", "bitcoin-ui"])
|
||||
.output()
|
||||
.await;
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args([
|
||||
"podman", "run", "-d", "--name", "bitcoin-ui",
|
||||
"run", "-d", "--name", "bitcoin-ui",
|
||||
"--restart=unless-stopped",
|
||||
"-p", "8334:80",
|
||||
"localhost/bitcoin-ui:latest",
|
||||
@@ -446,8 +446,8 @@ printtoconsole=1\n", rpc_pass);
|
||||
|
||||
/// Install Immich stack (postgres + redis + server)
|
||||
async fn install_immich_stack(&self) -> Result<serde_json::Value> {
|
||||
let check = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "ps", "-a", "--format", "{{.Names}}"])
|
||||
let check = tokio::process::Command::new("podman")
|
||||
.args(["ps", "-a", "--format", "{{.Names}}"])
|
||||
.output()
|
||||
.await
|
||||
.context("Failed to list containers")?;
|
||||
@@ -456,12 +456,12 @@ printtoconsole=1\n", rpc_pass);
|
||||
return Err(anyhow::anyhow!("Immich already installed. Stop and remove it first."));
|
||||
}
|
||||
if stdout.contains("immich\n") || stdout.lines().any(|l| l.trim() == "immich") {
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "stop", "immich"])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["stop", "immich"])
|
||||
.output()
|
||||
.await;
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "rm", "-f", "immich"])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["rm", "-f", "immich"])
|
||||
.output()
|
||||
.await;
|
||||
}
|
||||
@@ -472,8 +472,8 @@ printtoconsole=1\n", rpc_pass);
|
||||
"ghcr.io/immich-app/immich-server:release",
|
||||
];
|
||||
for img in &images {
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "pull", img])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["pull", img])
|
||||
.output()
|
||||
.await;
|
||||
}
|
||||
@@ -482,14 +482,14 @@ printtoconsole=1\n", rpc_pass);
|
||||
.args(["mkdir", "-p", "/var/lib/archipelago/immich", "/var/lib/archipelago/immich-db"])
|
||||
.output()
|
||||
.await;
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "network", "create", "immich-net"])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["network", "create", "immich-net"])
|
||||
.output()
|
||||
.await;
|
||||
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args([
|
||||
"podman", "run", "-d", "--name", "immich_postgres", "--restart", "unless-stopped",
|
||||
"run", "-d", "--name", "immich_postgres", "--restart", "unless-stopped",
|
||||
"--network", "immich-net",
|
||||
"-v", "/var/lib/archipelago/immich-db:/var/lib/postgresql/data",
|
||||
"-e", "POSTGRES_PASSWORD=immichpass", "-e", "POSTGRES_USER=postgres", "-e", "POSTGRES_DB=immich",
|
||||
@@ -499,9 +499,9 @@ printtoconsole=1\n", rpc_pass);
|
||||
.await;
|
||||
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
||||
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args([
|
||||
"podman", "run", "-d", "--name", "immich_redis", "--restart", "unless-stopped",
|
||||
"run", "-d", "--name", "immich_redis", "--restart", "unless-stopped",
|
||||
"--network", "immich-net",
|
||||
"docker.io/valkey/valkey:7-alpine",
|
||||
])
|
||||
@@ -509,9 +509,9 @@ printtoconsole=1\n", rpc_pass);
|
||||
.await;
|
||||
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||
|
||||
let run = tokio::process::Command::new("sudo")
|
||||
let run = tokio::process::Command::new("podman")
|
||||
.args([
|
||||
"podman", "run", "-d", "--name", "immich_server", "--restart", "unless-stopped",
|
||||
"run", "-d", "--name", "immich_server", "--restart", "unless-stopped",
|
||||
"--network", "immich-net", "-p", "2283:2283",
|
||||
"-v", "/var/lib/archipelago/immich:/usr/src/app/upload",
|
||||
"-e", "DB_HOSTNAME=immich_postgres", "-e", "DB_USERNAME=postgres",
|
||||
@@ -537,8 +537,8 @@ printtoconsole=1\n", rpc_pass);
|
||||
|
||||
/// Install Penpot stack (postgres + valkey + backend + exporter + frontend)
|
||||
async fn install_penpot_stack(&self) -> Result<serde_json::Value> {
|
||||
let check = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "ps", "-a", "--format", "{{.Names}}"])
|
||||
let check = tokio::process::Command::new("podman")
|
||||
.args(["ps", "-a", "--format", "{{.Names}}"])
|
||||
.output()
|
||||
.await
|
||||
.context("Failed to list containers")?;
|
||||
@@ -555,8 +555,8 @@ printtoconsole=1\n", rpc_pass);
|
||||
"docker.io/penpotapp/frontend:2.4",
|
||||
];
|
||||
for img in &images {
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "pull", img])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["pull", img])
|
||||
.output()
|
||||
.await;
|
||||
}
|
||||
@@ -565,8 +565,8 @@ printtoconsole=1\n", rpc_pass);
|
||||
.args(["mkdir", "-p", "/var/lib/archipelago/penpot-assets"])
|
||||
.output()
|
||||
.await;
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "network", "create", "penpot-net"])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["network", "create", "penpot-net"])
|
||||
.output()
|
||||
.await;
|
||||
|
||||
@@ -580,9 +580,9 @@ printtoconsole=1\n", rpc_pass);
|
||||
};
|
||||
let host_ip = &self.config.host_ip;
|
||||
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args([
|
||||
"podman", "run", "-d", "--name", "penpot-postgres", "--restart", "unless-stopped",
|
||||
"run", "-d", "--name", "penpot-postgres", "--restart", "unless-stopped",
|
||||
"--network", "penpot-net",
|
||||
"-v", "/var/lib/archipelago/penpot-postgres:/var/lib/postgresql/data",
|
||||
"-e", "POSTGRES_DB=penpot", "-e", "POSTGRES_USER=penpot", "-e", "POSTGRES_PASSWORD=penpot",
|
||||
@@ -592,9 +592,9 @@ printtoconsole=1\n", rpc_pass);
|
||||
.await;
|
||||
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
||||
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args([
|
||||
"podman", "run", "-d", "--name", "penpot-valkey", "--restart", "unless-stopped",
|
||||
"run", "-d", "--name", "penpot-valkey", "--restart", "unless-stopped",
|
||||
"--network", "penpot-net",
|
||||
"-e", "VALKEY_EXTRA_FLAGS=--maxmemory 128mb --maxmemory-policy volatile-lfu",
|
||||
"docker.io/valkey/valkey:8.1",
|
||||
@@ -603,9 +603,9 @@ printtoconsole=1\n", rpc_pass);
|
||||
.await;
|
||||
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
|
||||
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args([
|
||||
"podman", "run", "-d", "--name", "penpot-backend", "--restart", "unless-stopped",
|
||||
"run", "-d", "--name", "penpot-backend", "--restart", "unless-stopped",
|
||||
"--network", "penpot-net",
|
||||
"-v", "/var/lib/archipelago/penpot-assets:/opt/data/assets",
|
||||
"-e", &format!("PENPOT_PUBLIC_URI=http://{}:9001", host_ip),
|
||||
@@ -622,9 +622,9 @@ printtoconsole=1\n", rpc_pass);
|
||||
.await;
|
||||
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
||||
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args([
|
||||
"podman", "run", "-d", "--name", "penpot-exporter", "--restart", "unless-stopped",
|
||||
"run", "-d", "--name", "penpot-exporter", "--restart", "unless-stopped",
|
||||
"--network", "penpot-net",
|
||||
"-e", &format!("PENPOT_SECRET_KEY={}", secret),
|
||||
"-e", "PENPOT_PUBLIC_URI=http://penpot-frontend:8080",
|
||||
@@ -635,9 +635,9 @@ printtoconsole=1\n", rpc_pass);
|
||||
.await;
|
||||
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||
|
||||
let run = tokio::process::Command::new("sudo")
|
||||
let run = tokio::process::Command::new("podman")
|
||||
.args([
|
||||
"podman", "run", "-d", "--name", "penpot-frontend", "--restart", "unless-stopped",
|
||||
"run", "-d", "--name", "penpot-frontend", "--restart", "unless-stopped",
|
||||
"--network", "penpot-net", "-p", "9001:8080",
|
||||
"-v", "/var/lib/archipelago/penpot-assets:/opt/data/assets",
|
||||
"-e", &format!("PENPOT_PUBLIC_URI=http://{}:9001", host_ip),
|
||||
@@ -687,8 +687,8 @@ printtoconsole=1\n", rpc_pass);
|
||||
};
|
||||
|
||||
for name in to_start {
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "start", &name])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["start", &name])
|
||||
.output()
|
||||
.await;
|
||||
}
|
||||
@@ -710,16 +710,16 @@ printtoconsole=1\n", rpc_pass);
|
||||
let containers = get_containers_for_app(package_id).await?;
|
||||
if containers.is_empty() {
|
||||
let container_name = format!("archy-{}", package_id);
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "stop", &container_name])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["stop", &container_name])
|
||||
.output()
|
||||
.await;
|
||||
return Ok(serde_json::Value::Null);
|
||||
}
|
||||
|
||||
for name in containers {
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "stop", &name])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["stop", &name])
|
||||
.output()
|
||||
.await;
|
||||
}
|
||||
@@ -741,16 +741,16 @@ printtoconsole=1\n", rpc_pass);
|
||||
let containers = get_containers_for_app(package_id).await?;
|
||||
if containers.is_empty() {
|
||||
let container_name = format!("archy-{}", package_id);
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "restart", &container_name])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["restart", &container_name])
|
||||
.output()
|
||||
.await;
|
||||
return Ok(serde_json::Value::Null);
|
||||
}
|
||||
|
||||
for name in containers {
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "restart", &name])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["restart", &name])
|
||||
.output()
|
||||
.await;
|
||||
}
|
||||
@@ -786,8 +786,8 @@ printtoconsole=1\n", rpc_pass);
|
||||
|
||||
for name in &containers_to_remove {
|
||||
tracing::info!("Uninstall {}: stopping container {}", package_id, name);
|
||||
let stop_out = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "stop", "-t", "10", name])
|
||||
let stop_out = tokio::process::Command::new("podman")
|
||||
.args(["stop", "-t", "10", name])
|
||||
.output()
|
||||
.await;
|
||||
match stop_out {
|
||||
@@ -802,8 +802,8 @@ printtoconsole=1\n", rpc_pass);
|
||||
}
|
||||
|
||||
tracing::info!("Uninstall {}: removing container {}", package_id, name);
|
||||
let rm_out = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "rm", "-f", name])
|
||||
let rm_out = tokio::process::Command::new("podman")
|
||||
.args(["rm", "-f", name])
|
||||
.output()
|
||||
.await;
|
||||
match rm_out {
|
||||
@@ -885,8 +885,8 @@ printtoconsole=1\n", rpc_pass);
|
||||
.and_then(|v| v.as_array())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing volumes"))?;
|
||||
|
||||
let check_output = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "ps", "-a", "--format", "{{.Names}}", "--filter", &format!("name={}", app_id)])
|
||||
let check_output = tokio::process::Command::new("podman")
|
||||
.args(["ps", "-a", "--format", "{{.Names}}", "--filter", &format!("name={}", app_id)])
|
||||
.output()
|
||||
.await
|
||||
.context("Failed to check container")?;
|
||||
@@ -894,8 +894,8 @@ printtoconsole=1\n", rpc_pass);
|
||||
let existing = String::from_utf8_lossy(&check_output.stdout);
|
||||
|
||||
if existing.trim().is_empty() {
|
||||
let mut cmd = tokio::process::Command::new("sudo");
|
||||
cmd.args(["podman", "run", "-d", "--name", app_id]);
|
||||
let mut cmd = tokio::process::Command::new("podman");
|
||||
cmd.args(["run", "-d", "--name", app_id]);
|
||||
|
||||
for port in ports {
|
||||
if let (Some(host), Some(container)) = (
|
||||
@@ -938,8 +938,8 @@ printtoconsole=1\n", rpc_pass);
|
||||
return Err(anyhow::anyhow!("Failed to create container: {}", stderr));
|
||||
}
|
||||
} else {
|
||||
let output = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "start", app_id])
|
||||
let output = tokio::process::Command::new("podman")
|
||||
.args(["start", app_id])
|
||||
.output()
|
||||
.await
|
||||
.context("Failed to start container")?;
|
||||
@@ -965,8 +965,8 @@ printtoconsole=1\n", rpc_pass);
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing app_id"))?;
|
||||
validate_app_id(app_id)?;
|
||||
|
||||
let output = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "stop", app_id])
|
||||
let output = tokio::process::Command::new("podman")
|
||||
.args(["stop", app_id])
|
||||
.output()
|
||||
.await
|
||||
.context("Failed to stop container")?;
|
||||
@@ -1110,8 +1110,8 @@ fn parse_size_value(s: &str) -> Option<u64> {
|
||||
/// Get all container names for an app (handles multi-container apps like mempool)
|
||||
async fn get_containers_for_app(package_id: &str) -> Result<Vec<String>> {
|
||||
validate_app_id(package_id)?;
|
||||
let output = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "ps", "-a", "--format", "{{.Names}}"])
|
||||
let output = tokio::process::Command::new("podman")
|
||||
.args(["ps", "-a", "--format", "{{.Names}}"])
|
||||
.output()
|
||||
.await
|
||||
.context("Failed to list containers")?;
|
||||
@@ -1197,8 +1197,8 @@ const TRUSTED_REGISTRIES: &[&str] = &[
|
||||
/// Returns the container name to use as the RPC host (e.g., "bitcoin-knots").
|
||||
fn detect_bitcoin_container_name() -> String {
|
||||
// Synchronous check — called from get_app_config which is sync
|
||||
let output = std::process::Command::new("sudo")
|
||||
.args(["podman", "ps", "--format", "{{.Names}}"])
|
||||
let output = std::process::Command::new("podman")
|
||||
.args(["ps", "--format", "{{.Names}}"])
|
||||
.output();
|
||||
if let Ok(out) = output {
|
||||
let names = String::from_utf8_lossy(&out.stdout);
|
||||
|
||||
@@ -422,11 +422,11 @@ async fn detect_usb_hardware_wallets() -> Result<Vec<serde_json::Value>> {
|
||||
Ok(devices)
|
||||
}
|
||||
|
||||
/// Prune dangling container images via `sudo podman image prune -f`.
|
||||
/// Prune dangling container images via `podman image prune -f`.
|
||||
/// Returns estimated bytes freed.
|
||||
async fn prune_container_images() -> Result<u64> {
|
||||
let output = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "image", "prune", "-f"])
|
||||
let output = tokio::process::Command::new("podman")
|
||||
.args(["image", "prune", "-f"])
|
||||
.output()
|
||||
.await
|
||||
.context("Failed to run podman image prune")?;
|
||||
@@ -444,11 +444,11 @@ async fn prune_container_images() -> Result<u64> {
|
||||
Ok(pruned_count as u64 * 100_000_000) // rough estimate
|
||||
}
|
||||
|
||||
/// Prune container build cache via `sudo podman system prune -f`.
|
||||
/// Prune container build cache via `podman system prune -f`.
|
||||
async fn prune_build_cache() -> Result<u64> {
|
||||
// Just prune volumes and build cache (not containers or images — those are handled above)
|
||||
let output = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "volume", "prune", "-f"])
|
||||
let output = tokio::process::Command::new("podman")
|
||||
.args(["volume", "prune", "-f"])
|
||||
.output()
|
||||
.await
|
||||
.context("Failed to run podman volume prune")?;
|
||||
@@ -625,18 +625,18 @@ impl RpcHandler {
|
||||
|
||||
// 2. Remove all container images
|
||||
tracing::info!("Factory reset: pruning all container images");
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["-u", "archipelago", "podman", "rmi", "--all", "--force"])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["rmi", "--all", "--force"])
|
||||
.output()
|
||||
.await;
|
||||
|
||||
// 3. Prune volumes and build cache
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["-u", "archipelago", "podman", "volume", "prune", "-f"])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["volume", "prune", "-f"])
|
||||
.output()
|
||||
.await;
|
||||
let _ = tokio::process::Command::new("sudo")
|
||||
.args(["-u", "archipelago", "podman", "system", "prune", "-af"])
|
||||
let _ = tokio::process::Command::new("podman")
|
||||
.args(["system", "prune", "-af"])
|
||||
.output()
|
||||
.await;
|
||||
|
||||
|
||||
@@ -173,8 +173,8 @@ impl RpcHandler {
|
||||
|
||||
if !system_ok {
|
||||
// Fall back to container restart
|
||||
let container_ok = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "restart", "archy-tor"])
|
||||
let container_ok = tokio::process::Command::new("podman")
|
||||
.args(["restart", "archy-tor"])
|
||||
.status()
|
||||
.await
|
||||
.map(|s| s.success())
|
||||
@@ -339,8 +339,8 @@ impl RpcHandler {
|
||||
.unwrap_or(false);
|
||||
|
||||
if !system_ok {
|
||||
let container_ok = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "restart", "archy-tor"])
|
||||
let container_ok = tokio::process::Command::new("podman")
|
||||
.args(["restart", "archy-tor"])
|
||||
.status()
|
||||
.await
|
||||
.map(|s| s.success())
|
||||
|
||||
Reference in New Issue
Block a user