From 3e3dfafdfce9f4ccf4b0653c6918793a8593fde9 Mon Sep 17 00:00:00 2001 From: Dorian Date: Fri, 3 Apr 2026 05:06:45 +0100 Subject: [PATCH] feat: add Nostr VPN, FIPS, Routstr apps with status UIs Add three new marketplace apps: - Routstr (v0.4.3): Decentralized AI inference proxy with Cashu payments - Nostr VPN (v0.3.4): Mesh VPN with Nostr signaling + WireGuard tunnels - FIPS (v0.1.0): Self-organizing encrypted mesh network Includes status UI dashboards for headless apps (nostr-vpn-ui, fips-ui) with usage instructions, node identity display, and container logs. Nostr identity injected via env vars for all three apps. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../archipelago/src/api/rpc/package/config.rs | 4 +- .../src/api/rpc/package/install.rs | 6 + docker/fips-ui/Dockerfile | 10 + docker/fips-ui/index.html | 236 ++++++++++++++++++ docker/fips-ui/nginx.conf | 11 + docker/nostr-vpn-ui/Dockerfile | 10 + docker/nostr-vpn-ui/index.html | 232 +++++++++++++++++ docker/nostr-vpn-ui/nginx.conf | 11 + image-recipe/configs/nginx-archipelago.conf | 24 +- .../archipelago-https-app-proxies.conf | 22 +- .../src/views/appSession/appSessionConfig.ts | 4 + neode-ui/src/views/apps/appsConfig.ts | 1 + scripts/image-versions.sh | 2 + 13 files changed, 563 insertions(+), 10 deletions(-) create mode 100644 docker/fips-ui/Dockerfile create mode 100644 docker/fips-ui/index.html create mode 100644 docker/fips-ui/nginx.conf create mode 100644 docker/nostr-vpn-ui/Dockerfile create mode 100644 docker/nostr-vpn-ui/index.html create mode 100644 docker/nostr-vpn-ui/nginx.conf diff --git a/core/archipelago/src/api/rpc/package/config.rs b/core/archipelago/src/api/rpc/package/config.rs index 50f529c3..109202a6 100644 --- a/core/archipelago/src/api/rpc/package/config.rs +++ b/core/archipelago/src/api/rpc/package/config.rs @@ -359,8 +359,8 @@ pub(super) fn all_container_names(package_id: &str) -> Vec { "penpot-postgres".into(), "penpot-valkey".into(), "penpot-backend".into(), "penpot-exporter".into(), "penpot-frontend".into(), ], - "nostr-vpn" => vec!["nostr-vpn".into(), "archy-nostr-vpn".into()], - "fips" => vec!["fips".into(), "archy-fips".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], diff --git a/core/archipelago/src/api/rpc/package/install.rs b/core/archipelago/src/api/rpc/package/install.rs index 4f80305b..956439b8 100644 --- a/core/archipelago/src/api/rpc/package/install.rs +++ b/core/archipelago/src/api/rpc/package/install.rs @@ -861,6 +861,12 @@ autopilot.active=false\n", "electrumx" | "electrs" | "mempool-electrs" => { vec![("archy-electrs-ui", "/opt/archipelago/docker/electrs-ui", "electrs-ui")] } + "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/docker/fips-ui/Dockerfile b/docker/fips-ui/Dockerfile new file mode 100644 index 00000000..933ab7d8 --- /dev/null +++ b/docker/fips-ui/Dockerfile @@ -0,0 +1,10 @@ +FROM 80.71.235.15:3000/archipelago/nginx:1.27.4-alpine +COPY index.html /usr/share/nginx/html/ +COPY nginx.conf /etc/nginx/conf.d/default.conf +RUN sed -i 's/^user nginx;/user root;/' /etc/nginx/nginx.conf && \ + mkdir -p /var/cache/nginx/client_temp /var/cache/nginx/proxy_temp \ + /var/cache/nginx/fastcgi_temp /var/cache/nginx/uwsgi_temp \ + /var/cache/nginx/scgi_temp +EXPOSE 8202 +ENTRYPOINT [] +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/fips-ui/index.html b/docker/fips-ui/index.html new file mode 100644 index 00000000..52f9b7e5 --- /dev/null +++ b/docker/fips-ui/index.html @@ -0,0 +1,236 @@ + + + + + + + FIPS - Archipelago + + + +
+
+ +
+ +
+
+
+ + + +
+
+
+

FIPS

+ v0.1.0 +
+

Free Internetworking Peering System

+
+
+
+
+

Status

+

Checking...

+
+
+
+
+ + +
+

What is FIPS?

+

+ FIPS is a self-organizing encrypted mesh network. Each node gets a + secp256k1 keypair (same as Nostr/Bitcoin) that serves as its identity. + Nodes discover each other, negotiate encryption using the Noise protocol, + and route traffic without any central authority. A virtual network interface (fips0) + lets unmodified applications — SSH, web browsers, anything — communicate transparently over the mesh. + Think of it as a new internet layer, built on cryptographic identity. +

+
+
+
+ +
+
+

Zero Config

+

Self-organizing mesh

+
+
+
+
+ +
+
+

End-to-End Encrypted

+

Noise IK + XK protocols

+
+
+
+
+ +
+
+

Multi-Transport

+

UDP, TCP, Tor, BLE

+
+
+
+
+ + +
+

Node Identity

+

Your node's Nostr public key doubles as its FIPS mesh address. Share with peers to connect.

+
+
+
Nostr Public Key (npub)
+
+ Loading... + +
+
+
+
Mesh Ports
+
+ UDP 2121 / TCP 8443 + +
+
+
+
+ + +
+

How to Use

+
+
+
1
+
+

Install FIPS on your other devices

+

Download fips from GitHub. Build with cargo build --release (requires Rust 1.85+).

+
+
+
+
2
+
+

Configure peers in fips.yaml

+

Edit /etc/fips/fips.yaml on each device. Add your Archipelago node's IP and port as a peer. The node's npub above is its identity on the mesh.

+
+
+
+
3
+
+

Start the daemon and connect

+

Run fips --config /etc/fips/fips.yaml. A fips0 virtual interface appears. Use fipsctl show peers to see connected nodes. You can now SSH, browse, or run any IP app over the encrypted mesh using .fips DNS names.

+
+
+
+
+ + +
+

Container Logs

+
+ Fetching logs... +
+
+
+ + + + diff --git a/docker/fips-ui/nginx.conf b/docker/fips-ui/nginx.conf new file mode 100644 index 00000000..7990b509 --- /dev/null +++ b/docker/fips-ui/nginx.conf @@ -0,0 +1,11 @@ +server { + listen 8202; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/docker/nostr-vpn-ui/Dockerfile b/docker/nostr-vpn-ui/Dockerfile new file mode 100644 index 00000000..37252c9f --- /dev/null +++ b/docker/nostr-vpn-ui/Dockerfile @@ -0,0 +1,10 @@ +FROM 80.71.235.15:3000/archipelago/nginx:1.27.4-alpine +COPY index.html /usr/share/nginx/html/ +COPY nginx.conf /etc/nginx/conf.d/default.conf +RUN sed -i 's/^user nginx;/user root;/' /etc/nginx/nginx.conf && \ + mkdir -p /var/cache/nginx/client_temp /var/cache/nginx/proxy_temp \ + /var/cache/nginx/fastcgi_temp /var/cache/nginx/uwsgi_temp \ + /var/cache/nginx/scgi_temp +EXPOSE 8201 +ENTRYPOINT [] +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/nostr-vpn-ui/index.html b/docker/nostr-vpn-ui/index.html new file mode 100644 index 00000000..18c64dac --- /dev/null +++ b/docker/nostr-vpn-ui/index.html @@ -0,0 +1,232 @@ + + + + + + + Nostr VPN - Archipelago + + + +
+
+ +
+ +
+
+
+ + + +
+
+
+

Nostr VPN

+ v0.3.4 +
+

Decentralized mesh VPN with Nostr signaling

+
+
+
+
+

Status

+

Checking...

+
+
+
+
+ + +
+

What is Nostr VPN?

+

+ Nostr VPN creates a private mesh network between your devices using WireGuard tunnels. + Unlike traditional VPNs, there is no central server. Peers discover each other and exchange encryption keys over + Nostr relays, making the network censorship-resistant and self-sovereign. + Think of it as Tailscale, but decentralized — your node's Nostr identity is your network identity. +

+
+
+
+ +
+
+

No Central Server

+

Fully peer-to-peer mesh

+
+
+
+
+ +
+
+

WireGuard Tunnels

+

Fast, modern encryption

+
+
+
+
+ +
+
+

NAT Traversal

+

Works behind firewalls

+
+
+
+
+ + +
+

Node Identity

+

Your node's Nostr public key is used as its network identity. Share it with peers to connect.

+
+
Nostr Public Key (npub)
+
+ Loading... + +
+
+
+
VPN Listen Port
+
+ 51820/udp + +
+
+
+ + +
+

How to Use

+
+
+
1
+
+

Install the Nostr VPN client on your device

+

Download nvpn from GitHub Releases on your laptop, phone, or other devices you want to connect.

+
+
+
+
2
+
+

Create or join a network

+

Run nvpn network create on this node to create a new network, or join an existing one with an invite code. Each network gets a unique ID shared between members.

+
+
+
+
3
+
+

Connect your devices

+

Run nvpn start --daemon --connect on each device. Peers discover each other automatically over Nostr relays and establish direct WireGuard tunnels. Your devices are now privately connected.

+
+
+
+
+ + +
+

Container Logs

+
+ Fetching logs... +
+
+
+ + + + diff --git a/docker/nostr-vpn-ui/nginx.conf b/docker/nostr-vpn-ui/nginx.conf new file mode 100644 index 00000000..67d8cb19 --- /dev/null +++ b/docker/nostr-vpn-ui/nginx.conf @@ -0,0 +1,11 @@ +server { + listen 8201; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/image-recipe/configs/nginx-archipelago.conf b/image-recipe/configs/nginx-archipelago.conf index d32c762a..b5669a47 100644 --- a/image-recipe/configs/nginx-archipelago.conf +++ b/image-recipe/configs/nginx-archipelago.conf @@ -530,12 +530,28 @@ server { sub_filter '' ''; } location /app/nostr-vpn/ { - default_type application/json; - return 503 '{"error":{"code":"NO_WEB_UI","message":"Nostr VPN is managed via CLI"}}'; + proxy_pass http://127.0.0.1:8201/; + 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/fips/ { - default_type application/json; - return 503 '{"error":{"code":"NO_WEB_UI","message":"FIPS is managed via CLI"}}'; + 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/; diff --git a/image-recipe/configs/snippets/archipelago-https-app-proxies.conf b/image-recipe/configs/snippets/archipelago-https-app-proxies.conf index b1a96acf..4215c64e 100644 --- a/image-recipe/configs/snippets/archipelago-https-app-proxies.conf +++ b/image-recipe/configs/snippets/archipelago-https-app-proxies.conf @@ -212,12 +212,26 @@ location /app/routstr/ { sub_filter '' ''; } location /app/nostr-vpn/ { - default_type application/json; - return 503 '{"error":{"code":"NO_WEB_UI","message":"Nostr VPN is managed via CLI"}}'; + proxy_pass http://127.0.0.1:8201/; + 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/fips/ { - default_type application/json; - return 503 '{"error":{"code":"NO_WEB_UI","message":"FIPS is managed via CLI"}}'; + 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/; diff --git a/neode-ui/src/views/appSession/appSessionConfig.ts b/neode-ui/src/views/appSession/appSessionConfig.ts index a2d3f02e..c3609db0 100644 --- a/neode-ui/src/views/appSession/appSessionConfig.ts +++ b/neode-ui/src/views/appSession/appSessionConfig.ts @@ -38,6 +38,8 @@ export const APP_PORTS: Record = { 'fedimintd': 8175, 'fedimint-gateway': 8176, 'nostr-rs-relay': 18081, + 'nostr-vpn': 8201, + 'fips': 8202, 'routstr': 8200, 'indeedhub': 7777, 'dwn': 3100, @@ -85,6 +87,8 @@ export const HTTPS_PROXY_PATHS: Record = { 'grafana': '/app/grafana/', 'indeedhub': '/app/indeedhub/', 'routstr': '/app/routstr/', + 'nostr-vpn': '/app/nostr-vpn/', + 'fips': '/app/fips/', } /** External HTTPS apps -- always loaded directly */ diff --git a/neode-ui/src/views/apps/appsConfig.ts b/neode-ui/src/views/apps/appsConfig.ts index d6018325..27b835ee 100644 --- a/neode-ui/src/views/apps/appsConfig.ts +++ b/neode-ui/src/views/apps/appsConfig.ts @@ -12,6 +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', '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', diff --git a/scripts/image-versions.sh b/scripts/image-versions.sh index ad28a3e0..d937a3ee 100644 --- a/scripts/image-versions.sh +++ b/scripts/image-versions.sh @@ -64,7 +64,9 @@ VALKEY_IMAGE="$ARCHY_REGISTRY/valkey:8.1.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"