diff --git a/.gitea/workflows/build-iso-dev.yml b/.gitea/workflows/build-iso-dev.yml index a1965f1e..f2c3c150 100644 --- a/.gitea/workflows/build-iso-dev.yml +++ b/.gitea/workflows/build-iso-dev.yml @@ -58,6 +58,16 @@ jobs: echo "=== Orchestration integration tests ===" cargo test --test orchestration_tests --no-fail-fast --manifest-path core/Cargo.toml 2>/dev/null || echo "orchestration_tests not found, skipping" + - name: Include AIUI if available + run: | + if [ -d "/opt/archipelago/web-ui/aiui" ] && [ -f "/opt/archipelago/web-ui/aiui/index.html" ]; then + mkdir -p web/dist/neode-ui/aiui + cp -r /opt/archipelago/web-ui/aiui/* web/dist/neode-ui/aiui/ + echo "AIUI included from /opt/archipelago/web-ui/aiui/" + else + echo "WARNING: AIUI not found on build server — ISO will not include AIUI" + fi + - name: Configure root podman for insecure registry run: | sudo mkdir -p /etc/containers/registries.conf.d diff --git a/core/archipelago/src/api/rpc/auth.rs b/core/archipelago/src/api/rpc/auth.rs index 9482cadd..d09888f5 100644 --- a/core/archipelago/src/api/rpc/auth.rs +++ b/core/archipelago/src/api/rpc/auth.rs @@ -106,6 +106,16 @@ impl RpcHandler { pub(super) async fn handle_auth_onboarding_complete(&self) -> Result { self.auth_manager.complete_onboarding().await?; tracing::info!("[onboarding] onboarding marked complete"); + + // Auto-configure NostrVPN with the node's Nostr identity + let data_dir = self.config.data_dir.clone(); + tokio::spawn(async move { + match crate::vpn::configure_nostr_vpn(&data_dir).await { + Ok(()) => tracing::info!("[onboarding] NostrVPN configured and started"), + Err(e) => tracing::warn!("[onboarding] NostrVPN setup (non-fatal): {}", e), + } + }); + Ok(serde_json::json!(true)) } diff --git a/core/archipelago/src/api/rpc/package/config.rs b/core/archipelago/src/api/rpc/package/config.rs index b8525ae9..b050ab57 100644 --- a/core/archipelago/src/api/rpc/package/config.rs +++ b/core/archipelago/src/api/rpc/package/config.rs @@ -124,9 +124,9 @@ pub(super) fn get_app_capabilities(app_id: &str) -> Vec { "--cap-add=DAC_OVERRIDE".to_string(), "--cap-add=NET_BIND_SERVICE".to_string(), ], - // Nostr VPN and FIPS: mesh networking daemons need TUN + NET_ADMIN + // Nostr VPN: mesh networking needs TUN + NET_ADMIN // Note: --device=/dev/net/tun is added separately in install.rs - "nostr-vpn" | "fips" => vec![ + "nostr-vpn" => vec![ "--cap-add=NET_ADMIN".to_string(), "--cap-add=NET_RAW".to_string(), ], @@ -251,7 +251,6 @@ pub(super) fn get_health_check_args(app_id: &str, _rpc_pass: &str) -> Vec ("nvpn status || exit 1", "30s", "3"), - "fips" => ("fipsctl status || exit 1", "30s", "3"), _ => return vec![], }; @@ -294,7 +293,6 @@ pub(super) fn get_memory_limit(app_id: &str) -> &'static str { "nostr-rs-relay" | "nostr-relay" => "256m", "routstr" => "512m", "nostr-vpn" => "256m", - "fips" => "256m", "nginx-proxy-manager" => "256m", // Databases "archy-btcpay-db" | "archy-mempool-db" | "mysql-mempool" => "512m", @@ -360,7 +358,6 @@ pub(super) fn all_container_names(package_id: &str) -> Vec { "penpot-backend".into(), "penpot-exporter".into(), "penpot-frontend".into(), ], "nostr-vpn" => vec!["nostr-vpn".into(), "archy-nostr-vpn".into(), "archy-nostr-vpn-ui".into()], - "fips" => vec!["fips".into(), "archy-fips".into(), "archy-fips-ui".into()], "routstr" => vec!["routstr".into(), "archy-routstr".into()], // Default: exact name + archy- prefix _ => vec![base, archy], @@ -837,27 +834,6 @@ pub(super) async fn get_app_config( None, ) } - "fips" => { - let nsec = read_nostr_secret_hex(); - let mut env = vec![]; - if !nsec.is_empty() { - env.push(format!("FIPS_NSEC={}", nsec)); - env.push(format!("FIPS_NPUB={}", read_nostr_pubkey_hex())); - } - ( - vec![ - "2121:2121/udp".to_string(), - "8443:8443".to_string(), - ], - vec![ - "/var/lib/archipelago/fips/config:/etc/fips".to_string(), - "/var/lib/archipelago/fips/run:/run/fips".to_string(), - ], - env, - None, - None, - ) - } "dwn" => ( vec!["3100:3000".to_string()], vec!["/var/lib/archipelago/dwn:/dwn/data".to_string()], diff --git a/core/archipelago/src/api/rpc/package/install.rs b/core/archipelago/src/api/rpc/package/install.rs index 956439b8..36971ef9 100644 --- a/core/archipelago/src/api/rpc/package/install.rs +++ b/core/archipelago/src/api/rpc/package/install.rs @@ -226,7 +226,7 @@ impl RpcHandler { } // TUN device for mesh networking apps - if matches!(package_id, "nostr-vpn" | "fips") { + if package_id == "nostr-vpn" { run_args.push("--device=/dev/net/tun"); } @@ -265,21 +265,13 @@ impl RpcHandler { } // Pre-install: write Nostr identity key files for headless Nostr-aware apps - if matches!(package_id, "nostr-vpn" | "fips") { + if package_id == "nostr-vpn" { let nostr_secret = std::fs::read_to_string("/var/lib/archipelago/identity/nostr_secret") .map(|s| s.trim().to_string()) .unwrap_or_default(); if !nostr_secret.is_empty() { - let key_dir = match package_id { - "nostr-vpn" => "/var/lib/archipelago/nostr-vpn", - "fips" => "/var/lib/archipelago/fips/config", - _ => unreachable!(), - }; - let key_path = match package_id { - "nostr-vpn" => format!("{}/nostr_secret", key_dir), - "fips" => format!("{}/fips.key", key_dir), - _ => unreachable!(), - }; + let key_dir = "/var/lib/archipelago/nostr-vpn"; + let key_path = format!("{}/nostr_secret", key_dir); tokio::fs::create_dir_all(key_dir).await.ok(); tokio::fs::write(&key_path, &nostr_secret).await.ok(); // Restrict permissions on key file @@ -864,9 +856,6 @@ autopilot.active=false\n", "nostr-vpn" => { vec![("archy-nostr-vpn-ui", "/opt/archipelago/docker/nostr-vpn-ui", "nostr-vpn-ui")] } - "fips" => { - vec![("archy-fips-ui", "/opt/archipelago/docker/fips-ui", "fips-ui")] - } _ => vec![], }; diff --git a/image-recipe/configs/nginx-archipelago.conf b/image-recipe/configs/nginx-archipelago.conf index b5669a47..39ec29cf 100644 --- a/image-recipe/configs/nginx-archipelago.conf +++ b/image-recipe/configs/nginx-archipelago.conf @@ -541,18 +541,6 @@ server { proxy_hide_header Content-Security-Policy; add_header X-Content-Type-Options "nosniff" always; } - location /app/fips/ { - proxy_pass http://127.0.0.1:8202/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_hide_header X-Frame-Options; - add_header X-Frame-Options "SAMEORIGIN" always; - proxy_hide_header Content-Security-Policy; - add_header X-Content-Type-Options "nosniff" always; - } location /app/ollama/ { proxy_pass http://127.0.0.1:11434/; proxy_http_version 1.1; diff --git a/image-recipe/configs/nostr-vpn.service b/image-recipe/configs/nostr-vpn.service index 65ac0dca..df9ebebb 100644 --- a/image-recipe/configs/nostr-vpn.service +++ b/image-recipe/configs/nostr-vpn.service @@ -1,17 +1,36 @@ [Unit] -Description=Nostr VPN - Mesh VPN with Nostr signaling -After=network-online.target tor.service +Description=Nostr VPN - Mesh VPN with Nostr identity +After=network-online.target tor.service archipelago.service Wants=network-online.target [Service] Type=simple User=root EnvironmentFile=-/var/lib/archipelago/nostr-vpn/env +ExecStartPre=/bin/bash -c 'test -f /var/lib/archipelago/nostr-vpn/env || { echo "NostrVPN not configured — waiting for onboarding"; exit 1; }' ExecStart=/usr/local/bin/nvpn daemon Restart=on-failure RestartSec=10 +TimeoutStartSec=30 TimeoutStopSec=10 +# Networking capabilities (required for TUN/WireGuard) +AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE + +# Security hardening +NoNewPrivileges=yes +ProtectSystem=strict +ReadWritePaths=/var/lib/archipelago/nostr-vpn /run/nostr-vpn /dev/net/tun +ProtectHome=yes +PrivateTmp=yes +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK +RestrictRealtime=yes + +# Resource limits +MemoryMax=256M +TasksMax=64 + # Logging StandardOutput=journal StandardError=journal diff --git a/image-recipe/configs/snippets/archipelago-https-app-proxies.conf b/image-recipe/configs/snippets/archipelago-https-app-proxies.conf index 4215c64e..f2eb0917 100644 --- a/image-recipe/configs/snippets/archipelago-https-app-proxies.conf +++ b/image-recipe/configs/snippets/archipelago-https-app-proxies.conf @@ -222,17 +222,6 @@ location /app/nostr-vpn/ { add_header X-Frame-Options "SAMEORIGIN" always; proxy_hide_header Content-Security-Policy; } -location /app/fips/ { - proxy_pass http://127.0.0.1:8202/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_hide_header X-Frame-Options; - add_header X-Frame-Options "SAMEORIGIN" always; - proxy_hide_header Content-Security-Policy; -} location /app/ollama/ { proxy_pass http://127.0.0.1:11434/; proxy_http_version 1.1; diff --git a/neode-ui/src/views/appSession/appSessionConfig.ts b/neode-ui/src/views/appSession/appSessionConfig.ts index c3609db0..3475692b 100644 --- a/neode-ui/src/views/appSession/appSessionConfig.ts +++ b/neode-ui/src/views/appSession/appSessionConfig.ts @@ -39,7 +39,6 @@ export const APP_PORTS: Record = { 'fedimint-gateway': 8176, 'nostr-rs-relay': 18081, 'nostr-vpn': 8201, - 'fips': 8202, 'routstr': 8200, 'indeedhub': 7777, 'dwn': 3100, @@ -88,7 +87,6 @@ export const HTTPS_PROXY_PATHS: Record = { 'indeedhub': '/app/indeedhub/', 'routstr': '/app/routstr/', 'nostr-vpn': '/app/nostr-vpn/', - 'fips': '/app/fips/', } /** External HTTPS apps -- always loaded directly */ @@ -106,7 +104,7 @@ export const EXTERNAL_URLS: Record = { export const APP_TITLES: Record = { 'bitcoin-knots': 'Bitcoin', 'btcpay-server': 'BTCPay Server', 'indeedhub': 'Indeehub', 'botfights': 'BotFights', '484-kitchen': '484 Kitchen', 'arch-presentation': 'Presentation', - 'nostr-vpn': 'Nostr VPN', 'fips': 'FIPS', 'routstr': 'Routstr', + 'nostr-vpn': 'Nostr VPN', 'routstr': 'Routstr', 'homeassistant': 'Home Assistant', 'uptime-kuma': 'Uptime Kuma', 'nginx-proxy-manager': 'Nginx Proxy Manager', 'nostr-rs-relay': 'Nostr Relay', 'call-the-operator': 'Call The Operator', 'syntropy-institute': 'Syntropy Institute', diff --git a/neode-ui/src/views/apps/appsConfig.ts b/neode-ui/src/views/apps/appsConfig.ts index 27b835ee..5c925760 100644 --- a/neode-ui/src/views/apps/appsConfig.ts +++ b/neode-ui/src/views/apps/appsConfig.ts @@ -12,7 +12,7 @@ export const SERVICE_NAMES = new Set([ 'mysql-mempool', 'mempool-api', 'archy-mempool-web', 'archy-bitcoin-ui', 'archy-lnd-ui', 'archy-electrs-ui', 'indeedhub-postgres', 'indeedhub-redis', 'indeedhub-minio', - 'archy-nostr-vpn-ui', 'archy-fips-ui', + 'archy-nostr-vpn-ui', 'indeedhub-api', 'indeedhub-ffmpeg', 'indeedhub-relay', 'indeedhub-build_api_1', 'indeedhub-build_ffmpeg-worker_1', 'indeedhub-build_postgres_1', 'indeedhub-build_redis_1', 'indeedhub-build_minio_1', @@ -40,7 +40,7 @@ export const APP_CATEGORY_MAP: Record = { 'homeassistant': 'home', 'lorabell': 'home', 'endurain': 'home', 'searxng': 'community', 'ollama': 'community', 'grafana': 'data', 'nostr-rs-relay': 'nostr', 'nostrudel': 'nostr', - 'nostr-vpn': 'networking', 'fips': 'networking', 'routstr': 'community', + 'nostr-vpn': 'networking', 'routstr': 'community', 'tailscale': 'networking', 'nginx-proxy-manager': 'networking', 'portainer': 'networking', 'uptime-kuma': 'networking', 'dwn': 'data', 'botfights': 'l484', 'nwnn': 'l484', '484-kitchen': 'l484', diff --git a/neode-ui/src/views/discover/curatedApps.ts b/neode-ui/src/views/discover/curatedApps.ts index 970b2d91..ad1fa063 100644 --- a/neode-ui/src/views/discover/curatedApps.ts +++ b/neode-ui/src/views/discover/curatedApps.ts @@ -30,7 +30,6 @@ export function getCuratedAppList(): MarketplaceApp[] { { id: 'indeedhub', title: 'Indeehub', version: '0.1.0', description: 'Bitcoin documentary streaming with Nostr identity. Stream sovereignty content.', icon: '/assets/img/app-icons/indeedhub.png', author: 'Indeehub Team', dockerImage: 'localhost/indeedhub:latest', repoUrl: 'https://github.com/indeedhub/indeedhub' }, { id: 'dwn', title: 'Decentralized Web Node', version: '0.4.0', description: 'Own your data with DID-based access control. Sync across devices, sovereign.', icon: '/assets/img/app-icons/dwn.svg', author: 'TBD', dockerImage: `${R}/dwn-server:main`, repoUrl: 'https://github.com/TBD54566975/dwn-server' }, { id: 'nostr-vpn', title: 'Nostr VPN', version: '0.3.4', category: 'networking', description: 'Tailscale-style mesh VPN with Nostr control plane. Peer discovery and key exchange over relays, WireGuard tunnels.', icon: '/assets/img/app-icons/nostr-vpn.svg', author: 'Martti Malmi', dockerImage: `${R}/nostr-vpn:v0.3.4`, repoUrl: 'https://github.com/mmalmi/nostr-vpn' }, - { id: 'fips', title: 'FIPS', version: '0.1.0', category: 'networking', description: 'Free Internetworking Peering System. Self-organizing encrypted mesh network with Nostr identity.', icon: '/assets/img/app-icons/fips.svg', author: 'Jim Corgan', dockerImage: `${R}/fips:v0.1.0`, repoUrl: 'https://github.com/jmcorgan/fips' }, { id: 'routstr', title: 'Routstr', version: '0.4.3', category: 'community', description: 'Decentralized AI inference proxy. Pay-per-request with Cashu ecash, provider discovery via Nostr.', icon: '/assets/img/app-icons/routstr.svg', author: 'Routstr', dockerImage: `${R}/routstr:v0.4.3`, repoUrl: 'https://github.com/routstr/routstr-core' }, { id: 'nostrudel', title: 'noStrudel', version: '0.40.0', category: 'nostr', description: 'Feature-rich Nostr web client. Browse feeds, post notes, manage relays with NIP-07.', icon: '/assets/img/app-icons/nostrudel.svg', author: 'hzrd149', dockerImage: '', repoUrl: 'https://github.com/hzrd149/nostrudel', webUrl: 'https://nostrudel.ninja' }, { id: 'botfights', title: 'BotFights', version: '1.0.0', description: 'AI bot arena — build, train, and battle autonomous agents in strategy tournaments.', icon: '/assets/img/app-icons/botfights.svg', author: 'BotFights', dockerImage: '', repoUrl: 'https://botfights.net', webUrl: 'https://botfights.net' }, @@ -62,7 +61,6 @@ export const INSTALLED_ALIASES: Record = { tailscale: ['tailscale'], ollama: ['ollama'], 'nostr-vpn': ['nostr-vpn'], - fips: ['fips'], routstr: ['routstr'], } diff --git a/neode-ui/src/views/marketplace/marketplaceData.ts b/neode-ui/src/views/marketplace/marketplaceData.ts index 55585b63..6c02e8ad 100644 --- a/neode-ui/src/views/marketplace/marketplaceData.ts +++ b/neode-ui/src/views/marketplace/marketplaceData.ts @@ -55,7 +55,6 @@ export const INSTALLED_ALIASES: Record = { tailscale: ['tailscale'], ollama: ['ollama'], 'nostr-vpn': ['nostr-vpn'], - fips: ['fips'], routstr: ['routstr'], } @@ -417,18 +416,6 @@ export function getCuratedAppList(): MarketplaceApp[] { manifestUrl: undefined, repoUrl: 'https://github.com/mmalmi/nostr-vpn' }, - { - id: 'fips', - title: 'FIPS', - version: '0.1.0', - category: 'networking', - description: 'Free Internetworking Peering System. Self-organizing encrypted mesh network with Nostr identity.', - icon: '/assets/img/app-icons/fips.svg', - author: 'Jim Corgan', - dockerImage: `${REGISTRY}/fips:v0.1.0`, - manifestUrl: undefined, - repoUrl: 'https://github.com/jmcorgan/fips' - }, { id: 'routstr', title: 'Routstr', diff --git a/scripts/image-versions.sh b/scripts/image-versions.sh index d937a3ee..5d632959 100644 --- a/scripts/image-versions.sh +++ b/scripts/image-versions.sh @@ -65,8 +65,6 @@ NOSTR_RS_RELAY_IMAGE="$ARCHY_REGISTRY/nostr-rs-relay:0.9.0" STRFRY_IMAGE="$ARCHY_REGISTRY/strfry:1.0.4" NOSTR_VPN_IMAGE="$ARCHY_REGISTRY/nostr-vpn:v0.3.4" NOSTR_VPN_UI_IMAGE="$ARCHY_REGISTRY/nostr-vpn-ui:latest" -FIPS_IMAGE="$ARCHY_REGISTRY/fips:v0.1.0" -FIPS_UI_IMAGE="$ARCHY_REGISTRY/fips-ui:latest" # AI / Routing ROUTSTR_IMAGE="$ARCHY_REGISTRY/routstr:v0.4.3"