2026-03-04 05:23:42 +00:00
|
|
|
mod auth;
|
2026-03-05 13:56:29 +00:00
|
|
|
mod bitcoin;
|
2026-03-04 05:23:42 +00:00
|
|
|
mod container;
|
2026-03-09 07:43:12 +00:00
|
|
|
mod content;
|
|
|
|
|
mod credentials;
|
|
|
|
|
mod dwn;
|
|
|
|
|
mod identity;
|
2026-03-11 00:34:17 +00:00
|
|
|
mod interfaces;
|
2026-03-09 07:43:12 +00:00
|
|
|
mod names;
|
2026-03-05 13:56:29 +00:00
|
|
|
mod lnd;
|
2026-03-09 07:43:12 +00:00
|
|
|
mod network;
|
2026-03-04 05:23:42 +00:00
|
|
|
mod node;
|
2026-03-09 07:43:12 +00:00
|
|
|
mod nostr;
|
2026-03-04 05:23:42 +00:00
|
|
|
mod package;
|
|
|
|
|
mod peers;
|
2026-03-09 07:43:12 +00:00
|
|
|
mod router;
|
|
|
|
|
mod tor;
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
mod totp;
|
2026-03-11 00:22:57 +00:00
|
|
|
mod system;
|
2026-03-09 07:43:12 +00:00
|
|
|
mod update;
|
|
|
|
|
mod wallet;
|
2026-03-04 05:23:42 +00:00
|
|
|
|
|
|
|
|
use crate::auth::AuthManager;
|
|
|
|
|
use crate::config::Config;
|
|
|
|
|
use crate::container::DevContainerOrchestrator;
|
|
|
|
|
use crate::port_allocator::PortAllocator;
|
2026-03-06 03:26:56 +00:00
|
|
|
use crate::session::{self, LoginRateLimiter, SessionStore};
|
2026-03-04 05:23:42 +00:00
|
|
|
use crate::state::StateManager;
|
|
|
|
|
use anyhow::{Context, Result};
|
|
|
|
|
use hyper::{Request, Response, StatusCode};
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
2026-03-06 03:26:56 +00:00
|
|
|
use std::net::IpAddr;
|
2026-03-04 05:23:42 +00:00
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
|
use tracing::{debug, error};
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
|
struct RpcRequest {
|
|
|
|
|
method: String,
|
|
|
|
|
params: Option<serde_json::Value>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
struct RpcResponse {
|
|
|
|
|
result: Option<serde_json::Value>,
|
|
|
|
|
error: Option<RpcError>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
struct RpcError {
|
|
|
|
|
code: i32,
|
|
|
|
|
message: String,
|
|
|
|
|
data: Option<serde_json::Value>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Default dev password when no user is set up (matches mock-backend).
|
|
|
|
|
pub(crate) const DEV_DEFAULT_PASSWORD: &str = "password123";
|
|
|
|
|
|
2026-03-06 03:26:56 +00:00
|
|
|
/// Methods that do not require a valid session cookie.
|
|
|
|
|
const UNAUTHENTICATED_METHODS: &[&str] = &[
|
|
|
|
|
"auth.login",
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
"auth.login.totp",
|
|
|
|
|
"auth.login.backup",
|
2026-03-06 03:26:56 +00:00
|
|
|
"auth.isOnboardingComplete",
|
|
|
|
|
"health",
|
|
|
|
|
];
|
|
|
|
|
|
2026-03-04 05:23:42 +00:00
|
|
|
pub struct RpcHandler {
|
|
|
|
|
config: Config,
|
|
|
|
|
auth_manager: AuthManager,
|
|
|
|
|
orchestrator: Option<Arc<DevContainerOrchestrator>>,
|
|
|
|
|
state_manager: Arc<StateManager>,
|
|
|
|
|
port_allocator: Arc<Mutex<PortAllocator>>,
|
2026-03-06 03:26:56 +00:00
|
|
|
pub session_store: SessionStore,
|
|
|
|
|
login_rate_limiter: LoginRateLimiter,
|
2026-03-04 05:23:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RpcHandler {
|
2026-03-06 03:26:56 +00:00
|
|
|
pub async fn new(
|
|
|
|
|
config: Config,
|
|
|
|
|
state_manager: Arc<StateManager>,
|
|
|
|
|
session_store: SessionStore,
|
|
|
|
|
) -> Result<Self> {
|
2026-03-04 05:23:42 +00:00
|
|
|
let auth_manager = AuthManager::new(config.data_dir.clone());
|
|
|
|
|
let orchestrator = if config.dev_mode {
|
|
|
|
|
Some(Arc::new(
|
|
|
|
|
DevContainerOrchestrator::new(config.clone()).await?,
|
|
|
|
|
))
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
let port_allocator = Arc::new(Mutex::new(PortAllocator::new(&config.data_dir)?));
|
|
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
|
config,
|
|
|
|
|
auth_manager,
|
|
|
|
|
orchestrator,
|
|
|
|
|
state_manager,
|
|
|
|
|
port_allocator,
|
2026-03-06 03:26:56 +00:00
|
|
|
session_store,
|
|
|
|
|
login_rate_limiter: LoginRateLimiter::new(),
|
2026-03-04 05:23:42 +00:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-06 13:00:28 +00:00
|
|
|
fn cookie_suffix(&self) -> &'static str {
|
|
|
|
|
if self.config.dev_mode { "" } else { "; Secure" }
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-04 05:23:42 +00:00
|
|
|
pub async fn handle(
|
|
|
|
|
&self,
|
|
|
|
|
req: Request<hyper::Body>,
|
|
|
|
|
) -> Result<Response<hyper::Body>> {
|
2026-03-06 03:26:56 +00:00
|
|
|
// Extract session cookie before consuming the request
|
|
|
|
|
let (parts, body) = req.into_parts();
|
|
|
|
|
let session_token = session::extract_session_cookie(&parts.headers);
|
|
|
|
|
|
2026-03-04 05:23:42 +00:00
|
|
|
let body_bytes = hyper::body::to_bytes(body).await
|
|
|
|
|
.context("Failed to read body")?;
|
|
|
|
|
|
|
|
|
|
let rpc_req: RpcRequest = serde_json::from_slice(&body_bytes)
|
|
|
|
|
.context("Invalid RPC request")?;
|
|
|
|
|
|
|
|
|
|
debug!("RPC method: {}", rpc_req.method);
|
|
|
|
|
|
2026-03-06 03:26:56 +00:00
|
|
|
// Enforce authentication for non-allowlisted methods
|
|
|
|
|
let is_unauthenticated = UNAUTHENTICATED_METHODS.contains(&rpc_req.method.as_str());
|
|
|
|
|
if !is_unauthenticated {
|
|
|
|
|
let authenticated = match &session_token {
|
|
|
|
|
Some(token) => self.session_store.validate(token).await,
|
|
|
|
|
None => false,
|
|
|
|
|
};
|
|
|
|
|
if !authenticated {
|
|
|
|
|
let rpc_resp = RpcResponse {
|
|
|
|
|
result: None,
|
|
|
|
|
error: Some(RpcError {
|
|
|
|
|
code: 401,
|
|
|
|
|
message: "Unauthorized".to_string(),
|
|
|
|
|
data: None,
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
|
let resp_body = serde_json::to_vec(&rpc_resp)
|
|
|
|
|
.context("Failed to serialize response")?;
|
|
|
|
|
return Ok(Response::builder()
|
|
|
|
|
.status(StatusCode::UNAUTHORIZED)
|
|
|
|
|
.header("Content-Type", "application/json")
|
|
|
|
|
.body(hyper::Body::from(resp_body))
|
|
|
|
|
.unwrap());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rate limit login attempts
|
|
|
|
|
if rpc_req.method == "auth.login" {
|
|
|
|
|
let client_ip = extract_client_ip(&parts.headers);
|
|
|
|
|
if !self.login_rate_limiter.check(client_ip).await {
|
|
|
|
|
let rpc_resp = RpcResponse {
|
|
|
|
|
result: None,
|
|
|
|
|
error: Some(RpcError {
|
|
|
|
|
code: 429,
|
|
|
|
|
message: "Too many login attempts. Try again later.".to_string(),
|
|
|
|
|
data: None,
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
|
let resp_body = serde_json::to_vec(&rpc_resp)
|
|
|
|
|
.context("Failed to serialize response")?;
|
|
|
|
|
return Ok(Response::builder()
|
|
|
|
|
.status(StatusCode::TOO_MANY_REQUESTS)
|
|
|
|
|
.header("Content-Type", "application/json")
|
|
|
|
|
.header("Retry-After", "60")
|
|
|
|
|
.body(hyper::Body::from(resp_body))
|
|
|
|
|
.unwrap());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
// Extract params; clone for post-routing use (login 2FA check needs password)
|
|
|
|
|
let params = rpc_req.params;
|
|
|
|
|
let login_params: Option<serde_json::Value> = if rpc_req.method == "auth.login" {
|
|
|
|
|
params.clone()
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-04 05:23:42 +00:00
|
|
|
// Route to handler
|
|
|
|
|
let result = match rpc_req.method.as_str() {
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
"echo" => self.handle_echo(params).await,
|
|
|
|
|
"server.echo" => self.handle_echo(params).await,
|
|
|
|
|
"auth.login" => self.handle_auth_login(params).await,
|
2026-03-04 05:23:42 +00:00
|
|
|
"auth.logout" => self.handle_auth_logout().await,
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
"auth.changePassword" => self.handle_auth_change_password(params).await,
|
2026-03-04 05:23:42 +00:00
|
|
|
"auth.onboardingComplete" => self.handle_auth_onboarding_complete().await,
|
|
|
|
|
"auth.isOnboardingComplete" => self.handle_auth_is_onboarding_complete().await,
|
|
|
|
|
"auth.resetOnboarding" => self.handle_auth_reset_onboarding().await,
|
|
|
|
|
|
|
|
|
|
// Container orchestration (for Archipelago-managed containers)
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
"container-install" => self.handle_container_install(params).await,
|
|
|
|
|
"container-start" => self.handle_container_start(params).await,
|
|
|
|
|
"container-stop" => self.handle_container_stop(params).await,
|
|
|
|
|
"container-remove" => self.handle_container_remove(params).await,
|
2026-03-04 05:23:42 +00:00
|
|
|
"container-list" => self.handle_container_list().await,
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
"container-status" => self.handle_container_status(params).await,
|
|
|
|
|
"container-logs" => self.handle_container_logs(params).await,
|
|
|
|
|
"container-health" => self.handle_container_health(params).await,
|
2026-03-04 05:23:42 +00:00
|
|
|
|
|
|
|
|
// Package management (for docker-compose apps)
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
"package.install" => self.handle_package_install(params).await,
|
|
|
|
|
"package.start" => self.handle_package_start(params).await,
|
|
|
|
|
"package.stop" => self.handle_package_stop(params).await,
|
|
|
|
|
"package.restart" => self.handle_package_restart(params).await,
|
|
|
|
|
"package.uninstall" => self.handle_package_uninstall(params).await,
|
2026-03-04 05:23:42 +00:00
|
|
|
|
|
|
|
|
// Bundled app management (for pre-loaded container images)
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
"bundled-app-start" => self.handle_bundled_app_start(params).await,
|
|
|
|
|
"bundled-app-stop" => self.handle_bundled_app_stop(params).await,
|
2026-03-04 05:23:42 +00:00
|
|
|
|
|
|
|
|
// Node identity and P2P peers
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
"node-add-peer" => self.handle_node_add_peer(params).await,
|
2026-03-04 05:23:42 +00:00
|
|
|
"node-list-peers" => self.handle_node_list_peers().await,
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
"node-remove-peer" => self.handle_node_remove_peer(params).await,
|
|
|
|
|
"node-send-message" => self.handle_node_send_message(params).await,
|
|
|
|
|
"node-check-peer" => self.handle_node_check_peer(params).await,
|
2026-03-04 05:23:42 +00:00
|
|
|
"node-messages-received" => self.handle_node_messages_received().await,
|
|
|
|
|
"node-nostr-discover" => self.handle_node_nostr_discover().await,
|
|
|
|
|
"node.did" => self.handle_node_did().await,
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
"node.signChallenge" => self.handle_node_sign_challenge(params).await,
|
|
|
|
|
"node.createBackup" => self.handle_node_create_backup(params).await,
|
2026-03-04 05:23:42 +00:00
|
|
|
"node.tor-address" => self.handle_node_tor_address().await,
|
|
|
|
|
"node.nostr-publish" => self.handle_node_nostr_publish().await,
|
|
|
|
|
"node.nostr-pubkey" => self.handle_node_nostr_pubkey().await,
|
|
|
|
|
"node-nostr-verify-revoked" => self.handle_node_nostr_verify_revoked().await,
|
|
|
|
|
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
// TOTP 2FA
|
|
|
|
|
"auth.totp.setup.begin" => self.handle_totp_setup_begin(params).await,
|
|
|
|
|
"auth.totp.setup.confirm" => self.handle_totp_setup_confirm(params).await,
|
|
|
|
|
"auth.totp.disable" => self.handle_totp_disable(params).await,
|
|
|
|
|
"auth.totp.status" => self.handle_totp_status().await,
|
|
|
|
|
"auth.login.totp" => self.handle_login_totp(params, &session_token).await,
|
|
|
|
|
"auth.login.backup" => self.handle_login_backup(params, &session_token).await,
|
|
|
|
|
|
2026-03-05 13:56:29 +00:00
|
|
|
// Bitcoin & Lightning deep data
|
|
|
|
|
"bitcoin.getinfo" => self.handle_bitcoin_getinfo().await,
|
|
|
|
|
"lnd.getinfo" => self.handle_lnd_getinfo().await,
|
2026-03-09 07:43:12 +00:00
|
|
|
"lnd.listchannels" => self.handle_lnd_listchannels().await,
|
|
|
|
|
"lnd.openchannel" => self.handle_lnd_openchannel(params).await,
|
|
|
|
|
"lnd.closechannel" => self.handle_lnd_closechannel(params).await,
|
|
|
|
|
"lnd.newaddress" => self.handle_lnd_newaddress().await,
|
|
|
|
|
"lnd.sendcoins" => self.handle_lnd_sendcoins(params).await,
|
|
|
|
|
"lnd.createinvoice" => self.handle_lnd_createinvoice(params).await,
|
|
|
|
|
"lnd.payinvoice" => self.handle_lnd_payinvoice(params).await,
|
|
|
|
|
|
|
|
|
|
// Multi-identity management
|
|
|
|
|
"identity.list" => self.handle_identity_list(params).await,
|
|
|
|
|
"identity.create" => self.handle_identity_create(params).await,
|
|
|
|
|
"identity.get" => self.handle_identity_get(params).await,
|
|
|
|
|
"identity.delete" => self.handle_identity_delete(params).await,
|
|
|
|
|
"identity.set-default" => self.handle_identity_set_default(params).await,
|
|
|
|
|
"identity.sign" => self.handle_identity_sign(params).await,
|
|
|
|
|
"identity.verify" => self.handle_identity_verify(params).await,
|
|
|
|
|
"identity.create-nostr-key" => self.handle_identity_create_nostr_key(params).await,
|
|
|
|
|
"identity.nostr-sign" => self.handle_identity_nostr_sign(params).await,
|
|
|
|
|
|
|
|
|
|
// Bitcoin domain names (NIP-05)
|
|
|
|
|
"identity.register-name" => self.handle_identity_register_name(params).await,
|
|
|
|
|
"identity.remove-name" => self.handle_identity_remove_name(params).await,
|
|
|
|
|
"identity.resolve-name" => self.handle_identity_resolve_name(params).await,
|
|
|
|
|
"identity.list-names" => self.handle_identity_list_names(params).await,
|
|
|
|
|
"identity.link-name" => self.handle_identity_link_name(params).await,
|
|
|
|
|
|
|
|
|
|
// Verifiable Credentials
|
|
|
|
|
"identity.issue-credential" => self.handle_identity_issue_credential(params).await,
|
|
|
|
|
"identity.verify-credential" => self.handle_identity_verify_credential(params).await,
|
|
|
|
|
"identity.list-credentials" => self.handle_identity_list_credentials(params).await,
|
|
|
|
|
"identity.revoke-credential" => self.handle_identity_revoke_credential(params).await,
|
|
|
|
|
|
|
|
|
|
// Network overlay
|
|
|
|
|
"network.get-visibility" => self.handle_network_get_visibility().await,
|
|
|
|
|
"network.set-visibility" => self.handle_network_set_visibility(params).await,
|
|
|
|
|
"network.request-connection" => self.handle_network_request_connection(params).await,
|
|
|
|
|
"network.list-requests" => self.handle_network_list_requests().await,
|
|
|
|
|
"network.accept-request" => self.handle_network_accept_request(params).await,
|
|
|
|
|
"network.reject-request" => self.handle_network_reject_request(params).await,
|
|
|
|
|
|
|
|
|
|
// Tor hidden services
|
|
|
|
|
"tor.list-services" => self.handle_tor_list_services().await,
|
|
|
|
|
"tor.create-service" => self.handle_tor_create_service(params).await,
|
|
|
|
|
"tor.delete-service" => self.handle_tor_delete_service(params).await,
|
|
|
|
|
"tor.get-onion-address" => self.handle_tor_get_onion_address(params).await,
|
|
|
|
|
|
|
|
|
|
// Nostr relay management
|
|
|
|
|
"nostr.list-relays" => self.handle_nostr_list_relays().await,
|
|
|
|
|
"nostr.add-relay" => self.handle_nostr_add_relay(params).await,
|
|
|
|
|
"nostr.remove-relay" => self.handle_nostr_remove_relay(params).await,
|
|
|
|
|
"nostr.toggle-relay" => self.handle_nostr_toggle_relay(params).await,
|
|
|
|
|
"nostr.get-stats" => self.handle_nostr_get_stats().await,
|
|
|
|
|
|
|
|
|
|
// Router / UPnP
|
|
|
|
|
"router.discover" => self.handle_router_discover().await,
|
|
|
|
|
"router.list-forwards" => self.handle_router_list_forwards().await,
|
|
|
|
|
"router.add-forward" => self.handle_router_add_forward(params).await,
|
|
|
|
|
"router.remove-forward" => self.handle_router_remove_forward(params).await,
|
|
|
|
|
"network.diagnostics" => self.handle_network_diagnostics().await,
|
2026-03-11 00:34:17 +00:00
|
|
|
"network.list-interfaces" => self.handle_network_list_interfaces().await,
|
|
|
|
|
"network.scan-wifi" => self.handle_network_scan_wifi().await,
|
|
|
|
|
"network.configure-wifi" => self.handle_network_configure_wifi(params).await,
|
|
|
|
|
"network.configure-ethernet" => self.handle_network_configure_ethernet(params).await,
|
2026-03-09 07:43:12 +00:00
|
|
|
"router.detect" => self.handle_router_detect(params).await,
|
|
|
|
|
"router.info" => self.handle_router_info().await,
|
|
|
|
|
"router.configure" => self.handle_router_configure(params).await,
|
|
|
|
|
|
|
|
|
|
// Ecash wallet
|
|
|
|
|
"wallet.ecash-balance" => self.handle_wallet_ecash_balance().await,
|
|
|
|
|
"wallet.ecash-mint" => self.handle_wallet_ecash_mint(params).await,
|
|
|
|
|
"wallet.ecash-melt" => self.handle_wallet_ecash_melt(params).await,
|
|
|
|
|
"wallet.ecash-send" => self.handle_wallet_ecash_send(params).await,
|
|
|
|
|
"wallet.ecash-receive" => self.handle_wallet_ecash_receive(params).await,
|
|
|
|
|
"wallet.ecash-history" => self.handle_wallet_ecash_history().await,
|
|
|
|
|
"wallet.networking-profits" => self.handle_wallet_networking_profits().await,
|
|
|
|
|
|
|
|
|
|
// Content catalog management
|
|
|
|
|
"content.list-mine" => self.handle_content_list_mine().await,
|
|
|
|
|
"content.add" => self.handle_content_add(params).await,
|
|
|
|
|
"content.remove" => self.handle_content_remove(params).await,
|
|
|
|
|
"content.set-pricing" => self.handle_content_set_pricing(params).await,
|
|
|
|
|
"content.set-availability" => self.handle_content_set_availability(params).await,
|
|
|
|
|
"content.browse-peer" => self.handle_content_browse_peer(params).await,
|
|
|
|
|
|
|
|
|
|
// DWN (Decentralized Web Node)
|
|
|
|
|
"dwn.status" => self.handle_dwn_status().await,
|
|
|
|
|
"dwn.sync" => self.handle_dwn_sync().await,
|
|
|
|
|
|
2026-03-11 00:22:57 +00:00
|
|
|
// System monitoring
|
|
|
|
|
"system.stats" => self.handle_system_stats().await,
|
|
|
|
|
"system.processes" => self.handle_system_processes().await,
|
|
|
|
|
"system.temperature" => self.handle_system_temperature().await,
|
|
|
|
|
|
2026-03-09 07:43:12 +00:00
|
|
|
// System updates
|
|
|
|
|
"update.check" => self.handle_update_check().await,
|
|
|
|
|
"update.status" => self.handle_update_status().await,
|
|
|
|
|
"update.dismiss" => self.handle_update_dismiss().await,
|
2026-03-05 13:56:29 +00:00
|
|
|
|
2026-03-04 05:23:42 +00:00
|
|
|
_ => {
|
|
|
|
|
Err(anyhow::anyhow!("Unknown method: {}", rpc_req.method))
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Build response
|
|
|
|
|
let rpc_resp = match result {
|
|
|
|
|
Ok(data) => RpcResponse {
|
|
|
|
|
result: Some(data),
|
|
|
|
|
error: None,
|
|
|
|
|
},
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("RPC error: {}", e);
|
|
|
|
|
RpcResponse {
|
|
|
|
|
result: None,
|
|
|
|
|
error: Some(RpcError {
|
|
|
|
|
code: -1,
|
|
|
|
|
message: e.to_string(),
|
|
|
|
|
data: None,
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-06 03:26:56 +00:00
|
|
|
let resp_body = serde_json::to_vec(&rpc_resp)
|
2026-03-04 05:23:42 +00:00
|
|
|
.context("Failed to serialize response")?;
|
|
|
|
|
|
2026-03-06 03:26:56 +00:00
|
|
|
let mut response = Response::builder()
|
2026-03-04 05:23:42 +00:00
|
|
|
.status(StatusCode::OK)
|
|
|
|
|
.header("Content-Type", "application/json")
|
2026-03-06 03:26:56 +00:00
|
|
|
.body(hyper::Body::from(resp_body))
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
// Track failed login attempts for rate limiting
|
|
|
|
|
if rpc_req.method == "auth.login" && rpc_resp.error.is_some() {
|
|
|
|
|
let client_ip = extract_client_ip(&parts.headers);
|
|
|
|
|
self.login_rate_limiter.record_failure(client_ip).await;
|
|
|
|
|
}
|
|
|
|
|
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
// On successful login, check if 2FA is required
|
2026-03-06 03:26:56 +00:00
|
|
|
if rpc_req.method == "auth.login" && rpc_resp.error.is_none() {
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
let totp_enabled = self.auth_manager.is_totp_enabled().await.unwrap_or(false);
|
|
|
|
|
if totp_enabled {
|
|
|
|
|
// 2FA enabled: create a pending session with cached TOTP secret
|
|
|
|
|
// We need the password to decrypt the TOTP secret for step 2
|
|
|
|
|
let password = login_params
|
|
|
|
|
.as_ref()
|
|
|
|
|
.and_then(|p| p.get("password"))
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("");
|
|
|
|
|
if let Ok(Some(totp_data)) = self.auth_manager.get_totp_data().await {
|
|
|
|
|
if let Ok(secret) = crate::totp::decrypt_secret(&totp_data, password) {
|
|
|
|
|
let token = self.session_store.create_pending(secret).await;
|
|
|
|
|
response.headers_mut().insert(
|
|
|
|
|
"Set-Cookie",
|
2026-03-06 13:00:28 +00:00
|
|
|
format!("session={}; HttpOnly; SameSite=Strict; Path=/{}", token, self.cookie_suffix())
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
.parse()
|
|
|
|
|
.unwrap(),
|
|
|
|
|
);
|
|
|
|
|
// Override the response body to indicate TOTP is required
|
|
|
|
|
let totp_body = serde_json::json!({
|
|
|
|
|
"result": { "requires_totp": true },
|
|
|
|
|
"error": null
|
|
|
|
|
});
|
|
|
|
|
*response.body_mut() = hyper::Body::from(
|
|
|
|
|
serde_json::to_vec(&totp_body).unwrap_or_default(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// No 2FA: create a full session immediately
|
|
|
|
|
let token = self.session_store.create().await;
|
|
|
|
|
response.headers_mut().insert(
|
|
|
|
|
"Set-Cookie",
|
2026-03-06 13:00:28 +00:00
|
|
|
format!("session={}; HttpOnly; SameSite=Strict; Path=/{}", token, self.cookie_suffix())
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
.parse()
|
|
|
|
|
.unwrap(),
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-03-06 03:26:56 +00:00
|
|
|
}
|
|
|
|
|
|
feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305
encrypted secret storage, QR code generation, and bcrypt-hashed backup codes
- API key switcher: OAuth vs personal API key toggle in AIUI chat settings with
status indicator, key validation, and help text
- Login progress bar: server startup detection with health check polling, form
disabled until server is ready
- AI quarantine docs: comprehensive HTML page documenting all 6 security layers
- Settings: AI Data Access permission toggles with per-category control
- Alpha hardening plan: 28-task overnight automation plan across 7 phases
(onboarding, login, app install, AIUI, UI polish, security, ISO build)
- Backlog: node discovery spatial map feature for alpha demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:57 +00:00
|
|
|
// On successful TOTP verification, the session is already upgraded to full
|
|
|
|
|
// (handled inside handle_login_totp/handle_login_backup)
|
|
|
|
|
|
2026-03-06 03:26:56 +00:00
|
|
|
// On logout, invalidate session and expire the cookie
|
|
|
|
|
if rpc_req.method == "auth.logout" {
|
|
|
|
|
if let Some(token) = &session_token {
|
|
|
|
|
self.session_store.remove(token).await;
|
|
|
|
|
}
|
2026-03-06 13:00:28 +00:00
|
|
|
let logout_cookie = if self.config.dev_mode {
|
|
|
|
|
"session=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0".to_string()
|
|
|
|
|
} else {
|
|
|
|
|
"session=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0; Secure".to_string()
|
|
|
|
|
};
|
2026-03-06 03:26:56 +00:00
|
|
|
response.headers_mut().insert(
|
|
|
|
|
"Set-Cookie",
|
2026-03-06 13:00:28 +00:00
|
|
|
logout_cookie.parse().unwrap(),
|
2026-03-06 03:26:56 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(response)
|
2026-03-04 05:23:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn handle_echo(&self, params: Option<serde_json::Value>) -> Result<serde_json::Value> {
|
|
|
|
|
if let Some(p) = params {
|
|
|
|
|
if let Some(msg) = p.get("message").and_then(|v| v.as_str()) {
|
|
|
|
|
return Ok(serde_json::json!({ "message": msg }));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(serde_json::json!({ "message": "Hello from Archipelago!" }))
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-06 03:26:56 +00:00
|
|
|
|
|
|
|
|
/// Extract the client IP from request headers (X-Real-IP or X-Forwarded-For).
|
|
|
|
|
fn extract_client_ip(headers: &hyper::HeaderMap) -> IpAddr {
|
|
|
|
|
headers
|
|
|
|
|
.get("x-real-ip")
|
|
|
|
|
.or_else(|| headers.get("x-forwarded-for"))
|
|
|
|
|
.and_then(|v| v.to_str().ok())
|
|
|
|
|
.and_then(|s| s.split(',').next())
|
|
|
|
|
.and_then(|s| s.trim().parse::<IpAddr>().ok())
|
|
|
|
|
.unwrap_or(IpAddr::V4(std::net::Ipv4Addr::LOCALHOST))
|
|
|
|
|
}
|