diff --git a/Android/app/src/main/java/com/archipelago/app/ui/components/NESController.kt b/Android/app/src/main/java/com/archipelago/app/ui/components/NESController.kt index 19e75422..07cac98e 100644 --- a/Android/app/src/main/java/com/archipelago/app/ui/components/NESController.kt +++ b/Android/app/src/main/java/com/archipelago/app/ui/components/NESController.kt @@ -186,7 +186,7 @@ fun NESController( } } - // A/B Buttons in inlay (same size as D-pad inlay, more right margin) + // A/B/C Buttons in inlay (same size as D-pad inlay, more right margin) Inlay(c, Modifier.align(Alignment.CenterEnd).padding(end = 48.dp).size(140.dp)) { Row( Modifier.fillMaxSize(), @@ -194,15 +194,20 @@ fun NESController( verticalAlignment = Alignment.CenterVertically, ) { Column(horizontalAlignment = Alignment.CenterHorizontally) { - Spacer(Modifier.height(10.dp)) - RoundBtn(c, 52.dp) { onKey("Escape") } - Text("B", color = c.labelMuted, fontSize = 9.sp, fontWeight = FontWeight.Bold) + RoundBtn(c, 42.dp) { onKey("c") } + Text("C", color = c.labelMuted, fontSize = 8.sp, fontWeight = FontWeight.Bold) } - Spacer(Modifier.width(16.dp)) + Spacer(Modifier.width(10.dp)) Column(horizontalAlignment = Alignment.CenterHorizontally) { - RoundBtn(c, 52.dp) { onKey("Return") } - Text("A", color = c.labelMuted, fontSize = 9.sp, fontWeight = FontWeight.Bold) - Spacer(Modifier.height(10.dp)) + Spacer(Modifier.height(8.dp)) + RoundBtn(c, 42.dp) { onKey("b") } + Text("B", color = c.labelMuted, fontSize = 8.sp, fontWeight = FontWeight.Bold) + } + Spacer(Modifier.width(10.dp)) + Column(horizontalAlignment = Alignment.CenterHorizontally) { + RoundBtn(c, 42.dp) { onKey("a") } + Text("A", color = c.labelMuted, fontSize = 8.sp, fontWeight = FontWeight.Bold) + Spacer(Modifier.height(8.dp)) } } } diff --git a/Android/app/src/main/java/com/archipelago/app/ui/components/NESPortraitController.kt b/Android/app/src/main/java/com/archipelago/app/ui/components/NESPortraitController.kt index 662d160a..e825921a 100644 --- a/Android/app/src/main/java/com/archipelago/app/ui/components/NESPortraitController.kt +++ b/Android/app/src/main/java/com/archipelago/app/ui/components/NESPortraitController.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -24,7 +25,9 @@ import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.archipelago.app.R import com.archipelago.app.ui.theme.ControllerStyle import com.archipelago.app.ui.theme.NES @@ -113,16 +116,27 @@ fun NESPortraitController( Spacer(Modifier.height(12.dp)) - // A/B Buttons + // A/B/C Buttons Inlay(c, Modifier.fillMaxWidth()) { Row( - Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 10.dp), + Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 10.dp), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { - RoundBtn(c, 52.dp) { onKey("Escape") } - Spacer(Modifier.width(24.dp)) - RoundBtn(c, 52.dp) { onKey("Return") } + Column(horizontalAlignment = Alignment.CenterHorizontally) { + RoundBtn(c, 46.dp) { onKey("c") } + Text("C", color = c.labelMuted, fontSize = 8.sp, fontWeight = FontWeight.Bold) + } + Spacer(Modifier.width(16.dp)) + Column(horizontalAlignment = Alignment.CenterHorizontally) { + RoundBtn(c, 46.dp) { onKey("b") } + Text("B", color = c.labelMuted, fontSize = 8.sp, fontWeight = FontWeight.Bold) + } + Spacer(Modifier.width(16.dp)) + Column(horizontalAlignment = Alignment.CenterHorizontally) { + RoundBtn(c, 46.dp) { onKey("a") } + Text("A", color = c.labelMuted, fontSize = 8.sp, fontWeight = FontWeight.Bold) + } } } diff --git a/app-catalog/README.md b/app-catalog/README.md new file mode 100644 index 00000000..9e723be2 --- /dev/null +++ b/app-catalog/README.md @@ -0,0 +1,39 @@ +# Archipelago App Catalog + +Dynamic app catalog for the Archipelago marketplace. Nodes fetch this catalog to discover available apps. + +## How it works + +1. The Archipelago frontend fetches `catalog.json` from this repo +2. Apps listed here appear in every node's app store automatically +3. When a user installs an app, the backend pulls the Docker image and creates the container + +## Adding a new app + +Add an entry to `catalog.json`: + +```json +{ + "id": "my-app", + "title": "My App", + "version": "1.0.0", + "description": "What it does", + "icon": "/assets/img/app-icons/my-app.svg", + "author": "Author", + "category": "data", + "dockerImage": "git.tx1138.com/lfg2025/my-app:1.0.0", + "repoUrl": "https://github.com/...", + "containerConfig": { + "ports": ["8080:8080"], + "volumes": ["/var/lib/archipelago/my-app:/data"], + "env": ["NODE_ENV=production"] + } +} +``` + +For apps with hardcoded backend configs (Bitcoin, LND, etc.), `containerConfig` is optional. +For new apps, include `containerConfig` so the backend knows how to create the container. + +## Categories + +money, commerce, data, home, nostr, networking, community, development, l484 diff --git a/app-catalog/catalog.json b/app-catalog/catalog.json new file mode 100644 index 00000000..8029a456 --- /dev/null +++ b/app-catalog/catalog.json @@ -0,0 +1,242 @@ +{ + "version": 2, + "updated": "2026-04-12T00:00:00Z", + "registry": "git.tx1138.com/lfg2025", + "featured": { + "id": "indeedhub", + "banner": "/assets/img/featured/indeedhub-banner.jpg", + "headline": "Stream Sovereignty", + "description": "Bitcoin documentaries with Nostr identity.", + "tag": "NOSTR IDENTITY // YOUR NODE" + }, + "apps": [ + { + "id": "bitcoin-knots", "title": "Bitcoin Knots", "version": "28.1.0", + "description": "Run a full Bitcoin node. Validate and relay blocks and transactions.", + "icon": "/assets/img/app-icons/bitcoin-knots.webp", + "author": "Bitcoin Knots", "category": "money", "tier": "core", + "dockerImage": "git.tx1138.com/lfg2025/bitcoin-knots:latest", + "repoUrl": "https://github.com/bitcoinknots/bitcoin" + }, + { + "id": "lnd", "title": "LND", "version": "0.18.4", + "description": "Lightning Network Daemon. Fast Bitcoin payments through Lightning.", + "icon": "/assets/img/app-icons/lnd.svg", + "author": "Lightning Labs", "category": "money", "tier": "core", + "dockerImage": "git.tx1138.com/lfg2025/lnd:v0.18.4-beta", + "repoUrl": "https://github.com/lightningnetwork/lnd", + "requires": ["bitcoin-knots"] + }, + { + "id": "btcpay-server", "title": "BTCPay Server", "version": "1.13.7", + "description": "Self-hosted Bitcoin payment processor.", + "icon": "/assets/img/app-icons/btcpay-server.png", + "author": "BTCPay Server Foundation", "category": "commerce", "tier": "core", + "dockerImage": "git.tx1138.com/lfg2025/btcpayserver:1.13.7", + "repoUrl": "https://github.com/btcpayserver/btcpayserver", + "requires": ["bitcoin-knots"] + }, + { + "id": "mempool", "title": "Mempool Explorer", "version": "3.0.0", + "description": "Self-hosted Bitcoin blockchain and mempool visualizer.", + "icon": "/assets/img/app-icons/mempool.webp", + "author": "Mempool", "category": "money", "tier": "core", + "dockerImage": "git.tx1138.com/lfg2025/mempool-frontend:v3.0.0", + "repoUrl": "https://github.com/mempool/mempool", + "requires": ["bitcoin-knots", "electrumx"] + }, + { + "id": "electrumx", "title": "ElectrumX", "version": "1.18.0", + "description": "Electrum protocol server. Index the blockchain for fast wallet lookups.", + "icon": "/assets/img/app-icons/electrumx.webp", + "author": "Luke Childs", "category": "money", "tier": "core", + "dockerImage": "git.tx1138.com/lfg2025/electrumx:v1.18.0", + "repoUrl": "https://github.com/spesmilo/electrumx", + "requires": ["bitcoin-knots"] + }, + { + "id": "indeedhub", "title": "IndeeHub", "version": "1.0.0", + "description": "Bitcoin documentary streaming with Nostr identity.", + "icon": "/assets/img/app-icons/indeedhub.png", + "author": "IndeeHub", "category": "community", + "dockerImage": "git.tx1138.com/lfg2025/indeedhub:1.0.0", + "repoUrl": "https://github.com/indeedhub/indeedhub" + }, + { + "id": "botfights", "title": "BotFights", "version": "1.1.0", + "description": "Bot arena + 2-player arcade fighter with controller support and Adventure Mode.", + "icon": "/assets/img/app-icons/botfights.svg", + "author": "BotFights", "category": "community", + "dockerImage": "git.tx1138.com/lfg2025/botfights:1.1.0", + "repoUrl": "https://botfights.net" + }, + { + "id": "gitea", "title": "Gitea", "version": "1.23", + "description": "Self-hosted Git service with container registry, CI/CD, issue tracking.", + "icon": "/assets/img/app-icons/gitea.svg", + "author": "Gitea", "category": "development", + "dockerImage": "docker.io/gitea/gitea:1.23", + "repoUrl": "https://gitea.com" + }, + { + "id": "filebrowser", "title": "File Browser", "version": "2.27.0", + "description": "Web-based file manager.", + "icon": "/assets/img/app-icons/file-browser.webp", + "author": "File Browser", "category": "data", "tier": "core", + "dockerImage": "git.tx1138.com/lfg2025/filebrowser:v2.27.0", + "repoUrl": "https://github.com/filebrowser/filebrowser" + }, + { + "id": "vaultwarden", "title": "Vaultwarden", "version": "1.30.0", + "description": "Self-hosted password vault with zero-knowledge encryption.", + "icon": "/assets/img/app-icons/vaultwarden.webp", + "author": "Vaultwarden", "category": "data", "tier": "recommended", + "dockerImage": "git.tx1138.com/lfg2025/vaultwarden:1.30.0-alpine", + "repoUrl": "https://github.com/dani-garcia/vaultwarden" + }, + { + "id": "searxng", "title": "SearXNG", "version": "2024.1.0", + "description": "Privacy-respecting metasearch engine.", + "icon": "/assets/img/app-icons/searxng.png", + "author": "SearXNG", "category": "data", "tier": "recommended", + "dockerImage": "git.tx1138.com/lfg2025/searxng:latest", + "repoUrl": "https://github.com/searxng/searxng" + }, + { + "id": "nostr-rs-relay", "title": "Nostr Relay", "version": "0.9.0", + "description": "Your own Nostr relay. Store events locally, relay for friends.", + "icon": "/assets/img/app-icons/nostr-rs-relay.svg", + "author": "scsiblade", "category": "nostr", + "dockerImage": "git.tx1138.com/lfg2025/nostr-rs-relay:0.9.0", + "repoUrl": "https://sr.ht/~gheartsfield/nostr-rs-relay/" + }, + { + "id": "fedimint", "title": "Fedimint", "version": "0.10.0", + "description": "Federated Bitcoin mint with privacy through federated guardians.", + "icon": "/assets/img/app-icons/fedimint.png", + "author": "Fedimint", "category": "money", + "dockerImage": "git.tx1138.com/lfg2025/fedimintd:v0.10.0", + "repoUrl": "https://github.com/fedimint/fedimint" + }, + { + "id": "ollama", "title": "Ollama", "version": "0.5.4", + "description": "Run AI models locally. Private and on your hardware.", + "icon": "/assets/img/app-icons/ollama.png", + "author": "Ollama", "category": "data", + "dockerImage": "git.tx1138.com/lfg2025/ollama:latest", + "repoUrl": "https://github.com/ollama/ollama" + }, + { + "id": "nextcloud", "title": "Nextcloud", "version": "28", + "description": "Your own private cloud. File sync, calendars, contacts.", + "icon": "/assets/img/app-icons/nextcloud.webp", + "author": "Nextcloud", "category": "data", + "dockerImage": "git.tx1138.com/lfg2025/nextcloud:28", + "repoUrl": "https://github.com/nextcloud/server" + }, + { + "id": "jellyfin", "title": "Jellyfin", "version": "10.8.13", + "description": "Free media server. Stream movies, music, and photos.", + "icon": "/assets/img/app-icons/jellyfin.webp", + "author": "Jellyfin", "category": "data", + "dockerImage": "git.tx1138.com/lfg2025/jellyfin:10.8.13", + "repoUrl": "https://github.com/jellyfin/jellyfin" + }, + { + "id": "immich", "title": "Immich", "version": "1.90.0", + "description": "High-performance photo and video backup with ML.", + "icon": "/assets/img/app-icons/immich.png", + "author": "Immich", "category": "data", + "dockerImage": "git.tx1138.com/lfg2025/immich-server:release", + "repoUrl": "https://github.com/immich-app/immich" + }, + { + "id": "homeassistant", "title": "Home Assistant", "version": "2024.1", + "description": "Open-source home automation.", + "icon": "/assets/img/app-icons/homeassistant.png", + "author": "Home Assistant", "category": "home", + "dockerImage": "git.tx1138.com/lfg2025/home-assistant:2024.1", + "repoUrl": "https://github.com/home-assistant/core" + }, + { + "id": "grafana", "title": "Grafana", "version": "10.2.0", + "description": "Analytics and monitoring dashboards.", + "icon": "/assets/img/app-icons/grafana.png", + "author": "Grafana Labs", "category": "data", "tier": "recommended", + "dockerImage": "git.tx1138.com/lfg2025/grafana:10.2.0", + "repoUrl": "https://github.com/grafana/grafana" + }, + { + "id": "tailscale", "title": "Tailscale", "version": "1.78.0", + "description": "Zero-config VPN with WireGuard mesh networking.", + "icon": "/assets/img/app-icons/tailscale.webp", + "author": "Tailscale", "category": "networking", "tier": "recommended", + "dockerImage": "git.tx1138.com/lfg2025/tailscale:stable", + "repoUrl": "https://github.com/tailscale/tailscale" + }, + { + "id": "uptime-kuma", "title": "Uptime Kuma", "version": "1.23.0", + "description": "Self-hosted uptime monitoring.", + "icon": "/assets/img/app-icons/uptime-kuma.webp", + "author": "Uptime Kuma", "category": "data", "tier": "recommended", + "dockerImage": "git.tx1138.com/lfg2025/uptime-kuma:1", + "repoUrl": "https://github.com/louislam/uptime-kuma" + }, + { + "id": "nostr-vpn", "title": "Nostr VPN", "version": "0.3.7", + "description": "Tailscale-style mesh VPN with Nostr control plane.", + "icon": "/assets/img/app-icons/nostr-vpn.svg", + "author": "Martti Malmi", "category": "networking", + "dockerImage": "git.tx1138.com/lfg2025/nostr-vpn:v0.3.7", + "repoUrl": "https://github.com/mmalmi/nostr-vpn" + }, + { + "id": "fips", "title": "FIPS", "version": "0.1.0", + "description": "Free Internetworking Peering System. Encrypted mesh network.", + "icon": "/assets/img/app-icons/fips.svg", + "author": "Jim Corgan", "category": "networking", + "dockerImage": "git.tx1138.com/lfg2025/fips:v0.1.0", + "repoUrl": "https://github.com/jmcorgan/fips" + }, + { + "id": "routstr", "title": "Routstr", "version": "0.4.3", + "description": "Decentralized AI inference proxy with Cashu ecash.", + "icon": "/assets/img/app-icons/routstr.svg", + "author": "Routstr", "category": "community", + "dockerImage": "git.tx1138.com/lfg2025/routstr:v0.4.3", + "repoUrl": "https://github.com/routstr/routstr-core" + }, + { + "id": "dwn", "title": "Decentralized Web Node", "version": "0.4.0", + "description": "Own your data with DID-based access control.", + "icon": "/assets/img/app-icons/dwn.svg", + "author": "TBD", "category": "data", + "dockerImage": "git.tx1138.com/lfg2025/dwn-server:main", + "repoUrl": "https://github.com/TBD54566975/dwn-server" + }, + { + "id": "endurain", "title": "Endurain", "version": "0.8.0", + "description": "Self-hosted fitness tracking. Strava alternative.", + "icon": "/assets/img/app-icons/endurain.png", + "author": "Endurain", "category": "data", + "dockerImage": "git.tx1138.com/lfg2025/endurain:0.8.0", + "repoUrl": "https://github.com/joaovitoriasilva/endurain" + }, + { + "id": "penpot", "title": "Penpot", "version": "2.4", + "description": "Open-source design platform. Self-hosted Figma alternative.", + "icon": "/assets/img/app-icons/penpot.webp", + "author": "Penpot", "category": "data", + "dockerImage": "git.tx1138.com/lfg2025/penpot-frontend:2.4", + "repoUrl": "https://github.com/penpot/penpot" + }, + { + "id": "photoprism", "title": "PhotoPrism", "version": "240915", + "description": "AI-powered photo management with facial recognition.", + "icon": "/assets/img/app-icons/photoprism.svg", + "author": "PhotoPrism", "category": "data", + "dockerImage": "git.tx1138.com/lfg2025/photoprism:240915", + "repoUrl": "https://github.com/photoprism/photoprism" + } + ] +} diff --git a/apps/gitea/manifest.yml b/apps/gitea/manifest.yml index 4af96688..8f116c81 100644 --- a/apps/gitea/manifest.yml +++ b/apps/gitea/manifest.yml @@ -5,6 +5,7 @@ description: Self-hosted Git service with built-in container registry, CI/CD, an category: development icon: git-branch port: 3000 +internal_port: 3001 ssh_port: 2222 image: docker.io/gitea/gitea:1.23 tier: optional @@ -28,6 +29,16 @@ environment: GITEA__repository__ENABLE_PUSH_CREATE_USER: "true" GITEA__repository__ENABLE_PUSH_CREATE_ORG: "true" +# Gitea hardcodes X-Frame-Options: SAMEORIGIN which blocks iframe embedding. +# Container binds to internal_port (3001), nginx proxies public port (3000) +# stripping the X-Frame-Options header so the app works in Archipelago's iframe. +nginx_proxy: + listen: 3000 + proxy_pass: "http://127.0.0.1:3001" + extra_headers: + - "proxy_hide_header X-Frame-Options" + - "proxy_hide_header Content-Security-Policy" + health_check: endpoint: / interval: 120 diff --git a/core/archipelago/src/api/handler/remote_input.rs b/core/archipelago/src/api/handler/remote_input.rs index 123cb24f..56525169 100644 --- a/core/archipelago/src/api/handler/remote_input.rs +++ b/core/archipelago/src/api/handler/remote_input.rs @@ -4,7 +4,6 @@ use hyper::{Request, Response}; use hyper_ws_listener::WsStream; use serde::Deserialize; use std::time::Instant; -use tokio::process::Command; use tokio::sync::broadcast; use tokio_tungstenite::tungstenite::Message; use tracing::{debug, info, warn}; @@ -72,21 +71,9 @@ enum InputCommand { Ping, } -async fn xdotool(args: &[&str]) -> Result<()> { - let output = Command::new("xdotool") - .env("DISPLAY", ":0") - .args(args) - .output() - .await - .context("xdotool execution failed")?; - - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - debug!("xdotool error: {}", stderr); - } - Ok(()) -} - +/// Validate and acknowledge input — relay-only, no xdotool. +/// All input is forwarded to browser clients via the broadcast channel; +/// the browser's remote-relay.ts dispatches DOM events from there. async fn handle_input(msg: &str) -> Result> { let cmd: InputCommand = serde_json::from_str(msg) .context("invalid input command")?; @@ -97,25 +84,16 @@ async fn handle_input(msg: &str) -> Result> { warn!("rejected key: {}", k); return Ok(Some(r#"{"t":"e","m":"invalid key"}"#.to_string())); } - xdotool(&["key", "--clearmodifiers", k]).await?; } InputCommand::MouseMove { x, y } => { - let x = x.clamp(-50, 50); - let y = y.clamp(-50, 50); - let xs = x.to_string(); - let ys = y.to_string(); - xdotool(&["mousemove_relative", "--", &xs, &ys]).await?; + let _x = x.clamp(-50, 50); + let _y = y.clamp(-50, 50); } InputCommand::Click { b } => { - let b = b.clamp(1, 3); - let bs = b.to_string(); - xdotool(&["click", &bs]).await?; + let _b = b.clamp(1, 3); } InputCommand::Scroll { y } => { - // xdotool: button 4 = scroll up, button 5 = scroll down - let btn = if y < 0 { "4" } else { "5" }; - let count = y.unsigned_abs().clamp(1, 10).to_string(); - xdotool(&["click", "--repeat", &count, btn]).await?; + let _y = y.clamp(-10, 10); } InputCommand::Ping => { return Ok(Some(r#"{"t":"p"}"#.to_string())); diff --git a/core/archipelago/src/api/rpc/package/config.rs b/core/archipelago/src/api/rpc/package/config.rs index 2a54ed82..5f0dc8f1 100644 --- a/core/archipelago/src/api/rpc/package/config.rs +++ b/core/archipelago/src/api/rpc/package/config.rs @@ -214,7 +214,7 @@ pub(super) fn get_health_check_args(app_id: &str, _rpc_pass: &str) -> Vec ("curl -sf http://localhost:11434/ || exit 1", "30s", "3"), "fedimint" => ( - "curl -sf http://localhost:8174/health || exit 1", + "curl -sf http://localhost:8175/ || exit 1", "60s", "3", ), @@ -894,8 +894,10 @@ pub(super) async fn get_app_config( None, ) } + // Gitea binds to 3001 internally. Nginx on port 3000 strips X-Frame-Options + // so Gitea works in Archipelago's iframe. See nginx-gitea-iframe.conf. "gitea" => ( - vec!["3000:3000".to_string(), "2222:22".to_string()], + vec!["3001:3000".to_string(), "2222:22".to_string()], vec![ "/var/lib/archipelago/gitea/data:/data".to_string(), "/var/lib/archipelago/gitea/config:/etc/gitea".to_string(), @@ -912,6 +914,32 @@ pub(super) async fn get_app_config( None, None, ), - _ => (vec![], vec![], vec![], None, None), + _ => { + // Unknown app: try to load config from /var/lib/archipelago/app-configs/{id}.json + // This allows dynamic apps from the remote catalog to be installed + // without hardcoding their config here. + let config_path = format!("/var/lib/archipelago/app-configs/{}.json", app_id); + if let Ok(data) = tokio::fs::read_to_string(&config_path).await { + if let Ok(cfg) = serde_json::from_str::(&data) { + let ports = cfg.get("ports") + .and_then(|v| v.as_array()) + .map(|a| a.iter().filter_map(|v| v.as_str().map(String::from)).collect()) + .unwrap_or_default(); + let volumes = cfg.get("volumes") + .and_then(|v| v.as_array()) + .map(|a| a.iter().filter_map(|v| v.as_str().map(String::from)).collect()) + .unwrap_or_default(); + let env_vars = cfg.get("env") + .and_then(|v| v.as_array()) + .map(|a| a.iter().filter_map(|v| v.as_str().map(String::from)).collect()) + .unwrap_or_default(); + tracing::info!("Loaded dynamic config for app: {}", app_id); + return (ports, volumes, env_vars, None, None); + } + } + // No config found — use minimal defaults (container's own EXPOSE/VOLUME) + tracing::warn!("No config found for app: {} — using minimal defaults", app_id); + (vec![], vec![], vec![], None, None) + }, } } diff --git a/image-recipe/configs/nginx-archipelago.conf b/image-recipe/configs/nginx-archipelago.conf index 200a5ca1..8f4622d0 100644 --- a/image-recipe/configs/nginx-archipelago.conf +++ b/image-recipe/configs/nginx-archipelago.conf @@ -475,17 +475,19 @@ server { sub_filter 'src="/' 'src="/app/botfights/'; sub_filter "href='/" "href='/app/botfights/"; sub_filter "src='/" "src='/app/botfights/"; - sub_filter '' ''; + sub_filter '' ''; } location /app/gitea/ { - proxy_pass http://127.0.0.1:3000/; + # Gitea runs on 3001, nginx proxies 3000 stripping X-Frame-Options for iframe + proxy_pass http://127.0.0.1:3001/; proxy_http_version 1.1; proxy_set_header Host $http_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; client_max_body_size 1G; } location /app/lnd/ { diff --git a/image-recipe/configs/nginx-gitea-iframe.conf b/image-recipe/configs/nginx-gitea-iframe.conf new file mode 100644 index 00000000..9d33126c --- /dev/null +++ b/image-recipe/configs/nginx-gitea-iframe.conf @@ -0,0 +1,21 @@ +# Gitea iframe proxy — strips X-Frame-Options so Gitea works in Archipelago iframe. +# Gitea container binds to port 3001, this proxy listens on port 3000 (the public port). +# Deployed to /etc/nginx/conf.d/gitea-iframe.conf +server { + listen 3000; + server_name _; + client_max_body_size 1G; + + location / { + proxy_pass http://127.0.0.1:3001; + proxy_set_header Host $http_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_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_hide_header X-Frame-Options; + proxy_hide_header Content-Security-Policy; + } +} diff --git a/image-recipe/configs/snippets/archipelago-https-app-proxies.conf b/image-recipe/configs/snippets/archipelago-https-app-proxies.conf index 9b7bb48b..811615a0 100644 --- a/image-recipe/configs/snippets/archipelago-https-app-proxies.conf +++ b/image-recipe/configs/snippets/archipelago-https-app-proxies.conf @@ -291,7 +291,7 @@ location /app/botfights/ { sub_filter 'src="/' 'src="/app/botfights/'; sub_filter "href='/" "href='/app/botfights/"; sub_filter "src='/" "src='/app/botfights/"; - sub_filter '' ''; + sub_filter '' ''; } location /app/electrumx/ { proxy_pass http://127.0.0.1:50002/; diff --git a/neode-ui/public/assets/img/app-icons/gitea.svg b/neode-ui/public/assets/img/app-icons/gitea.svg index 9df6b83b..5fe29819 100644 --- a/neode-ui/public/assets/img/app-icons/gitea.svg +++ b/neode-ui/public/assets/img/app-icons/gitea.svg @@ -1,31 +1,12 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/neode-ui/public/catalog.json b/neode-ui/public/catalog.json index 790cab2a..9946ba78 100644 --- a/neode-ui/public/catalog.json +++ b/neode-ui/public/catalog.json @@ -1,48 +1,415 @@ { "version": 1, "updated": "2026-04-11T00:00:00Z", - "registry": "git.tx1138.com/lfg2025", + "registry": "23.182.128.160:3000/lfg2025", "featured": { "id": "indeedhub", "banner": "/assets/img/featured/indeedhub-banner.jpg", "headline": "Stream Sovereignty", - "description": "Bitcoin documentaries with Nostr identity. God Bless Bitcoin, The Bitcoin Psyop, and more — streaming from your own node.", + "description": "Bitcoin documentaries with Nostr identity. God Bless Bitcoin, The Bitcoin Psyop, and more \u2014 streaming from your own node.", "tag": "NOSTR IDENTITY // YOUR NODE" }, "apps": [ - { "id": "bitcoin-knots", "title": "Bitcoin Knots", "version": "28.1.0", "description": "Run a full Bitcoin node. Validate and relay blocks and transactions on the Bitcoin network.", "icon": "/assets/img/app-icons/bitcoin-knots.webp", "author": "Bitcoin Knots", "dockerImage": "bitcoin-knots:latest", "repoUrl": "https://github.com/bitcoinknots/bitcoin", "category": "money", "tier": "core" }, - { "id": "lnd", "title": "LND", "version": "0.18.4", "description": "Lightning Network Daemon. Fast and cheap Bitcoin payments through the Lightning Network.", "icon": "/assets/img/app-icons/lnd.svg", "author": "Lightning Labs", "dockerImage": "lnd:v0.18.4-beta", "repoUrl": "https://github.com/lightningnetwork/lnd", "category": "money", "tier": "core" }, - { "id": "btcpay-server", "title": "BTCPay Server", "version": "1.13.7", "description": "Self-hosted Bitcoin payment processor. Accept Bitcoin payments without intermediaries or fees.", "icon": "/assets/img/app-icons/btcpay-server.png", "author": "BTCPay Server Foundation", "dockerImage": "btcpayserver:1.13.7", "repoUrl": "https://github.com/btcpayserver/btcpayserver", "category": "commerce", "tier": "core" }, - { "id": "mempool", "title": "Mempool Explorer", "version": "3.0.0", "description": "Self-hosted Bitcoin blockchain and mempool visualizer. Monitor transactions without revealing your addresses.", "icon": "/assets/img/app-icons/mempool.webp", "author": "Mempool", "dockerImage": "mempool-frontend:v3.0.0", "repoUrl": "https://github.com/mempool/mempool", "category": "money", "tier": "core" }, - { "id": "electrumx", "title": "ElectrumX", "version": "1.18.0", "description": "Electrum protocol server. Index the blockchain for fast wallet lookups, privately.", "icon": "/assets/img/app-icons/electrumx.webp", "author": "Luke Childs", "dockerImage": "electrumx:v1.18.0", "repoUrl": "https://github.com/spesmilo/electrumx", "category": "money", "tier": "core" }, - { "id": "indeedhub", "title": "IndeeHub", "version": "1.0.0", "description": "Bitcoin documentary streaming with Nostr identity. Stream sovereignty content from your node.", "icon": "/assets/img/app-icons/indeedhub.png", "author": "IndeeHub Team", "dockerImage": "indeedhub:1.0.0", "repoUrl": "https://github.com/indeedhub/indeedhub", "category": "community" }, - { "id": "botfights", "title": "BotFights", "version": "1.0.0", "description": "Bot arena + 2-player arcade fighter with controller support.", "icon": "/assets/img/app-icons/botfights.svg", "author": "BotFights", "dockerImage": "botfights:1.1.0", "repoUrl": "https://botfights.net", "category": "community" }, - { "id": "filebrowser", "title": "File Browser", "version": "2.27.0", "description": "Web-based file manager. Browse, upload, and manage files on your server.", "icon": "/assets/img/app-icons/file-browser.webp", "author": "File Browser", "dockerImage": "filebrowser:v2.27.0", "repoUrl": "https://github.com/filebrowser/filebrowser", "category": "data", "tier": "core" }, - { "id": "vaultwarden", "title": "Vaultwarden", "version": "1.30.0", "description": "Self-hosted password vault. Bitwarden-compatible with zero-knowledge encryption.", "icon": "/assets/img/app-icons/vaultwarden.webp", "author": "Vaultwarden", "dockerImage": "vaultwarden:1.30.0-alpine", "repoUrl": "https://github.com/dani-garcia/vaultwarden", "category": "data", "tier": "recommended" }, - { "id": "searxng", "title": "SearXNG", "version": "2024.1.0", "description": "Privacy-respecting metasearch engine. Search the internet without being tracked.", "icon": "/assets/img/app-icons/searxng.png", "author": "SearXNG", "dockerImage": "searxng:latest", "repoUrl": "https://github.com/searxng/searxng", "category": "data", "tier": "recommended" }, - { "id": "nostr-rs-relay", "title": "Nostr Relay", "version": "0.9.0", "description": "Your own Nostr relay. Store events locally, relay for friends, publish over Tor.", "icon": "/assets/img/app-icons/nostr-rs-relay.svg", "author": "scsiblade", "dockerImage": "nostr-rs-relay:0.9.0", "repoUrl": "https://sr.ht/~gheartsfield/nostr-rs-relay/", "category": "nostr" }, - { "id": "fedimint", "title": "Fedimint", "version": "0.10.0", "description": "Federated Bitcoin mint. Private, scalable Bitcoin through federated guardians.", "icon": "/assets/img/app-icons/fedimint.png", "author": "Fedimint", "dockerImage": "fedimintd:v0.10.0", "repoUrl": "https://github.com/fedimint/fedimint", "category": "money" }, - { "id": "ollama", "title": "Ollama", "version": "0.5.4", "description": "Run AI models locally. Llama, Mistral, and more — on your hardware, completely private.", "icon": "/assets/img/app-icons/ollama.png", "author": "Ollama", "dockerImage": "ollama:latest", "repoUrl": "https://github.com/ollama/ollama", "category": "data" }, - { "id": "nextcloud", "title": "Nextcloud", "version": "28", "description": "Your own private cloud. File sync, calendars, contacts — all on your hardware.", "icon": "/assets/img/app-icons/nextcloud.webp", "author": "Nextcloud", "dockerImage": "nextcloud:28", "repoUrl": "https://github.com/nextcloud/server", "category": "data" }, - { "id": "jellyfin", "title": "Jellyfin", "version": "10.8.13", "description": "Free media server. Stream your movies, music, and photos to any device.", "icon": "/assets/img/app-icons/jellyfin.webp", "author": "Jellyfin", "dockerImage": "jellyfin:10.8.13", "repoUrl": "https://github.com/jellyfin/jellyfin", "category": "data" }, - { "id": "immich", "title": "Immich", "version": "1.90.0", "description": "High-performance photo and video backup. Mobile-first with ML features.", "icon": "/assets/img/app-icons/immich.png", "author": "Immich", "dockerImage": "immich-server:release", "repoUrl": "https://github.com/immich-app/immich", "category": "data" }, - { "id": "homeassistant", "title": "Home Assistant", "version": "2024.1", "description": "Open-source home automation. Control smart home devices privately.", "icon": "/assets/img/app-icons/homeassistant.png", "author": "Home Assistant", "dockerImage": "home-assistant:2024.1", "repoUrl": "https://github.com/home-assistant/core", "category": "home" }, - { "id": "grafana", "title": "Grafana", "version": "10.2.0", "description": "Analytics and monitoring platform. Dashboards for your node metrics.", "icon": "/assets/img/app-icons/grafana.png", "author": "Grafana Labs", "dockerImage": "grafana:10.2.0", "repoUrl": "https://github.com/grafana/grafana", "category": "data", "tier": "recommended" }, - { "id": "tailscale", "title": "Tailscale", "version": "1.78.0", "description": "Zero-config VPN. Secure remote access with WireGuard mesh networking.", "icon": "/assets/img/app-icons/tailscale.webp", "author": "Tailscale", "dockerImage": "tailscale:stable", "repoUrl": "https://github.com/tailscale/tailscale", "category": "networking", "tier": "recommended" }, - { "id": "penpot", "title": "Penpot", "version": "2.4", "description": "Open-source design platform. Self-hosted alternative to Figma.", "icon": "/assets/img/app-icons/penpot.webp", "author": "Penpot", "dockerImage": "penpot-frontend:2.4", "repoUrl": "https://github.com/penpot/penpot", "category": "data" }, - { "id": "photoprism", "title": "PhotoPrism", "version": "240915", "description": "AI-powered photo management with facial recognition, privately.", "icon": "/assets/img/app-icons/photoprism.svg", "author": "PhotoPrism", "dockerImage": "photoprism:240915", "repoUrl": "https://github.com/photoprism/photoprism", "category": "data" }, - { "id": "uptime-kuma", "title": "Uptime Kuma", "version": "1.23.0", "description": "Self-hosted uptime monitoring. Track HTTP, TCP, DNS, and more.", "icon": "/assets/img/app-icons/uptime-kuma.webp", "author": "Uptime Kuma", "dockerImage": "uptime-kuma:1", "repoUrl": "https://github.com/louislam/uptime-kuma", "category": "data", "tier": "recommended" }, - { "id": "nostr-vpn", "title": "Nostr VPN", "version": "0.3.7", "description": "Tailscale-style mesh VPN with Nostr control plane.", "icon": "/assets/img/app-icons/nostr-vpn.svg", "author": "Martti Malmi", "dockerImage": "nostr-vpn:v0.3.7", "repoUrl": "https://github.com/mmalmi/nostr-vpn", "category": "networking" }, - { "id": "fips", "title": "FIPS", "version": "0.1.0", "description": "Free Internetworking Peering System. Self-organizing encrypted mesh.", "icon": "/assets/img/app-icons/fips.svg", "author": "Jim Corgan", "dockerImage": "fips:v0.1.0", "repoUrl": "https://github.com/jmcorgan/fips", "category": "networking" }, - { "id": "routstr", "title": "Routstr", "version": "0.4.3", "description": "Decentralized AI inference proxy. Pay-per-request with Cashu ecash.", "icon": "/assets/img/app-icons/routstr.svg", "author": "Routstr", "dockerImage": "routstr:v0.4.3", "repoUrl": "https://github.com/routstr/routstr-core", "category": "community" }, - { "id": "dwn", "title": "Decentralized Web Node", "version": "0.4.0", "description": "Own your data with DID-based access control. Sync across devices.", "icon": "/assets/img/app-icons/dwn.svg", "author": "TBD", "dockerImage": "dwn-server:main", "repoUrl": "https://github.com/TBD54566975/dwn-server", "category": "data" }, - { "id": "cryptpad", "title": "CryptPad", "version": "2024.12.0", "description": "End-to-end encrypted documents and collaboration. Zero-knowledge.", "icon": "/assets/img/app-icons/cryptpad.webp", "author": "XWiki SAS", "dockerImage": "cryptpad:2024.12.0", "repoUrl": "https://github.com/cryptpad/cryptpad", "category": "data" }, - { "id": "nostrudel", "title": "noStrudel", "version": "0.40.0", "description": "Feature-rich Nostr web client.", "icon": "/assets/img/app-icons/nostrudel.svg", "author": "hzrd149", "dockerImage": "", "repoUrl": "https://github.com/hzrd149/nostrudel", "webUrl": "https://nostrudel.ninja", "category": "nostr" }, - { "id": "nwnn", "title": "Next Web News Network", "version": "1.0.0", "description": "Decentralized news aggregator.", "icon": "/assets/img/app-icons/nwnn.png", "author": "L484", "dockerImage": "", "webUrl": "https://nwnn.l484.com", "category": "l484" }, - { "id": "484-kitchen", "title": "484 Kitchen", "version": "1.0.0", "description": "K484 application platform.", "icon": "/assets/img/app-icons/484-kitchen.png", "author": "L484", "dockerImage": "", "webUrl": "https://484.kitchen", "category": "l484" }, - { "id": "call-the-operator", "title": "Call the Operator", "version": "1.0.0", "description": "Escape the Matrix.", "icon": "/assets/img/app-icons/call-the-operator.png", "author": "TX1138", "dockerImage": "", "webUrl": "https://cta.tx1138.com", "category": "l484" }, - { "id": "arch-presentation", "title": "Arch Presentation", "version": "1.0.0", "description": "The Future of Decentralized Infrastructure.", "icon": "/assets/img/app-icons/arch-presentation.png", "author": "L484", "dockerImage": "", "webUrl": "https://present.l484.com", "category": "l484" }, - { "id": "syntropy-institute", "title": "Syntropy Institute", "version": "1.0.0", "description": "Medicine Reimagined.", "icon": "/assets/img/app-icons/syntropy-institute.png", "author": "Syntropy Institute", "dockerImage": "", "webUrl": "https://syntropy.institute", "category": "l484" }, - { "id": "t-zero", "title": "T-0", "version": "1.0.0", "description": "Documentary series exploring decentralization.", "icon": "/assets/img/app-icons/t-zero.png", "author": "T-0", "dockerImage": "", "webUrl": "https://teeminuszero.net", "category": "l484" } + { + "id": "bitcoin-knots", + "title": "Bitcoin Knots", + "version": "28.1.0", + "description": "Run a full Bitcoin node. Validate and relay blocks and transactions on the Bitcoin network.", + "icon": "/assets/img/app-icons/bitcoin-knots.webp", + "author": "Bitcoin Knots", + "dockerImage": "bitcoin-knots:latest", + "repoUrl": "https://github.com/bitcoinknots/bitcoin", + "category": "money", + "tier": "core" + }, + { + "id": "lnd", + "title": "LND", + "version": "0.18.4", + "description": "Lightning Network Daemon. Fast and cheap Bitcoin payments through the Lightning Network.", + "icon": "/assets/img/app-icons/lnd.svg", + "author": "Lightning Labs", + "dockerImage": "lnd:v0.18.4-beta", + "repoUrl": "https://github.com/lightningnetwork/lnd", + "category": "money", + "tier": "core" + }, + { + "id": "btcpay-server", + "title": "BTCPay Server", + "version": "1.13.7", + "description": "Self-hosted Bitcoin payment processor. Accept Bitcoin payments without intermediaries or fees.", + "icon": "/assets/img/app-icons/btcpay-server.png", + "author": "BTCPay Server Foundation", + "dockerImage": "btcpayserver:1.13.7", + "repoUrl": "https://github.com/btcpayserver/btcpayserver", + "category": "commerce", + "tier": "core" + }, + { + "id": "mempool", + "title": "Mempool Explorer", + "version": "3.0.0", + "description": "Self-hosted Bitcoin blockchain and mempool visualizer. Monitor transactions without revealing your addresses.", + "icon": "/assets/img/app-icons/mempool.webp", + "author": "Mempool", + "dockerImage": "mempool-frontend:v3.0.0", + "repoUrl": "https://github.com/mempool/mempool", + "category": "money", + "tier": "core" + }, + { + "id": "electrumx", + "title": "ElectrumX", + "version": "1.18.0", + "description": "Electrum protocol server. Index the blockchain for fast wallet lookups, privately.", + "icon": "/assets/img/app-icons/electrumx.webp", + "author": "Luke Childs", + "dockerImage": "electrumx:v1.18.0", + "repoUrl": "https://github.com/spesmilo/electrumx", + "category": "money", + "tier": "core" + }, + { + "id": "indeedhub", + "title": "IndeeHub", + "version": "1.0.0", + "description": "Bitcoin documentary streaming with Nostr identity. Stream sovereignty content from your node.", + "icon": "/assets/img/app-icons/indeedhub.png", + "author": "IndeeHub Team", + "dockerImage": "indeedhub:1.0.0", + "repoUrl": "https://github.com/indeedhub/indeedhub", + "category": "community" + }, + { + "id": "botfights", + "title": "BotFights", + "version": "1.0.0", + "description": "Bot arena + 2-player arcade fighter with controller support.", + "icon": "/assets/img/app-icons/botfights.svg", + "author": "BotFights", + "dockerImage": "botfights:1.1.0", + "repoUrl": "https://botfights.net", + "category": "community" + }, + { + "id": "gitea", + "title": "Gitea", + "version": "1.23", + "description": "Self-hosted Git service with container registry, CI/CD, issue tracking, and package hosting.", + "icon": "/assets/img/app-icons/gitea.svg", + "author": "Gitea", + "dockerImage": "docker.io/gitea/gitea:1.23", + "repoUrl": "https://gitea.com", + "category": "development" + }, + { + "id": "filebrowser", + "title": "File Browser", + "version": "2.27.0", + "description": "Web-based file manager. Browse, upload, and manage files on your server.", + "icon": "/assets/img/app-icons/file-browser.webp", + "author": "File Browser", + "dockerImage": "filebrowser:v2.27.0", + "repoUrl": "https://github.com/filebrowser/filebrowser", + "category": "data", + "tier": "core" + }, + { + "id": "vaultwarden", + "title": "Vaultwarden", + "version": "1.30.0", + "description": "Self-hosted password vault. Bitwarden-compatible with zero-knowledge encryption.", + "icon": "/assets/img/app-icons/vaultwarden.webp", + "author": "Vaultwarden", + "dockerImage": "vaultwarden:1.30.0-alpine", + "repoUrl": "https://github.com/dani-garcia/vaultwarden", + "category": "data", + "tier": "recommended" + }, + { + "id": "searxng", + "title": "SearXNG", + "version": "2024.1.0", + "description": "Privacy-respecting metasearch engine. Search the internet without being tracked.", + "icon": "/assets/img/app-icons/searxng.png", + "author": "SearXNG", + "dockerImage": "searxng:latest", + "repoUrl": "https://github.com/searxng/searxng", + "category": "data", + "tier": "recommended" + }, + { + "id": "nostr-rs-relay", + "title": "Nostr Relay", + "version": "0.9.0", + "description": "Your own Nostr relay. Store events locally, relay for friends, publish over Tor.", + "icon": "/assets/img/app-icons/nostr-rs-relay.svg", + "author": "scsiblade", + "dockerImage": "nostr-rs-relay:0.9.0", + "repoUrl": "https://sr.ht/~gheartsfield/nostr-rs-relay/", + "category": "nostr" + }, + { + "id": "fedimint", + "title": "Fedimint", + "version": "0.10.0", + "description": "Federated Bitcoin mint. Private, scalable Bitcoin through federated guardians.", + "icon": "/assets/img/app-icons/fedimint.png", + "author": "Fedimint", + "dockerImage": "fedimintd:v0.10.0", + "repoUrl": "https://github.com/fedimint/fedimint", + "category": "money" + }, + { + "id": "ollama", + "title": "Ollama", + "version": "0.5.4", + "description": "Run AI models locally. Llama, Mistral, and more \u2014 on your hardware, completely private.", + "icon": "/assets/img/app-icons/ollama.png", + "author": "Ollama", + "dockerImage": "ollama:latest", + "repoUrl": "https://github.com/ollama/ollama", + "category": "data" + }, + { + "id": "nextcloud", + "title": "Nextcloud", + "version": "28", + "description": "Your own private cloud. File sync, calendars, contacts \u2014 all on your hardware.", + "icon": "/assets/img/app-icons/nextcloud.webp", + "author": "Nextcloud", + "dockerImage": "nextcloud:28", + "repoUrl": "https://github.com/nextcloud/server", + "category": "data" + }, + { + "id": "jellyfin", + "title": "Jellyfin", + "version": "10.8.13", + "description": "Free media server. Stream your movies, music, and photos to any device.", + "icon": "/assets/img/app-icons/jellyfin.webp", + "author": "Jellyfin", + "dockerImage": "jellyfin:10.8.13", + "repoUrl": "https://github.com/jellyfin/jellyfin", + "category": "data" + }, + { + "id": "immich", + "title": "Immich", + "version": "1.90.0", + "description": "High-performance photo and video backup. Mobile-first with ML features.", + "icon": "/assets/img/app-icons/immich.png", + "author": "Immich", + "dockerImage": "immich-server:release", + "repoUrl": "https://github.com/immich-app/immich", + "category": "data" + }, + { + "id": "homeassistant", + "title": "Home Assistant", + "version": "2024.1", + "description": "Open-source home automation. Control smart home devices privately.", + "icon": "/assets/img/app-icons/homeassistant.png", + "author": "Home Assistant", + "dockerImage": "home-assistant:2024.1", + "repoUrl": "https://github.com/home-assistant/core", + "category": "home" + }, + { + "id": "grafana", + "title": "Grafana", + "version": "10.2.0", + "description": "Analytics and monitoring platform. Dashboards for your node metrics.", + "icon": "/assets/img/app-icons/grafana.png", + "author": "Grafana Labs", + "dockerImage": "grafana:10.2.0", + "repoUrl": "https://github.com/grafana/grafana", + "category": "data", + "tier": "recommended" + }, + { + "id": "tailscale", + "title": "Tailscale", + "version": "1.78.0", + "description": "Zero-config VPN. Secure remote access with WireGuard mesh networking.", + "icon": "/assets/img/app-icons/tailscale.webp", + "author": "Tailscale", + "dockerImage": "tailscale:stable", + "repoUrl": "https://github.com/tailscale/tailscale", + "category": "networking", + "tier": "recommended" + }, + { + "id": "penpot", + "title": "Penpot", + "version": "2.4", + "description": "Open-source design platform. Self-hosted alternative to Figma.", + "icon": "/assets/img/app-icons/penpot.webp", + "author": "Penpot", + "dockerImage": "penpot-frontend:2.4", + "repoUrl": "https://github.com/penpot/penpot", + "category": "data" + }, + { + "id": "photoprism", + "title": "PhotoPrism", + "version": "240915", + "description": "AI-powered photo management with facial recognition, privately.", + "icon": "/assets/img/app-icons/photoprism.svg", + "author": "PhotoPrism", + "dockerImage": "photoprism:240915", + "repoUrl": "https://github.com/photoprism/photoprism", + "category": "data" + }, + { + "id": "uptime-kuma", + "title": "Uptime Kuma", + "version": "1.23.0", + "description": "Self-hosted uptime monitoring. Track HTTP, TCP, DNS, and more.", + "icon": "/assets/img/app-icons/uptime-kuma.webp", + "author": "Uptime Kuma", + "dockerImage": "uptime-kuma:1", + "repoUrl": "https://github.com/louislam/uptime-kuma", + "category": "data", + "tier": "recommended" + }, + { + "id": "nostr-vpn", + "title": "Nostr VPN", + "version": "0.3.7", + "description": "Tailscale-style mesh VPN with Nostr control plane.", + "icon": "/assets/img/app-icons/nostr-vpn.svg", + "author": "Martti Malmi", + "dockerImage": "nostr-vpn:v0.3.7", + "repoUrl": "https://github.com/mmalmi/nostr-vpn", + "category": "networking" + }, + { + "id": "fips", + "title": "FIPS", + "version": "0.1.0", + "description": "Free Internetworking Peering System. Self-organizing encrypted mesh.", + "icon": "/assets/img/app-icons/fips.svg", + "author": "Jim Corgan", + "dockerImage": "fips:v0.1.0", + "repoUrl": "https://github.com/jmcorgan/fips", + "category": "networking" + }, + { + "id": "routstr", + "title": "Routstr", + "version": "0.4.3", + "description": "Decentralized AI inference proxy. Pay-per-request with Cashu ecash.", + "icon": "/assets/img/app-icons/routstr.svg", + "author": "Routstr", + "dockerImage": "routstr:v0.4.3", + "repoUrl": "https://github.com/routstr/routstr-core", + "category": "community" + }, + { + "id": "dwn", + "title": "Decentralized Web Node", + "version": "0.4.0", + "description": "Own your data with DID-based access control. Sync across devices.", + "icon": "/assets/img/app-icons/dwn.svg", + "author": "TBD", + "dockerImage": "dwn-server:main", + "repoUrl": "https://github.com/TBD54566975/dwn-server", + "category": "data" + }, + { + "id": "cryptpad", + "title": "CryptPad", + "version": "2024.12.0", + "description": "End-to-end encrypted documents and collaboration. Zero-knowledge.", + "icon": "/assets/img/app-icons/cryptpad.webp", + "author": "XWiki SAS", + "dockerImage": "cryptpad:2024.12.0", + "repoUrl": "https://github.com/cryptpad/cryptpad", + "category": "data" + }, + { + "id": "nostrudel", + "title": "noStrudel", + "version": "0.40.0", + "description": "Feature-rich Nostr web client.", + "icon": "/assets/img/app-icons/nostrudel.svg", + "author": "hzrd149", + "dockerImage": "", + "repoUrl": "https://github.com/hzrd149/nostrudel", + "webUrl": "https://nostrudel.ninja", + "category": "nostr" + }, + { + "id": "nwnn", + "title": "Next Web News Network", + "version": "1.0.0", + "description": "Decentralized news aggregator.", + "icon": "/assets/img/app-icons/nwnn.png", + "author": "L484", + "dockerImage": "", + "webUrl": "https://nwnn.l484.com", + "category": "l484" + }, + { + "id": "484-kitchen", + "title": "484 Kitchen", + "version": "1.0.0", + "description": "K484 application platform.", + "icon": "/assets/img/app-icons/484-kitchen.png", + "author": "L484", + "dockerImage": "", + "webUrl": "https://484.kitchen", + "category": "l484" + }, + { + "id": "call-the-operator", + "title": "Call the Operator", + "version": "1.0.0", + "description": "Escape the Matrix.", + "icon": "/assets/img/app-icons/call-the-operator.png", + "author": "TX1138", + "dockerImage": "", + "webUrl": "https://cta.tx1138.com", + "category": "l484" + }, + { + "id": "arch-presentation", + "title": "Arch Presentation", + "version": "1.0.0", + "description": "The Future of Decentralized Infrastructure.", + "icon": "/assets/img/app-icons/arch-presentation.png", + "author": "L484", + "dockerImage": "", + "webUrl": "https://present.l484.com", + "category": "l484" + }, + { + "id": "syntropy-institute", + "title": "Syntropy Institute", + "version": "1.0.0", + "description": "Medicine Reimagined.", + "icon": "/assets/img/app-icons/syntropy-institute.png", + "author": "Syntropy Institute", + "dockerImage": "", + "webUrl": "https://syntropy.institute", + "category": "l484" + }, + { + "id": "t-zero", + "title": "T-0", + "version": "1.0.0", + "description": "Documentary series exploring decentralization.", + "icon": "/assets/img/app-icons/t-zero.png", + "author": "T-0", + "dockerImage": "", + "webUrl": "https://teeminuszero.net", + "category": "l484" + } + ], + "registries": [ + "23.182.128.160:3000/lfg2025", + "git.tx1138.com/lfg2025" ] -} +} \ No newline at end of file diff --git a/neode-ui/src/views/Discover.vue b/neode-ui/src/views/Discover.vue index 9231a745..c51cadf9 100644 --- a/neode-ui/src/views/Discover.vue +++ b/neode-ui/src/views/Discover.vue @@ -506,7 +506,12 @@ async function installCommunityApp(app: MarketplaceApp) { router.push('/dashboard/apps').catch(() => {}) try { installingApps.set(app.id, { ...installingApps.get(app.id)!, status: 'downloading', progress: 20, message: 'Downloading container image...' }) - await rpcClient.call({ method: 'package.install', params: { id: app.id, dockerImage: app.dockerImage, version: app.version }, timeout: 180000 }) + // Pass containerConfig from catalog if available (allows dynamic apps without hardcoded backend config) + const installParams: Record = { id: app.id, dockerImage: app.dockerImage, version: app.version } + if ((app as Record).containerConfig) { + installParams.containerConfig = (app as Record).containerConfig + } + await rpcClient.call({ method: 'package.install', params: installParams, timeout: 180000 }) installingApps.set(app.id, { ...installingApps.get(app.id)!, status: 'installing', progress: 60, message: 'Starting container...' }) startInstallPolling(app.id, 'Initializing application...') } catch (err) { diff --git a/neode-ui/src/views/appSession/MobileGamepad.vue b/neode-ui/src/views/appSession/MobileGamepad.vue index 63aac66a..f4901729 100644 --- a/neode-ui/src/views/appSession/MobileGamepad.vue +++ b/neode-ui/src/views/appSession/MobileGamepad.vue @@ -78,6 +78,13 @@
+