feat: v1.2.0-alpha — E2E encrypted mesh relay, steganography, relay status polling
Phase 5 mesh networking: - E2E encrypted TX relay (X25519 + ChaCha20-Poly1305) — non-Archy nodes relay encrypted blobs transparently via Meshcore native routing - Steganographic encoding modes (WeatherStation, SensorNetwork) — traffic looks like sensor data on the wire, 0xAA marker, configurable per-node - Pre-flight Bitcoin Core health check on relay node — specific error codes (bitcoin_unreachable, bitcoin_syncing, tx_rejected) instead of generic fails - mesh.relay-status RPC endpoint — frontend polls for relay result every 3s - On-Chain / Lightning tabs in Off-Grid Bitcoin panel - Archy Peers vs Mesh Broadcast relay mode selector - Mesh view fills viewport (no page scroll), internal panel scrolling - Version bump to 1.2.0-alpha Also includes: deploy hardening, container fixes, IndeedHub updates, boot screen, dashboard improvements, MASTER_PLAN task tracking 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
d1ac098edb
commit
f273816405
@@ -182,12 +182,23 @@ impl MemoryTracker {
|
||||
|
||||
/// Query container memory stats from podman.
|
||||
async fn check_container_memory() -> HashMap<String, u64> {
|
||||
let output = match tokio::process::Command::new("sudo")
|
||||
.args(["podman", "stats", "--no-stream", "--format", "{{.Name}} {{.MemUsage}}"])
|
||||
.output()
|
||||
.await
|
||||
let output = match tokio::time::timeout(
|
||||
std::time::Duration::from_secs(30),
|
||||
tokio::process::Command::new("sudo")
|
||||
.args(["podman", "stats", "--no-stream", "--format", "{{.Name}} {{.MemUsage}}"])
|
||||
.output(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(o) if o.status.success() => o,
|
||||
Ok(Ok(o)) if o.status.success() => o,
|
||||
Ok(Err(e)) => {
|
||||
debug!("podman stats failed: {}", e);
|
||||
return HashMap::new();
|
||||
}
|
||||
Err(_) => {
|
||||
debug!("podman stats timed out (30s)");
|
||||
return HashMap::new();
|
||||
}
|
||||
_ => return HashMap::new(),
|
||||
};
|
||||
|
||||
@@ -230,12 +241,23 @@ fn parse_memory_string(s: &str) -> Option<u64> {
|
||||
|
||||
/// Query all containers and their health status.
|
||||
async fn check_containers() -> Vec<ContainerHealth> {
|
||||
let output = match tokio::process::Command::new("sudo")
|
||||
.args(["podman", "ps", "-a", "--format", "json"])
|
||||
.output()
|
||||
.await
|
||||
let output = match tokio::time::timeout(
|
||||
std::time::Duration::from_secs(30),
|
||||
tokio::process::Command::new("sudo")
|
||||
.args(["podman", "ps", "-a", "--format", "json"])
|
||||
.output(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(o) if o.status.success() => o,
|
||||
Ok(Ok(o)) if o.status.success() => o,
|
||||
Ok(Err(e)) => {
|
||||
debug!("podman ps failed: {}", e);
|
||||
return Vec::new();
|
||||
}
|
||||
Err(_) => {
|
||||
debug!("podman ps timed out (30s)");
|
||||
return Vec::new();
|
||||
}
|
||||
_ => return Vec::new(),
|
||||
};
|
||||
|
||||
@@ -243,7 +265,7 @@ async fn check_containers() -> Vec<ContainerHealth> {
|
||||
let containers: Vec<serde_json::Value> =
|
||||
serde_json::from_str(&stdout).unwrap_or_default();
|
||||
|
||||
// Backend services to skip
|
||||
// Backend services and one-shot init containers to skip
|
||||
let skip = [
|
||||
"btcpay-db", "nbxplorer", "mempool-db", "mempool-api",
|
||||
"penpot-postgres", "penpot-backend", "penpot-exporter", "penpot-valkey",
|
||||
@@ -271,6 +293,11 @@ async fn check_containers() -> Vec<ContainerHealth> {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Skip podman-compose infrastructure and one-shot init containers
|
||||
if name.starts_with("indeedhub-build_") || name.contains("-init") {
|
||||
return None;
|
||||
}
|
||||
|
||||
let state = c.get("State")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("unknown")
|
||||
@@ -291,25 +318,32 @@ async fn check_containers() -> Vec<ContainerHealth> {
|
||||
/// Try to restart a container.
|
||||
async fn restart_container(name: &str) -> bool {
|
||||
info!("Auto-restarting unhealthy container: {}", name);
|
||||
let result = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "start", name])
|
||||
.output()
|
||||
.await;
|
||||
let result = tokio::time::timeout(
|
||||
std::time::Duration::from_secs(30),
|
||||
tokio::process::Command::new("sudo")
|
||||
.args(["podman", "start", name])
|
||||
.output(),
|
||||
)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(output) if output.status.success() => {
|
||||
Ok(Ok(output)) if output.status.success() => {
|
||||
info!("Successfully restarted container: {}", name);
|
||||
true
|
||||
}
|
||||
Ok(output) => {
|
||||
Ok(Ok(output)) => {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
warn!("Failed to restart container {}: {}", name, stderr.trim());
|
||||
false
|
||||
}
|
||||
Err(e) => {
|
||||
Ok(Err(e)) => {
|
||||
warn!("Failed to execute podman start for {}: {}", name, e);
|
||||
false
|
||||
}
|
||||
Err(_) => {
|
||||
warn!("Timeout starting container {} (30s)", name);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user