2026-04-22 17:46:47 -04:00
|
|
|
use crate::manifest::{AppManifest, BuildConfig};
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
use crate::podman_client::{ContainerState, ContainerStatus, PodmanClient};
|
2026-01-24 22:59:20 +00:00
|
|
|
use anyhow::{Context, Result};
|
|
|
|
|
use async_trait::async_trait;
|
|
|
|
|
use std::process::Command;
|
|
|
|
|
use tokio::process::Command as TokioCommand;
|
|
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
|
pub trait ContainerRuntime: Send + Sync {
|
|
|
|
|
async fn pull_image(&self, image: &str, signature: Option<&str>) -> Result<()>;
|
|
|
|
|
async fn create_container(
|
|
|
|
|
&self,
|
|
|
|
|
manifest: &AppManifest,
|
|
|
|
|
name: &str,
|
|
|
|
|
port_offset: u16,
|
|
|
|
|
) -> Result<String>;
|
|
|
|
|
async fn start_container(&self, name: &str) -> Result<()>;
|
|
|
|
|
async fn stop_container(&self, name: &str) -> Result<()>;
|
|
|
|
|
async fn remove_container(&self, name: &str) -> Result<()>;
|
|
|
|
|
async fn get_container_status(&self, name: &str) -> Result<ContainerStatus>;
|
|
|
|
|
async fn get_container_logs(&self, name: &str, lines: u32) -> Result<Vec<String>>;
|
|
|
|
|
async fn list_containers(&self) -> Result<Vec<ContainerStatus>>;
|
2026-04-22 17:46:47 -04:00
|
|
|
|
|
|
|
|
/// Check whether an image reference exists in local storage.
|
|
|
|
|
///
|
|
|
|
|
/// The reconciler calls this before deciding to build. `true` means
|
|
|
|
|
/// `image inspect <image_ref>` succeeded (or equivalent); `false` means
|
|
|
|
|
/// the image is not present. Registry/network state is explicitly NOT
|
|
|
|
|
/// consulted — this is a local-storage check only.
|
|
|
|
|
async fn image_exists(&self, image_ref: &str) -> Result<bool>;
|
|
|
|
|
|
|
|
|
|
/// Build a local image from a `BuildConfig`.
|
|
|
|
|
///
|
|
|
|
|
/// Equivalent to `podman build -t <tag> -f <dockerfile> [--build-arg K=V ...] <context>`.
|
|
|
|
|
/// The resulting image is referenceable by `config.tag` for subsequent
|
|
|
|
|
/// `create_container` / `image_exists` calls. Stdout/stderr are collected
|
|
|
|
|
/// and included in the error on failure; on success they are discarded.
|
|
|
|
|
async fn build_image(&self, config: &BuildConfig) -> Result<()>;
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct PodmanRuntime {
|
|
|
|
|
client: PodmanClient,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PodmanRuntime {
|
|
|
|
|
pub fn new(user: String) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
client: PodmanClient::new(user),
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-22 17:46:47 -04:00
|
|
|
|
|
|
|
|
/// Run `podman <args>`, returning an error with captured stderr on non-zero
|
|
|
|
|
/// exit. Used for operations (build, image inspect) that are awkward over the
|
|
|
|
|
/// HTTP API. The daemon runs as the target user already, so no sudo hop.
|
|
|
|
|
async fn podman_cli(&self, args: &[&str]) -> Result<std::process::Output> {
|
|
|
|
|
let mut cmd = TokioCommand::new("podman");
|
|
|
|
|
cmd.args(args);
|
|
|
|
|
cmd.output()
|
|
|
|
|
.await
|
|
|
|
|
.with_context(|| format!("failed to execute podman {}", args.join(" ")))
|
|
|
|
|
}
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
|
impl ContainerRuntime for PodmanRuntime {
|
|
|
|
|
async fn pull_image(&self, image: &str, signature: Option<&str>) -> Result<()> {
|
|
|
|
|
self.client.pull_image(image, signature).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn create_container(
|
|
|
|
|
&self,
|
|
|
|
|
manifest: &AppManifest,
|
|
|
|
|
name: &str,
|
|
|
|
|
port_offset: u16,
|
|
|
|
|
) -> Result<String> {
|
|
|
|
|
// Apply port offset to manifest ports
|
|
|
|
|
let mut dev_manifest = manifest.clone();
|
|
|
|
|
for port in &mut dev_manifest.app.ports {
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
port.host += port_offset;
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PodmanClient doesn't take port_offset, so we use the modified manifest
|
|
|
|
|
self.client.create_container(&dev_manifest, name).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn start_container(&self, name: &str) -> Result<()> {
|
|
|
|
|
self.client.start_container(name).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn stop_container(&self, name: &str) -> Result<()> {
|
|
|
|
|
self.client.stop_container(name).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn remove_container(&self, name: &str) -> Result<()> {
|
|
|
|
|
self.client.remove_container(name).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_container_status(&self, name: &str) -> Result<ContainerStatus> {
|
|
|
|
|
self.client.get_container_status(name).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_container_logs(&self, name: &str, lines: u32) -> Result<Vec<String>> {
|
|
|
|
|
self.client.get_container_logs(name, lines).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn list_containers(&self) -> Result<Vec<ContainerStatus>> {
|
|
|
|
|
self.client.list_containers().await
|
|
|
|
|
}
|
2026-04-22 17:46:47 -04:00
|
|
|
|
|
|
|
|
async fn image_exists(&self, image_ref: &str) -> Result<bool> {
|
|
|
|
|
// `podman image exists` returns 0 if present, 1 if absent. Any other
|
|
|
|
|
// exit code is an environment failure we should surface.
|
|
|
|
|
let output = self.podman_cli(&["image", "exists", image_ref]).await?;
|
|
|
|
|
match output.status.code() {
|
|
|
|
|
Some(0) => Ok(true),
|
|
|
|
|
Some(1) => Ok(false),
|
|
|
|
|
Some(code) => {
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
|
|
|
Err(anyhow::anyhow!(
|
|
|
|
|
"podman image exists {image_ref} exited with {code}: {stderr}"
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
None => Err(anyhow::anyhow!(
|
|
|
|
|
"podman image exists {image_ref} terminated by signal"
|
|
|
|
|
)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn build_image(&self, config: &BuildConfig) -> Result<()> {
|
|
|
|
|
let args = build_args_for_podman(config);
|
|
|
|
|
let borrowed: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
|
|
|
|
|
let output = self.podman_cli(&borrowed).await?;
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
return Err(anyhow::anyhow!(
|
|
|
|
|
"podman build -t {} failed: {stderr}{}{stdout}",
|
|
|
|
|
config.tag,
|
2026-04-28 15:00:58 -04:00
|
|
|
if stderr.is_empty() || stdout.is_empty() {
|
|
|
|
|
""
|
|
|
|
|
} else {
|
|
|
|
|
"\n---stdout---\n"
|
|
|
|
|
}
|
2026-04-22 17:46:47 -04:00
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Build the argv for `podman build` from a BuildConfig.
|
|
|
|
|
///
|
|
|
|
|
/// Extracted so it can be unit-tested without actually invoking podman.
|
|
|
|
|
/// Order is fixed for deterministic tests: subcommand, -t, -f, build-args
|
|
|
|
|
/// (sorted by key), context.
|
|
|
|
|
fn build_args_for_podman(config: &BuildConfig) -> Vec<String> {
|
|
|
|
|
let mut args: Vec<String> = vec![
|
|
|
|
|
"build".to_string(),
|
|
|
|
|
"-t".to_string(),
|
|
|
|
|
config.tag.clone(),
|
|
|
|
|
"-f".to_string(),
|
|
|
|
|
config.dockerfile.clone(),
|
|
|
|
|
];
|
|
|
|
|
let mut kv: Vec<(&String, &String)> = config.build_args.iter().collect();
|
|
|
|
|
kv.sort_by(|a, b| a.0.cmp(b.0));
|
|
|
|
|
for (k, v) in kv {
|
|
|
|
|
args.push("--build-arg".to_string());
|
|
|
|
|
args.push(format!("{k}={v}"));
|
|
|
|
|
}
|
|
|
|
|
args.push(config.context.clone());
|
|
|
|
|
args
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct DockerRuntime {
|
2026-02-01 05:42:05 +00:00
|
|
|
_user: String,
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl DockerRuntime {
|
|
|
|
|
pub fn new(user: String) -> Self {
|
2026-02-01 05:42:05 +00:00
|
|
|
Self { _user: user }
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn docker_async(&self) -> TokioCommand {
|
|
|
|
|
let mut cmd = TokioCommand::new("docker");
|
2026-01-27 23:21:26 +00:00
|
|
|
// Use actual HOME environment variable instead of hardcoded /home
|
|
|
|
|
if let Ok(home) = std::env::var("HOME") {
|
|
|
|
|
cmd.env("HOME", home);
|
|
|
|
|
}
|
2026-01-24 22:59:20 +00:00
|
|
|
cmd
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
|
impl ContainerRuntime for DockerRuntime {
|
|
|
|
|
async fn pull_image(&self, image: &str, _signature: Option<&str>) -> Result<()> {
|
|
|
|
|
let mut cmd = self.docker_async();
|
|
|
|
|
cmd.arg("pull").arg(image);
|
|
|
|
|
|
|
|
|
|
let output = cmd
|
|
|
|
|
.output()
|
|
|
|
|
.await
|
|
|
|
|
.context("Failed to execute docker pull")?;
|
|
|
|
|
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
|
|
|
return Err(anyhow::anyhow!("Failed to pull image: {}", stderr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn create_container(
|
|
|
|
|
&self,
|
|
|
|
|
manifest: &AppManifest,
|
|
|
|
|
name: &str,
|
|
|
|
|
port_offset: u16,
|
|
|
|
|
) -> Result<String> {
|
|
|
|
|
let mut cmd = self.docker_async();
|
|
|
|
|
cmd.arg("create");
|
|
|
|
|
|
|
|
|
|
cmd.arg("--name").arg(name);
|
|
|
|
|
|
|
|
|
|
if manifest.app.security.readonly_root {
|
|
|
|
|
cmd.arg("--read-only");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match manifest.app.security.network_policy.as_str() {
|
|
|
|
|
"host" => {
|
|
|
|
|
cmd.arg("--network").arg("host");
|
|
|
|
|
}
|
|
|
|
|
"isolated" => {
|
|
|
|
|
// Docker uses bridge network by default
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
cmd.arg("--network")
|
|
|
|
|
.arg(&manifest.app.security.network_policy);
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Port mappings with offset
|
|
|
|
|
for port in &manifest.app.ports {
|
|
|
|
|
let host_port = port.host + port_offset;
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
cmd.arg("-p")
|
|
|
|
|
.arg(format!("{}:{}", host_port, port.container));
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Volumes
|
|
|
|
|
for volume in &manifest.app.volumes {
|
|
|
|
|
let mut mount = format!("{}:{}", volume.source, volume.target);
|
|
|
|
|
if !volume.options.is_empty() {
|
|
|
|
|
mount.push_str(&format!(":{}", volume.options.join(",")));
|
|
|
|
|
}
|
|
|
|
|
cmd.arg("-v").arg(mount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Devices
|
|
|
|
|
for device in &manifest.app.devices {
|
|
|
|
|
cmd.arg("--device").arg(device);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Environment variables
|
|
|
|
|
for env in &manifest.app.environment {
|
|
|
|
|
cmd.arg("-e").arg(env);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Resource limits
|
|
|
|
|
if let Some(cpu) = manifest.app.resources.cpu_limit {
|
|
|
|
|
cmd.arg("--cpus").arg(cpu.to_string());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(memory) = &manifest.app.resources.memory_limit {
|
|
|
|
|
cmd.arg("--memory").arg(memory);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Capabilities
|
|
|
|
|
cmd.arg("--cap-drop").arg("ALL");
|
|
|
|
|
for cap in &manifest.app.security.capabilities {
|
|
|
|
|
cmd.arg("--cap-add").arg(cap);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 17:46:47 -04:00
|
|
|
let image_ref = manifest.app.container.image_ref().ok_or_else(|| {
|
|
|
|
|
anyhow::anyhow!(
|
|
|
|
|
"container config for {} has neither a valid image nor build source",
|
|
|
|
|
manifest.app.id
|
|
|
|
|
)
|
|
|
|
|
})?;
|
|
|
|
|
cmd.arg(&image_ref);
|
2026-01-24 22:59:20 +00:00
|
|
|
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
let output = cmd.output().await.context("Failed to create container")?;
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
|
|
|
return Err(anyhow::anyhow!("Failed to create container: {}", stderr));
|
|
|
|
|
}
|
|
|
|
|
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
let container_id = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
|
|
|
Ok(container_id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn start_container(&self, name: &str) -> Result<()> {
|
|
|
|
|
let mut cmd = self.docker_async();
|
|
|
|
|
cmd.arg("start").arg(name);
|
|
|
|
|
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
let output = cmd.output().await.context("Failed to start container")?;
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
|
|
|
return Err(anyhow::anyhow!("Failed to start container: {}", stderr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn stop_container(&self, name: &str) -> Result<()> {
|
|
|
|
|
let mut cmd = self.docker_async();
|
|
|
|
|
cmd.arg("stop").arg(name);
|
|
|
|
|
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
let output = cmd.output().await.context("Failed to stop container")?;
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
|
|
|
return Err(anyhow::anyhow!("Failed to stop container: {}", stderr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn remove_container(&self, name: &str) -> Result<()> {
|
|
|
|
|
let mut cmd = self.docker_async();
|
|
|
|
|
cmd.arg("rm").arg("-f").arg(name);
|
|
|
|
|
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
let output = cmd.output().await.context("Failed to remove container")?;
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
|
|
|
return Err(anyhow::anyhow!("Failed to remove container: {}", stderr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_container_status(&self, name: &str) -> Result<ContainerStatus> {
|
|
|
|
|
let mut cmd = self.docker_async();
|
|
|
|
|
cmd.arg("inspect")
|
|
|
|
|
.arg("--format")
|
|
|
|
|
.arg("{{.Id}}|{{.Name}}|{{.State.Status}}|{{.Config.Image}}|{{.Created}}|{{.NetworkSettings.Ports}}")
|
|
|
|
|
.arg(name);
|
|
|
|
|
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
let output = cmd.output().await.context("Failed to inspect container")?;
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
return Err(anyhow::anyhow!("Container not found: {}", name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let info = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
let parts: Vec<&str> = info.trim().split('|').collect();
|
|
|
|
|
|
|
|
|
|
if parts.len() < 5 {
|
|
|
|
|
return Err(anyhow::anyhow!("Invalid container inspect output"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(ContainerStatus {
|
|
|
|
|
id: parts[0].to_string(),
|
|
|
|
|
name: parts[1].to_string(),
|
|
|
|
|
state: crate::podman_client::ContainerState::from(parts[2]),
|
security+feat: v1.3.0 — pentest remediation, container reliability, UI overhaul
Security (33 pentest findings addressed):
- CRITICAL: backend binds 127.0.0.1, path traversal in tor.rs/dwn fixed
- HIGH: federation requires signatures, XSS login redirect, RBAC viewer restricted
- HIGH: tar slip prevention, S3 SSRF validation, backup ID validation
- MEDIUM: remember-me random secret, TOTP session rotation, password re-auth
- LOW: CSP unsafe-inline removed, CORS dev-only, onion/webhook validation
Container reliability:
- Memory limits on all 37 containers (OOM prevention)
- Exited vs stopped state distinction with health-aware status badges
- Crash recovery coordination (no more restart cascade)
- User-stopped tracking survives reboots
- Tiered boot recovery (databases → core → services → apps)
UI:
- Wallet TransactionsModal, health-aware app status badges
- Restart button on containers, exited/crashed red state
- Mesh view overhaul, glass button updates, BaseModal/ToggleSwitch
- Apps sticky header removed, dev faucet, mutable mock wallet
Infrastructure:
- LND REST port 8080 exposed over Tor (LND Connect fix)
- Nginx cookie_session fix, deploy script Tor config updated
- Dev environment: podman auto-start, boot mode simulation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 12:44:31 +00:00
|
|
|
health: None,
|
fix: overhaul container lifecycle — recovery, health, uninstall, UI state
Container recovery:
- Health monitor: MAX_RESTART_ATTEMPTS 3→10, interval 60s→120s
- Dependency-aware restarts: won't restart services before their deps
- Reset dependent counters when a dependency recovers
- Handle "created" state containers (were invisible to health monitor)
- Added IndeedHub, mempool-api, mysql to tier system
- Crash recovery: podman start timeout 30s→120s with retry
- Podman client: socket timeout 5s→30s, added restart policy
UI state representation:
- Exit code 0 shows "stopped" (gray), not "crashed" (red)
- Exit code 137 shows "killed (OOM)"
- Non-zero exit shows "crashed" (red)
- Added exit_code field to PackageDataEntry
Install/uninstall fixes:
- Install returns error when container doesn't start (was silent success)
- Post-install hooks awaited instead of fire-and-forget tokio::spawn
- Uninstall: graceful rm before force, volume prune, network cleanup
- Uninstall returns error on partial failure (was 200 OK)
Config consistency:
- DB passwords read from /var/lib/archipelago/secrets/ (was hardcoded)
- Bitcoin: added ZMQ ports 28332/28333 for LND block notifications
- IndeedHub port 7777→8190 (was conflicting with strfry)
- Marketplace versions: LND 0.17.4→0.18.4, Mempool 2.5.0→3.0.0
Performance:
- Metrics collector interval 60s→300s (was duplicating health monitor)
- Podman client: proper error propagation instead of unwrap_or_default
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 07:03:57 +01:00
|
|
|
exit_code: None,
|
security+feat: v1.3.0 — pentest remediation, container reliability, UI overhaul
Security (33 pentest findings addressed):
- CRITICAL: backend binds 127.0.0.1, path traversal in tor.rs/dwn fixed
- HIGH: federation requires signatures, XSS login redirect, RBAC viewer restricted
- HIGH: tar slip prevention, S3 SSRF validation, backup ID validation
- MEDIUM: remember-me random secret, TOTP session rotation, password re-auth
- LOW: CSP unsafe-inline removed, CORS dev-only, onion/webhook validation
Container reliability:
- Memory limits on all 37 containers (OOM prevention)
- Exited vs stopped state distinction with health-aware status badges
- Crash recovery coordination (no more restart cascade)
- User-stopped tracking survives reboots
- Tiered boot recovery (databases → core → services → apps)
UI:
- Wallet TransactionsModal, health-aware app status badges
- Restart button on containers, exited/crashed red state
- Mesh view overhaul, glass button updates, BaseModal/ToggleSwitch
- Apps sticky header removed, dev faucet, mutable mock wallet
Infrastructure:
- LND REST port 8080 exposed over Tor (LND Connect fix)
- Nginx cookie_session fix, deploy script Tor config updated
- Dev environment: podman auto-start, boot mode simulation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 12:44:31 +00:00
|
|
|
started_at: None,
|
2026-01-24 22:59:20 +00:00
|
|
|
image: parts[3].to_string(),
|
|
|
|
|
created: parts[4].to_string(),
|
|
|
|
|
ports: vec![],
|
2026-02-04 16:20:09 +00:00
|
|
|
lan_address: None,
|
2026-01-24 22:59:20 +00:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_container_logs(&self, name: &str, lines: u32) -> Result<Vec<String>> {
|
|
|
|
|
let mut cmd = self.docker_async();
|
|
|
|
|
cmd.arg("logs")
|
|
|
|
|
.arg("--tail")
|
|
|
|
|
.arg(lines.to_string())
|
|
|
|
|
.arg(name);
|
|
|
|
|
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
let output = cmd.output().await.context("Failed to get container logs")?;
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
|
|
|
return Err(anyhow::anyhow!("Failed to get logs: {}", stderr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let logs = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
Ok(logs.lines().map(|s| s.to_string()).collect())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn list_containers(&self) -> Result<Vec<ContainerStatus>> {
|
|
|
|
|
let mut cmd = self.docker_async();
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
cmd.arg("ps").arg("-a").arg("--format").arg("json");
|
2026-01-24 22:59:20 +00:00
|
|
|
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
let output = cmd.output().await.context("Failed to list containers")?;
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
|
|
|
return Err(anyhow::anyhow!("Failed to list containers: {}", stderr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let json = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
let mut result = Vec::new();
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
|
2026-01-27 23:21:26 +00:00
|
|
|
// Docker returns NDJSON (newline-delimited JSON), not a JSON array
|
|
|
|
|
for line in json.lines() {
|
|
|
|
|
if line.trim().is_empty() {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
|
2026-01-27 23:21:26 +00:00
|
|
|
let container: serde_json::Value = serde_json::from_str(line)
|
|
|
|
|
.context(format!("Failed to parse container JSON: {}", line))?;
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
|
2026-01-27 23:21:26 +00:00
|
|
|
// Extract ports from JSON
|
|
|
|
|
let ports_value = &container["Ports"];
|
|
|
|
|
let ports_str = ports_value.as_str().unwrap_or("");
|
|
|
|
|
let ports: Vec<String> = if !ports_str.is_empty() {
|
|
|
|
|
ports_str.split(", ").map(|s| s.to_string()).collect()
|
|
|
|
|
} else {
|
|
|
|
|
vec![]
|
|
|
|
|
};
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
|
2026-01-24 22:59:20 +00:00
|
|
|
result.push(ContainerStatus {
|
|
|
|
|
id: container["ID"].as_str().unwrap_or("").to_string(),
|
|
|
|
|
name: container["Names"].as_str().unwrap_or("").to_string(),
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
state: ContainerState::from(container["State"].as_str().unwrap_or("unknown")),
|
security+feat: v1.3.0 — pentest remediation, container reliability, UI overhaul
Security (33 pentest findings addressed):
- CRITICAL: backend binds 127.0.0.1, path traversal in tor.rs/dwn fixed
- HIGH: federation requires signatures, XSS login redirect, RBAC viewer restricted
- HIGH: tar slip prevention, S3 SSRF validation, backup ID validation
- MEDIUM: remember-me random secret, TOTP session rotation, password re-auth
- LOW: CSP unsafe-inline removed, CORS dev-only, onion/webhook validation
Container reliability:
- Memory limits on all 37 containers (OOM prevention)
- Exited vs stopped state distinction with health-aware status badges
- Crash recovery coordination (no more restart cascade)
- User-stopped tracking survives reboots
- Tiered boot recovery (databases → core → services → apps)
UI:
- Wallet TransactionsModal, health-aware app status badges
- Restart button on containers, exited/crashed red state
- Mesh view overhaul, glass button updates, BaseModal/ToggleSwitch
- Apps sticky header removed, dev faucet, mutable mock wallet
Infrastructure:
- LND REST port 8080 exposed over Tor (LND Connect fix)
- Nginx cookie_session fix, deploy script Tor config updated
- Dev environment: podman auto-start, boot mode simulation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 12:44:31 +00:00
|
|
|
health: None,
|
fix: overhaul container lifecycle — recovery, health, uninstall, UI state
Container recovery:
- Health monitor: MAX_RESTART_ATTEMPTS 3→10, interval 60s→120s
- Dependency-aware restarts: won't restart services before their deps
- Reset dependent counters when a dependency recovers
- Handle "created" state containers (were invisible to health monitor)
- Added IndeedHub, mempool-api, mysql to tier system
- Crash recovery: podman start timeout 30s→120s with retry
- Podman client: socket timeout 5s→30s, added restart policy
UI state representation:
- Exit code 0 shows "stopped" (gray), not "crashed" (red)
- Exit code 137 shows "killed (OOM)"
- Non-zero exit shows "crashed" (red)
- Added exit_code field to PackageDataEntry
Install/uninstall fixes:
- Install returns error when container doesn't start (was silent success)
- Post-install hooks awaited instead of fire-and-forget tokio::spawn
- Uninstall: graceful rm before force, volume prune, network cleanup
- Uninstall returns error on partial failure (was 200 OK)
Config consistency:
- DB passwords read from /var/lib/archipelago/secrets/ (was hardcoded)
- Bitcoin: added ZMQ ports 28332/28333 for LND block notifications
- IndeedHub port 7777→8190 (was conflicting with strfry)
- Marketplace versions: LND 0.17.4→0.18.4, Mempool 2.5.0→3.0.0
Performance:
- Metrics collector interval 60s→300s (was duplicating health monitor)
- Podman client: proper error propagation instead of unwrap_or_default
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 07:03:57 +01:00
|
|
|
exit_code: container["ExitCode"].as_i64().map(|c| c as i32),
|
security+feat: v1.3.0 — pentest remediation, container reliability, UI overhaul
Security (33 pentest findings addressed):
- CRITICAL: backend binds 127.0.0.1, path traversal in tor.rs/dwn fixed
- HIGH: federation requires signatures, XSS login redirect, RBAC viewer restricted
- HIGH: tar slip prevention, S3 SSRF validation, backup ID validation
- MEDIUM: remember-me random secret, TOTP session rotation, password re-auth
- LOW: CSP unsafe-inline removed, CORS dev-only, onion/webhook validation
Container reliability:
- Memory limits on all 37 containers (OOM prevention)
- Exited vs stopped state distinction with health-aware status badges
- Crash recovery coordination (no more restart cascade)
- User-stopped tracking survives reboots
- Tiered boot recovery (databases → core → services → apps)
UI:
- Wallet TransactionsModal, health-aware app status badges
- Restart button on containers, exited/crashed red state
- Mesh view overhaul, glass button updates, BaseModal/ToggleSwitch
- Apps sticky header removed, dev faucet, mutable mock wallet
Infrastructure:
- LND REST port 8080 exposed over Tor (LND Connect fix)
- Nginx cookie_session fix, deploy script Tor config updated
- Dev environment: podman auto-start, boot mode simulation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 12:44:31 +00:00
|
|
|
started_at: None,
|
2026-01-24 22:59:20 +00:00
|
|
|
image: container["Image"].as_str().unwrap_or("").to_string(),
|
|
|
|
|
created: container["CreatedAt"].as_str().unwrap_or("").to_string(),
|
2026-01-27 23:21:26 +00:00
|
|
|
ports,
|
2026-02-04 16:20:09 +00:00
|
|
|
lan_address: None,
|
2026-01-24 22:59:20 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
|
}
|
2026-04-22 17:46:47 -04:00
|
|
|
|
|
|
|
|
async fn image_exists(&self, image_ref: &str) -> Result<bool> {
|
|
|
|
|
// `docker image inspect` exits 1 when the image is absent. Any message
|
|
|
|
|
// to stderr in that case is informational; we swallow it.
|
|
|
|
|
let mut cmd = self.docker_async();
|
|
|
|
|
cmd.arg("image").arg("inspect").arg(image_ref);
|
2026-04-28 15:00:58 -04:00
|
|
|
let output = cmd
|
|
|
|
|
.output()
|
|
|
|
|
.await
|
|
|
|
|
.context("failed to execute docker image inspect")?;
|
2026-04-22 17:46:47 -04:00
|
|
|
match output.status.code() {
|
|
|
|
|
Some(0) => Ok(true),
|
|
|
|
|
Some(1) => Ok(false),
|
|
|
|
|
Some(code) => {
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
|
|
|
Err(anyhow::anyhow!(
|
|
|
|
|
"docker image inspect {image_ref} exited with {code}: {stderr}"
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
None => Err(anyhow::anyhow!(
|
|
|
|
|
"docker image inspect {image_ref} terminated by signal"
|
|
|
|
|
)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn build_image(&self, config: &BuildConfig) -> Result<()> {
|
|
|
|
|
let mut cmd = self.docker_async();
|
2026-04-28 15:00:58 -04:00
|
|
|
cmd.arg("build")
|
|
|
|
|
.arg("-t")
|
|
|
|
|
.arg(&config.tag)
|
|
|
|
|
.arg("-f")
|
|
|
|
|
.arg(&config.dockerfile);
|
2026-04-22 17:46:47 -04:00
|
|
|
for (k, v) in &config.build_args {
|
|
|
|
|
cmd.arg("--build-arg").arg(format!("{k}={v}"));
|
|
|
|
|
}
|
|
|
|
|
cmd.arg(&config.context);
|
2026-04-28 15:00:58 -04:00
|
|
|
let output = cmd
|
|
|
|
|
.output()
|
|
|
|
|
.await
|
|
|
|
|
.context("failed to execute docker build")?;
|
2026-04-22 17:46:47 -04:00
|
|
|
if !output.status.success() {
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
2026-04-28 15:00:58 -04:00
|
|
|
return Err(anyhow::anyhow!(
|
|
|
|
|
"docker build -t {} failed: {stderr}",
|
|
|
|
|
config.tag
|
|
|
|
|
));
|
2026-04-22 17:46:47 -04:00
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct AutoRuntime {
|
|
|
|
|
runtime: Box<dyn ContainerRuntime>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AutoRuntime {
|
|
|
|
|
pub async fn new(user: String) -> Result<Self> {
|
|
|
|
|
// Try Podman first
|
|
|
|
|
if Self::check_podman_available() {
|
|
|
|
|
Ok(Self {
|
|
|
|
|
runtime: Box::new(PodmanRuntime::new(user)),
|
|
|
|
|
})
|
|
|
|
|
} else if Self::check_docker_available() {
|
|
|
|
|
Ok(Self {
|
|
|
|
|
runtime: Box::new(DockerRuntime::new(user)),
|
|
|
|
|
})
|
|
|
|
|
} else {
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
Err(anyhow::anyhow!("Neither Podman nor Docker is available"))
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn check_podman_available() -> bool {
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
Command::new("podman").arg("--version").output().is_ok()
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn check_docker_available() -> bool {
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
Command::new("docker").arg("--version").output().is_ok()
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
|
impl ContainerRuntime for AutoRuntime {
|
|
|
|
|
async fn pull_image(&self, image: &str, signature: Option<&str>) -> Result<()> {
|
|
|
|
|
self.runtime.pull_image(image, signature).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn create_container(
|
|
|
|
|
&self,
|
|
|
|
|
manifest: &AppManifest,
|
|
|
|
|
name: &str,
|
|
|
|
|
port_offset: u16,
|
|
|
|
|
) -> Result<String> {
|
chore(ci): rustfmt + clippy clean-up to unblock the Rust CI job
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:23:46 -04:00
|
|
|
self.runtime
|
|
|
|
|
.create_container(manifest, name, port_offset)
|
|
|
|
|
.await
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn start_container(&self, name: &str) -> Result<()> {
|
|
|
|
|
self.runtime.start_container(name).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn stop_container(&self, name: &str) -> Result<()> {
|
|
|
|
|
self.runtime.stop_container(name).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn remove_container(&self, name: &str) -> Result<()> {
|
|
|
|
|
self.runtime.remove_container(name).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_container_status(&self, name: &str) -> Result<ContainerStatus> {
|
|
|
|
|
self.runtime.get_container_status(name).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_container_logs(&self, name: &str, lines: u32) -> Result<Vec<String>> {
|
|
|
|
|
self.runtime.get_container_logs(name, lines).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn list_containers(&self) -> Result<Vec<ContainerStatus>> {
|
|
|
|
|
self.runtime.list_containers().await
|
|
|
|
|
}
|
2026-04-22 17:46:47 -04:00
|
|
|
|
|
|
|
|
async fn image_exists(&self, image_ref: &str) -> Result<bool> {
|
|
|
|
|
self.runtime.image_exists(image_ref).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn build_image(&self, config: &BuildConfig) -> Result<()> {
|
|
|
|
|
self.runtime.build_image(config).await
|
|
|
|
|
}
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Runtime factory functions will be provided by the archipelago crate
|
|
|
|
|
// that imports this library and has access to Config
|
2026-04-22 17:46:47 -04:00
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
|
|
fn cfg(context: &str, tag: &str, dockerfile: &str, args: &[(&str, &str)]) -> BuildConfig {
|
|
|
|
|
BuildConfig {
|
|
|
|
|
context: context.to_string(),
|
|
|
|
|
dockerfile: dockerfile.to_string(),
|
|
|
|
|
tag: tag.to_string(),
|
|
|
|
|
build_args: args
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|(k, v)| (k.to_string(), v.to_string()))
|
|
|
|
|
.collect::<HashMap<_, _>>(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn build_args_minimal() {
|
|
|
|
|
let c = cfg("/tmp/ctx", "archy-bitcoin-ui:local", "Dockerfile", &[]);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
build_args_for_podman(&c),
|
|
|
|
|
vec![
|
|
|
|
|
"build",
|
|
|
|
|
"-t",
|
|
|
|
|
"archy-bitcoin-ui:local",
|
|
|
|
|
"-f",
|
|
|
|
|
"Dockerfile",
|
|
|
|
|
"/tmp/ctx",
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn build_args_custom_dockerfile() {
|
|
|
|
|
let c = cfg("/opt/archy/bitcoin-ui", "x:local", "Dockerfile.prod", &[]);
|
|
|
|
|
let got = build_args_for_podman(&c);
|
|
|
|
|
assert_eq!(got[3], "-f");
|
|
|
|
|
assert_eq!(got[4], "Dockerfile.prod");
|
|
|
|
|
assert_eq!(got.last().unwrap(), "/opt/archy/bitcoin-ui");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn build_args_are_sorted_deterministically() {
|
|
|
|
|
// HashMap iteration order is nondeterministic; the runtime sorts so that
|
|
|
|
|
// equivalent BuildConfigs produce identical commands (easier to debug,
|
|
|
|
|
// cache-friendly if we ever layer build-cache keys on top).
|
|
|
|
|
let c = cfg(
|
|
|
|
|
"/c",
|
|
|
|
|
"t",
|
|
|
|
|
"Dockerfile",
|
|
|
|
|
&[("BAR", "2"), ("FOO", "1"), ("BAZ", "3")],
|
|
|
|
|
);
|
|
|
|
|
let args = build_args_for_podman(&c);
|
|
|
|
|
let flat: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
|
|
|
|
|
// Build args appear as pairs of --build-arg K=V; locate them:
|
|
|
|
|
let mut pairs: Vec<&str> = Vec::new();
|
|
|
|
|
for w in flat.windows(2) {
|
|
|
|
|
if w[0] == "--build-arg" {
|
|
|
|
|
pairs.push(w[1]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
assert_eq!(pairs, vec!["BAR=2", "BAZ=3", "FOO=1"]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn build_args_context_is_last() {
|
|
|
|
|
// Context MUST be the final positional argument — podman treats any
|
|
|
|
|
// stray trailing arg after build-args as the context, so placement
|
|
|
|
|
// matters. Regression guard.
|
|
|
|
|
let c = cfg("/final/context", "t", "Dockerfile", &[("K", "V")]);
|
|
|
|
|
let args = build_args_for_podman(&c);
|
|
|
|
|
assert_eq!(args.last().unwrap(), "/final/context");
|
|
|
|
|
}
|
|
|
|
|
}
|