Companion UI containers (archy-bitcoin-ui, archy-lnd-ui, archy-electrs-ui) used to be launched as fire-and-forget tokio::spawn blocks from install.rs. If archipelago crashed mid-spawn or the container's cgroup was reaped, companions vanished from podman ps -a and only a manual rm/run could bring them back (the .228 incident). Now each companion is rendered as a Quadlet .container unit under ~/.config/containers/systemd/, daemon-reloaded, and started via systemctl --user. systemd owns supervision from that point on: - archipelago can crash, restart, or be uninstalled without touching any companion. - Quadlet's Restart=always + RestartSec=10 handles container exits. - A 30s reconcile tick in boot_reconciler enumerates expected companion units and re-installs any whose unit file or service vanished — defense-in-depth against external tampering. New module layout: - container/quadlet.rs: pure unit renderer + atomic write_if_changed + systemctl helpers (daemon_reload_user / enable_now / disable_remove / is_active). 6 unit tests, no I/O in the renderer. - container/companion.rs: per-app companion specs, install/remove/ reconcile, image presence (build local first, fall back to insecure registry only via image_uses_insecure_registry whitelist). 2 tests. install.rs handle_package_install now ends with a single call to companion::install_for(package_id), replacing 287 lines of spawn-and- hope shellouts plus a ~120-line nginx auth-injector helper that worked around per-node RPC password baking. The helper is gone too — the pre-start hook renders the per-node nginx.conf to /var/lib/archipelago/ bitcoin-ui/nginx.conf and the Quadlet unit bind-mounts it read-only. runtime.rs handle_package_uninstall now disables companions before the container rm loop. Otherwise systemd's Restart=always would respawn each companion within ~10s of removal. Tests: 53 container tests pass, including 6 quadlet renderer tests (host network, bridge network, capability set, atomic write idempotence) and 2 companion specs (per-app companion lookup, build_unit shape). boot_reconciler tests gain a #[cfg(test)] without_companion_stage() flag so the paused-clock fixtures don't race the real systemctl I/O. A bats regression test (companion-survives-archipelago-restart.bats, gated on ARCHY_ALLOW_DESTRUCTIVE=1) asserts the .228 failure mode cannot recur: every installed companion has a unit file, services stay active across systemctl --user restart archipelago, and a deleted unit file is recreated within one reconcile tick. Net delta: +941 / -363, but the +941 is mostly tests (~440 lines) and the new declarative layer; the imperative tokio::spawn block and its nginx-auth helper are gone, removing two failure classes (orphan companions on archipelago crash, and post-start exec races under tightly-confined cgroups) that previously needed manual SSH recovery. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Archipelago
Self-Sovereign Bitcoin Node OS
Archipelago is a bootable personal server OS. Flash it to a USB drive, install on any x86_64 or ARM64 machine, and manage Bitcoin infrastructure, self-hosted apps, and decentralized identity through a glassmorphism web UI.
Features
Bitcoin Infrastructure
- Bitcoin Knots full node with pruning support
- LND Lightning Network daemon with channel management
- ElectrumX Electrum server for wallet connectivity
- BTCPay Server for accepting Bitcoin payments
- Mempool block explorer and fee estimator
- Fedimint federation guardian and gateway
Self-Hosted Apps (30)
Bitcoin (ThunderHub), Storage (FileBrowser, Immich, Nextcloud), Productivity (Penpot, OnlyOffice, Vaultwarden), Media (Jellyfin, PhotoPrism), Search (SearXNG), AI (Ollama), Network (Tailscale, Nginx Proxy Manager), Home (Home Assistant), Nostr (nostr-rs-relay, Nostrudel), Dev (Grafana, Portainer), and more.
Decentralized Identity
- Ed25519 node identity with DID Documents (did:key)
- Multi-identity management (Personal/Business/Anonymous)
- W3C Verifiable Credentials issuance and verification
- Decentralized Web Node (DWN) with bidirectional sync over Tor
- Nostr relay integration and NIP-07 signing for iframe apps
Multi-Node Federation
- Invite-based node joining over Tor hidden services
- Trust levels (Trusted/Verified/Untrusted) with DID-based auth
- Bidirectional DWN state sync between federated nodes
- File sharing with access controls (free/peers-only/paid)
Mesh Networking
- LoRa radio communication via Meshcore protocol
- Device discovery and mesh routing
- Off-grid Bitcoin balance checks (planned)
System Updates
- OTA updates from self-hosted Gitea (git.tx1138.com) with SHA256 verification
- Three update modes: Manual, Daily Check, Auto Apply (3 AM window)
- Rollback support with automatic backup before applying
- Full UI for update management in Settings
Security
- ChaCha20-Poly1305 encrypted secrets at rest, Argon2id password hashing
- Rootless Podman: read-only root, cap-drop ALL, non-root user, no-new-privileges
- TOTP two-factor authentication
- Per-endpoint rate limiting, CSRF protection, input validation
- AppArmor profiles for container confinement
- Tor hidden services for all inter-node communication
- All crypto and container dependencies pinned to exact versions
- Full penetration test completed (33 findings, all remediated)
Quick Start
Install from ISO
- Download the ISO for your architecture (x86_64 or ARM64)
- Flash to USB drive with Balena Etcher or
dd - Boot from USB on target hardware
- Follow the automated installer
- Access the web UI at
http://<device-ip> - Set your password and start the onboarding wizard
Supported Hardware
| Platform | Examples | Minimum |
|---|---|---|
| x86_64 | Intel NUC, mini PCs, any 64-bit PC | 4GB RAM, 32GB storage |
| ARM64 | Raspberry Pi 5, ARM64 SBCs | 4GB RAM, 32GB storage |
Recommended: 8GB+ RAM, 1TB+ NVMe SSD (for full Bitcoin node)
Development
Prerequisites
- macOS or Linux for frontend development
- Linux dev server (Debian 13) for backend builds — never build Rust on macOS for Linux
- Node.js 20+, Rust stable toolchain
Frontend Development
cd neode-ui
npm install
npm start # Dev server on http://localhost:8100 (mock backend on :5959)
npm run type-check # TypeScript validation
npm run build # Production build → web/dist/neode-ui/
Deploy to Server
./scripts/deploy-to-target.sh --live # Deploy to primary dev server
./scripts/deploy-to-target.sh --both # Deploy to both LAN servers
Release (tarball-only)
Releases ship as a backend binary and a frontend tarball referenced by
releases/manifest.json. Nodes OTA-update via scripts/self-update.sh.
./scripts/create-release.sh 1.2.3
git push gitea-local main --tags
git push gitea-vps2 main --tags
ISO builds are archived under image-recipe/_archived/ and not part of the
release deliverable.
Architecture
Debian 13 (Trixie)
├── Rootless Podman (30 containers, archy-net DNS)
├── Nginx (reverse proxy, security headers, rate limiting)
├── Rust Backend (JSON-RPC API on 127.0.0.1:5678)
│ ├── core/archipelago/ — RPC endpoints, auth, identity, federation, mesh
│ ├── core/container/ — PodmanClient (REST API socket), manifests, health
│ ├── core/security/ — AppArmor, secrets, Cosign image verification
│ └── 6 more crates — models, helpers, js-engine, performance, etc.
├── Vue 3 Frontend (Composition API + TypeScript strict + Pinia + Tailwind)
└── System Tor (hidden services, SOCKS5 proxy)
~49,000 lines of Rust | ~47,000 lines of TypeScript/Vue | 78 shell scripts | 30 container apps
Documentation
| Doc | Purpose |
|---|---|
| Architecture | System design, codebase stats, data paths |
| Architecture Review (HTML) | Interactive guide with diagrams and learning path |
| Developer Guide | Dev setup, workflow, code conventions |
| API Reference | Complete RPC endpoint reference |
| App Developer Guide | Building and publishing apps |
| User Walkthrough | End-user installation and usage guide |
| Troubleshooting | Diagnostic scenarios and solutions |
| Operations Runbook | Ops commands and emergency recovery |
| Security Audit | Penetration test findings |
| Master Plan | Phased roadmap and task tracking |
Contributing
- Fork the repository
- Create a feature branch (
feature/description) - Follow the coding standards in CLAUDE.md
- Submit a pull request