fix: container installs, Tor, kiosk, GRUB, LUKS display, error messages

Critical:
- fix: container installs fail with "statfs: no such file or directory"
  Root cause: NoNewPrivileges=yes in systemd blocks sudo inside backend.
  Fix: use std::fs::create_dir_all + podman unshare chown (no sudo needed)
- fix: Tor services.json never written — \$ARCHY_TOR_DIR escaping bug
- fix: kiosk white screen — increase health wait to 60s, add --disable-gpu

Improvements:
- feat: LUKS encryption badge in Server disk stats (backend detects dm-crypt)
- fix: GRUB theme text scaling on 4:3 monitors — explicit fonts, wider menu
- fix: suppress default Debian MOTD (custom profile.d welcome is enough)
- fix: install error messages now show "Failed to pull/start" instead of
  generic "Operation failed" (middleware.rs allowlist expanded)
- fix: container-tests CI — source cargo env before running tests
- docs: interactive container architecture diagram (HTML)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-30 16:35:06 +01:00
co-authored by Claude Opus 4.6
parent 77765c90d0
commit 44bffee473
9 changed files with 522 additions and 37 deletions
@@ -98,7 +98,11 @@ impl RpcHandler {
/// system.disk-status — Disk usage with warning/critical thresholds.
pub(in crate::api::rpc) async fn handle_system_disk_status(&self) -> Result<serde_json::Value> {
let (used, total) = read_disk_usage().await.unwrap_or((0, 0));
// Prefer the encrypted data partition if it exists
let data_path = std::path::Path::new("/var/lib/archipelago");
let df_target = if data_path.exists() { "/var/lib/archipelago" } else { "/" };
let (used, total) = read_disk_usage_path(df_target).await.unwrap_or((0, 0));
let percent = if total > 0 {
(used as f64 / total as f64) * 100.0
} else {
@@ -114,12 +118,19 @@ impl RpcHandler {
"ok"
};
// Detect LUKS encryption (device name varies by install)
let encrypted = std::path::Path::new("/dev/mapper/archipelago_crypt").exists()
|| std::path::Path::new("/dev/mapper/archipelago-data").exists()
|| std::path::Path::new("/dev/mapper/archipelago_data").exists();
Ok(serde_json::json!({
"used_bytes": used,
"total_bytes": total,
"free_bytes": total.saturating_sub(used),
"used_percent": percent_rounded,
"level": level,
"encrypted": encrypted,
"partition": df_target,
}))
}
+6 -1
View File
@@ -157,8 +157,13 @@ pub(super) fn parse_meminfo_kb(val: &str) -> Result<u64> {
/// Read disk usage via `df` for the root filesystem.
/// Returns (used_bytes, total_bytes).
pub(super) async fn read_disk_usage() -> Result<(u64, u64)> {
read_disk_usage_path("/").await
}
/// Read disk usage via `df` for a given path.
pub(super) async fn read_disk_usage_path(path: &str) -> Result<(u64, u64)> {
let output = tokio::process::Command::new("df")
.args(["--block-size=1", "--output=used,size", "/"])
.args(["--block-size=1", "--output=used,size", path])
.output()
.await
.context("Failed to run df")?;