Archipelago System Architecture

Complete interactive map of every layer, protocol, container, and data path · Click anything to expand

34
Containers
260+
RPC Methods
9
Protocols
LUKS2
Encryption
Rootless
Podman
8 GB+
Recommended RAM
Kiosk Display Layer 8 · Physical
X11 + Chromium fullscreen on VT7, showing the web UI directly on connected monitor
The TV/monitor screen you see when the box is plugged in. No keyboard needed — it just shows the dashboard.
Web UI (Vue.js SPA) Layer 7 · Application
Vue 3 + TypeScript + Pinia frontend served by nginx, communicates via JSON-RPC and WebSocket
The dashboard you use in your browser to manage everything — apps, Bitcoin, settings.
Rust Backend Layer 6 · Service
Archipelago binary on 127.0.0.1:5678 — RPC server, auth, session management, container orchestration, Tor control
The brain of the system. Handles login, manages containers, talks to Bitcoin, and coordinates everything.
Container Layer (Podman Rootless) Layer 5 · Isolation
34 containers on archy-net (internal DNS) and bridge networks, managed by rootless Podman
Each app runs in its own sandbox. If one app crashes or gets hacked, the others are unaffected.
Network Layer Layer 4 · Network
Nginx reverse proxy (80/443), Tailscale mesh VPN, Tor hidden services, UFW firewall
Controls what traffic goes where. One front door (nginx) routes requests to the right app. Tor makes you reachable without exposing your IP.
Encryption Layer Layer 3 · Security
LUKS2 full-disk encryption on /var/lib/archipelago with auto-detected cipher (AES-XTS or ChaCha20-Adiantum)
All your Bitcoin data, passwords, and app data are encrypted. If someone steals the hard drive, they get nothing.
Operating System Layer 2 · OS
Debian 12 (Bookworm) minimal — systemd services, x86_64/ARM64, debootstrap custom base
The operating system. Debian is rock-solid Linux used by servers worldwide. We strip it down to just what's needed.
Hardware / Boot Layer 1 · Physical
UEFI + BIOS dual-boot, GPT partitions, USB flash installer, auto-detect disk + network + CPU features
The physical computer. Flash a USB stick, boot from it, and the installer sets everything up automatically.

Container Dependency Chain

// Startup order: Databases → Core → Services → Apps // Health monitor restarts in this order too mempool-db ───┐ btcpay-db ───┤ ├──→ bitcoin-knots ──→ electrumx ┌────┴────┬──────────┬──────────┐ lnd fedimint mempool-api nbxplorer fedi-gw mempool-web btcpay └──→ lnd-ui // IndeedHub stack (independent) ih-postgres ──→ ih-api ──→ indeedhub ih-redis ──→ ih-api ih-minio ──→ ih-api // Penpot stack (independent) penpot-pg ──→ penpot-be ──→ penpot-fe penpot-vk ──→ penpot-be ──→ penpot-exp // Tier 3: All independent — start in any order filebrowser grafana homeassist jellyfin photoprism vaultwarden nextcloud searxng uptime-kuma ollama onlyoffice nginx-pm portainer

System Resources

Hardware Requirements
Minimum RAM4 GB
Recommended RAM8 GB+ (core stack uses ~8–10 GB)
Minimum Disk32 GB SSD
Recommended Disk1 TB+ NVMe SSD
CPUx86_64 or ARM64, 4+ cores recommended
NetworkEthernet recommended (WiFi supported)
TargetsHP ProDesk, Intel NUC, any standard PC
Memory Budget (all containers)
Bitcoin Knots2 GB (1 GB low-memory mode)
ElectrumX1 GB
LND512 MB
BTCPay + DB1.5 GB (1 GB + 512 MB)
Mempool stack1.3 GB (512+256+512 MB)
Fedimint + GW1 GB (512+512 MB)
Ollama (AI)4 GB (1 GB low-memory)
All other apps128–1024 MB each
Total allocated~20 GB (not all run simultaneously)
Disk Usage by Component
Bitcoin blockchain (full)~600 GB
Bitcoin (pruned)~550 MB
ElectrumX index~50 GB
LND channels + wallet~1 GB
Databases (all)~2–10 GB
Container images~15 GB
Ollama models1–50 GB (varies)
Media (Jellyfin/Photos)User-determined
Network Ports (External)
80 / 443Nginx → Web UI, app proxies
8333Bitcoin P2P (node discovery)
9735Lightning P2P (payment routing)
50001Electrum protocol (wallet queries)
22SSH (admin access)
Internal only8332 (RPC), 10009 (gRPC), 8080 (REST), 8999, 4080, 3000, 3001, 8082–8096, 9000…
Container Security Defaults
Capabilities--cap-drop=ALL then add only needed: CHOWN, FOWNER, SETUID, SETGID, DAC_OVERRIDE. Some get NET_RAW (LND), NET_BIND_SERVICE (Vaultwarden, nginx-pm, LND-UI).
Privileges--security-opt=no-new-privileges on all containers
Health checksAll containers: --health-interval=120s --health-timeout=5s --health-retries=3
Low-memory modeAuto-detected: Bitcoin 2G→1G, PhotoPrism 1G→512M, OnlyOffice 2G→1G, Ollama 4G→1G
Disk modeAuto: if disk <1TB → Bitcoin prune=550, dbcache=512M. If ≥1TB → full txindex, dbcache=4G
RPC methods260+ registered across 20+ namespaces (auth, seed, package, bitcoin, lnd, identity, tor, nostr, mesh, federation, dwn, system, monitoring…)
1. Hardware / Boot Physical
UEFI + BIOS dual-boot installer, GPT partition table, auto-detect hardware
You flash a USB drive, plug it in, and the computer installs itself. Works on both old and new machines.

Partition Layout

  • 1 MB — BIOS boot (for older machines without UEFI)
  • 512 MB — EFI System Partition (UEFI boot files)
  • 30 GB — Root filesystem (Debian OS, binaries, Podman storage)
  • Remaining — LUKS2 encrypted → /var/lib/archipelago (all user data)

Installer Features

  • Auto-detects target disk (largest available, prefers NVMe)
  • Auto-detects AES-NI CPU support for encryption cipher selection
  • Debootstrap minimal Debian 12 (no bloat)
  • Configures GRUB for both UEFI and legacy BIOS
  • Creates archipelago user (UID 1000) with Podman subuid/subgid mapping

Hardware Targets

  • x86_64: HP ProDesk, Intel NUC, any standard PC
  • ARM64: Planned but not primary target yet
  • Minimum: 4 cores, 8GB RAM, 256GB disk
  • Recommended: 4+ cores, 16GB RAM, 1TB+ disk (for full Bitcoin node)
2. Operating System — Debian 12 OS
Minimal Debian Bookworm with systemd, custom kernel parameters, hardened services
The foundation. Debian is one of the most stable and trusted Linux versions. We remove everything unnecessary.

Key Packages

  • podman — Rootless container runtime (replaces Docker)
  • nginx — Reverse proxy (front door for all web traffic)
  • tor — Privacy network daemon
  • tailscale — Mesh VPN for remote access
  • chromium — Kiosk browser for local display
  • cryptsetup — LUKS disk encryption
  • xorg — Display server for kiosk mode

Kernel Tuning

  • net.ipv4.ip_unprivileged_port_start=80 — lets rootless Podman bind ports 80+
  • vm.overcommit_memory=1 — for Redis/Valkey container requirements
  • User namespaces enabled for rootless containers

Users

  • archipelago (UID 1000) — main user, owns all containers and data
  • root — only for Tor management, LUKS, and boot services
3. Encryption — LUKS2 Security
Full-disk encryption on all user data with auto-detected hardware-accelerated ciphers
Your Bitcoin wallet, passwords, photos — everything is scrambled. Without the key, the data is just noise.

Cipher Selection (auto-detected at install)

  • With AES-NI: aes-xts-plain64 (AES-256-XTS) — hardware-accelerated, fastest option
  • Without AES-NI: xchacha20,aes-adiantum-plain64 (ChaCha20-Adiantum) — fast on any CPU

Key Derivation

  • PBKDF: Argon2id (memory-hard, GPU-resistant)
  • Key size: 512 bits
  • Key file: /root/.luks-archipelago.key (4KB random, auto-generated)

What's Encrypted

  • Bitcoin blockchain data
  • LND wallet & Lightning channels
  • All database volumes (PostgreSQL, MariaDB)
  • All app data directories
  • Secrets (RPC passwords, macaroons, API keys)
  • Tor hidden service keys

What's NOT Encrypted

  • Root filesystem (OS binaries, system config) — no sensitive data here
  • EFI/boot partitions (must be readable to start)
4. Network Layer Network
Nginx reverse proxy, Tailscale mesh VPN, Tor hidden services, UFW firewall
One front door (nginx) for all traffic. Tor lets people reach you without knowing your real address. Tailscale lets YOU reach the box from anywhere.

Nginx Reverse Proxy

  • Listens on :80 (HTTP) and :443 (HTTPS with self-signed cert)
  • Serves Vue.js SPA at /
  • Proxies backend at /rpc/v1, /ws, /health
  • Proxies each app at /app/{name}/
  • Rate limits: auth (3/s), RPC (20/s), P2P (10/s)
  • Security headers: CSP, HSTS, X-Frame-Options, Permissions-Policy
  • Injects nostr-provider.js into all app iframes

Tor

  • System-level Tor daemon (not containerized)
  • SOCKS5 proxy at 127.0.0.1:9050
  • Hidden services for: web UI, LND, BTCPay, Mempool, Fedimint
  • Backend manages services via privileged helper script
  • Containers connect via host.containers.internal:9050

Tailscale

  • Mesh VPN — access your node from anywhere via encrypted tunnel
  • Runs as system service or container
  • Provides stable IP (e.g., 100.x.x.x) regardless of network

Firewall (UFW)

  • DEFAULT_FORWARD_POLICY=ACCEPT (required for rootless Podman)
  • Allow: 22 (SSH), 80 (HTTP), 443 (HTTPS), 8333 (Bitcoin P2P), 9735 (Lightning P2P)
5. Rust Backend Service
Archipelago binary — JSON-RPC server, auth, RBAC, container management, Tor control, DID identity
The control center. Every button you click in the dashboard sends a message here, and it makes things happen.

Bind

  • 127.0.0.1:5678 — localhost only, nginx handles external access

Endpoints

  • POST /rpc/v1 — JSON-RPC 2.0 (all commands)
  • WS /ws — WebSocket (live updates, container status, logs)
  • GET /health — Health check (no auth)
  • /archipelago/ — P2P node messaging
  • /content — Content sharing (via Tor)
  • /dwn — Decentralized Web Node protocol

Key RPC Methods

  • auth.* — login, TOTP, password change, onboarding
  • seed.* — generate, verify, restore wallet seeds
  • package.* — container CRUD (create, start, stop, remove)
  • node.* — DID identity, signing, backups
  • app.* — marketplace, app config

Systemd Service

  • Type: notify (signals readiness to systemd)
  • Watchdog: 300s (must ping every 120s or gets killed)
  • MemoryMax: 4GB
  • Crash recovery on startup (detects unclean shutdown, restarts containers)
  • Periodic container state snapshots for recovery
6. Container Layer — Rootless Podman Isolation
34 containers, custom bridge network (archy-net), UID mapping, security caps, memory limits
Apps run in sealed boxes. They can only see what we let them see, use only the memory we allow, and can't mess with each other.

Rootless Podman

  • All containers run as user archipelago (UID 1000)
  • No root access required — even if a container is compromised, it can't escalate to root
  • Subuid/subgid: archipelago:100000:65536
  • Socket: /run/user/1000/podman/podman.sock

Networks

  • archy-net (custom bridge) — Bitcoin stack + services, containers can reach each other by name (DNS)
  • bridge (default) — standalone apps, port-mapped only
  • host — Tailscale only (needs full network access)

Security Defaults (per container)

  • --cap-drop=ALL then add only what's needed (least privilege)
  • --security-opt=no-new-privileges
  • Memory limits (128MB to 4GB depending on app)
  • Health checks with auto-restart on failure
  • Read-only root filesystem where possible (--read-only)

UID Mapping (inside container → host)

  • root (0) → host UID 100000
  • postgres (70) → host UID 100070
  • bitcoin (101) → host UID 100101
  • grafana (472) → host UID 100472
  • mariadb (999) → host UID 100999

Registry

  • Private registry at git.tx1138.com/lfg2025/
  • HTTPS (self-hosted Gitea)
  • All images pre-pulled into registry; nodes pull on first boot
7. Web UI — Vue.js SPA Application
Vue 3 + TypeScript + Pinia + Vite, served as static files by nginx
The website that runs on the box. Open it in any browser on your network to manage everything.

Tech Stack

  • Framework: Vue 3 with <script setup lang="ts">
  • State: Pinia stores
  • Bundler: Vite 7
  • Styling: Global CSS with Tailwind utility classes in style.css

Communication

  • JSON-RPC: All commands go through rpc-client.tsPOST /rpc/v1
  • WebSocket: Real-time container status, logs, events via /ws
  • CSRF: Token in cookie + X-CSRF-Token header
  • Session: HttpOnly cookie, SameSite=Lax
  • Retry: Auto-retry 3x with exponential backoff on 502/503
  • Timeout: 15s default (configurable per call)

Key Views

  • / — Dashboard (system status, apps)
  • /kiosk — Kiosk mode (public, no auth)
  • /kiosk-recovery — Fallback with IP + QR code
  • /marketplace — App installer
  • /settings — System configuration

App Embedding

  • Apps open as iframes via /app/{name}/ proxy paths
  • Each iframe gets nostr-provider.js injected for identity
8. Kiosk Display Physical
X11 + Chromium in kiosk mode on VT7, auto-start, crash recovery
Plug in a monitor and the dashboard appears fullscreen. No login, no desktop, just your node. Press Ctrl+Alt+F1 for a terminal.

How It Works

  • X11 server (Xorg) starts on Virtual Terminal 7
  • Chromium launches in --kiosk --app=http://localhost/kiosk mode
  • No address bar, no tabs, no right-click — just the dashboard
  • Cursor hidden after 3 seconds of inactivity
  • Screen blanking disabled

Resource Limits

  • --disable-gpu — software rendering only
  • --renderer-process-limit=1 — single renderer process
  • --js-flags="--max-old-space-size=128" — 128MB JS heap max
  • --disable-metrics-reporting — no telemetry to Google
  • --enable-low-end-device-mode — reduce animations and compositing

Controls

  • Ctrl+Alt+F7 — switch to kiosk
  • Ctrl+Alt+F1 — switch to terminal
  • sudo archipelago-kiosk enable|disable|toggle|status
Tier 0 — Databases
archy-mempool-dbarchy-net
MariaDB database storing Bitcoin mempool transaction data for the Mempool block explorer.
A database that remembers pending Bitcoin transactions so the block explorer can show them.
mariadb:11.4.10
No exposed ports (internal only)
UID100999:100999 (mariadb user)
Memory512 MB
Healthmariadb -uroot -e 'SELECT 1'
Data/var/lib/archipelago/mysql-mempool
Databasemempool (user: mempool)
DepsNone
Needed bymempool-api
archy-btcpay-dbarchy-net
PostgreSQL database for BTCPay Server and NBXplorer, storing invoices, transactions, and merchant data.
Stores your payment invoices and transaction history for the Bitcoin payment processor.
postgres:15.17
No exposed ports (internal only)
UID100070:100070 (postgres user)
Memory512 MB
Healthpg_isready -U postgres
Data/var/lib/archipelago/postgres-btcpay
Databasesbtcpay, nbxplorer
Needed bynbxplorer btcpay
indeedhub-postgresarchy-net
PostgreSQL database for IndeedHub social platform, storing posts, user profiles, and relay data.
The database that stores all the social media posts and user data for IndeedHub.
postgres:16.13-alpine
No exposed ports (internal only)
Needed byindeedhub-api
indeedhub-redisarchy-net
Redis in-memory cache for IndeedHub, handling sessions, job queues, and real-time data.
A fast temporary memory store so IndeedHub pages load quickly and background tasks run smoothly.
redis:7.4.8-alpine
No exposed ports
Needed byindeedhub-api
penpot-postgresbridge
PostgreSQL database for Penpot design tool, storing projects, layers, and design assets.
Stores all the design projects and files for the Penpot design tool.
postgres:15
No exposed ports
Memory256 MB
Needed bypenpot-backend
penpot-valkeybridge
Valkey (Redis fork) cache for Penpot, handling sessions and real-time collaboration sync.
Fast memory cache that makes Penpot's real-time collaboration work smoothly.
valkey:8.1
No exposed ports
Memory128 MB
Needed bypenpot-backend
immich_postgresbridge
PostgreSQL with vector extensions for Immich photo AI/search. Optional — only if Immich is installed.
Database for the photo manager. Has special AI search features for finding photos by what's in them.
immich-postgres:14-vectorchord (optional)
Memory256 MB
Needed byimmich
immich_redisbridge
Valkey cache for Immich job queue (photo processing, thumbnail generation).
Manages the queue of photos waiting to be processed and thumbnailed.
valkey:8.1.6 (optional)
Memory128 MB
Needed byimmich
Tier 1 — Core Bitcoin Infrastructure
bitcoin-knotsarchy-net
Full Bitcoin node (Knots variant). Validates every transaction and block independently. The root dependency for the entire Bitcoin stack.
Your own copy of the entire Bitcoin network. Nobody can lie to you about your balance because you verify everything yourself.
bitcoin-knots:latest
Ports: 8332 8333 28332 28333
Port 8332JSON-RPC API (how other apps talk to Bitcoin)
Port 8333P2P network (connects to other Bitcoin nodes worldwide)
Port 28332ZMQ block notifications (instant alert when new block arrives)
Port 28333ZMQ transaction notifications (instant alert for new transactions)
Memory2 GB (1 GB on low-memory systems)
Healthbitcoin-cli getblockchaininfo
Data/var/lib/archipelago/bitcoin (~500GB full, ~550MB pruned)
Disk modeAuto: prune if <1TB, full txindex if ≥1TB
RPC AuthHMAC-SHA256 salted hash (no plaintext password in config)
TorRoutes P2P through Tor SOCKS5 for privacy
CapsCHOWN, FOWNER, SETUID, SETGID, DAC_OVERRIDE
DepsNone — ROOT DEPENDENCY
Needed byelectrumx lnd mempool nbxplorer fedimint
electrumxarchy-net
Electrum protocol server. Indexes the blockchain by address so wallets can look up balances instantly without scanning every block.
An index for Bitcoin. Like a book's table of contents — instead of reading every page to find your info, you jump straight to it.
electrumx:v1.18.0
Ports: 50001 8000
Port 50001Electrum protocol (wallet connections)
Port 8000Health check / status API
Memory1 GB
Data/var/lib/archipelago/electrumx
ProtocolElectrum JSON-RPC over TCP
Depsbitcoin-knots (reads blockchain via RPC)
Needed bymempool-api
Tier 2 — Services (depend on Bitcoin core)
lndarchy-net
Lightning Network Daemon. Enables instant, low-fee Bitcoin payments through payment channels.
Lets you send and receive Bitcoin instantly (instead of waiting 10+ minutes for a block). Like a tab at a bar — settle up later on-chain.
lnd:v0.18.4-beta
Ports: 9735 10009 8080
Port 9735Lightning P2P (connects to other Lightning nodes)
Port 10009gRPC API (admin operations, authenticated with macaroons)
Port 8080REST API (simpler HTTP interface to LND)
Memory512 MB
Data/var/lib/archipelago/lnd (wallet, channels, macaroons)
AuthMacaroon tokens (read-only for queries, admin for mutations)
TorActive with stream isolation (each connection uses different circuit)
CapsCHOWN, FOWNER, SETUID, SETGID, DAC_OVERRIDE, NET_RAW
Depsbitcoin-knots
Needed byfedi-gateway (LND mode) lnd-ui
mempool-apiarchy-net
Mempool.space backend API. Provides blockchain analytics, fee estimates, and transaction tracking.
The engine behind the block explorer. Shows you what's happening on the Bitcoin network in real time.
mempool-backend:v3.0.0
Ports: 8999
Memory512 MB
Data/var/lib/archipelago/mempool
Depsbitcoin-knots electrumx mempool-db
Needed bymempool-web
archy-mempool-webarchy-net
Mempool.space frontend. The visual block explorer with real-time mempool visualization and fee graphs.
Your personal mempool.space — watch Bitcoin blocks being mined, see fee rates, track your transactions.
mempool-frontend:v3.0.0
Ports: 4080
Memory256 MB
Depsmempool-api
Nginx path/app/mempool/
archy-nbxplorerarchy-net
NBXplorer blockchain scanner. Watches Bitcoin addresses for BTCPay and notifies when payments arrive.
Watches Bitcoin for incoming payments and tells BTCPay Server when money arrives for your invoices.
nbxplorer:2.6.0
Ports: 32838
Memory512 MB
Data/var/lib/archipelago/nbxplorer
Depsbitcoin-knots btcpay-db
Needed bybtcpay
btcpay-serverarchy-net
Self-hosted Bitcoin payment processor. Accept Bitcoin payments with invoices, checkout pages, and POS.
Your own payment terminal for Bitcoin. Create invoices, get paid, no middleman taking a cut.
btcpayserver:2.3.9
Ports: 23000
Memory1 GB
Data/var/lib/archipelago/btcpay
Depsnbxplorer btcpay-db
Nginx path/app/btcpay/
TorHas its own .onion address for receiving payments privately
fedimintarchy-net
Federated mint daemon. Enables community-run Bitcoin custody with threshold signing and e-cash tokens.
A way for a group of trusted people to collectively hold Bitcoin. No single person can steal the funds — you need a majority to approve.
fedimintd:v0.10.0
Ports: 8173 8174 8175
Port 8173P2P (federation member communication)
Port 8174API / WebSocket (client connections)
Port 8175Web UI (guardian dashboard)
Memory512 MB
Data/var/lib/archipelago/fedimint
Depsbitcoin-knots
Needed byfedi-gateway
fedimint-gatewayarchy-net
Lightning bridge for Fedimint. Connects the federation to the Lightning Network for instant payments.
Connects your community mint to Lightning so federation members can send/receive instant payments.
gatewayd:v0.10.0
Ports: 8176
Memory512 MB
Data/var/lib/archipelago/fedimint-gateway
ModeAuto-detect: uses LND if available, otherwise built-in LDK Lightning
Depsbitcoin-knots fedimint
immich_serverbridge
Self-hosted Google Photos replacement with AI-powered search, face detection, and automatic organization.
Like Google Photos but on your own hardware. Your photos never leave your box. AI finds faces and objects locally.
immich-server:release (optional)
Ports: 2283
Depsimmich_postgres immich_redis
Nginx path/app/immich/
Tier 3 — Applications (independent, no cross-dependencies)
archy-bitcoin-uiarchy-net
Custom Bitcoin node dashboard showing sync status, peer connections, and blockchain info.
A pretty dashboard for your Bitcoin node. See how synced you are, how many peers you have, block height.
bitcoin-ui:latest
Ports: 8334
Memory128 MB
Nginx path/app/bitcoin-ui/
archy-lnd-uiarchy-net
Custom Lightning dashboard showing channels, balances, routing stats, and payment history.
Dashboard for your Lightning node. See your channels, balance, and recent payments at a glance.
lnd-ui:latest
Ports: 8081
Memory128 MB
Nginx path/app/lnd/
archy-electrs-uihost
ElectrumX status dashboard showing sync progress, connected clients, and index health.
Shows whether the Electrum index is synced and healthy. How far behind it is, how many wallets are connected.
electrs-ui:latest
Ports: 50002
Memory128 MB
Networkhost (needs direct access to localhost:50001)
Nginx path/app/electrumx/
homeassistantbridge
Open-source home automation platform. Control lights, sensors, cameras, and IoT devices from one dashboard.
Smart home control center. Turn lights on, check sensors, automate your house — all locally, no cloud needed.
home-assistant:2024.1
Ports: 8123
Memory512 MB
Data/var/lib/archipelago/home-assistant
Nginx path/app/homeassistant/
grafanabridge
Monitoring and visualization platform. Dashboards for system metrics, Bitcoin stats, and container health.
Beautiful graphs and charts showing how your system is doing. CPU, memory, Bitcoin sync, everything visualized.
grafana:10.2.0
Ports: 3000
UID100472:100472 (grafana user)
Memory256 MB
Data/var/lib/archipelago/grafana
Read-onlyYes (tmpfs for /tmp, /run)
Nginx path/app/grafana/
uptime-kumabridge
Self-hosted uptime monitor. Pings your services and alerts you when something goes down.
Watches all your apps and sends alerts if anything stops working. Like a security guard for your services.
uptime-kuma:1
Ports: 3001
Memory256 MB
Data/var/lib/archipelago/uptime-kuma
jellyfinbridge
Self-hosted media server. Stream your movies, TV shows, and music from your own hardware.
Your own Netflix. Put movies on the box, watch them on any device. No subscription, no limits.
jellyfin:10.8.13
Ports: 8096
Memory1 GB
Data/var/lib/archipelago/jellyfin/{config,cache}
Transcodetmpfs /tmp (256MB, rw,exec)
Nginx path/app/jellyfin/
photoprismbridge
AI-powered photo management. Automatic face recognition, location mapping, and smart search.
Photo organizer that uses AI to tag and sort your pictures. Find photos by searching "sunset" or "cat."
photoprism:240915
Ports: 2342
Memory1 GB (512 MB on low-memory)
Data/var/lib/archipelago/photoprism
vaultwardenbridge
Bitwarden-compatible password manager. Store all your passwords encrypted, sync across devices.
Your personal password safe. Store every password securely and auto-fill them on your phone and computer.
vaultwarden:1.30.0-alpine
Ports: 8082
Memory256 MB
Data/var/lib/archipelago/vaultwarden
CapsCHOWN, SETUID, SETGID, NET_BIND_SERVICE
nextcloudbridge
Self-hosted file sync and collaboration platform. Dropbox/Google Drive replacement with calendar, contacts, and office docs.
Your own Dropbox. Sync files, share documents, manage calendar and contacts — all on your own hardware.
nextcloud:29
Ports: 8085
Memory1 GB
Data/var/lib/archipelago/nextcloud
searxngbridge
Privacy-respecting metasearch engine. Searches Google, Bing, DuckDuckGo and others without tracking you.
Private search engine. Searches the web without anyone tracking what you look for.
searxng:latest
Ports: 8888
Memory512 MB
Data/var/lib/archipelago/searxng
Read-onlyYes (tmpfs for /tmp, /run)
onlyofficebridge
Self-hosted document editor. Edit Word, Excel, and PowerPoint files collaboratively in the browser.
Like Google Docs but on your own box. Edit spreadsheets and documents with others in real time.
onlyoffice:latest
Ports: 9980
Memory2 GB (1 GB on low-memory)
ollamabridge
Local AI model runner. Run LLMs (like Llama, Mistral) entirely on your hardware, no cloud needed.
ChatGPT on your own box. Talk to AI privately — nothing you say leaves your machine.
ollama:latest (optional)
Ports: 11434
Memory4 GB (1 GB on low-memory)
Data/var/lib/archipelago/ollama
Read-onlyYes (tmpfs for /tmp, /run)
ProtocolREST API at :11434 (OpenAI-compatible)
filebrowserbridge
Web-based file manager. Browse, upload, and download files through the browser.
A file explorer in your browser. Upload, download, and manage files on the box without SSH.
filebrowser:v2.27.0
Ports: 8083
Memory256 MB
Data/var/lib/archipelago/filebrowser (served), filebrowser-data (DB)
Read-onlyYes
Max upload10 GB (nginx limit)
nginx-proxy-managerbridge
GUI for managing nginx proxy rules and SSL certificates. Point-and-click reverse proxy configuration.
A visual tool for routing web traffic. Point domains to services and manage HTTPS certificates with clicks, not config files.
nginx-proxy-manager:latest
Ports: 81 8084 8443
Port 81Admin dashboard
Port 8084HTTP proxy
Port 8443HTTPS proxy
Memory256 MB
portainerbridge
Container management UI. Visual dashboard for Podman containers — start, stop, inspect, view logs.
Visual control panel for all your containers. See what's running, restart things, read logs — no terminal needed.
portainer:latest
Ports: 9000
Memory256 MB
SocketPodman socket mounted as Docker socket
IndeedHub Stack — Nostr-based Social Platform
indeedhub-minioarchy-net
S3-compatible object storage for IndeedHub media files (images, videos, attachments).
File storage for IndeedHub. When someone posts an image, it lives here.
minio:RELEASE.2024-11-07
Needed byindeedhub-api
indeedhub-apiarchy-net
IndeedHub backend API. Handles Nostr events, user profiles, media uploads, and relay communication.
The engine behind IndeedHub. Processes posts, handles user accounts, talks to Nostr relays.
indeedhub-api (custom build)
Depspostgres redis minio
Needed byindeedhub
indeedhub-ffmpegarchy-net
Video transcoding worker for IndeedHub. Converts uploaded videos to web-friendly formats.
Converts videos so they play smoothly in the browser. Like a video format translator.
indeedhub-ffmpeg (custom build)
indeedhub-relayarchy-net
Nostr relay for IndeedHub. Stores and distributes Nostr events (posts, follows, reactions).
A message board that stores Nostr posts. Other Nostr apps can connect here to read and post.
indeedhub-relay (custom build)
indeedhubarchy-net
IndeedHub web frontend. Nostr-based social media client with feeds, profiles, messaging, and media.
The social media app itself. Post, follow people, send messages, share media — all on the Nostr protocol.
indeedhub-frontend (custom build)
Ports: 7777
Nginx path/app/indeedhub/
WebSocketYes (for real-time updates)
Depsindeedhub-api
Penpot Stack — Design Tool
penpot-backendbridge
Penpot application server. Handles design data, real-time collaboration, and file storage.
The engine behind the design tool. Saves your designs and lets multiple people edit at the same time.
penpot-backend:2.4
Memory512 MB
Depspenpot-postgres penpot-valkey
penpot-exporterbridge
Renders Penpot designs to PDF, SVG, and image formats for export.
Turns your designs into downloadable files — PDFs, images, SVGs.
penpot-exporter:2.4
Memory256 MB
Depspenpot-backend
penpot-frontendbridge
Penpot web UI. Open-source Figma alternative with vector editing, prototyping, and collaboration.
Your own Figma. Design interfaces, create prototypes, collaborate — completely self-hosted.
penpot-frontend:2.4
Ports: 9001
Memory256 MB
Nginx path/app/penpot/
Depspenpot-backend
archy-net (internal DNS, Bitcoin stack)
bridge (standalone, port-mapped)
host (direct network access)
Dimmed = optional / not always installed
JSON-RPC 2.0
Primary protocol between the web UI and the Rust backend. All commands are RPC calls.
Like texting the backend: you send a message ("please start this app"), it texts back ("done" or "error").
Endpoint: POST /rpc/v1
Format: {"jsonrpc":"2.0","method":"package.start","params":{"id":"bitcoin-knots"},"id":1}
Auth: Session cookie + CSRF token header
Timeout: 15s default
Retry: 3 attempts with exponential backoff on 502/503
Rate limit: 20 req/s (burst 40)
Used by: Vue.js frontend → Rust backend
WebSocket
Real-time bidirectional channel for live updates — container status changes, logs, events.
An open phone line between your browser and the server. Instead of asking "any updates?" every second, the server just tells you when something changes.
Endpoint: WS /ws (HTTP upgrade)
Auth: Session cookie
Read timeout: 86,400s (24 hours)
Events: Container state changes, log streams, system alerts
Used by: Vue.js frontend ↔ Rust backend
Bitcoin RPC (JSON-RPC 1.0)
How apps talk to the Bitcoin node. Authenticated with username + HMAC-hashed password.
The language apps use to ask Bitcoin questions: "what's the current block?" or "send this transaction."
Endpoint: bitcoin-knots:8332 (inside archy-net)
Auth: HTTP Basic with rpcauth hash (HMAC-SHA256, no plaintext)
Methods: getblockchaininfo, getmempoolinfo, sendrawtransaction, etc.
Timeout: 10s default, 30s for heavy ops
Used by: ElectrumX, LND, Mempool, NBXplorer, Fedimint → Bitcoin Knots
gRPC
High-performance RPC protocol used by LND for admin operations. Binary format, strongly typed.
A fast, structured way for apps to control the Lightning node. More efficient than regular HTTP for complex operations.
Endpoint: lnd:10009
Auth: Macaroon tokens (read-only for queries, admin for mutations)
TLS: Self-signed certificate (auto-generated)
Methods: OpenChannel, SendPayment, GetInfo, ListChannels, etc.
Used by: Fedimint Gateway, LND UI → LND
Electrum Protocol
Lightweight protocol for wallet address lookups. JSON-RPC over raw TCP sockets.
How Bitcoin wallets check their balance without downloading the entire blockchain. Ask "what transactions touched this address?" and get an instant answer.
Endpoint: electrumx:50001 (TCP)
Format: Newline-delimited JSON-RPC
Methods: blockchain.scripthash.get_balance, blockchain.transaction.get, etc.
Used by: Wallets (Sparrow, Electrum), Mempool API → ElectrumX
ZMQ (ZeroMQ)
Publish-subscribe messaging from Bitcoin node. Instant notifications for new blocks and transactions.
A broadcasting system. When a new Bitcoin block is found, Bitcoin instantly shouts it out and everyone listening hears immediately.
Endpoints:
tcp://bitcoin-knots:28332 — New block hashes (hashblock)
tcp://bitcoin-knots:28333 — New raw transactions (rawtx)
Pattern: PUB/SUB (publisher/subscriber)
Subscribers: LND, Mempool, ElectrumX
Tor (SOCKS5 + Hidden Services)
Privacy layer. Routes Bitcoin P2P through onion routing, exposes services as .onion addresses.
Like sending a letter through 3 random post offices so nobody knows where it came from. Also lets people reach your node without knowing your real IP.
SOCKS5 proxy: 127.0.0.1:9050
Container access: host.containers.internal:9050
Hidden services: Web UI, LND, BTCPay, Mempool, Fedimint
Managed by: System Tor daemon + tor-helper.sh (privileged helper)
Used by: Bitcoin P2P, LND P2P, BTCPay invoices
Nostr (NIP-01)
Decentralized social protocol. WebSocket-based relay communication for events (posts, follows, messages).
A social media protocol where no company controls the network. Your posts live on relays, and you own your identity with a cryptographic key.
Transport: WebSocket (WSS)
Format: JSON events signed with secp256k1 keys
Relay: IndeedHub relay (local), configurable external relays
Integration: nostr-provider.js injected into all app iframes
Identity: DID-based, linked to node Ed25519 keypair
DWN (Decentralized Web Node)
W3C protocol for storing encrypted data and messages in a decentralized way. Identity-linked storage.
A personal data vault. Apps can store data here that only you control. Like a safety deposit box that follows you across the internet.
Endpoint: /dwn (proxied through nginx)
Auth: Per-record DID-based permissions
Reachable via: Tor hidden service
Used for: Encrypted backups, cross-node messaging, app data sync
2
DID Methods
26+
Identity RPCs
W3C 2.0
VC Spec
Ed25519
Primary Key
DWN
Data Store
Dual Key
Ed25519 + secp256k1
Applications Layer 5 · UI
Vue.js views for identity management, credential issuance, DWN dashboard, and quick actions
The screens where you manage your digital identity, issue credentials, and control your personal data store.

Key Views

  • Web5Identities.vue — Create/manage identities (Personal, Business, Anonymous purposes)
  • Web5CredentialsSummary.vue — View issued/held credentials with status badges
  • Web5DWN.vue — DWN status, protocol registration, message browser
  • Web5QuickActions.vue — Copy DID, publish to DHT, trigger sync

Data Types (TypeScript)

  • ManagedIdentity — id, name, purpose, did, pubkey, nostr_pubkey, profile
  • VCData — id, issuer, subject, type, claims, status (active/revoked/expired)
  • DwnStatusData — running, sync_status, message_count, registered_protocols
Verifiable Credentials (W3C VC 2.0) Layer 4 · Trust
Issue, verify, revoke, and present credentials with Ed25519Signature2020 proofs — W3C VC Data Model 2.0
Digital certificates that prove things about you — signed by one identity, held by another, verified by anyone. Like a digitally signed diploma.

Three-Party Model

  • Issuer: Creates and signs the credential (any managed identity)
  • Holder: Stores credentials, creates Verifiable Presentations
  • Verifier: Checks signature + expiration + revocation status

Credential Structure

  • @context: W3C Credentials v2 + Ed25519 signature suite
  • type: ["VerifiableCredential", "CustomType"]
  • issuer: did:key or did:dht
  • credentialSubject: { id: did, claims: {...} }
  • proof: Ed25519Signature2020 with verification method reference
  • credentialStatus: CredentialStatusList2021 for revocation

Verifiable Presentations

  • Bundle one or more VCs with holder's own signature
  • Proof purpose: authentication (vs. assertionMethod for VCs)
  • Selective disclosure — present only relevant credentials

RPC Methods

  • identity.issue-credential — Issue from any managed identity
  • identity.verify-credential — Verify by credential ID
  • identity.list-credentials — List with optional filtering

Storage

  • /var/lib/archipelago/credentials/store.json
Decentralized Web Node (DWN) Layer 3 · Storage
Personal data store with protocol-governed records, peer sync over Tor, and DID-based authorization
Your personal database that YOU own. Apps ask permission to read/write data. Syncs with trusted peers automatically over Tor.

Records Interface

  • Records.Write — Store a message (UUID-based record_id)
  • Records.Read — Retrieve by record_id
  • Records.Query — Filter by protocol, schema, author, date range
  • Records.Delete — Remove record

Protocol Definitions

  • Declarative rule sets governing data structure and access permissions
  • types: Define allowed dataFormats and optional schema URIs
  • structure: Hierarchical — records can have child records (post → comment)
  • $actions: Who can create/read/update/delete (anyone, author, recipient)
  • Registered via dwn.register-protocol RPC, enforced automatically

Peer Sync

  • Bidirectional sync with trusted peers over Tor SOCKS5 proxy (127.0.0.1:9050)
  • Deduplication by record_id, batched (200 messages per sync)
  • 30s per-peer timeout, 90s total timeout
  • State persisted to /var/lib/archipelago/dwn/sync_state.json
  • Triggered manually or via background task

HTTP API

  • Endpoint: POST /dwn (proxied through nginx)
  • Reachable remotely via Tor hidden service

RPC Methods (8)

  • dwn.status — Running state, sync status, message count
  • dwn.sync — Trigger background sync with trusted peers
  • dwn.register-protocol / dwn.list-protocols / dwn.remove-protocol
  • dwn.write-message / dwn.query-messages / dwn.read-message / dwn.delete-message

Storage

  • Messages: /var/lib/archipelago/dwn/messages/{record_id}.json
  • Protocols: /var/lib/archipelago/dwn/protocols/{protocol_uri}.json
Decentralized Identifiers (DIDs) Layer 2 · Identity
W3C DID Core 1.0 — did:key (primary, offline-capable) and did:dht (discoverability via BitTorrent Mainline DHT)
Your self-sovereign digital identity. No company issues it, no platform controls it. You prove who you are with cryptographic keys.

did:key (Primary Method)

  • Self-contained — no external resolution, works fully offline
  • Format: did:key:z6Mk... (multicodec Ed25519 in base58btc)
  • Instant, zero-cost, no network dependency
  • Used for: VCs, federation trust, backup encryption, DWN signing
  • Cannot be rotated — key is the identifier

did:dht (Discovery Method)

  • Publishes DID Document to BitTorrent Mainline DHT via BEP-44 signed mutable items
  • Format: did:dht:z... (z-base-32 encoded Ed25519 pubkey)
  • Globally discoverable without any centralized registry
  • DID Document encoded as DNS Resource Records in DNS packet
  • Supports key rotation (increment sequence number, republish)
  • 1-hour TTL cache for performance
  • Replaced did:ion (Bitcoin-anchored) — simpler, no full node required

DID Document (W3C Core 1.0)

  • verificationMethod: Ed25519VerificationKey2020 + derived X25519KeyAgreementKey2020
  • authentication, assertionMethod, capabilityInvocation, capabilityDelegation
  • keyAgreement: X25519 (derived from Ed25519 via Curve25519)
  • Optional EcdsaSecp256k1VerificationKey2019 for Nostr interop
  • Service endpoints: DWN URL, Nostr relay list

Multi-Identity Manager

  • Users create multiple identities with purpose tags: Personal, Business, Anonymous
  • One default identity (marked with star in UI)
  • Each identity: Ed25519 key + optional Nostr secp256k1 key + optional NIP-01 profile
  • Stored as JSON in /var/lib/archipelago/identities/{id}.json

Identity RPC Methods (26+)

  • identity.create / .list / .get / .delete / .set-default
  • identity.sign / .verify — Ed25519 message signing
  • identity.resolve-did / .verify-did-document
  • identity.create-dht-did / .resolve-dht-did / .refresh-dht-did
  • identity.create-nostr-key / .nostr-sign
  • identity.nostr-encrypt-nip04 / .nostr-decrypt-nip04
  • identity.nostr-encrypt-nip44 / .nostr-decrypt-nip44
  • identity.update-profile / .resolve-remote-did
Cryptographic Keys Layer 1 · Foundation
Dual key architecture — Ed25519 for Web5/DIDs + secp256k1 for Bitcoin/Nostr, both derived from BIP-39 master seed
Two types of cryptographic keys derived from one master seed. One for identity (Web5), one for money and social (Bitcoin/Nostr).

Ed25519 (Web5 & Identity)

  • W3C DIDs, Verifiable Credentials (Ed25519Signature2020)
  • DWN message signing and authorization
  • Federation peer authentication and trust
  • Backup encryption (via derived X25519 key agreement)
  • Storage: /var/lib/archipelago/identity/node_key (32 bytes raw)

secp256k1 (Bitcoin & Nostr)

  • Nostr event signing (NIP-01), encrypted DMs (NIP-04, NIP-44)
  • Lightning Network node identity
  • Social presence and discovery (NIP-05, kind 30078)
  • Format: hex pubkey + Nostr npub (NIP-19 bech32)

Key Derivation

  • Single BIP-39 master seed (12 or 24 word mnemonic)
  • Deterministic derivation of both Ed25519 and secp256k1 keys
  • All keys recoverable from seed phrase alone
  • Seed generated at first boot, stored on LUKS-encrypted partition

Rust Dependencies

  • ed25519-dalek 2.2.0 — Ed25519 signatures
  • curve25519-dalek 4.1.3 — X25519 key agreement (Ed25519 → X25519 conversion)
  • nostr-sdk 0.44 — secp256k1 signing, NIP-04/44 encryption
  • mainline 2 — BitTorrent Mainline DHT client (did:dht)
  • zbase32 0.1 — z-base-32 encoding for DID identifiers

Specification Status

Web5 was initiated by TBD (Block/Jack Dorsey) and shut down November 2024. Open-source components were donated to the Decentralized Identity Foundation (DIF). The W3C specs (DIDs, VCs) are independent standards with broad industry adoption. Archipelago implements these W3C standards directly with a custom DWN — not dependent on TBD's SDK.

ComponentSpecStatusArchipelago
DID Core 1.0W3C RecommendationStabledid:key + did:dht, full DID Document generation
VC Data Model 2.0W3C Recommendation (May 2025)StableIssue/verify/revoke with Ed25519Signature2020
DWNDIF DraftDraftCustom Records interface, protocol management, Tor sync
did:dhtNear v1.0ActiveMainline DHT publishing via mainline crate
did:ion (Sidetree)DIF 1.0AbandonedNot implemented — requires Bitcoin + IPFS full nodes
Presentation Exchange 2.0DIF RatifiedStableVerifiable Presentations with holder proof

Architecture Decision Records

ADRDecisionRationale
ADR-002did:key as primary DID methodSelf-contained, offline-capable, zero-cost, aligns with sovereignty
ADR-008Dual key architecture (Ed25519 + secp256k1)Ed25519 for W3C/Web5, secp256k1 for Bitcoin/Lightning/Nostr ecosystems
ADR-011Custom DWN, not full W3C spec complianceTBD shut down, DWN spec stalled. Federation + Nostr relays prioritized for peer sync
1
BIOS / UEFI → GRUB Bootloader
Firmware loads GRUB from EFI partition or BIOS boot sector. GRUB loads the Linux kernel.
The computer turns on and finds the operating system to start. Works on both old and new machines.
GRUB installed for both UEFI (EFI partition) and legacy BIOS (1MB boot sector) — dual-boot compatible
2
LUKS Unlock → Mount Encrypted Partition
Cryptsetup opens the LUKS2 volume using /root/.luks-archipelago.key and mounts it at /var/lib/archipelago.
The encrypted safe is unlocked automatically (no password prompt). All your data becomes accessible.
Configured in /etc/crypttab for automatic boot-time unlock
3
systemd Starts → Network Online
systemd boots Debian, starts networking, reaches network-online.target. Tor and Tailscale start.
The operating system finishes starting up and connects to the internet.
4
Archipelago Backend Starts
archipelago.service launches the Rust binary. Runs crash recovery, starts container state snapshots, initializes JSON-RPC server on :5678.
The brain of the system wakes up. If there was a crash last time, it automatically recovers.
Type=notify, WatchdogSec=300s, MemoryMax=4G
5
First Boot: Container Creation
first-boot-containers.sh runs once (guarded by marker file). Creates all containers in tier order: databases → Bitcoin → services → apps.
On first startup only: installs all the apps. Databases first, then Bitcoin, then everything else. Takes 5-15 minutes.
ConditionPathExists=!/var/lib/archipelago/.first-boot-containers-done · Timeout: 900s
6
Nginx Ready → Web UI Accessible
Nginx serves the Vue.js SPA on :80/:443. Backend health check at /health passes.
The website is now live. Open a browser and go to the machine's IP address to see the dashboard.
7
Kiosk Starts (if enabled)
archipelago-kiosk.service waits for /health endpoint (up to 30s), then starts X11 + Chromium on VT7.
If a monitor is plugged in, the dashboard appears fullscreen automatically. No login needed for the local screen.
Polls /health 15 times at 2s intervals before launching Chromium
8
Background Services Start
Timers activate: container doctor (health repair), reconciler (spec enforcement), self-update. Tor helper watches for service config changes.
Maintenance robots start working in the background. They check on apps, fix broken ones, and keep everything updated.
archipelago-doctor.timer, archipelago-reconcile.timer, archipelago-update.timer, archipelago-tor-helper.path
LUKS2 Full-Disk Encryption
All user data on an encrypted partition. Auto-detects AES-NI for hardware acceleration, falls back to ChaCha20-Adiantum. Key derived with Argon2id (GPU-resistant).
If someone physically steals your hard drive, all they get is encrypted noise. The data is useless without the key.
Rootless Containers
All containers run as unprivileged user archipelago (UID 1000). Even a compromised container cannot escalate to root. UID remapping isolates container users from host users.
Apps run in sealed boxes without admin access. A hacked app can't take over the whole machine.
Capability Dropping
--cap-drop=ALL then add only specific capabilities needed. --security-opt=no-new-privileges prevents privilege escalation inside containers.
Each app only gets the exact permissions it needs, nothing more. Like giving a valet driver only the car key, not your house keys.
RBAC (Role-Based Access Control)
Backend uses explicit method allowlists per role. No prefix matching — each RPC method must be explicitly permitted. Session cookies are HttpOnly, SameSite=Lax.
Different users have different permissions. A viewer can't install apps, and nobody can run commands that aren't on the approved list.
Rate Limiting
Nginx rate limits: auth endpoints (3/s), RPC (20/s), P2P (10/s). Prevents brute-force attacks and API abuse.
If someone tries to guess your password by trying thousands of combinations, they get locked out after a few attempts per second.
Tor Privacy
Bitcoin P2P routes through Tor with stream isolation. Hidden services expose node without revealing IP. LND uses Tor for Lightning P2P.
Your Bitcoin node connects through the Tor privacy network. Nobody can see your real IP address or location.
Credential Management
Secrets auto-generated at first boot (CSPRNG). Stored in /var/lib/archipelago/secrets/ (mode 700). Bitcoin RPC uses HMAC-SHA256 auth hashes, never plaintext.
Passwords are randomly generated and stored securely. They never appear in config files as readable text.
Memory Limits & Health Checks
Every container has a memory limit (128MB–4GB). Health checks auto-restart failed containers. Backend has systemd watchdog (300s).
Runaway apps can't eat all the memory. Crashed apps restart automatically. The system self-heals.
Security Headers
CSP (Content Security Policy), HSTS, X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, strict Referrer-Policy, disabled camera/mic/geolocation.
The web UI tells browsers to follow strict security rules — no loading scripts from unknown sites, no accessing your camera.
Systemd Hardening
ProtectSystem=strict, MemoryDenyWriteExecute, RestrictRealtime, RestrictAddressFamilies. Backend can only write to approved paths.
The operating system restricts what the backend can do. It can only touch the files it needs to, nothing else on the system.
/var/lib/archipelago/ ← LUKS2 encrypted partition ├── bitcoin/ Bitcoin blockchain data (~500GB full, ~550MB pruned) ├── lnd/ Lightning wallet, channels, macaroons, TLS cert ├── electrumx/ Address index database ├── postgres-btcpay/ BTCPay PostgreSQL data ├── mysql-mempool/ Mempool MariaDB data ├── mempool/ Mempool backend cache ├── btcpay/ BTCPay server data, plugins ├── nbxplorer/ NBXplorer blockchain scan state ├── fedimint/ Federation data, consensus state ├── fedimint-gateway/ Gateway keys and routing table ├── home-assistant/ Smart home config, automations, database ├── grafana/ Dashboards, datasources, alerting rules ├── uptime-kuma/ Monitor definitions, status history ├── jellyfin/ Media library metadata, transcoding cache ├── photoprism/ Photo index, thumbnails, AI models ├── ollama/ Downloaded LLM models (can be multi-GB) ├── vaultwarden/ Encrypted password vault database ├── nextcloud/ Files, calendar, contacts, config ├── searxng/ Search engine settings ├── filebrowser/ Served files (user uploads) ├── filebrowser-data/ FileBrowser internal database ├── nginx-proxy-manager/ Proxy rules, Let's Encrypt certificates ├── portainer/ Portainer config and database ├── tailscale/ VPN state, node identity ├── secrets/ RPC passwords, DB passwords (mode 700) ├── identity/ Node Ed25519 keypair (DID identity) ├── identities/ User DIDs ├── tor-config/ Tor service definitions (backend-managed) ├── tor-hostnames/ .onion addresses (synced from /var/lib/tor) └── .first-boot-containers-done Marker: first boot completed /opt/archipelago/ ← Unencrypted (on root partition) ├── web-ui/ Vue.js SPA (static files served by nginx) ├── scripts/ Deploy, container, and maintenance scripts └── image-versions.sh Pinned container image versions /usr/local/bin/archipelago Rust backend binary /etc/nginx/ Nginx config (reverse proxy rules) /etc/tor/torrc Tor daemon configuration
Podman
Daemonless container engine (OCI-compatible, Docker alternative)
A tool that runs apps in isolated sandboxes. Like Docker but doesn't need a background service running as root.
Rootless
Containers run entirely in user namespace, no root privileges required
The sandboxes run without admin access. Even if someone breaks into one, they can't take over the system.
LUKS2
Linux Unified Key Setup v2 — dm-crypt disk encryption with Argon2 KDF
Industry-standard disk encryption for Linux. Scrambles the entire partition so data is unreadable without the key.
Argon2id
Memory-hard password hashing function resistant to GPU/ASIC brute-force
A way to protect passwords that requires lots of memory to crack, making it extremely expensive to brute-force even with specialized hardware.
Nginx
High-performance HTTP reverse proxy and web server
The front door of the system. All web traffic goes through nginx, which directs each request to the right app.
Reverse Proxy
Server that forwards client requests to backend services based on URL path or hostname
A traffic cop for web requests. You visit one address, and the proxy routes you to the right app behind the scenes.
JSON-RPC
Remote procedure call protocol using JSON over HTTP/TCP
A simple way for one program to ask another to do something. Send a JSON message, get a JSON reply.
WebSocket
Full-duplex TCP communication channel over HTTP upgrade
A persistent connection between browser and server. Instead of repeatedly asking "anything new?", the server pushes updates instantly.
gRPC
Google's high-performance RPC framework using Protocol Buffers over HTTP/2
A fast, structured way for programs to communicate. Used by LND because it handles many Lightning operations efficiently.
ZMQ (ZeroMQ)
Asynchronous messaging library for pub/sub and push/pull patterns
A broadcasting system. Bitcoin publishes "new block!" and every subscribed app hears it instantly.
Tor
Onion routing network for anonymous communication via encrypted relay circuits
A privacy network that bounces your traffic through multiple servers so nobody can trace it back to you.
Hidden Service (.onion)
Tor service accessible via .onion address without revealing server IP
A way to make your node reachable on the internet without revealing your IP address or location.
Tailscale
WireGuard-based mesh VPN with NAT traversal and SSO integration
A private tunnel to your node from anywhere. Like a VPN but easier — install the app on your phone, and you can access the node from a coffee shop.
Macaroon
Bearer token with embedded caveats (permissions) used by LND for API auth
A special key for LND that says exactly what you're allowed to do. A "read-only" macaroon can check balance but can't send money.
CSRF Token
Cross-Site Request Forgery prevention token sent in cookie + header
A secret code that proves your browser request is genuine and not a trick from a malicious website.
DID (Decentralized Identifier)
W3C standard for self-sovereign identity using cryptographic keypairs
Your digital identity that you own completely. Like a passport that no government issued — you prove who you are with math, not authority.
Nostr
Notes and Other Stuff Transmitted by Relays — decentralized social protocol
A social media protocol where you own your identity. No company can ban you because your account is just a cryptographic key.
Lightning Network
Bitcoin Layer 2 payment channel network for instant, low-fee transactions
A way to send Bitcoin instantly (milliseconds) for tiny fees. Works by opening "tabs" between nodes and settling on-chain later.
Fedimint
Federated Bitcoin custody protocol with threshold signing and Chaumian e-cash
A community Bitcoin bank where a group of trusted guardians hold funds together. No single person can steal — you need a majority to sign.
archy-net
Custom Podman bridge network with DNS resolution for Bitcoin-stack containers
A private network inside the box where Bitcoin apps can find each other by name. Like a local phone book for containers.
Capability (CAP)
Fine-grained Linux privilege (e.g. CAP_NET_RAW, CAP_CHOWN) instead of full root
Instead of giving an app all admin powers, we give it only the specific abilities it needs. A file manager gets "change file ownership" but not "change network settings."
Systemd
Linux init system and service manager (PID 1)
The thing that starts everything when Linux boots. Manages all services, restarts crashed ones, and enforces resource limits.
RBAC
Role-Based Access Control — permissions assigned by user role, not individually
Different users get different permissions based on their role (admin, viewer, etc). Prevents regular users from doing dangerous things.

Architecture analysis sourced from Start9Labs/start-os on GitHub (master branch). Click any layer to expand.

LXC
Container Runtime
Rust
Backend (startd)
Angular 21
Frontend
S9PK v2
Package Format
Optional
LUKS Encryption
btrfs
Filesystem
Angular 21 + Taiga UI 5UI
Three Angular apps: admin UI, setup wizard, VPN management. Patch-DB reactive sync via CBOR diffs over WebSocket.
Rust Backend (startd / startbox)Service
Single binary with 5 personalities (symlinks). Built-in reverse proxy (Axum), DNS (hickory-server), ACME, WireGuard, SOCKS5.
LXC ContainersIsolation
Two-layer model: outer LXC per service (SquashFS + OverlayFS), inner subcontainers from S9PK images. JSON-RPC over Unix sockets.
Network (built-in)Network
VHostController reverse proxy, hickory-server DNS, ACME TLS, WireGuard tunnels, SOCKS5 at 10.0.3.1:1080. No nginx/caddy.
Optional LUKS on btrfsSecurity
User chooses encrypted or unencrypted during setup. LVM with btrfs for COW snapshots enabling safe app installs.
Debian BookwormOS
Same Debian 12 base. Targets x86_64, ARM64 (aarch64), and RISC-V (riscv64).
Web UI — Angular 21 Application
Angular 21 + TypeScript + Taiga UI 5 components, served directly by the Rust backend (Axum)
The dashboard you use in your browser. Built with Angular (Google's web framework), not served by a separate web server.

Three Separate Angular Apps

  • projects/ui/ — Main admin interface
  • projects/setup-wizard/ — Initial setup flow
  • projects/start-tunnel/ — VPN management UI

State Management

  • Patch-DB: Backend pushes CBOR diffs over WebSocket
  • Frontend applies diffs and notifies observers via PatchDB.watch$()
  • Converted to Angular signals via toSignal()
  • Reactive — UI updates automatically when backend state changes

Communication

  • JSON-RPC exclusively (not REST)
  • ApiService abstract class with 100+ methods
  • i18n: 5 languages (en, es, de, fr, pl)
Rust Backend — startd (startbox) Service
Single Rust binary (multi-personality via symlinks: startd, start-cli, start-container, registrybox, tunnelbox)
The brain of the system. One binary that does everything — serves the UI, manages containers, handles networking, runs the built-in reverse proxy.

Key Components

  • Axum web server: Serves UI + JSON-RPC API (no separate web server)
  • VHostController: Built-in reverse proxy with TLS termination (no nginx/caddy)
  • Patch-DB: Custom CBOR-encoded reactive database with diff-based WebSocket sync
  • LxcManager: Container lifecycle (create, destroy, garbage collection)
  • NetController: DNS (hickory-server), SOCKS5, ACME, WiFi, WireGuard, port forwarding
  • Service Actors: Per-service state machines managing lifecycle

Binary Personalities (symlinks)

  • startd — Main daemon
  • start-cli — CLI interface
  • start-container — Runs inside LXC containers, communicates with host
  • registrybox — Package registry daemon
  • tunnelbox — WireGuard VPN tunnel daemon

Key Dependencies

  • Async: Tokio · Web: Axum 0.8 + Hyper 1.5 · TLS: tokio-rustls 0.26 + OpenSSL (vendored)
  • DNS: hickory-server · Crypto: blake3, ed25519, x25519-dalek, aes
  • TypeScript bindings: ts-rs (auto-generates TS types from Rust structs)

Systemd

  • startd.service: Type=simple, Restart=always, RestartSec=3
  • LimitNOFILE=65536
Container Layer — LXC Isolation
Linux Containers (LXC) with two-layer model: outer LXC per service + inner subcontainers from S9PK images
Each app gets its own sealed Linux environment. Unlike Docker, these are full system containers with their own init process.

Two-Layer Container Model

  • Outer LXC container: One per service. Created by Rust backend via lxc-create/destroy
  • Base rootfs: SquashFS image (/usr/lib/startos/container-runtime/rootfs.squashfs) mounted as OverlayFS
  • Inner subcontainers: Node.js container runtime inside each LXC can launch additional containers from S9PK-bundled images
  • Timeout: 30-second container creation timeout

LXC Configuration

  • User namespaces: lxc.idmap = u 0 100000 65536
  • AppArmor profile: generated with nesting allowed
  • Network: veth bridge on lxcbr0 (10.0.3.x subnet, host at 10.0.3.1)
  • OverlayFS rootfs (base read-only squashfs, writes to overlay)
  • GPU passthrough support: /dev/dri, /dev/nvidia*, /dev/kfd

Communication

  • JSON-RPC over Unix domain sockets
  • /media/startos/rpc/service.sock — Inbound (runtime listens)
  • /media/startos/rpc/host.sock — Host callbacks (effects)
Network Layer Network
Built-in reverse proxy (Axum/Hyper), DNS (hickory-server), SOCKS5, ACME (Let's Encrypt), WireGuard tunnels
No nginx or caddy — the Rust backend IS the web server, proxy, and DNS. Also manages VPN tunnels and encryption certificates.

Built-in Reverse Proxy

  • VHostController in core/src/net/vhost.rs handles all HTTP routing
  • TLS termination via tokio-rustls with SNI-based routing
  • No external proxy software (no nginx, no caddy, no traefik)
  • Virtual hosting with per-service domain assignment

DNS

  • Built-in DNS server using hickory-server (formerly trust-dns)
  • Service discovery and resolution for containers
  • mDNS via avahi-resolve-host-name for .local domains

TLS / Certificates

  • Self-signed root CA per server (NIST P-256 via OpenSSL)
  • Built-in ACME client (async-acme) for Let's Encrypt with TLS-ALPN-01 challenge
  • Certificate store managed in Patch-DB

Connectivity

  • SOCKS5 proxy: Built-in at 10.0.3.1:1080 for container outbound traffic
  • WireGuard: First-class support via wg-quick + x25519-dalek
  • Multi-gateway: Supports multiple interfaces (Ethernet, WiFi, WireGuard) with separate domain configs
  • Port forwarding: iptables-based via InterfacePortForwardController

Tor (Status: Removed in v0.4)

  • Architecture doc mentions "Tor via Arti" but Arti is absent from current Cargo.toml
  • Previous versions (0.3.x) used the C Tor daemon for hidden services
  • Likely planned for re-integration but not yet implemented in the 0.4 rewrite
Encryption — Optional LUKS on btrfs Security
Optional LUKS encryption on LVM volumes, btrfs filesystem with COW snapshots for safe installs
Disk encryption is optional (you choose during setup). Uses btrfs which can make instant copies of data for safe app updates.

Encryption (Optional)

  • User chooses encrypted or unencrypted during setup
  • LVM volume groups: STARTOS_<random> (encrypted) or STARTOS_<random>_UNENC
  • LUKS via cryptsetup luksFormat/luksOpen with password-based key
  • Default password: "password" (changed during setup)

Filesystem: btrfs

  • Copy-on-Write (COW) snapshots for safe service installs
  • cp --reflink=always for instant volume snapshots before upgrades
  • If install fails, volumes restored from snapshot automatically

Volume Layout (LVM)

  • main (8 GB) — System data, Patch-DB
  • package-data (100% remaining) — All service/app data
Operating System — Debian Bookworm OS
Debian 12 (same base as Archipelago), systemd services, x86_64 + ARM64 + RISC-V targets
Same stable Debian foundation as Archipelago. Supports more CPU architectures including RISC-V.

Platform Targets

  • x86_64: Standard PCs and servers
  • aarch64: ARM64 (Raspberry Pi 4/5, etc.)
  • riscv64: RISC-V (emerging architecture)

LXC Container Model

Two-Layer Architecture
Outer: One LXC container per service, created by the Rust backend. Base rootfs is a read-only SquashFS image mounted as OverlayFS. Inner: Node.js container runtime inside each LXC can launch subcontainers from S9PK-bundled images.
Each app gets its own sealed Linux environment with a read-only base. Any changes go to a separate overlay layer.
Communication
JSON-RPC over Unix domain sockets. /media/startos/rpc/service.sock (inbound) and host.sock (host callbacks). Services export init(), uninit(), main() via JavaScript ABI.
Apps talk to the system through socket files, not network ports. Each app implements 3 required JavaScript functions.
Isolation
User namespaces (container UID 0 → host UID 100000, range 65536). AppArmor profiles with nesting. veth bridge on lxcbr0 (10.0.3.x subnet). GPU passthrough support via manifest flag.
Container root maps to an unprivileged host user. Each container gets its own virtual network interface.
Lifecycle
LxcManager handles creation (30s timeout), garbage collection, and cleanup. Service actors manage per-service state machines. btrfs reflink snapshots before install/upgrade for atomic rollback.

S9PK Package Format (v2)

Signed Merkle Archive
Ed25519 signatures with prehashed content (SHA-512 over blake3 merkle root). Magic bytes: 0x3b 0x3b 0x02. Enables partial downloads, integrity verification of subsets, and efficient delta updates.
App packages are cryptographically signed and structured so you can verify integrity without downloading the entire thing.
Archive Contents
manifest.json (metadata) + javascript.squashfs (service logic, Node.js) + images/<arch>/*.squashfs (container filesystems per CPU architecture) + assets.squashfs (optional static assets) + icon + LICENSE.md
Each package bundles its own containers, logic code, icon, and license in one downloadable file.
Service ABI (JavaScript/Node.js)
Services implement init(), uninit(), and main() in JavaScript. The container runtime provides an Effects interface for host callbacks (dependency queries, config, health reporting).
App developers write their service logic in JavaScript. The system provides a standard API for the app to interact with the host.
JSON-RPC (Host ↔ Service)
All communication between the Rust backend and services uses JSON-RPC over Unix domain sockets.
Apps and the system talk through a structured messaging format over local socket files — fast and secure.
Transport: Unix domain sockets
Inbound: /media/startos/rpc/service.sock
Host callbacks: /media/startos/rpc/host.sock (Effects interface)
Library: rpc-toolkit (custom Rust crate)
Used by: All service containers ↔ startd
JSON-RPC (UI ↔ Backend)
The Angular frontend communicates with startd via JSON-RPC over HTTP. 100+ API methods. State sync via Patch-DB WebSocket.
The dashboard sends commands and gets responses in JSON format. Live updates stream automatically through a WebSocket.
Transport: HTTP POST (commands) + WebSocket (state sync)
State sync: Patch-DB pushes CBOR-encoded diffs over WebSocket
Frontend applies: PatchDB.watch$() → Angular signals via toSignal()
Methods: 100+ via ApiService abstract class
Patch-DB (CBOR Reactive Sync)
Custom reactive database using CBOR encoding. Backend pushes diffs over WebSocket — UI updates automatically without polling.
Instead of the UI constantly asking "what changed?", the backend pushes only what changed, in a compact binary format.
Encoding: CBOR (Concise Binary Object Representation, RFC 8949)
Sync model: Server-push diffs, not request-response
Storage: /media/startos/data/main/
Advantage: Much smaller than JSON, real-time without polling
HTTPS / TLS (Built-in)
TLS termination handled directly by the Rust backend (tokio-rustls). Self-signed root CA per server + ACME for public domains.
The backend IS the web server — no nginx or caddy needed. It handles encryption directly.
TLS library: tokio-rustls 0.26 + OpenSSL (vendored, for cert generation)
Local certs: Self-signed root CA (NIST P-256 keys)
Public certs: ACME client (async-acme) with TLS-ALPN-01 challenge
Routing: SNI-based virtual hosting via VHostController
WireGuard (VPN Tunnels)
First-class WireGuard support for remote access. Users add WireGuard configs as "gateways." Managed by tunnelbox daemon.
Built-in VPN for accessing your node from anywhere. Add a WireGuard config and get a secure tunnel.
Implementation: wg-quick + x25519-dalek (Rust)
Daemon: tunnelbox (symlink of startbox binary)
Multi-gateway: Supports multiple interfaces with separate domain configs
DNS (hickory-server)
Built-in DNS server for service discovery and resolution. Also uses mDNS (avahi) for .local domain access on LAN.
The system runs its own DNS so containers can find each other by name. Your phone finds the node via .local address.
Library: hickory-server (formerly trust-dns)
mDNS: avahi-resolve-host-name for .local domains
Container network: lxcbr0 bridge, host at 10.0.3.1

Not Implemented in StartOS

StartOS does not implement Web5 (DIDs, DWNs, or Verifiable Credentials).
Authentication uses password-based sessions and public/private key signatures.

LXC + User Namespaces
Each service in its own LXC container with UID/GID mapping (container 0 → host 100000, range 65536). AppArmor profiles with nesting. OverlayFS rootfs (base read-only).
Apps run in isolated Linux environments with their own user systems. Container root is mapped to an unprivileged host user.
Package Signing (Ed25519)
All S9PK packages signed with Ed25519 over blake3 merkle roots. Signature verified before installation. Prevents supply chain attacks.
Every app package is cryptographically signed. If someone tampers with it, the signature check fails and installation is blocked.
btrfs Snapshots
COW filesystem snapshots before every install/upgrade. If an install fails, data is atomically restored to the pre-install state.
The system takes a snapshot before every app update. If the update fails, your data is automatically rolled back.
Authentication
Password-based + session cookies. Local authcookie for CLI. Public/private key signatures for remote admin. Encrypted wire protocol during setup (public key exchange + encrypted password).
1
Preinit Script
Optional /media/startos/config/preinit.sh runs before anything else. Enables local auth cookie.
2
Load Database + SSH Keys
Patch-DB loaded from disk (CBOR format). SSH developer keys written. MOK enrollment for Secure Boot if applicable.
3
Network Controller
DNS server (hickory-server), SOCKS5 proxy, VHost reverse proxy, port forwarding, ACME client, WiFi configuration — all start together.
4
System Initialization
Mount logs to data drive, load CA certificate, set CPU governor to performance, NTP clock sync, enable zram, hardware inventory via lshw.
5
Launch Service Intranet + Services
LXC bridge network (lxcbr0) created. Database validated. Service actors start all installed services. Postinit script runs.
/media/startos/data/ ← Root data directory (optionally LUKS encrypted) ├── main/ System data, Patch-DB (8 GB LVM volume) └── package-data/ All service data (remaining disk space) ├── volumes/{pkg-id}/data/{vol}/ Per-service volume data ├── volumes/{pkg-id}/assets/{ver}/ Per-service read-only assets └── logs/{pkg-id}/ Per-service log output /usr/lib/startos/ ← System binaries and base images ├── container-runtime/rootfs.squashfs Base LXC container image └── package/ Mounted JS from S9PK inside containers /var/lib/lxc/ LXC container storage /media/startos/config/ System config (preinit.sh, postinit.sh, standby) /media/startos/backups/ Backup mount points per service

Architecture analysis sourced from getumbrel/umbrel on GitHub (master branch). Click any layer to expand.

Docker
Container Runtime
Node.js
Backend (umbreld)
React 19
Frontend
Compose
App Format
None
Disk Encryption
A/B Boot
Rugix Partitions
React 19 + Tailwind 4 + Radix UIUI
Static SPA served by umbreld's Express server. Zustand + TanStack React Query for state. tRPC for typed API. 8+ languages.
umbreld (Node.js 22 / TypeScript)Service
Single daemon on port 80: Express + tRPC API, app lifecycle via Docker Compose, file manager, backups (Kopia), terminal (node-pty).
Docker 28.5 (rootful)Containers
Each app is a Docker Compose project. Flat bridge network (10.21.0.0/16). Per-app auth proxy containers. All containers destroyed on boot.
NetworkingNetwork
No reverse proxy. Express serves on :80 directly. Optional Tor (containerized). mDNS via avahi. No TLS by default.
Debian Trixie (testing) + RugixOS
Date-pinned Debian testing. Rugix A/B root partitions for atomic OS updates with automatic rollback. /data partition persists.
Web UI — React 19 Application
React 19 + TypeScript + Vite 6 + Tailwind 4 + Radix UI, served as static SPA by umbreld's Express server
The dashboard you use in your browser. Built with React (Meta's web framework), styled with Tailwind.

Tech Stack

  • Framework: React 19 + TypeScript (strict)
  • Build: Vite 6
  • Styling: Tailwind CSS 4 + Radix UI primitives + shadcn/ui patterns
  • State: Zustand (client) + TanStack React Query v5 (server)
  • API: tRPC React Query v11 for typed RPC
  • i18n: i18next (8+ languages)
  • Animations: Framer Motion (as motion package)
  • Terminal: xterm.js for in-browser terminal
  • Charts: Recharts for data visualization
Backend — umbreld (Node.js/TypeScript) Service
Single Node.js 22 daemon handling web server, tRPC API, app lifecycle, Tor, backups, file management, and OS updates
The brain of the system. A TypeScript process that does everything — web server, app manager, backup handler.

Key Modules

  • Server: Express 4 + tRPC v11 over HTTP and WebSocket on port 80
  • Apps: Docker Compose lifecycle (install, start, stop, update, uninstall)
  • AppStore: Git-based — clones getumbrel/umbrel-apps, pulls every 5 minutes
  • User: Single-user JWT auth (bcrypt $2b$, 12 rounds) + optional TOTP 2FA
  • Files: File browser with Samba sharing, thumbnails, external storage
  • Hardware: RAID (ZFS) for Umbrel Home Pro, internal/external storage detection
  • Backups: Kopia v0.19.0 encrypted backups to external drives
  • Notifications: In-app notification system + widgets
  • Terminal: WebSocket-based terminal (node-pty + xterm.js)
  • Dbus: D-Bus interface to systemd for reboot/shutdown/hostname

State Storage

  • YAML file: umbrel.yaml — no database, just a YAML file
  • Validation: Zod schemas
  • Docker: dockerode library + execa shell calls
  • Git: isomorphic-git for app store management

Legacy Compat Layer

  • App lifecycle handled by a large bash script (app-script)
  • Shells out to: docker compose, yq, envsubst, openssl
  • Explicitly labeled "legacy" in the codebase

Systemd

  • umbrel.service: After=network-online.target docker.service
  • Restart=always, 15-minute stop timeout, StartLimitInterval=0
Container Layer — Docker (rootful) Isolation
Docker 28.5.0 (rootful, not rootless) + Docker Compose v2. Each app is a separate Compose project.
Apps run in Docker containers managed by Docker Compose. Unlike Podman, Docker runs as root — simpler but less isolated.

Docker Setup

  • Installed via official Docker install script, pinned to v28.5.0
  • Rootful (runs as root) — not rootless
  • Each app: separate Docker Compose project (--project-name <app-id>)
  • Legacy container naming: <app-id>_<service>_1 for DNS compat

Network: Flat Bridge

  • Single shared network: umbrel_main_network (10.21.0.0/16)
  • All apps share one flat network — any container can talk to any other
  • Static IPs assigned per service (defined in exports.sh)
  • No per-app network isolation

Per-App Proxy

  • Each app gets an app_proxy container (Node.js Express, getumbrel/app-proxy)
  • Handles JWT authentication for iframe embedding
  • Proxies to the actual app container on its internal port
  • UI renders apps in iframes pointing to proxy port

Boot Cleanup

  • On every startup: stops ALL containers, prunes ALL networks
  • Prevents stale state from previous versions
  • Pre-loads images from /images/ (tor, auth-server baked into ISO)
Network Layer Network
No traditional reverse proxy. umbreld serves on port 80. Per-app proxy containers. Optional Tor via container.
No nginx, no caddy — the backend itself serves on port 80. Each app has its own mini proxy container for authentication.

HTTP

  • umbreld's Express server listens directly on port 80
  • Serves UI static files + tRPC API
  • No port 443/TLS by default — HTTP only on LAN
  • mDNS via avahi for HOSTNAME.local access

Tor (Optional)

  • Toggle per-system (not per-app)
  • tor_proxy container on 10.21.21.11 (SOCKS5)
  • Each app gets a tor_server container creating hidden services
  • Dashboard also gets its own hidden service
  • Provides end-to-end encryption for remote access

Inter-App Communication

  • Via static IPs on the flat 10.21.0.0/16 bridge network
  • No DNS-based service discovery — IPs hardcoded in exports.sh
Operating System — Debian Trixie (testing) OS
Debian Trixie (testing branch, not stable), built from date-pinned snapshot for reproducibility, Rugix A/B partitions
Uses Debian's "testing" branch (less stable than Bookworm). Has a clever A/B partition system for safe OS updates.

OS Build

  • Built inside Docker via multi-stage Dockerfile (umbrelos.Dockerfile)
  • Date-pinned Debian snapshot (e.g., 20251229) for reproducibility
  • Includes: NetworkManager, avahi, systemd-timesyncd, Bluetooth, SSH
  • Node.js 22.13.0 baked in

Rugix A/B Partitions

  • Two root partitions — active and standby
  • OS updates write to inactive partition, then swap on reboot
  • If boot fails, automatic rollback to previous partition
  • Root filesystem committed after successful boot

Persistent Bind Mounts

  • /var/log/data/umbrel-os/var/log
  • /var/lib/docker/data/umbrel-os/var/lib/docker
  • /home/data/umbrel-os/home
  • Separate /data partition persists across OS updates

User

  • umbrel (UID 1000), default password umbrel
  • Synced to web UI password after onboarding
  • Has sudo access

Docker Container Model

Docker 28.5 (Rootful)
Docker daemon runs as root (not rootless). Each app is a separate Docker Compose v2 project (--project-name <app-id>). Legacy container naming: <app-id>_<service>_1 for DNS compatibility.
Standard Docker running with root permissions. Each app is managed as a Compose project with its own containers.
Flat Network (No Isolation)
Single shared bridge: umbrel_main_network (10.21.0.0/16). All apps share one network — any container can communicate with any other. Static IPs assigned per service via exports.sh.
All apps are on the same network. A compromised app could potentially reach other apps' services directly.
Per-App Auth Proxy
Each app gets an app_proxy container (getumbrel/app-proxy, Node.js Express) that handles JWT authentication for iframe embedding. Proxies to the actual app on its internal port.
Each app has a mini web server in front of it that checks your login before letting you in.
Boot Cleanup
On every startup: stops ALL containers, prunes ALL networks to prevent stale state. Pre-loads images from /images/ (tor, auth-server baked into ISO).
Every reboot starts fresh by destroying all containers and recreating them. Clean but adds startup time.

App Packaging

umbrel-app.yml
App manifest with: id, name, version, port, category, dependencies, permissions (GPU), gallery images, release notes, widgets, torOnly flag, installSize. Validated by Zod schema.
A YAML file describing the app — what it does, what it needs, what ports it uses, and how to display it in the store.
docker-compose.yml
Standard Docker Compose v3.7. Services reference umbrel_main_network. Images pinned by SHA256 digest. Apps define their own security constraints (no enforced capability dropping).
Standard Docker Compose file that defines the app's containers, networks, and volumes.
exports.sh + hooks/
exports.sh exports environment variables (IPs, ports, credentials) for dependency resolution. hooks/ directory with lifecycle scripts: pre/post-install, pre/post-start, pre/post-stop, pre/post-update, pre-uninstall.
Shell scripts that set up environment variables so apps can find each other, plus hooks that run at key lifecycle moments.
App Store (Git repo)
Apps distributed via Git repository (getumbrel/umbrel-apps). Cloned locally, pulled every 5 minutes. Community app stores supported. implements field enables alternative implementations (e.g., Bitcoin Knots for Bitcoin Core).
The app store is just a Git repository. Umbrel checks for updates every 5 minutes by pulling the latest commits.
tRPC (UI ↔ Backend)
TypeScript-first RPC framework with end-to-end type safety. Runs over both HTTP and WebSocket on port 80.
A typed communication channel between the dashboard and the backend. If the API changes, TypeScript catches errors automatically.
Version: tRPC v11
Transport: HTTP + WebSocket (via Express 4)
Port: 80 (Express serves both UI and API)
Type safety: Server types flow directly to client (TanStack React Query v5)
Used by: React 19 frontend ↔ umbreld
Docker Compose (App Lifecycle)
Each app managed via Docker Compose v2. Install/start/stop/update handled by a bash script (app-script) calling docker compose.
Apps are defined as Docker Compose projects. A bash script handles the lifecycle by calling docker compose commands.
Compose version: v2 (docker compose plugin, not docker-compose binary)
Lifecycle script: app-script (bash, labeled "legacy")
Tools used: docker compose, yq, envsubst, openssl
Hooks: pre/post-install, pre/post-start, pre/post-stop, pre/post-update, pre-uninstall
exports.sh (Dependency Resolution)
Shell scripts that export environment variables (IPs, ports, RPC credentials). When app B depends on app A, A's exports.sh is sourced first.
Apps share their connection details through environment variables set by shell scripts.
Variables exported: IP addresses (static), ports, RPC passwords, hidden service hostnames
Resolution: Transitive deps resolved in post-order (depth-first)
Alternative implementations: settings.yml can map dependency (e.g., bitcoin → bitcoin-knots)
Deps NOT auto-installed: UI warns users to install dependencies first
Tor (Optional, Containerized)
Toggle per-system. tor_proxy container provides SOCKS5 at 10.21.21.11. Per-app tor_server containers create hidden services.
Tor is optional and runs in its own container. When enabled, each app gets its own .onion address for remote access.
SOCKS5: 10.21.21.11 (tor_proxy container)
Per-app: tor_server container creates hidden service pointing to app_proxy
Dashboard: Also gets its own hidden service
Provides: End-to-end encryption for remote access (since no TLS by default)
JWT + Proxy Tokens (Auth)
JWT for API authentication. Separate "proxy tokens" validate iframe requests to app_proxy containers. bcrypt password hashing.
Login tokens that prove who you are. Separate tokens for the dashboard API and for accessing individual apps.
API auth: JWT (jsonwebtoken library)
Password: bcrypt ($2b$, 12 rounds)
App auth: UMBREL_PROXY_TOKEN cookie validated by app_proxy containers
2FA: Optional TOTP
Git (App Store)
App store is a Git repository cloned locally via isomorphic-git. Pulled every 5 minutes for updates.
The app catalog is just a Git repo. Umbrel checks for new apps and updates by pulling the latest commits every 5 minutes.
Default repo: getumbrel/umbrel-apps (GitHub)
Library: isomorphic-git (pure JS Git implementation)
Pull interval: Every 5 minutes
Community stores: Supported (add custom Git URLs)

Not Implemented in umbrelOS

umbrelOS does not implement Web5 (DIDs, DWNs, or Verifiable Credentials).
Authentication uses a single-user JWT model. Per-app passwords are derived from a deterministic seed via HMAC-SHA256.

No Disk Encryption
No LUKS, no dm-crypt. All data stored unencrypted on disk. Backups use Kopia with per-repository passwords. A deterministic seed (256-byte random token) derives per-app passwords via HMAC-SHA256.
If someone physically steals the drive, all data is readable. No encryption at rest.
Flat Network (No App Isolation)
All apps share one Docker bridge (10.21.0.0/16). Any container can communicate with any other container. The app_proxy adds authentication but not network isolation.
All apps are on the same network. A compromised app could potentially access other apps' services.
Authentication
Single user model. Password hashed with bcrypt ($2b$, 12 rounds). JWT tokens for API auth. Separate "proxy tokens" for app iframe auth. Optional TOTP 2FA. Session cookie: UMBREL_PROXY_TOKEN.
Rootful Docker
Docker daemon runs as root. Containers run as UID 1000 where possible, but no enforced capability dropping or security profiles. No --cap-drop=ALL, no no-new-privileges by default.
Docker has root access to the machine. Individual containers may or may not restrict their own privileges.
1
GRUB / Rugix → Select Active Partition
GRUB (amd64) or tryboot (RPi) loads the kernel from the active A/B partition. Rugix commits to current partition on successful boot.
2
systemd → Docker → umbreld
systemd starts, brings up networking (NetworkManager) and Docker daemon. umbrel.service starts umbreld --data-directory=/home/umbrel/umbrel.
3
umbreld Initialization
Runs startup migrations, syncs system password, restores WiFi, waits for NTP sync (10s, important for RPi with no RTC).
4
Docker Clean Slate
Stops and removes ALL containers, prunes ALL networks. Pre-loads images from /images/. Prevents stale state from previous versions.
Destructive reset on every boot — ensures clean state but adds startup time
5
Start App Environment + All Apps
Starts tor_proxy + auth containers first, then all installed apps in parallel. Express HTTP server starts on port 80. App store update loop begins (every 5 min).
/home/umbrel/umbrel/ ← Main data directory (NO encryption) ├── umbrel.yaml Main config/state (YAML file, not a database) ├── app-data/{app-id}/ Per-app data, compose files, manifests ├── app-stores/ Git clones of app store repositories ├── tor/data/app-{id}/hostname Per-app .onion addresses ├── db/umbrel-seed/seed Deterministic seed (256-byte) for per-app passwords ├── secrets/jwt JWT signing secret └── home/ User files, backups /opt/umbreld/ umbreld daemon (npm-linked) /opt/umbreld/ui/ React SPA static files /images/ Pre-loaded Docker images (tor, auth-server)
3
Systems Compared
Rust
2/3 Backends
Debian
3/3 Base OS
3
Container Runtimes
3
Frontend Frameworks
Aspect Archipelago StartOS umbrelOS Notes / Trade-offs
Core Architecture
Backend Language Rust Rust TypeScript / Node.js 22 Rust: memory safety, performance, no GC pauses. Node.js: faster prototyping, larger ecosystem, but runtime overhead.
Frontend Vue 3 + Vite 7 + Tailwind Angular 21 + Taiga UI 5 React 19 + Vite 6 + Tailwind 4 All modern choices. Angular is heaviest (TypeScript-only). Vue/React are lighter. Tailwind enables rapid UI iteration.
API Protocol JSON-RPC 2.0 JSON-RPC (rpc-toolkit) tRPC v11 JSON-RPC is standard and language-agnostic. tRPC gives end-to-end TypeScript type safety but couples frontend/backend.
State Sync Pinia + JSON Patch over WebSocket Patch-DB (CBOR diffs over WebSocket) Zustand + TanStack React Query Patch-DB is most efficient (binary diffs). Archipelago and StartOS push updates; Umbrel polls via React Query.
Reverse Proxy Nginx (external, battle-tested) Built into Rust backend (Axum/Hyper) None (Express on :80 + per-app proxy containers) Nginx: proven, configurable, rate limiting. Built-in: fewer moving parts. Umbrel: no central proxy means no rate limiting or security headers.
Container Isolation
Container Runtime Podman (rootless, OCI) LXC (system containers, AppArmor) Docker 28.5 (rootful) Podman: no daemon, rootless by design. LXC: heavier isolation (full system containers). Docker: rootful daemon is a larger attack surface.
Rootless Containers Yes (all containers as UID 1000) User namespaces (UID 0 → host 100000) No (Docker daemon runs as root) Rootless Podman: container escape = unprivileged user. LXC: namespace mapping mitigates. Docker rootful: escape = root on host.
Capability Dropping --cap-drop=ALL + whitelist AppArmor profiles (generated) Not enforced by default Archipelago: explicit least-privilege. StartOS: AppArmor provides MAC. Umbrel: apps define their own security (inconsistent).
Network Isolation Per-tier networks (archy-net + bridge) Per-service veth on lxcbr0 Flat bridge (10.21.0.0/16, all apps share) Archipelago/StartOS: apps can't see each other unless connected. Umbrel: any container can reach any other.
Memory Limits Per-container (128MB–4GB) Configurable via manifest Not enforced by default Memory limits prevent a single app from consuming all RAM and crashing the system.
Security
Disk Encryption Mandatory LUKS2 (AES-XTS or ChaCha20-Adiantum) Optional LUKS on LVM/btrfs None Physical theft risk: Archipelago data is unreadable. StartOS depends on user choice. Umbrel data is fully exposed.
Security Headers CSP, HSTS, X-Frame-Options, Permissions-Policy Partial (built-in proxy) None (no central proxy) Headers prevent XSS, clickjacking, and protocol downgrade attacks. Critical for browser-based management.
Rate Limiting Auth 3/s, RPC 20/s, P2P 10/s RBAC via method metadata None Rate limiting prevents brute-force password attacks and API abuse.
TLS Self-signed cert on :443 + HSTS Self-signed CA + ACME (Let's Encrypt) None (HTTP only on LAN) Without TLS, any device on the LAN can intercept credentials. Tor provides encryption for remote but not LAN access.
Auth Model RBAC (Admin/Viewer/AppUser) + CSRF + session cookies Password + session cookies + key signatures Single-user JWT + optional TOTP Archipelago supports multiple roles. Others are single-user only.
App Ecosystem
App Format Container images from private registry S9PK v2 (signed merkle archive) docker-compose.yml + umbrel-app.yml S9PK: most sophisticated (signed, partial downloads, delta updates). Compose: simplest for developers. Registry: fast deployment.
Package Signing Registry-based trust Ed25519 over blake3 merkle roots Docker image digests only StartOS has the strongest supply chain security. Archipelago trusts its private registry. Umbrel relies on Docker content trust.
App Store Built-in marketplace (curated) Registry-based (marketplace) Git repository (pulled every 5 min) Git-based: easy for devs to contribute. Registry: more control. Curated: quality gate but slower additions.
Update Mechanism ISO reflash / manual upgrade Registry-based OTA Rugix A/B partitions (atomic, rollback) Umbrel has the smoothest update path with automatic rollback. Archipelago's ISO approach is most disruptive.
Networking & Privacy
Tor System daemon + hidden services (always available) Removed in v0.4 (planned re-integration) Optional (containerized) Archipelago: Tor is first-class. StartOS temporarily lost Tor in 0.4 rewrite. Umbrel: toggle on/off.
VPN Tailscale (WireGuard mesh) WireGuard (first-class, tunnelbox) None built-in Both Archipelago and StartOS offer remote access without port forwarding. Umbrel relies on Tor or manual setup.
DNS System DNS + container NetAvark DNS Built-in (hickory-server) Docker DNS + static IPs in exports.sh StartOS has the most integrated DNS. Archipelago uses standard tools. Umbrel hardcodes IPs.
Identity & Web5
DID Support did:key + did:dht + W3C DID Documents None None Archipelago is the only node OS with decentralized identity support. Enables credential issuance and cross-node trust.
Verifiable Credentials W3C VC 2.0 (Ed25519Signature2020) None None Archipelago can issue and verify digital certificates without any central authority.
DWN (Data Store) Custom implementation + peer sync via Tor None None Personal data store that syncs across nodes. Unique to Archipelago.
Nostr Integration NIP-01/04/44, nostr-provider.js in iframes None None Archipelago injects Nostr identity into every app iframe for seamless decentralized social integration.
Infrastructure
Base OS Debian 12 Bookworm (stable) Debian Bookworm Debian Trixie (testing) Stable: proven, security patches. Testing: newer packages but less battle-tested, potential for regressions.
Filesystem ext4 btrfs (COW snapshots) ext4 (A/B partitions) btrfs snapshots enable instant rollback on failed installs. ext4 is simpler and more mature. A/B adds OS-level rollback.
Kiosk Display X11 + Chromium on VT7 None None Plug in a monitor and the dashboard appears fullscreen. Unique physical UX for dedicated hardware.
Boot Recovery Crash recovery + container state snapshots btrfs snapshots + preinit/postinit hooks Destroys all containers on every boot Archipelago/StartOS: resume from last known state. Umbrel: clean slate every boot (slower but deterministic).

Summary

Archipelago
Strengths: Security (rootless, LUKS, caps, rate limiting, CSP), identity (DIDs, VCs, DWN, Nostr), kiosk display, Tor first-class.
Trade-offs: No OTA updates (ISO reflash), ext4 lacks snapshot rollback, smaller app ecosystem.
StartOS
Strengths: Package signing (S9PK), btrfs snapshots, built-in reverse proxy (fewer moving parts), WireGuard VPN, multi-arch (x86/ARM/RISC-V).
Trade-offs: Tor removed in v0.4, no identity system, Angular is heavier, LXC is less container-ecosystem-compatible.
umbrelOS
Strengths: Easiest setup, A/B OTA updates with rollback, largest app ecosystem, Git-based app store (easy contributions), React UI polish.
Trade-offs: No disk encryption, flat network (no isolation), rootful Docker, no TLS, no rate limiting, no security headers, Node.js backend.