# 2-Year Production Roadmap — Archipelago v1.0 **Goal**: Take Archipelago from developer preview to a flawless, mass-market Bitcoin Node OS. Every app installs perfectly, every service runs reliably, every interaction is polished and intuitive — on desktop and mobile. **Timeline**: March 2026 → March 2028 (8 quarters) **Method**: Quarterly phases, each building on the last. Deploy and verify after every task. --- ## Q1 2026 (Mar–May): Foundation Hardening ### Phase 1A: App Store Reliability — Every App Installs Without Fail - [x] **APP-101** — fix(marketplace): audit and fix all 24 marketplace app install flows. For each app in `getCuratedAppList()` in `neode-ui/src/views/Marketplace.vue` (bitcoin-knots, electrs, btcpay-server, lnd, mempool, homeassistant, grafana, searxng, ollama, onlyoffice, penpot, nextcloud, vaultwarden, jellyfin, photoprism, immich, filebrowser, nginx-proxy-manager, portainer, uptime-kuma, tailscale, fedimint, indeedhub), verify each one: (1) marketplace card renders correctly with icon, (2) clicking Install triggers `package.install` RPC, (3) container pulls and creates successfully, (4) container starts on the correct ports per `apps/PORTS.md`, (5) status shows "Running" in My Apps. Fix any broken apps. Deploy with `./scripts/deploy-to-target.sh --live`. Test each app at http://192.168.1.228. - [ ] **APP-102** — fix(apps): ensure iframe vs new-tab behavior is correct for all apps. In `neode-ui/src/stores/appLauncher.ts`, verify `mustOpenInNewTab()` includes all apps that set `X-Frame-Options: DENY/SAMEORIGIN`. Currently covers BTCPay (23000), Home Assistant (8123), Nextcloud (8085), Immich (2283). Test each running app by clicking "Open" in AppDetails.vue — iframe apps must load inside the overlay, new-tab apps must open in a fresh browser tab. If any app fails to load in iframe, either fix the nginx proxy to strip X-Frame-Options or add it to `mustOpenInNewTab()`. Deploy and verify each app. - [ ] **APP-103** — fix(apps): verify all PORT_TO_PROXY mappings in appLauncher.ts match nginx config. Cross-reference every entry in `PORT_TO_PROXY` in `neode-ui/src/stores/appLauncher.ts` with the actual nginx location blocks in `image-recipe/configs/nginx-archipelago.conf` and `image-recipe/configs/snippets/archipelago-https-app-proxies.conf`. Any missing nginx proxy blocks must be added. Any port mismatches must be corrected. Deploy nginx config and verify each app loads via its proxy path. - [ ] **APP-104** — fix(deploy): ensure first-boot-containers.sh creates every marketplace app container. Compare the apps listed in `scripts/first-boot-containers.sh` with `scripts/deploy-to-target.sh`. Any app that deploy creates but first-boot doesn't must be added to first-boot. This ensures fresh ISO installs have all containers ready. - [ ] **APP-105** — fix(backend): verify get_app_config() handles all 24 apps. In `core/archipelago/src/api/rpc/package.rs`, check `get_app_config()` returns correct ports, volumes, env vars, and custom args for every marketplace app. Any app missing its config will fail to install. Add missing configs. - [ ] **APP-106** — fix(backend): verify get_app_metadata() for all 24 apps. In `core/archipelago/src/container/docker_packages.rs`, check `get_app_metadata()` returns correct title, description, icon path, and repo URL for every marketplace app. Fix missing or incorrect entries. ### Phase 1B: App Dependencies — Bitcoin, Lightning, Fedimint Chains - [ ] **DEP-101** — fix(backend): implement robust dependency checking for all apps. In `core/archipelago/src/api/rpc/package.rs`, ensure dependency checks work: Electrs requires Bitcoin Knots running, LND requires Bitcoin Knots running, BTCPay requires LND running, Mempool requires Bitcoin Knots + Electrs. When installing an app with unmet dependencies, the UI should either auto-install dependencies or show a clear message: "Bitcoin Knots must be installed and running first." Deploy and verify by trying to install Electrs without Bitcoin. - [ ] **DEP-102** — fix(ui): show dependency status in MarketplaceAppDetails.vue. When viewing an app that has dependencies, show a "Requirements" section listing each dependency with a green checkmark (installed & running), yellow warning (installed but stopped), or red X (not installed). Add an "Install All Requirements" button that queues dependency installations in order. This lives in `neode-ui/src/views/MarketplaceAppDetails.vue`. - [ ] **DEP-103** — feat(fedimint): integrate Fedimint Guardian + Gateway as paired services. Fedimint currently runs as a single container (fedimintd). Add Fedimint Gateway as a companion service that runs alongside the Guardian. In `scripts/deploy-to-target.sh`, when creating fedimint, also create `fedimint-gateway` container using `fedimint/gatewayd` image. Configure the gateway to auto-connect to the guardian. In the Marketplace, show Fedimint as one app that runs both services. The UI should show both Guardian and Gateway status. - [ ] **DEP-104** — feat(fedimint): auto-configure Fedimint Gateway to use LND. The Fedimint Gateway needs a Lightning backend. When both LND and Fedimint are installed, auto-configure the gateway to use LND's gRPC endpoint. In `core/archipelago/src/api/rpc/package.rs`, add Fedimint Gateway config that reads LND's tls.cert and admin.macaroon from the LND data volume. The user should only need to open lightning channels — everything else should be automatic. - [ ] **DEP-105** — feat(ui): lightning channel management interface. Create `neode-ui/src/views/apps/LightningChannels.vue` accessible from the LND app detail page. Show: (1) list of open channels with capacity bars, (2) "Open Channel" button with peer URI input and amount, (3) channel status (pending open/close, active, inactive), (4) total inbound/outbound liquidity summary. Use existing RPC to call LND's REST API through the backend proxy. This is critical for Fedimint Gateway to be useful. ### Phase 1C: Animation & UI Polish - [ ] **ANIM-101** — fix(css): audit and improve all transition animations in style.css. In `neode-ui/src/style.css`, review every `transition` property. Standardize: (1) hover lifts use `transform: translateY(-2px)` with `transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1)`, (2) active presses use `translateY(1px)`, (3) color transitions use `transition: color 0.2s ease, background-color 0.2s ease`, (4) modal/overlay entrances use `transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)`. Replace all `transition: all 0.3s ease` with specific properties to avoid animating layout properties. Deploy and verify animations feel smooth. - [ ] **ANIM-102** — fix(ui): add smooth page transitions between routes. In `neode-ui/src/views/Dashboard.vue`, wrap `` in a `` component with `name="page"`. In `style.css`, add: `.page-enter-active, .page-leave-active { transition: opacity 0.2s ease, transform 0.2s ease; }` `.page-enter-from { opacity: 0; transform: translateY(8px); }` `.page-leave-to { opacity: 0; }`. This gives every page navigation a subtle fade-up entrance. Deploy and verify navigation feels smooth. - [ ] **ANIM-103** — fix(ui): add staggered entrance animations for card grids. In views that display card grids (Apps.vue, Marketplace.vue, Home.vue, Web5.vue), add staggered entrance animations so cards appear one after another with a 50ms delay. Use CSS `animation-delay` with `nth-child()` or Vue's ``. The effect should be subtle — cards fade in and slide up slightly. Deploy and verify. - [ ] **ANIM-104** — fix(ui): smooth loading state transitions. In all views that have loading states, ensure the transition from loading to loaded is animated — not an instant swap. Use Vue's `` with `mode="out-in"` around loading/content states. The loading spinner should fade out as the content fades in. Deploy and verify on Apps, Marketplace, Server, and Home views. - [ ] **ANIM-105** — fix(ui): polish the app launcher overlay animation. In `neode-ui/src/components/AppLauncherOverlay.vue`, ensure the overlay slides up smoothly when opening an app. Add a subtle backdrop blur transition. The iframe should have a loading indicator that fades out when the app loads. The close animation should slide down. Use `will-change: transform` for GPU acceleration. Deploy and verify. ### Phase 1D: Mobile Responsiveness - [ ] **MOB-101** — fix(ui): audit and fix all views at 375px (iPhone SE) width. Test every view at 375px: Login, Dashboard sidebar, Home, Apps, Marketplace, AppDetails, MarketplaceAppDetails, Settings, Web5, Cloud, Server, Chat. Fix: horizontal overflow, overlapping elements, text truncation, buttons too small to tap (min 44px touch target), broken grid layouts. Add or fix responsive Tailwind classes in `style.css`. Deploy and verify. - [ ] **MOB-102** — fix(ui): optimize sidebar navigation for mobile. The Dashboard sidebar should collapse to a bottom tab bar on mobile (< 768px). Show icons only with labels below. The mode switcher should be accessible from Settings on mobile. Ensure the sidebar doesn't overlap content on mobile. Deploy and verify. - [ ] **MOB-103** — fix(ui): optimize app launcher overlay for mobile. The app iframe launcher should be full-screen on mobile with a sticky top bar (app title + close + open-in-new-tab). On desktop, maintain the current overlay style. Deploy and verify apps are usable on mobile. --- ## Q2 2026 (Jun–Aug): Identity & Onboarding ### Phase 2A: Multi-Identity System - [ ] **ID-101** — feat(backend): implement identity manager with multiple DIDs. Create `core/archipelago/src/identity/mod.rs` with: (1) `IdentityManager` struct that stores multiple identities in `/var/lib/archipelago/identity/`, (2) each identity has an Ed25519 keypair, a DID, a display name, and a purpose tag (personal, business, anonymous), (3) the first identity is created during onboarding, (4) RPC endpoints: `identity.list`, `identity.create`, `identity.delete`, `identity.get`, `identity.set-default`. Store identities encrypted using the node's master key. Build on server and deploy. - [ ] **ID-102** — feat(backend): implement identity signing service. Add `identity.sign` and `identity.verify` RPC endpoints. `identity.sign` takes a DID id and a message, returns a detached Ed25519 signature. `identity.verify` takes a DID, message, and signature, returns boolean. This enables apps to request signatures from the user's chosen identity. Build on server and deploy. - [ ] **ID-103** — feat(ui): identity management view. Create a new "Identity" section in the Web5 view (replace the hidden `v-if="false"` DID section at line 429 of `Web5.vue`). Show: (1) list of all identities with name, DID (truncated), purpose badge, (2) "Create Identity" button that opens a modal with name + purpose selector, (3) each identity card has Copy DID, Set Default, Delete actions, (4) the default identity shows a star badge. Wire to the backend RPC endpoints from ID-101. Deploy and verify. - [ ] **ID-104** — feat(ui): identity picker for service connections. Create a reusable `` component that shows a dropdown of the user's identities with their names and truncated DIDs. When a service (like Indeehub) needs a DID, it calls this component to let the user choose which identity to use. The selected identity's DID and signing capability are then passed to the service. - [ ] **ID-105** — feat(backend): Nostr identity bridge. Each identity can optionally have an associated Nostr keypair (secp256k1). Add `identity.create-nostr-key` RPC that generates a Nostr keypair linked to an identity. Add `identity.nostr-sign` for NIP-01 event signing. This bridges the DID world with Nostr. The user's Nostr pubkey is derivable from their identity. Build on server and deploy. - [ ] **ID-106** — feat(apps): Indeehub identity integration. When opening Indeehub, pass the user's selected identity DID via the iframe URL or postMessage. Indeehub should recognize the user's sovereign identity without requiring account creation. Implement the postMessage protocol: parent sends `{ type: 'archipelago:identity', did: '...', signature: '...' }`, Indeehub responds with `{ type: 'archipelago:identity:ack' }`. Deploy and verify Indeehub recognizes the user. ### Phase 2B: Onboarding Flow Polish - [ ] **ONB-101** — fix(ui): polish onboarding intro animation. In `neode-ui/src/views/OnboardingIntro.vue`, add a cinematic entrance: the Archipelago logo fades in with a subtle scale (0.95 → 1.0), followed by the tagline sliding up, then the "Get Started" button fading in. Total duration: 2 seconds. Use CSS keyframe animations. Deploy and verify. - [ ] **ONB-102** — fix(ui): improve onboarding DID step UX. In `OnboardingDid.vue`, when the backend generates the DID, show a brief animation of key generation (spinning lock icon → checkmark). Display the DID in a styled card with a copy button. Explain in plain language: "This is your sovereign digital identity. It proves you are you, without any company in the middle." Deploy and verify. - [ ] **ONB-103** — fix(ui): add identity purpose selection to onboarding. After DID creation in onboarding, add a step where the user names their first identity (default: "Personal") and optionally selects a purpose (Personal, Business, Anonymous). This feeds into the multi-identity system from ID-101. Deploy and verify. - [ ] **ONB-104** — fix(ui): smooth transition between onboarding steps. Add a horizontal slide transition between onboarding steps — swiping left to advance, right to go back. Use `` with `name="slide"` and direction-aware classes. Deploy and verify the flow feels like swiping through cards. --- ## Q3 2026 (Sep–Nov): Network & Node Discovery ### Phase 3A: Node Overlay Network - [ ] **NET-101** — feat(backend): implement node visibility signaling. Create `core/archipelago/src/network/overlay.rs` with: (1) a `NodeVisibility` enum (Hidden, Discoverable, Public), (2) RPC endpoints `network.set-visibility` and `network.get-visibility`, (3) when set to Discoverable, the node publishes a Nostr NIP-33 replaceable event (kind 30078, tag `d:archipelago-node`) with its onion address and public DID, (4) when set to Hidden, the event is deleted. This uses the existing Nostr discovery code in `core/archipelago/src/nostr_discovery.rs`. Build on server and deploy. - [ ] **NET-102** — feat(backend): implement connection request protocol. Add RPC endpoints: `network.request-connection` (sends a connection request to a peer's onion address over Tor), `network.list-requests` (shows pending incoming requests), `network.accept-request` (adds peer to trusted list), `network.reject-request`. Connection requests are sent as encrypted Nostr DMs (NIP-04) containing the sender's DID and onion address. Build on server and deploy. - [ ] **NET-103** — feat(ui): node visibility controls in Web5 view. In the Web5 view, add a "Node Visibility" card (replace or augment the existing Connected Nodes section). Show: (1) current visibility status (Hidden/Discoverable/Public), (2) toggle to change visibility, (3) when Discoverable, show the node's onion address, (4) warning: "Making your node discoverable lets other Archipelago users find and connect with you." Wire to NET-101 RPCs. Deploy and verify. - [ ] **NET-104** — feat(ui): connection request management. In the Web5 view, add a "Connection Requests" tab to the Connected Nodes section. Show: (1) incoming requests with sender DID and timestamp, (2) Accept/Reject buttons, (3) notification badge on the Web5 sidebar icon when requests are pending. Wire to NET-102 RPCs. Deploy and verify. - [ ] **NET-105** — feat(backend): implement peer health monitoring. Add a background task that periodically (every 5 minutes) checks if connected peers are reachable over Tor. Update peer status in the database. Send WebSocket events when peer status changes. The existing `rpcClient.checkPeerReachable()` in Web5.vue already calls this — ensure the backend implementation is robust with timeouts. Build on server and deploy. ### Phase 3B: Tor Services Management - [ ] **TOR-101** — feat(backend): implement Tor hidden service management RPC. Create RPC endpoints: `tor.list-services` (returns all configured hidden services with their .onion addresses), `tor.create-service` (creates a new hidden service for a given local port), `tor.delete-service`, `tor.get-onion-address`. Read from `/var/lib/archipelago/tor/` directory structure. Currently Tor setup is hardcoded in deploy script — make it dynamic. Build on server and deploy. - [ ] **TOR-102** — feat(ui): Tor services management in Settings or Web5. Add a "Tor Services" section showing: (1) list of all hidden services with their .onion addresses and what app they expose, (2) toggle to enable/disable Tor for each service, (3) "Broadcast my services over Tor" master toggle, (4) copy .onion address button for each service. Wire to TOR-101 RPCs. Deploy and verify. - [ ] **TOR-103** — fix(deploy): make Tor hidden service creation dynamic. Refactor `scripts/deploy-to-target.sh` Tor section (lines 471-530) to read from a config file (`/var/lib/archipelago/tor/services.json`) instead of hardcoding services. When an app is installed that supports Tor, automatically add a hidden service entry. When uninstalled, remove it. Rebuild torrc from the config file and restart the Tor container. Deploy and verify. - [ ] **TOR-104** — feat(backend): Tor-based content serving. When a peer accesses your node over Tor, serve only the content you've explicitly made available. Create `core/archipelago/src/network/content_server.rs` with: (1) a list of shared content items (files, streams), (2) access control per item (free, paid via ecash), (3) a lightweight HTTP handler that serves content to authenticated peers. This is the foundation for content streaming. Build on server and deploy. --- ## Q4 2026 (Dec–Feb 2027): Ecash & Content Economy ### Phase 4A: Ecash Integration - [ ] **ECASH-101** — feat(backend): implement Cashu ecash wallet. Create `core/archipelago/src/wallet/ecash.rs` with: (1) Cashu wallet client that connects to the local Fedimint mint, (2) RPC endpoints: `wallet.ecash-balance`, `wallet.ecash-mint` (create ecash tokens from Lightning), `wallet.ecash-melt` (redeem ecash to Lightning), `wallet.ecash-send` (create ecash token for peer), `wallet.ecash-receive` (accept ecash token from peer). Use the Cashu protocol for interoperability. Build on server and deploy. - [ ] **ECASH-102** — feat(ui): ecash wallet in Web5 view. Replace the dummy "Web5 Wallet" card in Web5.vue (lines 221-268) with a real ecash wallet UI. Show: (1) ecash balance in sats, (2) Mint button (Lightning → ecash), (3) Melt button (ecash → Lightning), (4) Send button (generates ecash token string), (5) Receive button (paste ecash token), (6) transaction history. Wire to ECASH-101 RPCs. Deploy and verify. - [ ] **ECASH-103** — feat(backend): implement pay-per-access content gating. Extend the content server from TOR-104 with ecash payment verification. When content is marked as "paid", the server returns a 402 Payment Required with a Cashu invoice. The requesting peer pays with ecash, receives a receipt token, and includes it in subsequent requests. Implement in `core/archipelago/src/network/content_server.rs`. Build on server and deploy. - [ ] **ECASH-104** — feat(ui): content pricing controls. In the content sharing UI (to be built in Phase 4B), add pricing controls: (1) free/paid toggle per content item, (2) price in sats input, (3) "Pay what you want" option with minimum, (4) preview: "Peers will pay X sats to access this." Wire to backend content server config. Deploy and verify. ### Phase 4B: Content Streaming & File Sharing - [ ] **CONTENT-101** — feat(backend): implement content catalog RPC. Create `core/archipelago/src/network/content_catalog.rs` with: (1) `content.list-mine` — list content I'm sharing, (2) `content.add` — add a file or stream to my catalog, (3) `content.remove` — stop sharing, (4) `content.set-pricing` — free or ecash-gated, (5) `content.set-availability` — available to all peers, specific peers, or nobody. Store catalog in `/var/lib/archipelago/content/catalog.json`. Build on server and deploy. - [ ] **CONTENT-102** — feat(backend): implement peer content browsing. Add `content.browse-peer` RPC that connects to a peer's onion address over Tor and fetches their content catalog. Returns a list of available items with titles, descriptions, sizes, and prices. The peer's content server (TOR-104) serves the catalog at a well-known endpoint. Build on server and deploy. - [ ] **CONTENT-103** — feat(backend): implement content streaming protocol. For media files (video, audio), implement chunked streaming over Tor. The requesting node sends a range request, the serving node streams the content chunk by chunk. For paid content, payment is per-chunk (micropayments via ecash). Use HTTP range requests over the Tor hidden service. Build on server and deploy. - [ ] **CONTENT-104** — feat(ui): content sharing dashboard. Create a "Content" tab in the Web5 view. Show: (1) "My Shared Content" — list of files/streams you're sharing with pricing, (2) "Add Content" button — file picker to add from Cloud/FileBrowser, (3) "Browse Peers" — select a connected peer and browse their catalog, (4) download/stream buttons with payment flow for paid content. Deploy and verify. - [ ] **CONTENT-105** — feat(ui): content streaming player. When a user clicks to stream video/audio from a peer, open a media player in the app launcher overlay. Show: (1) video/audio player with standard controls, (2) streaming progress indicator, (3) cost tracker (total sats spent on this stream), (4) quality selector if multiple qualities available. Use HTML5 `