- core/archipelago/src/bootstrap.rs (NEW): embed scripts/container-doctor.sh
and image-recipe/configs/archipelago-doctor.{service,timer} via
include_str! and sync to disk + enable the timer on every archipelago
startup. Idempotent (content-hash compare), dev-box symlink guard keeps
the git checkout untouched, best-effort (warn-only on failure) so
bootstrap never blocks server readiness. Wired in main.rs as a
background tokio task.
- scripts/container-doctor.sh: add fix_rootless_netns_egress(). Detects
when the rootless-netns has lost its pasta tap (container-to-container
still works but outbound DNS/TCP fails) via an nsenter probe into
aardvark-dns; with a two-probe 10s debounce to rule out transients and
a host-precheck that bails out if the host itself is offline. When the
rootless-netns is truly broken, does a graceful podman stop --all /
start --all so pasta + aardvark-dns rebuild the netns from scratch.
Bitcoin-knots and every other outbound container recover in one cycle.
- core/archipelago/src/update.rs: host_sudo → pub(crate) so bootstrap.rs
can reuse the existing systemd-run escape hatch.
- apps/bitcoin-core/manifest.yml: bump app version 24.0.0 → 28.4.0 and
image bitcoin/bitcoin:24.0 → bitcoin/bitcoin:28.4. Resources aligned
with the real container-specs.sh large-disk tune (4 GiB memory cap,
cpu_limit: 0 so bitcoind can run -par=auto across every core).
- neode-ui/src/views/apps/AppCard.vue + Apps.vue: add an Update button
+ Updating spinner to every app card that has available-update set.
Wires through serverStore.updatePackage(id) — the same RPC the detail
view already calls. common.update / common.updating i18n keys added in
en.json and es.json.
- core/archipelago/src/identity_manager.rs: add create_from_signing_key()
that mirrors an existing Ed25519 key as a manager-level identity with
a deterministic id (`node-<pubkey16>`). Idempotent across restarts,
gets the hex-SVG master avatar.
- core/archipelago/src/server.rs: the auto-create path on first boot now
mirrors the node's own signing_key (seed-derived on onboarded installs)
as a "Node" identity instead of generating a random "Default" keypair.
Once this ships, the DID on the Web5 DID Status card (via node.did
RPC), the Node entry on the Identities page (via identity.list), and
the DID used for peer-to-peer connects (via server_info.pubkey) all
resolve to the same seed-derived pubkey.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Archipelago Web UI
Vue 3 + TypeScript + Vite + Tailwind CSS + Pinia
The web interface for Archipelago — a self-sovereign Bitcoin Node OS.
Quick Start
cd neode-ui
npm install
npm start
Visit http://localhost:8100 — login with password: password123
This starts:
- Mock backend on port 5959 (no Docker required)
- Vite dev server on port 8100 with HMR
Stop with npm stop.
Architecture
neode-ui/
├── src/
│ ├── api/ # RPC client (rpc-client.ts), WebSocket, container client
│ ├── stores/ # Pinia stores (app, container, appLauncher, monitoring)
│ ├── views/ # Page components (Dashboard, Marketplace, Settings, etc.)
│ ├── components/ # Reusable components (SplashScreen, AppSession, etc.)
│ ├── router/ # Vue Router configuration
│ ├── types/ # TypeScript type definitions
│ └── style.css # Global styles + Tailwind utilities
├── public/assets/ # Static assets (images, fonts, app icons, audio)
├── mock-backend.js # Mock backend server (simulates Rust backend)
├── docker/ # Docker configs (nginx, entrypoint)
└── vite.config.ts # Vite config with backend proxy
Dev Modes
The mock backend supports multiple startup modes via VITE_DEV_MODE:
| Mode | Command | Behavior |
|---|---|---|
| default | npm start |
Fully set up, login screen |
| existing | VITE_DEV_MODE=existing npm run dev:mock |
Same as default |
| setup | VITE_DEV_MODE=setup npm run dev:mock |
First-time password setup flow |
| onboarding | VITE_DEV_MODE=onboarding npm run dev:mock |
Post-setup onboarding flow |
| boot | npm run dev:boot |
25s simulated boot sequence |
Mock Backend
The mock backend (mock-backend.js) simulates the full Rust backend for local development:
Pre-installed apps (always visible in My Apps):
- Bitcoin Core, LND, Electrs, Mempool, FileBrowser, LoraBell, ThunderHub, Fedimint
Marketplace: 30+ curated apps with Docker images, install/uninstall simulation
Features simulated:
- Authentication (login, password change, TOTP 2FA)
- System metrics (CPU, memory, disk — randomized for realism)
- Node identity (DID, Nostr pubkey, Tor address)
- Federation (3 mock nodes with apps, metrics, trust levels)
- Mesh networking (4 LoRa peers, encrypted messaging, invoices)
- Peer-to-peer messaging
- FileBrowser API (mock file system with Music, Documents, Photos, Videos)
- DWN sync status
- Transport layer (mesh/LAN/Tor routing)
- Notifications (5 realistic entries)
- Claude AI chat proxy (requires
ANTHROPIC_API_KEY)
Container runtime: If Docker/Podman is available, the mock backend will run real containers for installed apps. Otherwise, it simulates them.
Demo Deployment (Portainer)
Deploy the demo via Docker Compose for showcasing:
docker compose -f docker-compose.demo.yml build
docker compose -f docker-compose.demo.yml up -d
Or deploy through Portainer Stacks:
- Stacks > Add stack > name:
archy-demo - Web editor: paste
docker-compose.demo.ymlcontents - Add environment variable:
ANTHROPIC_API_KEY(for Claude chat) - Deploy
Access at http://your-host:4848 — password: password123
Development Commands
npm start # Start mock backend + Vite (recommended)
npm stop # Stop all servers
npm run dev:mock # Same as start, without port cleanup
npm run dev:boot # Boot mode (simulated startup delay)
npm run backend:mock # Mock backend only
npm run dev # Vite only (needs backend running separately)
npm run dev:real # Vite with real Rust backend
npm run build # Production build (outputs to ../web/dist/neode-ui/)
npm run build:docker # Build for Docker (no type checking)
npm run type-check # TypeScript type checking
npm test # Run tests
Design System
Glass Classes
| Class | Use |
|---|---|
.glass-card |
Content containers, modals, panels |
.glass-button |
ALL buttons (primary and secondary) |
.path-option-card |
Interactive cards with hover lift |
.info-card |
Status badges, metric displays |
Tokens
- Font: Avenir Next (primary), Montserrat (
font-archipelago) - Glass:
bg: rgba(0,0,0,0.60),blur: 24px,border: rgba(255,255,255,0.22) - Accent:
#fb923c(Bitcoin orange),#4ade80(green),#ef4444(red) - Text:
rgba(255,255,255,0.9)primary,rgba(255,255,255,0.6)muted
Rules
- Global CSS classes in
style.cssonly — never inline Tailwind in components .gradient-buttonis banned — use.glass-button- All components use
<script setup lang="ts">
API
import { rpcClient } from '@/api/rpc-client'
await rpcClient.login('password')
await rpcClient.startPackage('bitcoin')
const metrics = await rpcClient.getMetrics()
State management via Pinia stores. WebSocket patches applied automatically.
Build Output
- Dev build:
../web/dist/neode-ui/ - Docker build:
dist/(deployed to nginx) - Production deploy: via
scripts/deploy-to-target.sh --live
License
MIT