2026-01-24 22:59:20 +00:00
|
|
|
|
use crate::api::ApiHandler;
|
2026-01-27 23:21:26 +00:00
|
|
|
|
use crate::config::{Config, ContainerRuntime};
|
2026-02-17 15:03:34 +00:00
|
|
|
|
use crate::container::{docker_packages, DockerPackageScanner};
|
|
|
|
|
|
use crate::identity::{self, NodeIdentity};
|
2026-03-11 11:11:02 +00:00
|
|
|
|
use crate::monitoring::MetricsStore;
|
2026-03-09 07:43:12 +00:00
|
|
|
|
use crate::node_message;
|
2026-02-17 15:03:34 +00:00
|
|
|
|
use crate::nostr_discovery;
|
2026-03-12 12:56:59 +00:00
|
|
|
|
use crate::nostr_handshake;
|
2026-03-09 07:43:12 +00:00
|
|
|
|
use crate::peers;
|
2026-01-27 23:06:18 +00:00
|
|
|
|
use crate::state::StateManager;
|
2026-01-24 22:59:20 +00:00
|
|
|
|
use anyhow::Result;
|
Update archipelago: API, auth, container, parmanode, performance, security
- API handler, RPC, and server updates
- Auth and coding rules
- Container data manager, dev orchestrator, health monitor, podman client
- Parmanode script runner
- Performance resource manager
- Security container policies and secrets manager
- Add build scripts and documentation
2026-01-27 22:27:17 +00:00
|
|
|
|
use hyper::server::conn::Http;
|
|
|
|
|
|
use hyper::service::service_fn;
|
2026-04-02 01:28:11 +01:00
|
|
|
|
use std::collections::HashMap;
|
2026-01-24 22:59:20 +00:00
|
|
|
|
use std::net::SocketAddr;
|
|
|
|
|
|
use std::sync::Arc;
|
2026-01-27 23:21:26 +00:00
|
|
|
|
use std::time::Duration;
|
2026-01-24 22:59:20 +00:00
|
|
|
|
use tokio::net::TcpListener;
|
2026-03-11 11:11:02 +00:00
|
|
|
|
use tracing::{debug, error, info, warn};
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
|
|
|
|
|
pub struct Server {
|
Update archipelago: API, auth, container, parmanode, performance, security
- API handler, RPC, and server updates
- Auth and coding rules
- Container data manager, dev orchestrator, health monitor, podman client
- Parmanode script runner
- Performance resource manager
- Security container policies and secrets manager
- Add build scripts and documentation
2026-01-27 22:27:17 +00:00
|
|
|
|
_config: Config,
|
2026-02-17 15:03:34 +00:00
|
|
|
|
_identity: Arc<NodeIdentity>,
|
2026-01-24 22:59:20 +00:00
|
|
|
|
api_handler: Arc<ApiHandler>,
|
2026-02-01 05:42:05 +00:00
|
|
|
|
_state_manager: Arc<StateManager>,
|
2026-01-24 22:59:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Server {
|
|
|
|
|
|
pub async fn new(config: Config) -> Result<Self> {
|
2026-01-27 23:06:18 +00:00
|
|
|
|
let state_manager = Arc::new(StateManager::new());
|
2026-02-17 15:03:34 +00:00
|
|
|
|
|
2026-03-31 00:50:43 +01:00
|
|
|
|
// Load node identity and set stable server_info.
|
|
|
|
|
|
// Detect seed-backed vs legacy vs fresh install.
|
2026-02-17 15:03:34 +00:00
|
|
|
|
let identity_dir = config.data_dir.join("identity");
|
2026-03-31 00:50:43 +01:00
|
|
|
|
let has_seed = crate::seed::seed_exists(&config.data_dir);
|
|
|
|
|
|
let has_node_key = NodeIdentity::key_exists(&identity_dir);
|
|
|
|
|
|
|
|
|
|
|
|
let identity = if has_node_key {
|
|
|
|
|
|
// Existing keys on disk (seed-derived or legacy random) — load them.
|
|
|
|
|
|
NodeIdentity::load_or_create(&identity_dir).await?
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// Fresh install — create a temporary identity.
|
|
|
|
|
|
// Onboarding will overwrite this with seed-derived keys.
|
|
|
|
|
|
NodeIdentity::load_or_create(&identity_dir).await?
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-02-17 15:03:34 +00:00
|
|
|
|
let (mut data, _) = state_manager.get_snapshot().await;
|
|
|
|
|
|
data.server_info.id = identity.node_id();
|
|
|
|
|
|
data.server_info.pubkey = identity.pubkey_hex();
|
2026-03-31 00:50:43 +01:00
|
|
|
|
data.server_info.seed_backed = has_seed;
|
2026-03-14 03:00:29 +00:00
|
|
|
|
// Load persisted server name
|
|
|
|
|
|
let name_file = config.data_dir.join("server-name");
|
|
|
|
|
|
if let Ok(name) = tokio::fs::read_to_string(&name_file).await {
|
|
|
|
|
|
let name = name.trim().to_string();
|
|
|
|
|
|
if !name.is_empty() {
|
|
|
|
|
|
data.server_info.name = Some(name);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-21 01:21:08 +00:00
|
|
|
|
data.server_info.tor_address = docker_packages::read_tor_address("archipelago").await;
|
2026-02-17 15:03:34 +00:00
|
|
|
|
if let Some(ref tor) = data.server_info.tor_address {
|
|
|
|
|
|
data.server_info.node_address = Some(identity.node_address(tor));
|
|
|
|
|
|
}
|
|
|
|
|
|
state_manager.update_data(data.clone()).await;
|
|
|
|
|
|
|
2026-03-31 05:11:55 +01:00
|
|
|
|
// Retry Tor address in background — Tor may not be ready at startup
|
|
|
|
|
|
if data.server_info.tor_address.is_none() {
|
|
|
|
|
|
let sm = state_manager.clone();
|
|
|
|
|
|
let pubkey = identity.pubkey_hex();
|
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
|
for delay in [5, 10, 20, 30, 60] {
|
|
|
|
|
|
tokio::time::sleep(std::time::Duration::from_secs(delay)).await;
|
|
|
|
|
|
if let Some(tor) = docker_packages::read_tor_address("archipelago").await {
|
|
|
|
|
|
let (mut d, _) = sm.get_snapshot().await;
|
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 addr =
|
|
|
|
|
|
format!("archipelago://{}#{}", tor.trim_end_matches('/'), pubkey);
|
2026-03-31 05:11:55 +01:00
|
|
|
|
d.server_info.tor_address = Some(tor.clone());
|
|
|
|
|
|
d.server_info.node_address = Some(addr);
|
|
|
|
|
|
sm.update_data(d).await;
|
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
|
|
|
|
tracing::info!(
|
|
|
|
|
|
"Tor address discovered after startup: {}",
|
|
|
|
|
|
&tor[..20.min(tor.len())]
|
|
|
|
|
|
);
|
2026-03-31 05:11:55 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-20 08:26:40 +00:00
|
|
|
|
// Load persisted messages (Archipelago channel)
|
|
|
|
|
|
node_message::init(&config.data_dir).await;
|
|
|
|
|
|
|
2026-03-15 05:18:12 +00:00
|
|
|
|
// Auto-create default identity if none exist (fresh boot or factory reset)
|
|
|
|
|
|
{
|
|
|
|
|
|
let im = crate::identity_manager::IdentityManager::new(&config.data_dir).await;
|
|
|
|
|
|
if let Ok(mgr) = im {
|
|
|
|
|
|
if let Ok((list, _)) = mgr.list().await {
|
|
|
|
|
|
if list.is_empty() {
|
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
|
|
|
|
match mgr
|
|
|
|
|
|
.create(
|
|
|
|
|
|
"Default".to_string(),
|
|
|
|
|
|
crate::identity_manager::IdentityPurpose::Personal,
|
|
|
|
|
|
)
|
|
|
|
|
|
.await
|
|
|
|
|
|
{
|
2026-03-15 05:18:12 +00:00
|
|
|
|
Ok(record) => {
|
|
|
|
|
|
let _ = mgr.create_nostr_key(&record.id).await;
|
|
|
|
|
|
tracing::info!(did = %record.did, "Auto-created default identity with Nostr key");
|
|
|
|
|
|
}
|
|
|
|
|
|
Err(e) => tracing::debug!("Auto-identity creation (non-fatal): {}", e),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-17 15:03:34 +00:00
|
|
|
|
// Revoke any previously published Nostr data (runs before publish so revocation is not overwritten)
|
|
|
|
|
|
let identity_dir = config.data_dir.join("identity");
|
|
|
|
|
|
let tor_proxy_revoke = config.nostr_tor_proxy.clone();
|
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
|
|
|
|
if let Err(e) =
|
|
|
|
|
|
nostr_discovery::revoke_if_needed(&identity_dir, tor_proxy_revoke.as_deref()).await
|
|
|
|
|
|
{
|
2026-02-17 15:03:34 +00:00
|
|
|
|
tracing::debug!("Nostr revoke (non-fatal): {}", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 00:19:30 +00:00
|
|
|
|
// Publish presence-only to Nostr (DID + Nostr pubkey, NO onion address).
|
|
|
|
|
|
// Onion addresses are exchanged privately via NIP-44 encrypted DMs.
|
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
|
|
|
|
if config.nostr_discovery_enabled && !config.nostr_relays.is_empty() {
|
2026-02-17 15:03:34 +00:00
|
|
|
|
let identity_dir = config.data_dir.join("identity");
|
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 did =
|
|
|
|
|
|
identity::did_key_from_pubkey_hex(&data.server_info.pubkey).unwrap_or_default();
|
2026-02-17 15:03:34 +00:00
|
|
|
|
let version = data.server_info.version.clone();
|
|
|
|
|
|
let relays = config.nostr_relays.clone();
|
|
|
|
|
|
let tor_proxy = config.nostr_tor_proxy.clone();
|
|
|
|
|
|
tokio::spawn(async move {
|
2026-03-12 00:19:30 +00:00
|
|
|
|
if let Err(e) = nostr_handshake::publish_presence(
|
2026-02-17 15:03:34 +00:00
|
|
|
|
&identity_dir,
|
|
|
|
|
|
&did,
|
|
|
|
|
|
&version,
|
|
|
|
|
|
&relays,
|
|
|
|
|
|
tor_proxy.as_deref(),
|
|
|
|
|
|
)
|
|
|
|
|
|
.await
|
|
|
|
|
|
{
|
2026-03-12 00:19:30 +00:00
|
|
|
|
tracing::debug!("Nostr presence publish (non-fatal): {}", e);
|
2026-02-17 15:03:34 +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
|
|
|
|
info!(
|
|
|
|
|
|
"🔑 Node identity: {} (pubkey: {}...)",
|
|
|
|
|
|
identity.node_id(),
|
|
|
|
|
|
&identity.pubkey_hex()[..16.min(identity.pubkey_hex().len())]
|
|
|
|
|
|
);
|
2026-02-17 15:03:34 +00:00
|
|
|
|
|
|
|
|
|
|
let identity = Arc::new(identity);
|
2026-03-11 11:11:02 +00:00
|
|
|
|
|
|
|
|
|
|
// Create metrics store and spawn background collector
|
2026-03-22 03:30:21 +00:00
|
|
|
|
let metrics_store = Arc::new(MetricsStore::with_data_dir(config.data_dir.clone()).await);
|
feat(TASK-12): periodic telemetry reporter — 15min interval, collector POST
Background task spawned on server startup: every 15 minutes, checks opt-in
status, builds anonymous health report (node ID hash, version, uptime,
CPU/RAM/disk %, container states, recent alerts), saves to disk, and POSTs
to TELEMETRY_COLLECTOR_URL env var if configured. Non-fatal on failure.
Fixed FiredAlert field references (kind not rule_type, timestamp not
fired_at) in both monitoring and analytics modules.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 23:36:57 +00:00
|
|
|
|
let metrics_for_telemetry = metrics_store.clone();
|
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
|
|
|
|
crate::monitoring::spawn_metrics_collector(
|
|
|
|
|
|
metrics_store.clone(),
|
|
|
|
|
|
Some(state_manager.clone()),
|
|
|
|
|
|
Some(config.data_dir.clone()),
|
2026-03-11 11:11:02 +00:00
|
|
|
|
);
|
2026-01-27 23:21:26 +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 api_handler =
|
|
|
|
|
|
Arc::new(ApiHandler::new(config.clone(), state_manager.clone(), metrics_store).await?);
|
|
|
|
|
|
|
2026-03-17 00:03:08 +00:00
|
|
|
|
// Initialize mesh networking service (if config has enabled: true)
|
|
|
|
|
|
{
|
|
|
|
|
|
let data_dir = config.data_dir.clone();
|
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 did =
|
|
|
|
|
|
identity::did_key_from_pubkey_hex(&data.server_info.pubkey).unwrap_or_default();
|
2026-03-17 00:03:08 +00:00
|
|
|
|
let pubkey_hex = identity.pubkey_hex();
|
|
|
|
|
|
let signing_key = identity.signing_key();
|
|
|
|
|
|
match crate::mesh::MeshService::new(&data_dir, signing_key, &did, &pubkey_hex).await {
|
|
|
|
|
|
Ok(mut mesh_service) => {
|
2026-04-18 11:53:06 -04:00
|
|
|
|
// Pass the human-readable server name for mesh adverts
|
|
|
|
|
|
mesh_service.set_server_name(data.server_info.name.clone());
|
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 mut mesh_config = crate::mesh::load_config(&data_dir)
|
|
|
|
|
|
.await
|
|
|
|
|
|
.unwrap_or_default();
|
2026-03-31 00:50:43 +01:00
|
|
|
|
|
|
|
|
|
|
// Auto-enable mesh if a radio is detected and no config exists yet
|
|
|
|
|
|
if !mesh_config.enabled {
|
|
|
|
|
|
let devices = crate::mesh::detect_devices().await;
|
|
|
|
|
|
if !devices.is_empty() {
|
|
|
|
|
|
info!("📡 Auto-detected mesh radio: {:?} — enabling mesh", devices);
|
|
|
|
|
|
mesh_config.enabled = true;
|
|
|
|
|
|
mesh_config.device_path = Some(devices[0].clone());
|
|
|
|
|
|
let _ = crate::mesh::save_config(&data_dir, &mesh_config).await;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-17 00:03:08 +00:00
|
|
|
|
if mesh_config.enabled {
|
|
|
|
|
|
if let Err(e) = mesh_service.start() {
|
|
|
|
|
|
warn!("Mesh service start failed (non-fatal): {}", e);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
info!("📡 Mesh networking started");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
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
|
|
|
|
api_handler
|
|
|
|
|
|
.rpc_handler()
|
|
|
|
|
|
.set_mesh_service(mesh_service)
|
|
|
|
|
|
.await;
|
2026-03-17 00:03:08 +00:00
|
|
|
|
info!("📡 Mesh service initialized");
|
|
|
|
|
|
}
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
|
warn!("Mesh service init failed (non-fatal): {}", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Initialize transport router (unified routing: mesh > lan > tor)
|
|
|
|
|
|
{
|
|
|
|
|
|
let data_dir = config.data_dir.clone();
|
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 did =
|
|
|
|
|
|
identity::did_key_from_pubkey_hex(&data.server_info.pubkey).unwrap_or_default();
|
2026-03-17 00:03:08 +00:00
|
|
|
|
let pubkey_hex = identity.pubkey_hex();
|
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 mesh_config = crate::mesh::load_config(&data_dir)
|
|
|
|
|
|
.await
|
|
|
|
|
|
.unwrap_or_default();
|
2026-03-17 00:03:08 +00:00
|
|
|
|
let mesh_only = mesh_config.mesh_only_mode.unwrap_or(false);
|
|
|
|
|
|
|
|
|
|
|
|
match crate::transport::PeerRegistry::load(&data_dir).await {
|
|
|
|
|
|
Ok(registry) => {
|
|
|
|
|
|
let registry = std::sync::Arc::new(registry);
|
|
|
|
|
|
let mut transports: Vec<Box<dyn crate::transport::NodeTransport>> = Vec::new();
|
|
|
|
|
|
|
|
|
|
|
|
// Tor transport (always register — availability checked dynamically)
|
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
|
|
|
|
transports.push(Box::new(crate::transport::tor::TorTransport::new(
|
|
|
|
|
|
&pubkey_hex,
|
|
|
|
|
|
)));
|
2026-03-17 00:03:08 +00:00
|
|
|
|
|
|
|
|
|
|
// Mesh transport (wraps the mesh service)
|
|
|
|
|
|
transports.push(Box::new(
|
|
|
|
|
|
crate::transport::mesh_transport::MeshTransport::new(
|
|
|
|
|
|
api_handler.rpc_handler().mesh_service_arc(),
|
|
|
|
|
|
),
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
// LAN transport (mDNS discovery)
|
|
|
|
|
|
let mut lan = crate::transport::lan::LanTransport::new(&did, &pubkey_hex, 5678);
|
|
|
|
|
|
match lan.start(registry.clone()) {
|
|
|
|
|
|
Ok(()) => info!("📡 LAN transport (mDNS) started"),
|
|
|
|
|
|
Err(e) => debug!("LAN transport init (non-fatal): {}", e),
|
|
|
|
|
|
}
|
|
|
|
|
|
transports.push(Box::new(lan));
|
|
|
|
|
|
|
|
|
|
|
|
let router = std::sync::Arc::new(crate::transport::TransportRouter::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
|
|
|
|
transports, registry, mesh_only,
|
2026-03-17 00:03:08 +00:00
|
|
|
|
));
|
|
|
|
|
|
api_handler.rpc_handler().set_transport_router(router).await;
|
|
|
|
|
|
info!("📡 Transport router initialized (mesh_only={})", mesh_only);
|
|
|
|
|
|
}
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
|
warn!("Transport router init failed (non-fatal): {}", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-14 03:00:29 +00:00
|
|
|
|
// Register Archipelago DWN protocols (background, non-blocking)
|
|
|
|
|
|
{
|
|
|
|
|
|
let data_dir = config.data_dir.clone();
|
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
|
if let Err(e) = register_dwn_protocols(&data_dir).await {
|
|
|
|
|
|
debug!("DWN protocol registration (non-fatal): {}", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-17 15:03:34 +00:00
|
|
|
|
// Periodic Tor address refresh (runs regardless of dev_mode)
|
|
|
|
|
|
// Picks up hostname when Tor creates it after startup/rotation (30-60s delay)
|
|
|
|
|
|
{
|
|
|
|
|
|
let state = state_manager.clone();
|
|
|
|
|
|
let identity_clone = identity.clone();
|
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
|
let mut interval = tokio::time::interval(Duration::from_secs(30));
|
|
|
|
|
|
loop {
|
|
|
|
|
|
interval.tick().await;
|
|
|
|
|
|
if let Err(e) = refresh_tor_address(&state, identity_clone.as_ref()).await {
|
|
|
|
|
|
debug!("Tor address refresh (non-fatal): {}", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 00:19:30 +00:00
|
|
|
|
// Initialize container scanner — discovers installed apps from Podman/Docker
|
|
|
|
|
|
{
|
2026-01-27 23:21:26 +00:00
|
|
|
|
let scanner = create_docker_scanner(&config).await?;
|
|
|
|
|
|
let state = state_manager.clone();
|
2026-02-17 15:03:34 +00:00
|
|
|
|
let identity_clone = identity.clone();
|
2026-03-12 00:19:30 +00:00
|
|
|
|
|
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>
2026-03-17 23:56:37 +00:00
|
|
|
|
// Initial scan (delayed to let crash recovery finish first)
|
2026-01-27 23:21:26 +00:00
|
|
|
|
tokio::spawn(async move {
|
2026-03-18 14:22:00 +00:00
|
|
|
|
// Brief delay for containers to stabilize after boot
|
|
|
|
|
|
tokio::time::sleep(Duration::from_secs(3)).await;
|
2026-03-12 00:19:30 +00:00
|
|
|
|
info!("🐳 Scanning containers...");
|
2026-04-02 01:28:11 +01:00
|
|
|
|
// Tracks how many consecutive scans each container has been absent from.
|
|
|
|
|
|
// Prevents UI flapping when podman intermittently returns incomplete results.
|
|
|
|
|
|
let mut absence_tracker: HashMap<String, u32> = HashMap::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
|
|
|
|
if let Err(e) = scan_and_update_packages(
|
|
|
|
|
|
&scanner,
|
|
|
|
|
|
&state,
|
|
|
|
|
|
identity_clone.as_ref(),
|
|
|
|
|
|
&mut absence_tracker,
|
|
|
|
|
|
)
|
|
|
|
|
|
.await
|
|
|
|
|
|
{
|
2026-03-12 00:19:30 +00:00
|
|
|
|
error!("Failed to scan containers: {}", e);
|
2026-01-27 23:21:26 +00:00
|
|
|
|
}
|
2026-03-12 00:19:30 +00:00
|
|
|
|
|
2026-04-07 20:25:09 +01:00
|
|
|
|
// Periodic scan every 60 seconds (only broadcasts if state changed)
|
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>
2026-03-17 23:56:37 +00:00
|
|
|
|
// Uses an in-flight guard to skip scans when a previous one is still running
|
2026-04-07 20:25:09 +01:00
|
|
|
|
let mut interval = tokio::time::interval(Duration::from_secs(60));
|
|
|
|
|
|
// Skip missed ticks instead of catching up — prevents burst of scans
|
|
|
|
|
|
// after a slow podman response (which causes DB lock storms)
|
|
|
|
|
|
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
|
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>
2026-03-17 23:56:37 +00:00
|
|
|
|
let scanning = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
2026-01-27 23:21:26 +00:00
|
|
|
|
loop {
|
|
|
|
|
|
interval.tick().await;
|
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>
2026-03-17 23:56:37 +00:00
|
|
|
|
if scanning.load(std::sync::atomic::Ordering::Relaxed) {
|
|
|
|
|
|
debug!("Skipping container scan — previous scan still in progress");
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
scanning.store(true, std::sync::atomic::Ordering::Relaxed);
|
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
|
|
|
|
if let Err(e) = scan_and_update_packages(
|
|
|
|
|
|
&scanner,
|
|
|
|
|
|
&state,
|
|
|
|
|
|
identity_clone.as_ref(),
|
|
|
|
|
|
&mut absence_tracker,
|
|
|
|
|
|
)
|
|
|
|
|
|
.await
|
|
|
|
|
|
{
|
2026-03-12 00:19:30 +00:00
|
|
|
|
error!("Failed to update containers: {}", e);
|
2026-01-27 23:21:26 +00:00
|
|
|
|
}
|
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>
2026-03-17 23:56:37 +00:00
|
|
|
|
scanning.store(false, std::sync::atomic::Ordering::Relaxed);
|
2026-01-27 23:21:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
2026-03-09 07:43:12 +00:00
|
|
|
|
// Peer health monitoring — check every 5 minutes
|
|
|
|
|
|
{
|
|
|
|
|
|
let state = state_manager.clone();
|
|
|
|
|
|
let data_dir = config.data_dir.clone();
|
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
|
let mut interval = tokio::time::interval(Duration::from_secs(300));
|
|
|
|
|
|
loop {
|
|
|
|
|
|
interval.tick().await;
|
|
|
|
|
|
if let Err(e) = check_peer_health(&state, &data_dir).await {
|
|
|
|
|
|
debug!("Peer health check (non-fatal): {}", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-15 04:59:20 +00:00
|
|
|
|
// did:dht auto-refresh — re-publish DHT records every 2 hours
|
|
|
|
|
|
if config.nostr_discovery_enabled {
|
|
|
|
|
|
let data_dir = config.data_dir.clone();
|
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
|
let mut interval = tokio::time::interval(Duration::from_secs(7200));
|
|
|
|
|
|
loop {
|
|
|
|
|
|
interval.tick().await;
|
|
|
|
|
|
let identity_dir = data_dir.join("identity");
|
|
|
|
|
|
let node_key_path = identity_dir.join("node_key");
|
|
|
|
|
|
if !node_key_path.exists() {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
match tokio::fs::read(&node_key_path).await {
|
|
|
|
|
|
Ok(key_bytes) if key_bytes.len() == 32 => {
|
|
|
|
|
|
let mut seed = [0u8; 32];
|
|
|
|
|
|
seed.copy_from_slice(&key_bytes);
|
|
|
|
|
|
let signing_key = ed25519_dalek::SigningKey::from_bytes(&seed);
|
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
|
|
|
|
match crate::network::did_dht::create_and_publish(&signing_key, &[])
|
|
|
|
|
|
.await
|
|
|
|
|
|
{
|
2026-03-15 04:59:20 +00:00
|
|
|
|
Ok(did) => tracing::info!(did = %did, "did:dht record refreshed"),
|
|
|
|
|
|
Err(e) => tracing::debug!("did:dht refresh (non-fatal): {}", e),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
tracing::debug!("did:dht refresh skipped: no valid node key");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-19 08:32:11 -04:00
|
|
|
|
// Periodic federation state sync — every 30 min we call
|
|
|
|
|
|
// federation::sync_with_peer on each Trusted peer. Without this
|
|
|
|
|
|
// users had to manually click Sync for `fips_npub`/transport
|
|
|
|
|
|
// badge/state updates to propagate; now it happens in the
|
|
|
|
|
|
// background. Staggers peers with a 5s delay so we don't thunder
|
|
|
|
|
|
// the Tor SOCKS proxy. Sync itself already prefers FIPS.
|
|
|
|
|
|
{
|
|
|
|
|
|
let data_dir = config.data_dir.clone();
|
|
|
|
|
|
let state = state_manager.clone();
|
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
|
// First run 60s after boot to let onboarding settle.
|
|
|
|
|
|
tokio::time::sleep(Duration::from_secs(60)).await;
|
|
|
|
|
|
let mut interval = tokio::time::interval(Duration::from_secs(1800));
|
|
|
|
|
|
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
|
|
|
|
|
|
loop {
|
|
|
|
|
|
interval.tick().await;
|
|
|
|
|
|
let Ok(nodes) = crate::federation::load_nodes(&data_dir).await else {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
};
|
|
|
|
|
|
if nodes.is_empty() {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
let (data, _) = state.get_snapshot().await;
|
|
|
|
|
|
let Ok(local_did) =
|
|
|
|
|
|
crate::identity::did_key_from_pubkey_hex(&data.server_info.pubkey)
|
|
|
|
|
|
else {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
};
|
|
|
|
|
|
let identity_dir = data_dir.join("identity");
|
|
|
|
|
|
let Ok(node_identity) =
|
|
|
|
|
|
crate::identity::NodeIdentity::load_or_create(&identity_dir).await
|
|
|
|
|
|
else {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for node in &nodes {
|
|
|
|
|
|
if node.trust_level == crate::federation::TrustLevel::Untrusted {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
match crate::federation::sync_with_peer(
|
|
|
|
|
|
&data_dir,
|
|
|
|
|
|
node,
|
|
|
|
|
|
&local_did,
|
|
|
|
|
|
|bytes| node_identity.sign(bytes),
|
|
|
|
|
|
)
|
|
|
|
|
|
.await
|
|
|
|
|
|
{
|
|
|
|
|
|
Ok(_) => debug!(
|
|
|
|
|
|
"Periodic federation sync ok: {}",
|
|
|
|
|
|
node.did.chars().take(20).collect::<String>()
|
|
|
|
|
|
),
|
|
|
|
|
|
Err(e) => debug!(
|
|
|
|
|
|
"Periodic federation sync with {}: {}",
|
|
|
|
|
|
node.did.chars().take(20).collect::<String>(),
|
|
|
|
|
|
e
|
|
|
|
|
|
),
|
|
|
|
|
|
}
|
|
|
|
|
|
tokio::time::sleep(Duration::from_secs(5)).await;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-11 11:11:02 +00:00
|
|
|
|
// Container health monitoring — auto-restart unhealthy containers
|
2026-03-12 22:19:04 +00:00
|
|
|
|
// Respects webhook config: skips when disabled or ContainerCrash not subscribed
|
|
|
|
|
|
crate::health_monitor::spawn_health_monitor(state_manager.clone(), config.data_dir.clone());
|
2026-03-11 11:11:02 +00:00
|
|
|
|
|
feat(TASK-12): periodic telemetry reporter — 15min interval, collector POST
Background task spawned on server startup: every 15 minutes, checks opt-in
status, builds anonymous health report (node ID hash, version, uptime,
CPU/RAM/disk %, container states, recent alerts), saves to disk, and POSTs
to TELEMETRY_COLLECTOR_URL env var if configured. Non-fatal on failure.
Fixed FiredAlert field references (kind not rule_type, timestamp not
fired_at) in both monitoring and analytics modules.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 23:36:57 +00:00
|
|
|
|
// Periodic telemetry reporter (every 15 min when opted in)
|
|
|
|
|
|
crate::monitoring::spawn_telemetry_reporter(
|
|
|
|
|
|
metrics_for_telemetry,
|
|
|
|
|
|
Some(state_manager.clone()),
|
|
|
|
|
|
config.data_dir.clone(),
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-01-24 22:59:20 +00:00
|
|
|
|
Ok(Self {
|
Update archipelago: API, auth, container, parmanode, performance, security
- API handler, RPC, and server updates
- Auth and coding rules
- Container data manager, dev orchestrator, health monitor, podman client
- Parmanode script runner
- Performance resource manager
- Security container policies and secrets manager
- Add build scripts and documentation
2026-01-27 22:27:17 +00:00
|
|
|
|
_config: config,
|
2026-02-17 15:03:34 +00:00
|
|
|
|
_identity: identity,
|
2026-01-24 22:59:20 +00:00
|
|
|
|
api_handler,
|
2026-02-01 05:42:05 +00:00
|
|
|
|
_state_manager: state_manager,
|
2026-01-24 22:59:20 +00:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-11 11:11:02 +00:00
|
|
|
|
/// Serve with a graceful shutdown signal.
|
2026-04-19 01:12:39 -04:00
|
|
|
|
///
|
|
|
|
|
|
/// `main_addr` is the primary listener (historically `127.0.0.1:5678`).
|
2026-04-19 04:21:20 -04:00
|
|
|
|
/// The main listener always comes up on `main_addr`. The FIPS peer
|
|
|
|
|
|
/// listener (path-filtered, bound to `fips0`'s ULA) is managed by a
|
|
|
|
|
|
/// late-binding task that polls every 30s: if fips0 isn't up at
|
|
|
|
|
|
/// startup (pre-onboarding install, legacy node pre-fips.install),
|
|
|
|
|
|
/// it keeps trying until the interface appears — no archipelago
|
|
|
|
|
|
/// restart required after the user activates FIPS.
|
2026-04-19 01:12:39 -04:00
|
|
|
|
///
|
|
|
|
|
|
/// When `shutdown` completes, both listeners stop accepting and drain
|
|
|
|
|
|
/// in-flight requests (bounded by `DRAIN_TIMEOUT`).
|
2026-03-11 11:11:02 +00:00
|
|
|
|
pub async fn serve_with_shutdown(
|
|
|
|
|
|
&self,
|
2026-04-19 01:12:39 -04:00
|
|
|
|
main_addr: SocketAddr,
|
2026-03-11 11:11:02 +00:00
|
|
|
|
shutdown: impl std::future::Future<Output = ()>,
|
|
|
|
|
|
) -> Result<()> {
|
|
|
|
|
|
let active_connections = Arc::new(tokio::sync::Semaphore::new(1024));
|
2026-04-19 01:12:39 -04:00
|
|
|
|
let (tx, rx_main) = tokio::sync::watch::channel(false);
|
|
|
|
|
|
|
|
|
|
|
|
let main_task = tokio::spawn(accept_loop(
|
|
|
|
|
|
self.api_handler.clone(),
|
|
|
|
|
|
TcpListener::bind(main_addr).await?,
|
|
|
|
|
|
active_connections.clone(),
|
|
|
|
|
|
false, // main listener: no path filter
|
|
|
|
|
|
rx_main,
|
|
|
|
|
|
main_addr,
|
|
|
|
|
|
));
|
|
|
|
|
|
|
2026-04-19 04:21:20 -04:00
|
|
|
|
// Peer listener: late-binding so we don't need an archipelago
|
|
|
|
|
|
// restart when fips0 comes up after onboarding.
|
|
|
|
|
|
let peer_task = tokio::spawn(peer_late_bind_loop(
|
|
|
|
|
|
self.api_handler.clone(),
|
|
|
|
|
|
active_connections.clone(),
|
|
|
|
|
|
tx.subscribe(),
|
|
|
|
|
|
));
|
2026-03-11 11:11:02 +00:00
|
|
|
|
|
2026-04-19 01:12:39 -04:00
|
|
|
|
shutdown.await;
|
|
|
|
|
|
info!("Shutdown signal received, draining connections...");
|
|
|
|
|
|
let _ = tx.send(true);
|
|
|
|
|
|
|
|
|
|
|
|
// Wait up to 5s for in-flight requests.
|
|
|
|
|
|
let drain_start = std::time::Instant::now();
|
|
|
|
|
|
let drain_timeout = std::time::Duration::from_secs(5);
|
|
|
|
|
|
while active_connections.available_permits() < 1024 {
|
|
|
|
|
|
if drain_start.elapsed() > drain_timeout {
|
|
|
|
|
|
warn!("Drain timeout reached, forcing shutdown");
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
|
|
|
|
|
}
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
2026-04-19 01:12:39 -04:00
|
|
|
|
let _ = main_task.await;
|
2026-04-19 04:21:20 -04:00
|
|
|
|
let _ = peer_task.await;
|
2026-03-11 11:11:02 +00:00
|
|
|
|
|
2026-04-19 01:12:39 -04:00
|
|
|
|
info!("Shutdown complete");
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-19 04:21:20 -04:00
|
|
|
|
/// Poll every 30s for `fips0`'s ULA; when it appears, bind the peer
|
|
|
|
|
|
/// listener and run the normal accept loop. If the bind fails (port
|
|
|
|
|
|
/// already taken, permissions), log and keep retrying. Returns on
|
|
|
|
|
|
/// shutdown. First tick fires immediately so the hot path for
|
|
|
|
|
|
/// already-up fips0 is still zero-cost.
|
|
|
|
|
|
async fn peer_late_bind_loop(
|
|
|
|
|
|
handler: Arc<ApiHandler>,
|
|
|
|
|
|
active_connections: Arc<tokio::sync::Semaphore>,
|
|
|
|
|
|
mut shutdown_rx: tokio::sync::watch::Receiver<bool>,
|
|
|
|
|
|
) {
|
|
|
|
|
|
let mut interval = tokio::time::interval(std::time::Duration::from_secs(30));
|
|
|
|
|
|
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
|
|
|
|
|
|
loop {
|
|
|
|
|
|
tokio::select! {
|
|
|
|
|
|
_ = interval.tick() => {
|
|
|
|
|
|
let Some(ip) = crate::fips::iface::fips0_ula() else { continue };
|
|
|
|
|
|
let addr = SocketAddr::new(
|
|
|
|
|
|
std::net::IpAddr::V6(ip),
|
|
|
|
|
|
crate::fips::dial::PEER_PORT,
|
|
|
|
|
|
);
|
|
|
|
|
|
let listener = match TcpListener::bind(addr).await {
|
|
|
|
|
|
Ok(l) => l,
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
|
warn!("FIPS peer listener bind {} failed: {} — retrying in 30s", addr, e);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
info!("FIPS peer listener bound {}", addr);
|
|
|
|
|
|
// Once bound, serve until shutdown fires. accept_loop
|
|
|
|
|
|
// returns on shutdown, which also ends this outer loop.
|
|
|
|
|
|
accept_loop(
|
|
|
|
|
|
handler,
|
|
|
|
|
|
listener,
|
|
|
|
|
|
active_connections,
|
|
|
|
|
|
true, // peer listener: apply path filter
|
|
|
|
|
|
shutdown_rx,
|
|
|
|
|
|
addr,
|
|
|
|
|
|
)
|
|
|
|
|
|
.await;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
_ = shutdown_rx.changed() => {
|
|
|
|
|
|
if *shutdown_rx.borrow() { return; }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-19 01:12:39 -04:00
|
|
|
|
/// Whitelist of HTTP paths reachable via the peer-facing (FIPS) listener.
|
|
|
|
|
|
/// Every entry is an endpoint already protected by cryptographic auth
|
|
|
|
|
|
/// (ed25519 signature verification inside the handler, federation DID
|
|
|
|
|
|
/// headers checked by the content server, or JSON-RPC methods whose
|
|
|
|
|
|
/// handlers verify per-message signatures).
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Anything not on this list returns 404 on the peer listener.
|
|
|
|
|
|
pub fn is_peer_allowed_path(path: &str) -> bool {
|
|
|
|
|
|
// Exact matches
|
|
|
|
|
|
matches!(
|
|
|
|
|
|
path,
|
|
|
|
|
|
"/health"
|
|
|
|
|
|
| "/rpc/v1"
|
|
|
|
|
|
| "/archipelago/node-message"
|
|
|
|
|
|
| "/archipelago/mesh-typed"
|
|
|
|
|
|
| "/dwn"
|
|
|
|
|
|
| "/transport/inbox"
|
|
|
|
|
|
)
|
|
|
|
|
|
// Prefix-matched content endpoints (peer file browse + fetch)
|
|
|
|
|
|
|| path.starts_with("/content/")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async fn accept_loop(
|
|
|
|
|
|
handler: Arc<ApiHandler>,
|
|
|
|
|
|
listener: TcpListener,
|
|
|
|
|
|
active_connections: Arc<tokio::sync::Semaphore>,
|
|
|
|
|
|
peer_only: bool,
|
|
|
|
|
|
mut shutdown_rx: tokio::sync::watch::Receiver<bool>,
|
|
|
|
|
|
local_addr: SocketAddr,
|
|
|
|
|
|
) {
|
|
|
|
|
|
loop {
|
|
|
|
|
|
tokio::select! {
|
|
|
|
|
|
result = listener.accept() => {
|
|
|
|
|
|
let (stream, peer_addr) = match result {
|
|
|
|
|
|
Ok(c) => c,
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
|
error!("{} accept error: {}", local_addr, e);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
let handler = handler.clone();
|
|
|
|
|
|
let permit = active_connections.clone().acquire_owned().await;
|
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
|
let _permit = permit;
|
|
|
|
|
|
let service = service_fn(move |req: hyper::Request<hyper::Body>| {
|
|
|
|
|
|
let handler = handler.clone();
|
|
|
|
|
|
async move {
|
|
|
|
|
|
if peer_only && !is_peer_allowed_path(req.uri().path()) {
|
|
|
|
|
|
let resp = hyper::Response::builder()
|
|
|
|
|
|
.status(hyper::StatusCode::NOT_FOUND)
|
|
|
|
|
|
.body(hyper::Body::empty())
|
|
|
|
|
|
.expect("static response builds");
|
|
|
|
|
|
return Ok::<_, std::io::Error>(resp);
|
|
|
|
|
|
}
|
|
|
|
|
|
handler
|
|
|
|
|
|
.handle_request(req)
|
|
|
|
|
|
.await
|
|
|
|
|
|
.map_err(|e| std::io::Error::other(format!("{}", e)))
|
2026-03-11 11:11:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-04-19 01:12:39 -04:00
|
|
|
|
if let Err(e) = Http::new()
|
|
|
|
|
|
.http1_keep_alive(false)
|
|
|
|
|
|
.serve_connection(stream, service)
|
|
|
|
|
|
.with_upgrades()
|
|
|
|
|
|
.await
|
|
|
|
|
|
{
|
|
|
|
|
|
error!("Error serving connection from {}: {}", peer_addr, e);
|
2026-01-24 22:59:20 +00:00
|
|
|
|
}
|
2026-04-19 01:12:39 -04:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
_ = shutdown_rx.changed() => {
|
|
|
|
|
|
if *shutdown_rx.borrow() {
|
|
|
|
|
|
return;
|
2026-01-24 22:59:20 +00:00
|
|
|
|
}
|
2026-03-11 11:11:02 +00:00
|
|
|
|
}
|
2026-01-24 22:59:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-27 23:21:26 +00:00
|
|
|
|
|
|
|
|
|
|
async fn create_docker_scanner(config: &Config) -> Result<DockerPackageScanner> {
|
|
|
|
|
|
let user = std::env::var("USER").unwrap_or_else(|_| "archipelago".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
|
|
|
|
|
|
|
|
|
|
let runtime: Arc<dyn archipelago_container::ContainerRuntime> = match &config.container_runtime
|
|
|
|
|
|
{
|
2026-01-27 23:21:26 +00:00
|
|
|
|
ContainerRuntime::Podman => {
|
|
|
|
|
|
Arc::new(archipelago_container::PodmanRuntime::new(user.clone()))
|
|
|
|
|
|
}
|
|
|
|
|
|
ContainerRuntime::Docker => {
|
|
|
|
|
|
Arc::new(archipelago_container::DockerRuntime::new(user.clone()))
|
|
|
|
|
|
}
|
|
|
|
|
|
ContainerRuntime::Auto => {
|
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
|
|
|
|
Arc::new(archipelago_container::AutoRuntime::new(user.clone()).await?)
|
2026-01-27 23:21:26 +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
|
|
|
|
|
2026-01-27 23:21:26 +00:00
|
|
|
|
Ok(DockerPackageScanner::new(runtime))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-17 15:03:34 +00:00
|
|
|
|
async fn refresh_tor_address(state: &StateManager, identity: &NodeIdentity) -> Result<()> {
|
2026-03-21 01:21:08 +00:00
|
|
|
|
let tor_addr = docker_packages::read_tor_address("archipelago").await;
|
2026-02-17 15:03:34 +00:00
|
|
|
|
let (current_data, _) = state.get_snapshot().await;
|
|
|
|
|
|
if tor_addr != current_data.server_info.tor_address {
|
|
|
|
|
|
let mut data = current_data;
|
|
|
|
|
|
data.server_info.tor_address = tor_addr.clone();
|
|
|
|
|
|
data.server_info.node_address = tor_addr.as_ref().map(|t| identity.node_address(t));
|
|
|
|
|
|
state.update_data(data).await;
|
|
|
|
|
|
if let Some(ref addr) = tor_addr {
|
|
|
|
|
|
info!("🔒 Tor address updated: {}", addr);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-02 01:28:11 +01:00
|
|
|
|
/// Number of consecutive absent scans before removing a container from state.
|
|
|
|
|
|
/// 3 scans × 30s = 90 seconds of absence before removal.
|
|
|
|
|
|
const CONTAINER_ABSENCE_THRESHOLD: u32 = 3;
|
|
|
|
|
|
|
2026-01-27 23:21:26 +00:00
|
|
|
|
async fn scan_and_update_packages(
|
|
|
|
|
|
scanner: &DockerPackageScanner,
|
|
|
|
|
|
state: &StateManager,
|
2026-02-17 15:03:34 +00:00
|
|
|
|
identity: &NodeIdentity,
|
2026-04-02 01:28:11 +01:00
|
|
|
|
absence_tracker: &mut HashMap<String, u32>,
|
2026-01-27 23:21:26 +00:00
|
|
|
|
) -> Result<()> {
|
|
|
|
|
|
let packages = scanner.scan_containers().await?;
|
2026-04-02 01:28:11 +01:00
|
|
|
|
|
2026-02-17 15:03:34 +00:00
|
|
|
|
let (current_data, _) = state.get_snapshot().await;
|
2026-03-21 01:21:08 +00:00
|
|
|
|
let tor_addr = docker_packages::read_tor_address("archipelago").await;
|
2026-02-17 15:03:34 +00:00
|
|
|
|
let tor_changed = tor_addr != current_data.server_info.tor_address;
|
2026-03-18 11:46:38 +00:00
|
|
|
|
let first_scan = !current_data.server_info.status_info.containers_scanned;
|
2026-02-17 15:03:34 +00:00
|
|
|
|
|
2026-04-01 16:18:58 +01:00
|
|
|
|
// Check if update scheduler has found an available update
|
|
|
|
|
|
let update_available = crate::update::load_state(std::path::Path::new("/var/lib/archipelago"))
|
|
|
|
|
|
.await
|
|
|
|
|
|
.map(|s| s.available_update.is_some())
|
|
|
|
|
|
.unwrap_or(false);
|
|
|
|
|
|
let update_changed = update_available != current_data.server_info.status_info.updated;
|
|
|
|
|
|
|
2026-04-02 01:28:11 +01:00
|
|
|
|
// Empty scan result = podman failure or timeout, preserve existing state
|
|
|
|
|
|
if packages.is_empty() && !first_scan {
|
|
|
|
|
|
if tor_changed || update_changed {
|
|
|
|
|
|
let mut data = current_data;
|
|
|
|
|
|
data.server_info.tor_address = tor_addr.clone();
|
|
|
|
|
|
data.server_info.node_address = tor_addr.as_ref().map(|t| identity.node_address(t));
|
|
|
|
|
|
data.server_info.status_info.updated = update_available;
|
|
|
|
|
|
state.update_data(data).await;
|
|
|
|
|
|
}
|
|
|
|
|
|
return Ok(());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Merge scan results with current state instead of full replacement.
|
|
|
|
|
|
// This prevents containers from vanishing when podman intermittently
|
|
|
|
|
|
// returns incomplete results under heavy load.
|
|
|
|
|
|
let mut merged = current_data.package_data.clone();
|
|
|
|
|
|
let mut changed = false;
|
|
|
|
|
|
|
|
|
|
|
|
// Update/add containers found in this scan
|
|
|
|
|
|
for (id, pkg) in &packages {
|
|
|
|
|
|
absence_tracker.remove(id);
|
|
|
|
|
|
if merged.get(id) != Some(pkg) {
|
|
|
|
|
|
merged.insert(id.clone(), pkg.clone());
|
|
|
|
|
|
changed = true;
|
2026-02-01 13:24:03 +00:00
|
|
|
|
}
|
2026-04-02 01:28:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Track containers in state but missing from this scan.
|
|
|
|
|
|
// Only remove after CONTAINER_ABSENCE_THRESHOLD consecutive absent scans.
|
|
|
|
|
|
let current_ids: Vec<String> = merged.keys().cloned().collect();
|
|
|
|
|
|
for id in current_ids {
|
|
|
|
|
|
if !packages.contains_key(&id) {
|
|
|
|
|
|
let count = absence_tracker.entry(id.clone()).or_insert(0);
|
|
|
|
|
|
*count += 1;
|
|
|
|
|
|
if *count >= CONTAINER_ABSENCE_THRESHOLD {
|
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
|
|
|
|
debug!(
|
|
|
|
|
|
"Removing {} from state after {} consecutive absent scans",
|
|
|
|
|
|
id, count
|
|
|
|
|
|
);
|
2026-04-02 01:28:11 +01:00
|
|
|
|
merged.remove(&id);
|
|
|
|
|
|
absence_tracker.remove(&id);
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if changed || tor_changed || first_scan || update_changed {
|
|
|
|
|
|
let mut data = current_data;
|
|
|
|
|
|
data.package_data = merged;
|
2026-02-17 15:03:34 +00:00
|
|
|
|
data.server_info.tor_address = tor_addr.clone();
|
|
|
|
|
|
data.server_info.node_address = tor_addr.as_ref().map(|t| identity.node_address(t));
|
2026-03-18 11:46:38 +00:00
|
|
|
|
data.server_info.status_info.containers_scanned = true;
|
2026-04-01 16:18:58 +01:00
|
|
|
|
data.server_info.status_info.updated = update_available;
|
2026-02-17 15:03:34 +00:00
|
|
|
|
state.update_data(data).await;
|
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
|
|
|
|
debug!(
|
|
|
|
|
|
"📦 State changed (packages={}, tor={}, first_scan={}, update={}), broadcasting update",
|
|
|
|
|
|
changed, tor_changed, first_scan, update_changed
|
|
|
|
|
|
);
|
2026-01-27 23:21:26 +00:00
|
|
|
|
}
|
2026-03-09 07:43:12 +00:00
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-14 03:00:29 +00:00
|
|
|
|
/// Register Archipelago DWN protocols on startup.
|
|
|
|
|
|
async fn register_dwn_protocols(data_dir: &std::path::Path) -> Result<()> {
|
|
|
|
|
|
use crate::network::dwn_store::{DwnStore, ProtocolDefinition};
|
|
|
|
|
|
|
|
|
|
|
|
let protocols = [
|
|
|
|
|
|
("https://archipelago.dev/protocols/node-identity/v1", true),
|
|
|
|
|
|
("https://archipelago.dev/protocols/file-catalog/v1", true),
|
|
|
|
|
|
("https://archipelago.dev/protocols/federation/v1", false),
|
|
|
|
|
|
("https://archipelago.dev/protocols/app-deploy/v1", false),
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
let store = DwnStore::new(data_dir).await?;
|
|
|
|
|
|
let existing = store.list_protocols().await?;
|
|
|
|
|
|
let existing_uris: std::collections::HashSet<String> =
|
|
|
|
|
|
existing.iter().map(|p| p.protocol.clone()).collect();
|
|
|
|
|
|
|
|
|
|
|
|
let mut registered = 0;
|
|
|
|
|
|
for (uri, published) in &protocols {
|
|
|
|
|
|
if existing_uris.contains(*uri) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
let def = ProtocolDefinition {
|
|
|
|
|
|
protocol: uri.to_string(),
|
|
|
|
|
|
published: *published,
|
|
|
|
|
|
types: std::collections::HashMap::new(),
|
|
|
|
|
|
structure: std::collections::HashMap::new(),
|
|
|
|
|
|
date_registered: chrono::Utc::now().to_rfc3339(),
|
|
|
|
|
|
};
|
|
|
|
|
|
store.register_protocol(&def).await?;
|
|
|
|
|
|
registered += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if registered > 0 {
|
|
|
|
|
|
info!("📋 Registered {registered} DWN protocols");
|
|
|
|
|
|
}
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 07:43:12 +00:00
|
|
|
|
/// Periodically check peer reachability and broadcast status changes.
|
|
|
|
|
|
async fn check_peer_health(state: &StateManager, data_dir: &std::path::Path) -> Result<()> {
|
|
|
|
|
|
let known_peers = peers::load_peers(data_dir).await.unwrap_or_default();
|
|
|
|
|
|
if known_peers.is_empty() {
|
|
|
|
|
|
return Ok(());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let mut new_health = std::collections::HashMap::new();
|
|
|
|
|
|
for peer in &known_peers {
|
2026-04-19 01:36:04 -04:00
|
|
|
|
let fips_npub = crate::federation::fips_npub_for_onion(data_dir, &peer.onion).await;
|
|
|
|
|
|
let reachable =
|
|
|
|
|
|
node_message::check_peer_reachable(&peer.onion, fips_npub.as_deref())
|
|
|
|
|
|
.await
|
|
|
|
|
|
.unwrap_or(false);
|
2026-03-09 07:43:12 +00:00
|
|
|
|
new_health.insert(peer.onion.clone(), reachable);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let (current_data, _) = state.get_snapshot().await;
|
|
|
|
|
|
if current_data.peer_health != new_health {
|
|
|
|
|
|
let mut data = current_data;
|
|
|
|
|
|
data.peer_health = new_health;
|
|
|
|
|
|
state.update_data(data).await;
|
|
|
|
|
|
debug!("🔗 Peer health updated, broadcasting changes");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-27 23:21:26 +00:00
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|