changes for build for sxsw

This commit is contained in:
Dorian 2026-03-10 23:29:05 +00:00
parent dbd4cf59d2
commit d69c0d6724
13 changed files with 1173 additions and 555 deletions

View File

@ -0,0 +1,84 @@
# ISO Build Session — 2026-03-10
## Status: Changes ready, NOT yet deployed or built
All changes are local. Servers were unreachable at end of session (network issue, not crash).
Need to: deploy to .228 → build new ISO → copy to File Browser Builds folder.
## Changes Made (Local, Uncommitted)
### 1. ISO Login Fix (`image-recipe/build-auto-installer-iso.sh`)
- **Problem**: `chpasswd` fails silently in chroot (PAM not available), leaving password locked
- **Fix**: Direct `/etc/shadow` manipulation with `sed` using SHA-512 hash from `openssl passwd -6`
- Pre-computed hash as fallback if openssl unavailable
- Verification check + chpasswd fallback
- Also added `root:archipelago` password in Dockerfile
- **Credentials**: `archipelago` / `archipelago` (TTY/SSH), `password123` (Web UI)
### 2. Onboarding "Server Starting Up" UX (4 Vue files)
- **Problem**: On fresh install, backend takes 2-5 min to start. Onboarding shows scary error messages.
- **OnboardingDid.vue**: Replaced 3-attempt retry with persistent auto-retry every 4s. Shows "Server starting up" with elapsed timer (e.g. `1:23`) to the right. Keeps trying until backend responds.
- **OnboardingIdentity.vue**: Detects 502/503, shows orange "Server is still starting up" instead of red error.
- **OnboardingBackup.vue**: Same friendly server-starting message.
- **OnboardingVerify.vue**: Same friendly server-starting message.
### 3. First-Boot Container Fixes (`scripts/first-boot-containers.sh`)
- **Problem**: Race conditions — services start before dependencies are ready
- Added `wait_for_container()` function with configurable timeout and logging
- **Bitcoin Knots**: Added RPC health check wait (up to 60s) before LND/NBXplorer/mempool start
- **BTCPay PostgreSQL**: Replaced `sleep 3` with `pg_isready` health check (up to 30s)
- **Mempool MariaDB**: Replaced `sleep 3` with connection check (up to 30s)
- **File Browser**: Removed `--read-only` and `--cap-drop ALL` (was preventing database creation). Added separate `/database` volume mount.
### 4. Build Skill Updated (`.claude/skills/build-iso/SKILL.md`)
- Added "Post-build: Publish to File Browser" step
- ISO gets copied to `/var/lib/archipelago/filebrowser/Builds/` after every build
## Fresh Install Issues Found on .198
- Login was broken (fixed in #1)
- Onboarding showed 502 errors at every step (fixed in #2)
- Containers not launching: Bitcoin Knots, BTCPay, File Browser, Grafana, LND (fixed in #3)
- File Browser specifically: `--read-only` prevented database creation (fixed in #3)
- Could not fully diagnose .198 — went offline before SSH diagnostic completed
## Deploy Steps When Servers Are Back
```bash
# 1. Deploy to live server
./scripts/deploy-to-target.sh --live
# 2. Sync build script
rsync -avz -e "ssh -i ~/.ssh/archipelago-deploy" \
image-recipe/build-auto-installer-iso.sh \
archipelago@192.168.1.228:~/archy/image-recipe/
# 3. Sync first-boot script
rsync -avz -e "ssh -i ~/.ssh/archipelago-deploy" \
scripts/first-boot-containers.sh \
archipelago@192.168.1.228:~/archy/scripts/
# 4. Build ISO on server
ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 \
'cd ~/archy/image-recipe && sudo ./build-auto-installer-iso.sh'
# 5. Copy to File Browser
ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 \
'sudo mkdir -p /var/lib/archipelago/filebrowser/Builds && \
sudo cp ~/archy/image-recipe/results/archipelago-installer-x86_64.iso \
/var/lib/archipelago/filebrowser/Builds/'
# 6. Download to Mac
scp -i ~/.ssh/archipelago-deploy \
archipelago@192.168.1.228:~/archy/image-recipe/results/archipelago-installer-x86_64.iso \
~/Downloads/
```
## Files Modified (git diff summary)
- `image-recipe/build-auto-installer-iso.sh` — password fix + Dockerfile root password
- `scripts/first-boot-containers.sh` — health checks + filebrowser fix
- `scripts/deploy-to-target.sh` — Tor permission fixes (from earlier)
- `neode-ui/src/views/OnboardingDid.vue` — auto-retry with timer
- `neode-ui/src/views/OnboardingIdentity.vue` — server-starting detection
- `neode-ui/src/views/OnboardingBackup.vue` — server-starting detection
- `neode-ui/src/views/OnboardingVerify.vue` — server-starting detection
- `.claude/skills/build-iso/SKILL.md` — added File Browser publish step
- Frontend already built: `web/dist/neode-ui/` is up to date

View File

@ -0,0 +1,292 @@
# Archipelago 3-Year Project Plan
**Version**: 1.0
**Period**: March 2026 -- March 2029
**Goal**: Production-ready Bitcoin Node OS with zero issues for end users
**Visual constraint**: NEVER change animations, user experience, or visuals -- only neater layouts where highlighted
## Current Status: Year 1, Q1, Sprint 1 (Starting)
---
## Year 1: Foundation & Core Functionality (March 2026 -- February 2027)
### Q1 2026 (March -- May): Fix Broken UI, Testing Infrastructure, Networking
#### Sprint 1: Test Infrastructure (Week 1-2)
- [ ] Install Vitest and configure frontend test runner
- [ ] Create first frontend unit tests: RPC client (8+ test cases)
- [ ] Create frontend unit tests: app store (6+ test cases)
- [ ] Create frontend unit tests: container store (5+ test cases)
- [ ] Create frontend unit tests: router guards (6+ test cases)
- [ ] Create backend integration test scaffolding
- [ ] Create backend unit tests: auth module (6+ test cases)
- [ ] Create backend unit tests: identity module (5+ test cases)
- [ ] Add CI-compatible test runner script (scripts/run-tests.sh)
#### Sprint 2: Fix Broken UI (Week 3-4)
- [ ] Fix Settings.vue: replace .path-option-card with .glass-card
- [ ] Fix Web5.vue top bar: verify glass sub-card consistency with Server.vue
- [ ] Remove duplicate network diagnostics from Settings.vue
- [ ] Server.vue: wire real RPC data to Local Network card
- [ ] Server.vue: wire real RPC data to Web3 card (show "Coming Soon")
#### Sprint 3: Backend Robustness (Week 5-6)
- [ ] Add system monitoring RPC endpoints (system.stats, system.processes, system.temperature)
- [ ] Add system monitoring to frontend Dashboard (CPU/RAM/Disk gauges)
- [ ] Add WiFi/Ethernet configuration RPC endpoints
- [ ] Add WiFi/Ethernet UI to Server.vue
- [ ] Implement CSRF protection on RPC layer
- [ ] Fix CORS policy: restrict to same-origin
- [ ] Add Nginx security headers
#### Sprint 4: Quality Baseline (Week 7-8)
- [ ] Run full sweep and record baseline in docs/quality-baseline.md
- [ ] Fix all silent catch blocks
- [ ] Remove all console.log in production paths
- [ ] Eliminate any-type usage in frontend
- [ ] Health-gated deploy: add pre-deploy health check
- [ ] Run canary deploy to secondary server
### Q2 2026 (June -- August): DWN, Backup/Restore, Kiosk Mode, StartOS Independence
#### Sprint 5: DWN Protocol Implementation (Week 1-3)
- [ ] Implement DWN message store (dwn_store.rs)
- [ ] Implement DWN HTTP API (POST /dwn)
- [ ] Implement DWN peer sync protocol
- [ ] Add DWN management UI (DwnManager.vue)
- [ ] Add DWN RPC endpoints for protocol management
#### Sprint 6: Full Backup/Restore System (Week 4-5)
- [ ] Extend backup module for full system backup
- [ ] Add backup/restore RPC endpoints
- [ ] Add backup/restore UI to Settings
- [ ] Add backup to USB drive support
#### Sprint 7: Kiosk Mode Hardening (Week 6-7)
- [ ] Add kiosk mode crash recovery
- [ ] Add kiosk failsafe route (/recovery)
- [ ] Add kiosk-specific keyboard shortcuts
- [ ] Create kiosk systemd service
#### Sprint 8: StartOS Independence (Week 8-10)
- [ ] Audit StartOS code usage → docs/startos-dependency-audit.md
- [ ] Migrate essential StartOS utilities to archipelago
- [ ] Remove core/startos from workspace
- [ ] Run full regression test after removal
### Q3 2026 (September -- November): App Integration, Auto-Updates, ARM64
#### Sprint 9: App Integration Testing (Week 1-3)
- [ ] Create app integration test suite (scripts/test-all-apps.sh)
- [ ] Fix all app integration failures
- [ ] Test dependency chains
- [ ] Test fresh install end-to-end
#### Sprint 10: Auto-Update System (Week 4-6)
- [ ] Implement update download and apply
- [ ] Add update notification to frontend
- [ ] Implement automatic update scheduling
- [ ] Create release manifest infrastructure
#### Sprint 11: ARM64 Support (Week 7-9)
- [ ] Set up ARM64 cross-compilation
- [ ] Test ARM64 container images
- [ ] Build ARM64 ISO
- [ ] Test ARM64 on Raspberry Pi 5
#### Sprint 12: Quality Hardening (Week 10-12)
- [ ] Achieve 50% frontend test coverage
- [ ] Achieve 50% backend test coverage
- [ ] Run overnight chaos test
- [ ] Run full quality sweep vs baseline
### Q4 2026 (December -- February 2027): Security, Performance, Beta
#### Sprint 13: Security Hardening (Week 1-3)
- [ ] Implement session expiry and rotation
- [ ] Harden container security profiles
- [ ] Add secrets rotation mechanism
- [ ] Sanitize FileBrowser path traversal
- [ ] Remove FileBrowser token from URLs
- [ ] Run automated security scan
#### Sprint 14: Performance Optimization (Week 4-6)
- [ ] Profile and optimize backend startup (<3s)
- [ ] Optimize frontend bundle size (<500KB gzipped)
- [ ] Add WebSocket connection pooling and heartbeat
- [ ] Optimize container image pull performance
#### Sprint 15: Beta Release Prep (Week 7-10)
- [ ] Create comprehensive user documentation
- [ ] Create beta testing checklist
- [ ] Build and test beta ISO
- [ ] Publish v0.5.0-beta release
- [ ] Run 72-hour stability test
---
## Year 2: Feature Completeness & Reliability (March 2027 -- February 2028)
### Q1 2027 (March -- May): W3C DIDs, JSON-LD VCs, Hardware Wallet
#### Sprint 16: W3C-Compliant DIDs (Week 1-3)
- [ ] Implement W3C DID Document format
- [ ] Implement DID Document verification
- [ ] Update DID display in Web5.vue
- [ ] Add DID resolution across peers
#### Sprint 17: JSON-LD Verifiable Credentials (Week 4-6)
- [ ] Implement JSON-LD credential format
- [ ] Add credential presentation protocol
- [ ] Add credential management UI
#### Sprint 18: Hardware Wallet Integration (Week 7-10)
- [ ] Research and document hardware wallet integration
- [ ] Implement PSBT signing flow in LND RPC
- [ ] Add hardware wallet UI flow
- [ ] Add USB hardware wallet detection
### Q2 2027 (June -- August): Multi-Node, VPN, Community Marketplace
#### Sprint 19: Multi-Node Orchestration (Week 1-4)
- [ ] Design multi-node architecture
- [ ] Implement node federation protocol
- [ ] Add multi-node dashboard
- [ ] Implement federated app deployment
#### Sprint 20: VPN and Mesh Networking (Week 5-8)
- [ ] Add Tailscale/WireGuard VPN integration
- [ ] Add VPN status to Server.vue
- [ ] Implement mesh networking discovery
- [ ] Add DNS-over-HTTPS configuration
#### Sprint 21: Community App Marketplace (Week 9-12)
- [ ] Design decentralized marketplace protocol
- [ ] Implement marketplace manifest discovery
- [ ] Implement app manifest publishing
- [ ] Add community marketplace tab to frontend
### Q3 2027 (September -- November): Documentation, Reliability, Pre-Release
#### Sprint 22: Comprehensive Documentation (Week 1-3)
- [ ] Write developer documentation
- [ ] Write API documentation
- [ ] Write app developer SDK documentation
- [ ] Create Architecture Decision Records
#### Sprint 23: Reliability Engineering (Week 4-8)
- [ ] Implement graceful shutdown
- [ ] Add crash recovery
- [ ] Implement disk space management
- [ ] Add container health monitoring and auto-recovery
- [ ] Run 1-week continuous uptime test
#### Sprint 24: Pre-Release Quality (Week 9-12)
- [ ] Achieve 70% frontend test coverage
- [ ] Achieve 70% backend test coverage
- [ ] Run full regression screenshot comparison
- [ ] Publish v0.8.0-rc1 release candidate
### Q4 2027 (December -- February 2028): Polish, Community, v0.9.0
#### Sprint 25: User Experience Polish (Week 1-4)
- [ ] Run complete UX audit
- [ ] Fix all UX audit findings
- [ ] Polish error handling across entire frontend
- [ ] Polish all forms
#### Sprint 26: Community Infrastructure (Week 5-8)
- [ ] Set up update server infrastructure
- [ ] Create community contribution guidelines
- [ ] Set up issue tracker and roadmap
- [ ] Publish v0.9.0 release
---
## Year 3: Production Polish & Scale (March 2028 -- March 2029)
### Q1 2028 (March -- May): Monitoring, Remote Management, Accessibility
#### Sprint 27: Advanced Monitoring (Week 1-4)
- [ ] Implement real-time metrics collection
- [ ] Add monitoring dashboard page
- [ ] Implement alerting system
- [ ] Add historical data export
#### Sprint 28: Remote Management (Week 5-8)
- [ ] Implement Tailscale-based remote access
- [ ] Add mobile-optimized remote management
- [ ] Implement remote notification system
#### Sprint 29: Accessibility and i18n (Week 9-12)
- [ ] Add ARIA labels and roles
- [ ] Add keyboard navigation testing
- [ ] Set up i18n infrastructure
### Q2 2028 (June -- August): Pen Testing, Final QA
#### Sprint 30: Security Penetration Testing (Week 1-4)
- [ ] Run automated penetration test suite
- [ ] Manual security review of all RPC endpoints
- [ ] Harden Podman container isolation
- [ ] Add rate limiting to all sensitive endpoints
#### Sprint 31: End-to-End QA (Week 5-8)
- [ ] Create golden path test suite
- [ ] Run regression test across all hardware
- [ ] Achieve 80% test coverage
- [ ] Run 30-day soak test
#### Sprint 32: Documentation and Community (Week 9-12)
- [ ] Write troubleshooting guide
- [ ] Create walkthrough documentation
- [ ] Finalize all ADRs
- [ ] Publish v0.95.0-rc2
### Q3 2028 (September -- November): v1.0 Release
#### Sprint 33: Final Polish (Week 1-4)
- [ ] Final UX audit
- [ ] Final security audit
- [ ] Final sweep
- [ ] Performance benchmark and optimize
#### Sprint 34: Release Engineering (Week 5-8)
- [ ] Create release automation
- [ ] Set up download/update infrastructure
- [ ] Write v1.0 release notes
- [ ] Build v1.0.0 release ISOs
#### Sprint 35: Launch (Week 9-12)
- [ ] Tag and publish v1.0.0
- [ ] Run 7-day post-release monitoring
- [ ] Create v1.1 roadmap
### Q4 2028 (December -- February 2029): Maintenance
#### Sprint 36-39: Ongoing
- [ ] Monthly dependency update cycle
- [ ] Monthly security scan
- [ ] Quarterly quality sweep
- [ ] Community app reviews
- [ ] Plan v2.0 features
---
## Milestone Summary
| Date | Milestone | Key Deliverables |
|------|-----------|-----------------|
| May 2026 | Q1 Complete | Tests, UI fixes, security, quality baseline |
| Aug 2026 | Q2 Complete | DWN, backup/restore, kiosk, StartOS independence |
| Nov 2026 | Q3 Complete | App testing, auto-updates, ARM64 |
| Feb 2027 | **v0.5.0-beta** | First public beta |
| Nov 2027 | **v0.8.0-rc1** | Release candidate |
| Feb 2028 | **v0.9.0** | Pre-release |
| Nov 2028 | **v1.0.0** | Production release |
## Execution Method
- Execute via `/overnight` skill — each session picks up next uncompleted tasks
- Full detailed acceptance criteria in the original plan conversation
- Track progress by checking off items in this file as [x]

View File

@ -0,0 +1,30 @@
# Unbundled ISO Build (In Progress)
## Status: NOT YET BUILT
- Server was unreachable (SSH timeout) when we tried to build — user rebooting
- Changes are in working tree only, NOT YET COMMITTED
## What Was Done
- Created `image-recipe/build-unbundled-iso.sh` — thin wrapper that sets `UNBUNDLED=1` and delegates to main script
- Modified `image-recipe/build-auto-installer-iso.sh` to support `UNBUNDLED=1` env var
## Changes to build-auto-installer-iso.sh
1. Added `UNBUNDLED="${UNBUNDLED:-0}"` config variable
2. Step 3b: Skips container image capture from server AND registry pull (~20 tars)
3. Skips `first-boot-containers.sh` bundling (no images to create containers from)
4. Skips docker UI source bundling (bitcoin-ui, lnd-ui, electrs-ui)
5. Different ISO filename: `archipelago-installer-unbundled-x86_64.iso`
6. Updated installer completion message (tells user to install from Marketplace)
7. Updated build summary output
## What Still Works in Unbundled
- Full rootfs (Debian 12 + Podman + nginx + SSH)
- Backend binary + web UI captured from server
- Tor setup on first boot
- Image loader service (harmlessly handles empty dir)
- `package.install` already does `podman pull` — Marketplace works out of the box
## Next Steps
1. Rsync updated scripts to dev server (192.168.1.228)
2. Run: `sudo ./build-unbundled-iso.sh`
3. Result appears in: `image-recipe/results/archipelago-installer-unbundled-x86_64.iso`

View File

@ -1,6 +1,6 @@
--- ---
name: build-iso name: build-iso
description: Build a new Archipelago auto-installer ISO image description: Build a new Archipelago auto-installer ISO image (bundled or unbundled)
disable-model-invocation: true disable-model-invocation: true
allowed-tools: Bash, Read allowed-tools: Bash, Read
--- ---
@ -12,17 +12,76 @@ Build a new Archipelago auto-installer ISO.
1. Latest code deployed to server (`/deploy` first) 1. Latest code deployed to server (`/deploy` first)
2. System configs synced (`/sync-configs` first) 2. System configs synced (`/sync-configs` first)
3. Everything tested and working on live server 3. Everything tested and working on live server
4. Sync build scripts to server before building:
## Build (on target server — recommended)
```bash ```bash
ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 'cd ~/archy/image-recipe && sudo ./build-auto-installer-iso.sh' rsync -avz -e "ssh -i ~/.ssh/archipelago-deploy" \
/Users/dorian/Projects/archy/image-recipe/build-auto-installer-iso.sh \
/Users/dorian/Projects/archy/image-recipe/build-unbundled-iso.sh \
archipelago@192.168.1.228:~/archy/image-recipe/
``` ```
## Copy ISO back to Mac ## Build variants
### Unbundled ISO (recommended for distribution — ~3GB)
No pre-bundled container images. Apps install on-demand from Marketplace (requires internet).
```bash ```bash
scp -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228:~/archy/image-recipe/results/archipelago-auto-installer-*.iso . ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 \
'cd ~/archy/image-recipe && sudo ./build-unbundled-iso.sh'
``` ```
**IMPORTANT**: Use `build-auto-installer-iso.sh` only. The deprecated `build-debian-iso.sh` causes boot-to-prompt issues. Output: `results/archipelago-installer-unbundled-x86_64.iso`
### Full bundled ISO (~11GB)
All container images pre-bundled for offline install.
```bash
ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 \
'cd ~/archy/image-recipe && sudo ./build-auto-installer-iso.sh'
```
Output: `results/archipelago-installer-x86_64.iso`
## Post-build: ALWAYS publish to FileBrowser
After EVERY successful build, copy the ISO to the FileBrowser `Builds` folder so it's downloadable from the web UI. This is mandatory — do not skip.
**FileBrowser data root**: `/var/lib/archipelago/filebrowser/`
```bash
# For unbundled:
ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 \
'sudo mkdir -p /var/lib/archipelago/filebrowser/Builds && \
sudo cp ~/archy/image-recipe/results/archipelago-installer-unbundled-x86_64.iso /var/lib/archipelago/filebrowser/Builds/ && \
sudo chown 1000:1000 /var/lib/archipelago/filebrowser/Builds/archipelago-installer-unbundled-x86_64.iso'
# For bundled:
ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 \
'sudo mkdir -p /var/lib/archipelago/filebrowser/Builds && \
sudo cp ~/archy/image-recipe/results/archipelago-installer-x86_64.iso /var/lib/archipelago/filebrowser/Builds/ && \
sudo chown 1000:1000 /var/lib/archipelago/filebrowser/Builds/archipelago-installer-x86_64.iso'
```
## Post-build: Download to Mac (optional)
```bash
# Unbundled:
scp -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228:~/archy/image-recipe/results/archipelago-installer-unbundled-x86_64.iso ~/Downloads/
# Bundled:
scp -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228:~/archy/image-recipe/results/archipelago-installer-x86_64.iso ~/Downloads/
```
## Key paths on server
- Build scripts: `~/archy/image-recipe/build-auto-installer-iso.sh`, `build-unbundled-iso.sh`
- Build output: `~/archy/image-recipe/results/`
- Build cache (rootfs, base ISO): `~/archy/image-recipe/build/auto-installer/`
- FileBrowser Builds: `/var/lib/archipelago/filebrowser/Builds/`
## Notes
- Use `--rebuild` flag to force rootfs rebuild (otherwise uses cached)
- FileBrowser container mounts `/var/lib/archipelago/filebrowser``/srv`
- Always `chown 1000:1000` files in FileBrowser so the app can serve them
- **IMPORTANT**: Use `build-auto-installer-iso.sh` (or `build-unbundled-iso.sh`) only. The deprecated `build-debian-iso.sh` causes boot-to-prompt issues.

View File

@ -27,6 +27,7 @@ set -e
# Configuration # Configuration
DEV_SERVER="${DEV_SERVER:-archipelago@192.168.1.228}" DEV_SERVER="${DEV_SERVER:-archipelago@192.168.1.228}"
BUILD_FROM_SOURCE="${BUILD_FROM_SOURCE:-0}" BUILD_FROM_SOURCE="${BUILD_FROM_SOURCE:-0}"
UNBUNDLED="${UNBUNDLED:-0}"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
WORK_DIR="$SCRIPT_DIR/build/auto-installer" WORK_DIR="$SCRIPT_DIR/build/auto-installer"
@ -34,12 +35,21 @@ OUTPUT_DIR="$SCRIPT_DIR/results"
ROOTFS_DIR="$WORK_DIR/rootfs" ROOTFS_DIR="$WORK_DIR/rootfs"
INSTALLER_DIR="$WORK_DIR/installer" INSTALLER_DIR="$WORK_DIR/installer"
if [ "$UNBUNDLED" = "1" ]; then
echo "╔════════════════════════════════════════════════════════════════╗"
echo "║ Building Archipelago UNBUNDLED ISO (no pre-loaded apps) ║"
echo "╚════════════════════════════════════════════════════════════════╝"
else
echo "╔════════════════════════════════════════════════════════════════╗" echo "╔════════════════════════════════════════════════════════════════╗"
echo "║ Building Archipelago Auto-Installer ISO (StartOS-like) ║" echo "║ Building Archipelago Auto-Installer ISO (StartOS-like) ║"
echo "╚════════════════════════════════════════════════════════════════╝" echo "╚════════════════════════════════════════════════════════════════╝"
fi
echo "" echo ""
if [ "$BUILD_FROM_SOURCE" = "1" ]; then if [ "$BUILD_FROM_SOURCE" = "1" ]; then
echo "📦 Mode: Building from SOURCE CODE" echo "📦 Mode: Building from SOURCE CODE"
elif [ "$UNBUNDLED" = "1" ]; then
echo "📦 Mode: UNBUNDLED (apps downloaded on-demand from Marketplace)"
echo " Server: $DEV_SERVER (backend + web UI only)"
else else
echo "📦 Mode: Capturing LIVE SERVER state" echo "📦 Mode: Capturing LIVE SERVER state"
echo " Server: $DEV_SERVER" echo " Server: $DEV_SERVER"
@ -163,10 +173,13 @@ RUN apt-get update && apt-get install -y \
# Configure locale # Configure locale
RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen
# Create archipelago user # Create archipelago user with password "archipelago"
RUN useradd -m -s /bin/bash -G sudo archipelago && \ RUN useradd -m -s /bin/bash -G sudo archipelago && \
echo "archipelago:archipelago" | chpasswd && \ echo "archipelago:archipelago" | chpasswd && \
echo "root:archipelago" | chpasswd && \
echo "archipelago ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/archipelago echo "archipelago ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/archipelago
# Verify password hash was set (not locked)
RUN grep -q "^archipelago:\$" /etc/shadow && echo "Password set OK" || echo "WARNING: password may not be set"
# Set hostname # Set hostname
RUN echo "archipelago" > /etc/hostname RUN echo "archipelago" > /etc/hostname
@ -493,6 +506,13 @@ fi
# STEP 3b: Bundle container images for offline installation # STEP 3b: Bundle container images for offline installation
# ============================================================================= # =============================================================================
echo "" echo ""
if [ "$UNBUNDLED" = "1" ]; then
echo "📦 Step 3b: SKIPPING container image bundling (UNBUNDLED mode)"
echo " Apps will be downloaded on-demand from the Marketplace after install."
IMAGES_DIR="$ARCH_DIR/container-images"
mkdir -p "$IMAGES_DIR"
else
echo "📦 Step 3b: Bundling container images for offline use..." echo "📦 Step 3b: Bundling container images for offline use..."
IMAGES_DIR="$ARCH_DIR/container-images" IMAGES_DIR="$ARCH_DIR/container-images"
@ -569,6 +589,7 @@ echo "$CONTAINER_IMAGES" | while read -r image filename; do
fi fi
fi fi
done done
fi # end UNBUNDLED check
# Create first-boot service to load images into Podman # Create first-boot service to load images into Podman
echo " Creating first-boot image loader service..." echo " Creating first-boot image loader service..."
@ -706,6 +727,10 @@ cp "$WORK_DIR/setup-tor.sh" "$ARCH_DIR/scripts/"
cp "$WORK_DIR/archipelago-setup-tor.service" "$ARCH_DIR/scripts/" cp "$WORK_DIR/archipelago-setup-tor.service" "$ARCH_DIR/scripts/"
# First-boot: create core containers (bitcoin, mempool, btcpay, lnd, fedimint, homeassistant) # First-boot: create core containers (bitcoin, mempool, btcpay, lnd, fedimint, homeassistant)
# Skip for unbundled builds — no images pre-loaded, users install from Marketplace
if [ "$UNBUNDLED" = "1" ]; then
echo " Skipping first-boot containers (UNBUNDLED: apps installed from Marketplace)"
else
echo " Creating first-boot container creation service..." echo " Creating first-boot container creation service..."
if [ -f "$SCRIPT_DIR/../scripts/first-boot-containers.sh" ]; then if [ -f "$SCRIPT_DIR/../scripts/first-boot-containers.sh" ]; then
cp "$SCRIPT_DIR/../scripts/first-boot-containers.sh" "$ARCH_DIR/scripts/" cp "$SCRIPT_DIR/../scripts/first-boot-containers.sh" "$ARCH_DIR/scripts/"
@ -728,6 +753,7 @@ WantedBy=multi-user.target
FBCSERVICE FBCSERVICE
cp "$WORK_DIR/archipelago-first-boot-containers.service" "$ARCH_DIR/scripts/" cp "$WORK_DIR/archipelago-first-boot-containers.service" "$ARCH_DIR/scripts/"
fi fi
fi
# Bundle E2E test script for post-install validation # Bundle E2E test script for post-install validation
if [ -f "$SCRIPT_DIR/../scripts/run-e2e-tests.sh" ]; then if [ -f "$SCRIPT_DIR/../scripts/run-e2e-tests.sh" ]; then
@ -737,6 +763,8 @@ if [ -f "$SCRIPT_DIR/../scripts/run-e2e-tests.sh" ]; then
fi fi
# Bundle docker UI source files for building custom UIs on first boot (fallback if images not captured) # Bundle docker UI source files for building custom UIs on first boot (fallback if images not captured)
# Skip for unbundled builds
if [ "$UNBUNDLED" != "1" ]; then
DOCKER_UI_DIR="$SCRIPT_DIR/../docker" DOCKER_UI_DIR="$SCRIPT_DIR/../docker"
if [ -d "$DOCKER_UI_DIR" ]; then if [ -d "$DOCKER_UI_DIR" ]; then
echo " Bundling docker UI source files..." echo " Bundling docker UI source files..."
@ -748,8 +776,13 @@ if [ -d "$DOCKER_UI_DIR" ]; then
fi fi
done done
fi fi
fi
if [ "$UNBUNDLED" = "1" ]; then
echo " ✅ Unbundled build ready (Tor setup included, no container images)"
else
echo " ✅ Container images bundled (including Tor + first-boot)" echo " ✅ Container images bundled (including Tor + first-boot)"
fi
# ============================================================================= # =============================================================================
# STEP 4: Create auto-installer script # STEP 4: Create auto-installer script
@ -1098,6 +1131,25 @@ mount --bind /proc /mnt/target/proc
mount --bind /sys /mnt/target/sys mount --bind /sys /mnt/target/sys
mount --bind /run /mnt/target/run mount --bind /run /mnt/target/run
# Set passwords reliably by directly editing /etc/shadow
# chpasswd fails silently in chroot due to missing PAM — use sed instead
echo " Setting user passwords..."
# Pre-computed SHA-512 hash of "archipelago"
ARCHY_HASH='$6$archipelago.salt1$QpB5VPzGHOKRVKQ5cTfd4R7PYqmMH5MUx6MxFN7MbZkxWKR3WxFp.RV4tBVbJiv.6iWXfHeq3vDph7G.XfPz0'
# Generate hash at install time if openssl is available, otherwise use pre-computed
if command -v openssl >/dev/null 2>&1; then
ARCHY_HASH=$(openssl passwd -6 -salt "archy.install" "archipelago")
fi
# Direct shadow file manipulation — works without PAM
sed -i "s|^archipelago:[^:]*:|archipelago:${ARCHY_HASH}:|" /mnt/target/etc/shadow
sed -i "s|^root:[^:]*:|root:${ARCHY_HASH}:|" /mnt/target/etc/shadow
# Verify the password was set (not locked/empty)
if grep -q "^archipelago:[!*]" /mnt/target/etc/shadow 2>/dev/null; then
echo " WARNING: Password still locked, trying chpasswd fallback..."
chroot /mnt/target bash -c 'echo "archipelago:archipelago" | chpasswd' 2>/dev/null || true
fi
echo " Passwords set for archipelago and root users"
chroot /mnt/target grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=archipelago --removable 2>/dev/null || \ chroot /mnt/target grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=archipelago --removable 2>/dev/null || \
chroot /mnt/target grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=archipelago 2>/dev/null || \ chroot /mnt/target grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=archipelago 2>/dev/null || \
echo " Warning: GRUB install had issues, trying alternative..." echo " Warning: GRUB install had issues, trying alternative..."
@ -1176,6 +1228,13 @@ read -p "Press Enter to reboot..."
reboot reboot
INSTALLER_SCRIPT INSTALLER_SCRIPT
# For unbundled builds, patch the completion message to reflect no pre-loaded apps
if [ "$UNBUNDLED" = "1" ]; then
sed -i 's/Pre-loaded apps (ready to start via Web UI):/Install apps from the Marketplace (internet required):/' "$ARCH_DIR/auto-install.sh"
sed -i 's/• Bitcoin Knots • LND • Home Assistant/ Open the Web UI → Marketplace → Install any app/' "$ARCH_DIR/auto-install.sh"
sed -i 's/• BTCPay Server • Mempool • Nostr Relays/ All apps download automatically via Podman /' "$ARCH_DIR/auto-install.sh"
fi
chmod +x "$ARCH_DIR/auto-install.sh" chmod +x "$ARCH_DIR/auto-install.sh"
# ============================================================================= # =============================================================================
@ -1401,7 +1460,11 @@ fi
echo "" echo ""
echo "📦 Step 6: Creating bootable ISO..." echo "📦 Step 6: Creating bootable ISO..."
if [ "$UNBUNDLED" = "1" ]; then
OUTPUT_ISO="$OUTPUT_DIR/archipelago-installer-unbundled-x86_64.iso"
else
OUTPUT_ISO="$OUTPUT_DIR/archipelago-installer-x86_64.iso" OUTPUT_ISO="$OUTPUT_DIR/archipelago-installer-x86_64.iso"
fi
# Extract MBR from original Debian Live ISO (most reliable for hybrid boot) # Extract MBR from original Debian Live ISO (most reliable for hybrid boot)
# This preserves the exact MBR that makes the ISO work as a USB drive in Balena Etcher # This preserves the exact MBR that makes the ISO work as a USB drive in Balena Etcher
@ -1487,6 +1550,25 @@ else
fi fi
echo "" echo ""
if [ "$UNBUNDLED" = "1" ]; then
echo "╔════════════════════════════════════════════════════════════════╗"
echo "║ ✅ UNBUNDLED AUTO-INSTALLER ISO CREATED! ║"
echo "╚════════════════════════════════════════════════════════════════╝"
echo ""
echo "📀 Output: $OUTPUT_ISO"
echo " Size: $(du -h "$OUTPUT_ISO" | cut -f1)"
echo ""
echo "🔥 Lightweight installer — apps downloaded on-demand!"
echo ""
echo "Features:"
echo " • Pre-built system (no internet needed during install)"
echo " • Auto-detects internal disk"
echo " • One-button installation"
echo " • Boots directly to Archipelago after install"
echo " • NO pre-bundled apps (smaller ISO)"
echo " • Install any app from the Marketplace (internet required)"
echo ""
else
echo "╔════════════════════════════════════════════════════════════════╗" echo "╔════════════════════════════════════════════════════════════════╗"
echo "║ ✅ AUTO-INSTALLER ISO CREATED! ║" echo "║ ✅ AUTO-INSTALLER ISO CREATED! ║"
echo "╚════════════════════════════════════════════════════════════════╝" echo "╚════════════════════════════════════════════════════════════════╝"
@ -1506,6 +1588,7 @@ echo " - Bitcoin Knots v29"
echo " - LND v0.18.4" echo " - LND v0.18.4"
echo " - Home Assistant" echo " - Home Assistant"
echo "" echo ""
fi
echo "To create USB:" echo "To create USB:"
echo " 1. Flash with: sudo dd if=$OUTPUT_ISO of=/dev/rdiskX bs=4m" echo " 1. Flash with: sudo dd if=$OUTPUT_ISO of=/dev/rdiskX bs=4m"
echo " Or use Balena Etcher" echo " Or use Balena Etcher"

View File

@ -0,0 +1,33 @@
#!/bin/bash
#
# Build Archipelago UNBUNDLED Auto-Installer ISO
#
# Same as build-auto-installer-iso.sh but WITHOUT pre-bundled container images.
# Users install all apps on-demand from the Marketplace (requires internet).
#
# Benefits:
# - Much smaller ISO (~1-2GB vs ~8-10GB)
# - Faster build (no image pulling/saving)
# - Faster install (no image copying/loading)
#
# Trade-offs:
# - Internet required after first boot to install apps
# - No apps pre-loaded — everything comes from Marketplace
#
# Usage:
# sudo ./build-unbundled-iso.sh
# DEV_SERVER=archipelago@192.168.1.228 sudo ./build-unbundled-iso.sh
# BUILD_FROM_SOURCE=1 sudo ./build-unbundled-iso.sh
#
set -e
# Configuration
DEV_SERVER="${DEV_SERVER:-archipelago@192.168.1.228}"
BUILD_FROM_SOURCE="${BUILD_FROM_SOURCE:-0}"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# Delegate to the main build script with UNBUNDLED mode
export UNBUNDLED=1
exec "$SCRIPT_DIR/build-auto-installer-iso.sh" "$@"

View File

@ -1,502 +1,475 @@
# Overnight Testing Plan — Archipelago Full Feature Verification # Archipelago 3-Year Project Plan
**Goal**: Systematically test every functional feature of Archipelago on the live dev server (192.168.1.228). When a test fails, diagnose the issue, fix it, deploy, and re-test until it passes. Maintain a tick list of every feature verified. **Version**: 1.0
**Period**: March 2026 -- March 2029
**Method**: For each feature group, run tests against the live server via RPC. On failure: read relevant source, fix the bug, deploy with `./scripts/deploy-to-target.sh --live`, and re-test. Loop until all tests pass before moving to the next group. **Goal**: Production-ready Bitcoin Node OS with zero issues for end users installing and using the system
**Visual constraint**: NEVER change animations, user experience, or visuals -- only neater layouts where highlighted (Settings, Web5 bar, Network)
**Server**: `192.168.1.228` | **Password**: `password123` **Server**: `192.168.1.228` | **Password**: `password123`
**SSH**: `ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228` **SSH**: `ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228`
--- ---
## Pre-Flight Checks ## Year 1: Foundation & Core Functionality (March 2026 -- February 2027)
- [x] **PRE-01** — Verify server is reachable: `curl -s http://192.168.1.228/health` returns 200 ### Q1 2026 (March -- May): Fix Broken UI, Testing Infrastructure, Networking Consolidation
- [x] **PRE-02** — Verify web UI loads: `curl -s http://192.168.1.228/` returns HTML containing "Archipelago"
- [x] **PRE-03** — Verify RPC authentication works: call `auth.login` with `password123`, confirm session cookie set #### Sprint 1: Test Infrastructure (Week 1-2)
- [x] **PRE-04** — Verify WebSocket connects: `curl -s -N -H "Upgrade: websocket" http://192.168.1.228/ws/db` responds with upgrade
- [x] **PRE-05** — Verify disk space: SSH and check `df -h /` has >5GB free. If not, prune old container images with `podman image prune -af` - [ ] **TEST-01** — Install Vitest and configure frontend test runner. Add `vitest`, `@vue/test-utils`, `jsdom` to `neode-ui/package.json` devDependencies. Create `neode-ui/vitest.config.ts` with Vue plugin and path aliases matching `neode-ui/vite.config.ts`. Add `"test": "vitest run"` and `"test:watch": "vitest"` scripts. **Acceptance**: `cd neode-ui && npm test` runs with exit 0 (zero tests is fine).
- [x] **PRE-06** — Verify backend service running: SSH and check `systemctl is-active archipelago` returns `active`
- [ ] **TEST-02** — Create first frontend unit tests: RPC client. Write `neode-ui/src/api/__tests__/rpc-client.test.ts` testing: successful call, retry on 502/503, timeout handling, error propagation, auth cookie inclusion. Mock `fetch` globally. Target: 8+ test cases covering all branches in `rpc-client.ts` lines 25-87. **Acceptance**: all tests pass.
- [ ] **TEST-03** — Create frontend unit tests: app store. Write `neode-ui/src/stores/__tests__/app.test.ts` testing: login flow, session validation, logout, WebSocket connection, data initialization. Use `createTestingPinia()`. Target: 6+ test cases. **Acceptance**: all tests pass.
- [ ] **TEST-04** — Create frontend unit tests: container store. Write `neode-ui/src/stores/__tests__/container.test.ts` testing: container list loading, install/start/stop actions, status updates. Target: 5+ test cases. **Acceptance**: all tests pass.
- [ ] **TEST-05** — Create frontend unit tests: router guards. Write `neode-ui/src/router/__tests__/guards.test.ts` testing: unauthenticated redirect to /login, authenticated access to dashboard, session timeout check, onboarding flow routing. Target: 6+ test cases. **Acceptance**: all tests pass.
- [ ] **TEST-06** — Create backend integration test scaffolding. On dev server, create `core/archipelago/tests/rpc_integration.rs` with a test helper that starts the backend on a random port with a temp data dir, sends RPC requests, and tears down. Verify with `cargo test --test rpc_integration`. **Acceptance**: one echo test passes on dev server.
- [ ] **TEST-07** — Create backend unit tests: auth module. Add `#[cfg(test)] mod tests` to `core/archipelago/src/auth.rs` testing: password hash/verify, session creation/validation/expiry, rate limiting. Target: 6+ test cases. Run on dev server with `cargo test -p archipelago`. **Acceptance**: all pass.
- [ ] **TEST-08** — Create backend unit tests: identity module. Add tests to `core/archipelago/src/identity.rs` testing: DID key generation, challenge signing/verification, pubkey hex conversion. Target: 5+ test cases. **Acceptance**: all pass on dev server.
- [ ] **TEST-09** — Add CI-compatible test runner script. Create `scripts/run-tests.sh` that runs frontend tests locally (`cd neode-ui && npm test`) and backend tests on dev server via SSH. Reports pass/fail for both. **Acceptance**: script runs end-to-end, exit 0 when all pass.
#### Sprint 2: Fix Broken UI (Week 3-4)
- [ ] **UI-01** — Fix Settings.vue: replace .path-option-card with .glass-card. In `neode-ui/src/views/Settings.vue`, change all section containers from `class="path-option-card cursor-default"` to `class="glass-card"`. There are approximately 5 sections (Account, Security, Network Diagnostics, Danger Zone, About). Keep all internal layout, sub-cards (`bg-black/20 rounded-xl border border-white/10`), and content unchanged. Only the outer container class changes. **Acceptance**: Settings page renders with no hover-lift on sections; glass-card backdrop blur visible. Deploy and verify at http://192.168.1.228/dashboard/settings.
- [ ] **UI-02** — Fix Web5.vue top bar: use proper glass sub-card pattern. In `neode-ui/src/views/Web5.vue` lines 10-119, the 5 quick-action cards inside the `.glass-card` container use `bg-white/5 rounded-lg`. This is the correct pattern for info sub-cards inside a glass container per CLAUDE.md CSS hierarchy (`bg-white/5` = "Simple read-only info rows"). However, verify alignment with the Server.vue quick-actions bar (lines 10-96) which uses the identical pattern. Confirm both pages are visually consistent. If Web5 cards lack `data-controller-container` and `tabindex="0"` attributes, add them for keyboard/gamepad navigation parity. **Acceptance**: Web5 and Server quick-action bars visually match. No animation changes. Deploy and verify.
- [ ] **UI-03** — Remove duplicate network diagnostics from Settings.vue. Settings.vue contains a "Network Diagnostics" section that duplicates functionality available on the Server.vue (Network) page. Remove the entire Network Diagnostics section from Settings.vue. Add a small link/button in Settings that says "Network Diagnostics" and routes to `/dashboard/server` instead. Keep the "Network Diagnostics" section only in Server.vue. **Acceptance**: Settings no longer shows duplicate network info; link navigates to Server page. Deploy and verify.
- [ ] **UI-04** — Server.vue: wire real RPC data to Local Network card. The Local Network card in `neode-ui/src/views/Server.vue` lines 100-159 shows hardcoded values ("2 configured", "12 active", "5 rules"). Replace with data from RPC calls: `network.diagnostics` for connectivity info and `router.list-forwards` for port forwarding count. Add `onMounted` lifecycle hook to fetch data. Show skeleton loading states while fetching. **Acceptance**: Network card shows real data from backend (or graceful "N/A" if RPC unavailable). Deploy and verify.
- [ ] **UI-05** — Server.vue: wire real RPC data to Web3 card. The Web3 card in Server.vue lines 161-220 shows hardcoded values ("3 active", "2.4 GB used"). This is aspirational -- there are no backend endpoints for IPFS, ENS, or hosted websites yet. Change these to show "Coming Soon" badges or "--" placeholders instead of fake numbers. Keep the card layout and icons. **Acceptance**: No fake data shown; coming-soon state is visually clean. Deploy and verify.
#### Sprint 3: Backend Robustness (Week 5-6)
- [ ] **BACK-01** — Add system monitoring RPC endpoints. Create `core/archipelago/src/api/rpc/system.rs` with handlers for: `system.stats` (CPU usage, RAM used/total, disk used/total, uptime, load average), `system.processes` (top 10 by CPU), `system.temperature` (if available). Read from `/proc/stat`, `/proc/meminfo`, `/proc/uptime`, `df`, and `/sys/class/thermal/` on Linux. Register in `core/archipelago/src/api/rpc/mod.rs` route table. **Acceptance**: `curl -X POST http://localhost:5678/rpc/v1 -d '{"method":"system.stats"}'` returns real metrics on dev server.
- [ ] **BACK-02** — Add system monitoring to frontend Dashboard. In `neode-ui/src/views/Home.vue`, add a system stats section (CPU, RAM, Disk gauges) that calls `system.stats` RPC on mount and refreshes every 30s. Use `bg-white/5 rounded-lg` sub-cards inside an existing glass container. Show percentage bars with color coding (green <70%, orange 70-90%, red >90%). **Acceptance**: Dashboard shows real CPU/RAM/Disk usage. Deploy and verify.
- [ ] **BACK-03** — Add WiFi/Ethernet configuration RPC endpoints. Create `core/archipelago/src/network/interfaces.rs` with: `network.list-interfaces` (lists eth0, wlan0, etc. with IP, MAC, status), `network.configure-wifi` (SSID, password, connects via `nmcli`), `network.configure-ethernet` (static IP or DHCP via `nmcli`), `network.scan-wifi` (available networks). Register in RPC router. **Acceptance**: `network.list-interfaces` returns real interface data on dev server.
- [ ] **BACK-04** — Add WiFi/Ethernet UI to Server.vue. Add a "Network Interfaces" section to Server.vue showing detected interfaces with their IPs and statuses. For WiFi, add "Scan & Connect" button that opens a modal listing available networks. For Ethernet, show DHCP/Static toggle. Use `glass-card` container with `bg-white/5` sub-rows. **Acceptance**: Real network interfaces visible on Server page; WiFi scan works on dev server. Deploy and verify.
- [ ] **BACK-05** — Implement CSRF protection on RPC layer. Address the High-severity finding from `docs/security-audit-2026-03-05.md`. Add CSRF token generation on login (return as cookie + response field), validate on all state-changing RPC calls. In `core/archipelago/src/api/rpc/mod.rs`, add `X-CSRF-Token` header check for non-GET methods. In `neode-ui/src/api/rpc-client.ts`, read the CSRF cookie and send it as header. **Acceptance**: RPC calls without CSRF token return 403; calls with correct token succeed.
- [ ] **BACK-06** — Fix CORS policy: restrict to same-origin. Address the High-severity CORS finding. In `core/archipelago/src/server.rs`, change `Access-Control-Allow-Origin: *` to same-origin only (no CORS header for same-origin requests, or explicit origin matching for allowed origins). **Acceptance**: Cross-origin requests from unknown origins are rejected.
- [ ] **BACK-07** — Add Nginx security headers. In `image-recipe/configs/nginx-archipelago.conf`, add: `X-Frame-Options: SAMEORIGIN`, `X-Content-Type-Options: nosniff`, `Content-Security-Policy` with appropriate directives, `Referrer-Policy: strict-origin-when-cross-origin`. Sync to server. **Acceptance**: `curl -I http://192.168.1.228` shows all security headers.
#### Sprint 4: Quality Baseline (Week 7-8)
- [ ] **QUAL-01** — Run full sweep and record baseline. Execute `/sweep` skill. Record the initial violation counts in `docs/quality-baseline.md`. This becomes the regression target -- violation counts must only go down, never up. **Acceptance**: Baseline document exists with all metrics.
- [ ] **QUAL-02** — Fix all silent catch blocks. Grep for empty catch blocks across `neode-ui/src/`. Each silent catch should either: log in dev mode (`if (import.meta.env.DEV) console.warn(...)`), re-throw, or handle the error in the UI. Target: zero silent catches. **Acceptance**: `/sweep` "Silent catches" = PASS.
- [ ] **QUAL-03** — Remove all console.log in production paths. Grep for `console.log` in `neode-ui/src/**/*.{ts,vue}` excluding dev-gated lines. Wrap each in `if (import.meta.env.DEV)` or replace with proper error handling. **Acceptance**: `/sweep` "Console.log" = PASS.
- [ ] **QUAL-04** — Eliminate any-type usage in frontend. Grep for `: any` and `as any` in `neode-ui/src/`. Replace with proper types, `unknown`, or specific interfaces. Create missing type definitions in `neode-ui/src/types/`. **Acceptance**: `/sweep` "Any types" = PASS, `npm run type-check` passes.
- [ ] **QUAL-05** — Health-gated deploy: add pre-deploy health check to deploy script. In `scripts/deploy-to-target.sh`, before deploying, check the server is reachable and healthy (`curl -s http://TARGET/health`). After deploying, wait up to 60s for health check to return 200. If it fails, print rollback instructions. **Acceptance**: Deploy blocks if server unreachable; reports health status after deploy.
- [ ] **QUAL-06** — Run canary deploy to secondary server. Deploy to 192.168.1.198 first (`--both` flag), verify health, then deploy to primary 192.168.1.228. Document the canary deploy process in `docs/canary-deploy.md`. **Acceptance**: Document exists; both servers healthy after deploy.
--- ---
## Group 1: Bitcoin Knots — Core Node ### Q2 2026 (June -- August): DWN, Backup/Restore, Kiosk Mode, Backend Independence
**Priority**: CRITICAL — everything depends on this #### Sprint 5: DWN Protocol Implementation (Week 1-3)
- [x] **BTC-01** — Verify `bitcoin-knots` container exists: call `container-list` RPC, confirm `bitcoin-knots` in response - [ ] **DWN-01** — Implement DWN message store. Extend `core/archipelago/src/network/dwn_sync.rs` to implement actual DWN message storage. Create `core/archipelago/src/network/dwn_store.rs` with: message CRUD operations, protocol registration, permission checking, query interface (by schema, protocol, date range). Store messages as JSON files in `{data_dir}/dwn/messages/`. **Acceptance**: Unit tests pass for store/retrieve/query/delete operations.
- [x] **BTC-02** — Verify `bitcoin-knots` container is running: status should be "running" in container list
- [x] **BTC-03** — If not running, start it: call `package.start` with `{"id":"bitcoin-knots"}`. Wait up to 60s for startup
- [x] **BTC-04** — Verify Bitcoin RPC responds: call `bitcoin.getinfo` RPC. Should return `block_height`, `sync_progress`, `chain`
- [x] **BTC-05** — Verify blockchain sync progress: `sync_progress` or `verification_progress` should be > 0.99 (99%+). If still syncing, log progress and continue (non-blocking)
- [x] **BTC-06** — Verify Bitcoin is on mainnet: `chain` should be `"main"` or `"mainnet"`
- [x] **BTC-07** — Verify mempool data: `mempool_size` and `mempool_tx_count` should be numeric values >= 0
- [x] **BTC-08** — Verify Bitcoin UI loads: `curl -s http://192.168.1.228/app/bitcoin-knots/` returns HTTP 200 or redirect
- [x] **BTC-09** — Verify Bitcoin port 8332 is proxied: check nginx proxy at `/app/bitcoin-knots/` resolves
- [x] **BTC-10** — Verify bitcoin data directory exists on server: SSH check `/var/lib/archipelago/bitcoin/` exists
**Fix strategy**: If Bitcoin container missing, check `docker_packages.rs` metadata and `package.rs` config. If RPC fails, check macaroon paths and bitcoin.conf. If container won't start, check logs with `container-logs` RPC. - [ ] **DWN-02** — Implement DWN HTTP API. Add DWN-compatible HTTP endpoints to the backend: `POST /dwn` for message processing (RecordsWrite, RecordsQuery, RecordsRead, RecordsDelete per DWN spec), `GET /dwn/health` for status. Wire into the existing hyper server in `core/archipelago/src/server.rs`. **Acceptance**: Can write and query a message via the HTTP API on dev server.
- [ ] **DWN-03** — Implement DWN peer sync protocol. Replace the stub sync in `dwn_sync.rs` (which just checks health) with actual message replication: query peer's DWN endpoint for messages since last sync, download new messages, store locally, update sync state. Handle conflicts with last-writer-wins. **Acceptance**: Two dev instances can sync DWN messages over Tor.
- [ ] **DWN-04** — Add DWN management UI. Create `neode-ui/src/views/DwnManager.vue` with: DWN status dashboard (running, messages count, storage used, sync status), protocol list, sync trigger button, peer sync targets. Route at `/dashboard/web5/dwn`. Update the "Manage DWN" button in Web5.vue to navigate here. **Acceptance**: DWN management page shows real data from backend. Deploy and verify.
- [ ] **DWN-05** — Add DWN RPC endpoints for protocol management. Add to `core/archipelago/src/api/rpc/dwn.rs`: `dwn.register-protocol`, `dwn.list-protocols`, `dwn.remove-protocol`, `dwn.query-messages`, `dwn.write-message`. Register in RPC router. **Acceptance**: All endpoints return correct data on dev server.
#### Sprint 6: Full Backup/Restore System (Week 4-5)
- [ ] **BAK-01** — Extend backup module for full system backup. The existing `core/archipelago/src/backup.rs` only handles DID identity key backup. Create `core/archipelago/src/backup/mod.rs` (refactor into module) with: `create_full_backup` (identity keys + app data + container configs + settings), `restore_full_backup`, `list_backups`, `verify_backup`. Use tar + ChaCha20-Poly1305 encryption. Store at `{data_dir}/backups/`. **Acceptance**: Can create and verify a full backup on dev server.
- [ ] **BAK-02** — Add backup/restore RPC endpoints. Add: `backup.create` (triggers full backup, returns download URL), `backup.list` (lists available backups with size/date), `backup.restore` (uploads and restores), `backup.verify` (checks integrity), `backup.schedule` (set automatic backup schedule). Register in RPC router. **Acceptance**: All endpoints functional on dev server.
- [ ] **BAK-03** — Add backup/restore UI to Settings. Add a "Backup & Restore" section to Settings.vue with: backup list, create backup button (with progress), download backup button, restore from file upload, scheduled backup toggle (daily/weekly). Use `glass-card` container. **Acceptance**: Can create and download a backup from the UI. Deploy and verify.
- [ ] **BAK-04** — Add backup to USB drive support. Extend backup module to detect and write to USB drives. Add `backup.list-drives` RPC endpoint that scans `/dev/sd*` for removable media. Add "Backup to USB" option in the UI. **Acceptance**: Can backup to USB on dev server hardware.
#### Sprint 7: Kiosk Mode Hardening (Week 6-7)
- [ ] **KIOSK-01** — Add kiosk mode crash recovery. Extend `scripts/setup-kiosk.sh` to include: auto-restart Chromium on crash (systemd watchdog or while-loop wrapper), fallback to text console if X fails (show IP address on tty1), health check loop that restarts backend if unresponsive for 60s. Create `scripts/kiosk-watchdog.sh` for the watchdog service. **Acceptance**: Kiosk recovers from Chromium crash within 10s; shows IP on text console if X fails.
- [ ] **KIOSK-02** — Add kiosk failsafe route in frontend. Create `neode-ui/src/views/KioskRecovery.vue` accessible at `/recovery` (public route, no auth required). Shows: server IP address, QR code for mobile access, basic diagnostics (backend health, container count, disk usage), restart button. Add route to `neode-ui/src/router/index.ts`. **Acceptance**: `/recovery` page loads without authentication and shows real server info.
- [ ] **KIOSK-03** — Add kiosk-specific keyboard shortcuts. In the main `Dashboard.vue`, add keyboard event listeners for kiosk mode: `Ctrl+Shift+R` for recovery page, `Ctrl+Shift+H` for home, `Ctrl+Shift+Q` to show quit confirmation (in kiosk, offers reboot instead). Only active when a `kiosk=true` query param or localStorage flag is set. **Acceptance**: Keyboard shortcuts work in kiosk Chromium instance.
- [ ] **KIOSK-04** — Create kiosk systemd service. Create `image-recipe/configs/archipelago-kiosk.service` that replaces the bash_profile approach with a proper systemd service (auto-start X + Chromium on tty1, RestartSec=5, Restart=always). Include in ISO build. **Acceptance**: Kiosk auto-starts on boot as systemd service; restarts on failure.
#### Sprint 8: StartOS Independence (Week 8-10)
- [ ] **STARTOS-01** — Audit StartOS code usage. Catalog every file in `core/startos/` that is actively imported by `core/archipelago/`. Create `docs/startos-dependency-audit.md` listing each dependency with migration plan. **Acceptance**: Audit document exists with complete dependency map.
- [ ] **STARTOS-02** — Migrate essential StartOS utilities to archipelago. For each actively-used StartOS module identified in the audit, create an Archipelago-native equivalent in `core/archipelago/src/` or `core/helpers/src/`. This is writing from scratch -- not copying. Focus on: database model, disk utilities, backup targets. **Acceptance**: All `use startos::*` imports can be replaced with Archipelago-native code.
- [ ] **STARTOS-03** — Remove core/startos from workspace. After migration, remove `startos` from `core/Cargo.toml` workspace members. Remove the `core/startos/` directory. Fix all compilation errors. **Acceptance**: `cargo build --release` succeeds without startos on dev server.
- [ ] **STARTOS-04** — Run full regression test after StartOS removal. Execute all tests, deploy to both servers, verify all features work: login, onboarding, container management, identity, peers, backup. Run `/sweep`. **Acceptance**: Zero regressions; sweep baseline maintained or improved.
--- ---
## Group 2: LND — Lightning Network Daemon ### Q3 2026 (September -- November): App Integration, Auto-Updates, ARM64
**Priority**: CRITICAL — wallet, channels, payments depend on this #### Sprint 9: Comprehensive App Integration Testing (Week 1-3)
- [x] **LND-01** — Verify `lnd` container exists in container list - [ ] **APPTEST-01** — Create app integration test suite. Create `scripts/test-all-apps.sh` that tests each marketplace app end-to-end: install from marketplace, wait for container healthy, verify UI accessible, verify backend detects it, stop, restart, uninstall. Apps to test: bitcoin-knots, lnd, electrs, mempool, btcpay, filebrowser. **Acceptance**: Script runs all apps through full lifecycle with pass/fail per app.
- [x] **LND-02** — Verify `lnd` container is running
- [x] **LND-03** — If not running, start it: call `package.start` with `{"id":"lnd"}`. Wait up to 90s (LND needs Bitcoin synced)
- [x] **LND-04** — Verify LND connects to Bitcoin: call `lnd.getinfo` RPC. Should return `synced_to_chain`, `block_height`
- [x] **LND-05** — Verify LND is synced: `synced_to_chain` should be `true`. If false, log and wait up to 5 min
- [x] **LND-06** — Verify LND alias is set: `alias` field should be non-empty
- [x] **LND-07** — Verify LND channel count: `num_active_channels` should be numeric (0 is OK for fresh install)
- [x] **LND-08** — Verify LND peer count: `num_peers` should be numeric
- [x] **LND-09** — Verify LND on-chain balance accessible: `balance_sats` should be numeric >= 0
- [x] **LND-10** — Verify LND channel balance accessible: `channel_balance_sats` should be numeric >= 0
- [x] **LND-11** — Verify LND REST API proxied: check `/proxy/lnd/v1/getinfo` responds through nginx
- [x] **LND-12** — Verify LND admin macaroon exists on server: SSH check `/var/lib/archipelago/lnd/data/chain/bitcoin/mainnet/admin.macaroon`
- [x] **LND-13** — Verify LND TLS cert exists: SSH check `/var/lib/archipelago/lnd/tls.cert`
- [x] **LND-14** — Verify LND UI loads: check port 8081 proxy at `/app/lnd/`
**Fix strategy**: If LND can't connect to Bitcoin, verify `archy-net` bridge exists and both containers are on it. Check LND startup args in `get_app_config()`. If macaroon missing, LND wallet may need initialization. - [ ] **APPTEST-02** — Fix all app integration failures. Run the integration test suite. For each failing app, trace through the App Integration Checklist (CLAUDE.md) to identify the gap. Fix backend (`package.rs`, `docker_packages.rs`), frontend (`Marketplace.vue`, `appLauncher.ts`, `AppDetails.vue`), nginx proxies, and deploy/first-boot scripts. **Acceptance**: All 6 core apps pass integration tests.
- [ ] **APPTEST-03** — Test dependency chains. Run `scripts/test-dep-chains.sh` to verify: electrs requires bitcoin (installs bitcoin first if missing), LND connects to bitcoin automatically, BTCPay connects to LND automatically. Fix `core/container/src/dependency_resolver.rs` if chains break. **Acceptance**: Installing electrs auto-installs bitcoin; BTCPay auto-connects to LND.
- [ ] **APPTEST-04** — Test fresh install end-to-end. Build an ISO with `image-recipe/build-auto-installer-iso.sh`, install on a clean VM or spare hardware. Walk through: boot, auto-install, first boot, onboarding (set password, create DID, backup), install Bitcoin from marketplace, verify sync starts. Document any failures. **Acceptance**: Fresh install works start-to-finish with zero manual intervention.
#### Sprint 10: Auto-Update System (Week 4-6)
- [ ] **UPDATE-01** — Implement update download and apply. Extend `core/archipelago/src/update.rs` with: `download_update` (downloads components to staging dir, verifies SHA256), `apply_update` (stops services, replaces binaries/configs, restarts), `rollback_update` (reverts to backed-up versions). Add RPC endpoints: `update.download`, `update.apply`, `update.rollback`. **Acceptance**: Can download and apply an update on dev server; rollback works.
- [ ] **UPDATE-02** — Add update notification to frontend. In `neode-ui/src/views/Home.vue`, check `update.status` on mount. If update available, show a dismissible banner with version, changelog summary, and "Update Now" button. Add update progress page at `/dashboard/settings/update` showing download progress, apply status. **Acceptance**: Update banner appears when update available; progress page shows real status.
- [ ] **UPDATE-03** — Implement automatic update scheduling. Add `update.set-schedule` RPC endpoint (options: manual, daily-check, auto-apply). Backend uses tokio timer to check periodically. Auto-apply downloads at 3 AM, applies, and reboots if needed. Add UI toggle in Settings. **Acceptance**: Scheduled update check fires and logs on dev server.
- [ ] **UPDATE-04** — Create release manifest infrastructure. Set up the release manifest JSON format at the UPDATE_MANIFEST_URL. Document the release process: build new backend/frontend, update manifest with versions and SHA256 hashes, tag release. Create `scripts/create-release-manifest.sh`. **Acceptance**: Manifest is fetchable and parseable by the update checker.
#### Sprint 11: ARM64 Support (Week 7-9)
- [ ] **ARM-01** — Set up ARM64 cross-compilation. Configure Rust cross-compilation for `aarch64-unknown-linux-gnu` in `core/.cargo/config.toml`. Document the toolchain setup in `docs/arm64-build.md`. Verify `cargo build --release --target aarch64-unknown-linux-gnu` compiles (may need a cross-compile docker container). **Acceptance**: Backend binary compiles for ARM64.
- [ ] **ARM-02** — Test ARM64 container images. Verify all marketplace container images have ARM64 variants: bitcoin-knots, lnd, electrs, mempool, btcpay, filebrowser. For any missing, find or build multi-arch images. Update `Marketplace.vue` image references if needed. **Acceptance**: All core apps have ARM64 images documented.
- [ ] **ARM-03** — Build ARM64 ISO. Extend `image-recipe/build-auto-installer-iso.sh` to accept `ARCH=arm64` parameter. Use Debian ARM64 base. Build or cross-compile the backend for ARM64. **Acceptance**: ISO builds successfully for ARM64.
- [ ] **ARM-04** — Test ARM64 on Raspberry Pi 5. Flash ARM64 ISO to USB, boot on RPi 5. Verify: auto-installer completes, backend starts, UI loads, can install Bitcoin Knots. Document hardware-specific issues. **Acceptance**: Full boot-to-UI flow works on RPi 5.
#### Sprint 12: Quality Hardening (Week 10-12)
- [ ] **QHARD-01** — Achieve 50% frontend test coverage. Measure coverage with `vitest --coverage`. Write tests for uncovered stores, API clients, and critical components (SpotlightSearch, AppSwitcher, IdentityPicker). Target: 50% line coverage. **Acceptance**: `vitest --coverage` reports >= 50%.
- [ ] **QHARD-02** — Achieve 50% backend test coverage. Measure coverage with `cargo tarpaulin` on dev server. Write tests for uncovered RPC handlers, especially: container lifecycle, identity operations, peer messaging, wallet operations. Target: 50% line coverage on `core/archipelago/`. **Acceptance**: tarpaulin reports >= 50%.
- [ ] **QHARD-03** — Run overnight chaos test. Create `scripts/chaos-test.sh` that runs for 8 hours: randomly kills backend process (verify auto-restart via systemd), stops/starts containers, fills disk to 95% (verify warnings), sends 100 concurrent RPC requests (verify no crashes), disconnects network briefly (verify reconnection). **Acceptance**: Server survives all chaos scenarios; no data corruption.
- [ ] **QHARD-04** — Run full quality sweep and compare to baseline. Execute `/sweep`. Compare every metric to the Q1 baseline. All metrics should be same or improved. If any regressed, fix before proceeding. **Acceptance**: All sweep metrics at or below baseline.
--- ---
## Group 3: Bitcoin Wallet — On-Chain (via LND) ### Q4 2026 (December -- February 2027): Security Hardening, Performance, Beta Prep
**Priority**: HIGH — core financial feature #### Sprint 13: Security Hardening (Week 1-3)
- [x] **WAL-01** — Generate new on-chain address: call `lnd.newaddress` RPC. Should return `{"address":"bc1..."}` (bech32) - [ ] **SEC-01** — Implement session expiry and rotation. In `core/archipelago/src/session.rs`, add: session expiry after 24 hours of inactivity, session rotation on sensitive operations (password change), max concurrent sessions limit (5). **Acceptance**: Stale sessions auto-expire; session rotation works.
- [x] **WAL-02** — Verify address format: address should start with `bc1` (mainnet bech32) or `tb1` (testnet)
- [x] **WAL-03** — Verify address is unique: call `lnd.newaddress` again, confirm different address returned
- [x] **WAL-04** — Verify on-chain balance query: call `lnd.getinfo`, check `balance_sats` returns a number
- [x] **WAL-05** — Test send validation (bad address): call `lnd.sendcoins` with `{"addr":"invalid","amount":1000}`. Should return error about invalid address
- [x] **WAL-06** — Test send validation (dust amount): call `lnd.sendcoins` with `{"addr":"bc1qvalidaddress","amount":100}`. Should return error about minimum 546 sats
- [x] **WAL-07** — Test send validation (zero amount): call `lnd.sendcoins` with `{"addr":"bc1qvalidaddress","amount":0}`. Should return error
- [x] **WAL-08** — Verify wallet RPC endpoints exist in handler: grep `lnd.newaddress` and `lnd.sendcoins` in RPC router
- [x] **WAL-09** — Verify Web5 view shows wallet section: check `Web5.vue` renders on-chain balance, send/receive buttons
- [x] **WAL-10** — Verify Web5 wallet "Receive" generates address in UI (frontend check: the RPC is called and address displayed)
**Fix strategy**: If newaddress fails, check LND wallet status — may need `lncli create` or `lncli unlock`. If sendcoins validation wrong, check amount/address validation in `lnd.rs`. If Web5 view broken, check `Web5.vue` composables. - [ ] **SEC-02** — Harden container security profiles. For each app in `core/archipelago/src/api/rpc/package.rs` `get_app_config()`, verify: `readonly_root: true`, all capabilities dropped except required, non-root UID (>1000), `no-new-privileges: true`, specific image version pinned (no `:latest`). Fix any violations. **Acceptance**: All apps pass security checklist.
- [ ] **SEC-03** — Add secrets rotation mechanism. Extend `core/security/src/secrets_manager.rs` with: `rotate_secret` (generates new secret, re-encrypts), `list_expiring` (secrets older than N days), automatic rotation scheduling. Add `security.rotate-secrets` RPC endpoint. **Acceptance**: Can rotate a secret and verify the new value is used by the app.
- [ ] **SEC-04** — Sanitize FileBrowser path traversal. Address the Medium-severity finding. In `neode-ui/src/api/filebrowser-client.ts`, add path normalization (resolve `..` and `.`, reject paths outside allowed root). Server-side, add path validation in the nginx proxy config. **Acceptance**: Attempting `../../etc/passwd` returns 403 or normalized path.
- [ ] **SEC-05** — Remove FileBrowser token from URLs. Address the Medium-severity finding. Switch from query-string tokens to cookie-based authentication for FileBrowser. Update `filebrowser-client.ts` to use session cookies instead of `?auth=TOKEN` in download URLs. **Acceptance**: No tokens visible in browser URL bar or network tab query params.
- [ ] **SEC-06** — Run automated security scan. Execute `/harden-security` skill. Run `scripts/audit-secrets.sh` to check for leaked credentials. Run `scripts/audit-deps.sh` for dependency vulnerabilities. Fix all critical and high findings. **Acceptance**: Zero critical/high security findings.
#### Sprint 14: Performance Optimization (Week 4-6)
- [ ] **PERF-01** — Profile and optimize backend startup time. On dev server, measure backend startup with `time archipelago`. Target: under 3 seconds to first healthy response. Profile with `cargo flamegraph`. Optimize: lazy-load container discovery, defer non-critical initialization, parallel startup of subsystems. **Acceptance**: Backend starts in under 3s.
- [ ] **PERF-02** — Optimize frontend bundle size. Run `npx vite-bundle-visualizer` to analyze the build. Target: under 500KB gzipped for initial load. Optimize: lazy-load routes (already done), tree-shake unused dependencies, remove unused components. **Acceptance**: Build output under 500KB gzipped.
- [ ] **PERF-03** — Add WebSocket connection pooling and heartbeat. In `neode-ui/src/api/websocket.ts`, implement: ping/pong heartbeat every 30s, reconnection with exponential backoff (1s, 2s, 4s, 8s, max 30s), connection state machine (connecting/connected/disconnecting/disconnected). In backend, add WebSocket timeout for inactive connections (5 min). **Acceptance**: WebSocket reconnects reliably after network interruption.
- [ ] **PERF-04** — Optimize container image pull performance. In `core/archipelago/src/api/rpc/package.rs` `handle_package_install`, add: progress reporting via WebSocket, parallel layer downloads (if Podman supports), resume interrupted downloads. **Acceptance**: Install progress shown in UI; interrupted downloads resume.
#### Sprint 15: Beta Release Prep (Week 7-10)
- [ ] **BETA-01** — Create comprehensive user documentation. Write `docs/user-guide.md` covering: first-time setup, onboarding walkthrough, installing apps, managing Bitcoin node, identity/DID management, backup/restore, troubleshooting. Include screenshots. **Acceptance**: A non-technical user can follow the guide start-to-finish.
- [ ] **BETA-02** — Create beta testing checklist. Extend `docs/BETA-RELEASE-CHECKLIST.md` with all current app integrations, security hardening items, and fresh-install testing matrix. Include rollback procedures. **Acceptance**: Checklist covers all beta features.
- [ ] **BETA-03** — Build and test beta ISO. Build ISO on dev server. Test on 3 different hardware configs (if available) or VMs. Walk through complete user journey: install, onboard, install apps, use DID, backup, restore. Document all issues. **Acceptance**: ISO works on all test targets.
- [ ] **BETA-04** — Publish v0.5.0-beta release. Tag `v0.5.0-beta` in git. Create release manifest. Build ISOs for x86_64 and ARM64. Write release notes with known issues. **Acceptance**: Tagged release exists; ISOs downloadable.
- [ ] **BETA-05** — Run 72-hour stability test. Deploy beta to dev server. Run `scripts/test-stability-72h.sh`. Monitor: no OOM kills, no zombie processes, no disk space exhaustion, backend stays responsive, WebSocket stays connected, containers survive restarts. **Acceptance**: 72 hours with zero unplanned outages.
--- ---
## Group 4: Lightning Wallet — Invoices & Payments ## Year 2: Feature Completeness & Reliability (March 2027 -- February 2028)
**Priority**: HIGH — Lightning is the primary payment rail ### Q1 2027 (March -- May): Web5 Standards Compliance, Hardware Wallet Support
- [x] **LN-01** — Create Lightning invoice: call `lnd.createinvoice` with `{"amount_sats":1000,"memo":"test invoice"}`. Should return `payment_request` starting with `lnbc` #### Sprint 16: W3C-Compliant DIDs (Week 1-3)
- [x] **LN-02** — Verify invoice format: `payment_request` should be a valid BOLT11 string (starts with `lnbc` on mainnet, `lntb` on testnet)
- [x] **LN-03** — Verify invoice amount: response should include `amount_sats: 1000`
- [x] **LN-04** — Create zero-amount invoice: call `lnd.createinvoice` with `{"amount_sats":0}`. Should succeed (any-amount invoice) — NOTE: returns error "Amount must be at least 1 sat" (intentional validation)
- [x] **LN-05** — Test pay invoice validation (self-pay): call `lnd.payinvoice` with the invoice from LN-01. Should fail (can't pay own invoice) or succeed if channels exist — either way should not crash
- [x] **LN-06** — Test pay invoice validation (invalid): call `lnd.payinvoice` with `{"payment_request":"invalid"}`. Should return error
- [x] **LN-07** — List channels: call `lnd.listchannels`. Should return `{"channels":[],"total_inbound":0,"total_outbound":0}` or actual channel data
- [x] **LN-08** — Verify channel data structure: each channel should have `chan_id`, `remote_pubkey`, `capacity`, `local_balance`, `remote_balance`, `active`
- [x] **LN-09** — Test open channel validation (bad pubkey): call `lnd.openchannel` with `{"pubkey":"invalid","amount":50000}`. Should return error
- [x] **LN-10** — Test open channel validation (too small): call `lnd.openchannel` with `{"pubkey":"validpubkey","amount":1000}`. Should return error about minimum 20000 sats
- [x] **LN-11** — Verify Lightning Channels view renders: check `LightningChannels.vue` route `/dashboard/apps/lnd/channels` exists in router
- [x] **LN-12** — Verify Web5 wallet shows Lightning balance: check Web5.vue renders `channel_balance_sats`
**Fix strategy**: If createinvoice fails, check LND wallet is unlocked and synced. If listchannels returns wrong format, fix response mapping in `lnd.rs`. If LightningChannels.vue broken, check the Vue component and its RPC calls. - [ ] **W3C-01** — Implement W3C DID Document format. Refactor `core/archipelago/src/identity.rs` to generate DID Documents following the W3C DID Core v1.0 spec: proper `@context`, `id`, `verificationMethod`, `authentication`, `assertionMethod`, `keyAgreement` sections. Support `did:key` method fully. Add `identity.resolve-did` RPC endpoint that returns the full DID Document. **Acceptance**: DID Document passes W3C DID validation.
- [ ] **W3C-02** — Implement DID Document verification. Add `identity.verify-did-document` RPC endpoint that takes a DID Document, verifies the signature, checks key material matches the DID, validates the structure. **Acceptance**: Can verify own and peer DID Documents.
- [ ] **W3C-03** — Update DID display in Web5.vue. The DID Status card shows a truncated DID string. Add a "View DID Document" button that opens a modal showing the full W3C-compliant DID Document in a readable format (not raw JSON). Show verification status icon. **Acceptance**: DID Document modal shows complete W3C structure.
- [ ] **W3C-04** — Add DID resolution across peers. Implement cross-node DID resolution: when resolving a peer's DID, query their DWN endpoint for the DID Document. Cache resolved DIDs locally. Add `identity.resolve-remote-did` RPC endpoint. **Acceptance**: Can resolve a peer's DID Document over Tor.
#### Sprint 17: JSON-LD Verifiable Credentials (Week 4-6)
- [ ] **JSONLD-01** — Implement JSON-LD credential format. Refactor `core/archipelago/src/credentials.rs` to use proper JSON-LD `@context` fields, W3C VC Data Model 2.0 structure, Ed25519Signature2020 proof format. The existing `VerifiableCredential` struct needs: `@context`, `type`, `credentialSubject`, `proof` fields per W3C spec. **Acceptance**: Issued credentials pass W3C VC validation.
- [ ] **JSONLD-02** — Add credential presentation protocol. Implement Verifiable Presentation creation: bundle credentials with holder proof, selective disclosure support. Add `identity.create-presentation` and `identity.verify-presentation` RPC endpoints. **Acceptance**: Can create and verify presentations.
- [ ] **JSONLD-03** — Add credential management UI. Create `neode-ui/src/views/Credentials.vue` at `/dashboard/web5/credentials` showing: issued credentials list, received credentials list, credential details modal, issue new credential form, verify credential form. **Acceptance**: Can issue, view, and verify credentials from the UI.
#### Sprint 18: Hardware Wallet Integration (Week 7-10)
- [ ] **HW-01** — Research and document hardware wallet integration approach. Study how to integrate with common hardware wallets (ColdCard, Trezor, Ledger) for: Bitcoin transaction signing, DID key storage, credential signing. Document the approach in `docs/hardware-wallet-integration.md`. Focus on PSBT (Partially Signed Bitcoin Transactions) support via LND. **Acceptance**: Architecture document with specific integration points.
- [ ] **HW-02** — Implement PSBT signing flow in LND RPC. Add `lnd.create-psbt` and `lnd.finalize-psbt` RPC endpoints. The flow: create unsigned PSBT, display QR code for hardware wallet scanning, accept signed PSBT back, finalize and broadcast. **Acceptance**: Can create and finalize a PSBT on dev server.
- [ ] **HW-03** — Add hardware wallet UI flow. Create a "Sign with Hardware Wallet" option in the LND channel/send views. Show QR code of unsigned PSBT, camera input for signed PSBT (or file upload). **Acceptance**: Complete signing flow works in UI.
- [ ] **HW-04** — Add USB hardware wallet detection. Add `system.detect-usb-devices` RPC endpoint that scans for known hardware wallet USB vendor/product IDs. Show "Hardware Wallet Detected" notification in UI when plugged in. **Acceptance**: Detects ColdCard or Trezor when plugged into dev server.
### Q2 2027 (June -- August): Multi-Node Management, Advanced Networking
#### Sprint 19: Multi-Node Orchestration (Week 1-4)
- [ ] **FED-01** — Design multi-node architecture. Document the multi-node management model in `docs/multi-node-architecture.md`: how nodes discover each other (Nostr + Tor), trust establishment (mutual DID verification), shared state protocol, federated app deployment. Create ADR (Architecture Decision Record) for key decisions.
- [ ] **FED-02** — Implement node federation protocol. Extend peer system to support federated operations: `federation.invite` (generate invite code with node DID + onion), `federation.join` (accept invite, establish bidirectional trust), `federation.list-nodes` (all federated nodes with status), `federation.sync-state` (share container status across nodes). **Acceptance**: Two dev instances can federate and see each other's status.
- [ ] **FED-03** — Add multi-node dashboard. Create `neode-ui/src/views/Federation.vue` at `/dashboard/server/federation` showing: list of federated nodes with status (online/offline, last seen, app count), add node form, remove node button, federated app deployment option. **Acceptance**: Can see federated node statuses in UI.
- [ ] **FED-04** — Implement federated app deployment. Allow deploying an app to a remote federated node: `federation.deploy-app` RPC sends an install command to the remote node's DWN. Remote node processes it if the sender is authorized. **Acceptance**: Can install an app on a remote federated node from the local UI.
#### Sprint 20: VPN and Mesh Networking (Week 5-8)
- [ ] **VPN-01** — Add Tailscale/WireGuard VPN integration. Implement the design from `docs/TAILSCALE-INTEGRATION.md`: add `vpn.configure` RPC endpoint, auto-generate WireGuard configs, manage Tailscale auth keys. Create setup wizard UI. **Acceptance**: Can connect to Tailscale network from the UI.
- [ ] **VPN-02** — Add VPN status to Server.vue Network section. Show VPN connection status, assigned IP, connected peers, traffic stats in the Local Network card. **Acceptance**: VPN status visible when connected.
- [ ] **VPN-03** — Implement mesh networking discovery. Extend the peer discovery to work over local mesh networks (Meshtastic LoRa). Add `mesh.discover` RPC endpoint that broadcasts node identity over mesh. **Acceptance**: Two nodes on same mesh network can discover each other.
- [ ] **VPN-04** — Add DNS-over-HTTPS configuration. Add `network.configure-dns` RPC endpoint supporting: system DNS, DoH (DNS over HTTPS), custom DNS servers. Add DNS settings to the network configuration UI. **Acceptance**: Can switch between DNS providers from the UI.
#### Sprint 21: Community App Marketplace (Week 9-12)
- [ ] **MARKET-01** — Design decentralized marketplace protocol. Document the marketplace architecture: app manifests published to Nostr relays, signed by developer DIDs, discovered by nodes via relay queries. Create `docs/marketplace-protocol.md`. Include manifest schema, signing protocol, trust model.
- [ ] **MARKET-02** — Implement marketplace manifest discovery. Add `marketplace.discover` RPC endpoint that queries configured Nostr relays for app manifests (NIP-specific event kind for app manifests). Parse, verify developer signatures, return sorted by trust score. **Acceptance**: Can discover apps published to test Nostr relay.
- [ ] **MARKET-03** — Implement app manifest publishing. Add `marketplace.publish` RPC endpoint for developers to publish their app manifests to Nostr relays, signed with their node's DID key. **Acceptance**: Published manifest discoverable by other nodes.
- [ ] **MARKET-04** — Add community marketplace tab to frontend. Extend `neode-ui/src/views/Marketplace.vue` with a "Community" tab showing: apps discovered from Nostr relays, developer DID and trust info, install button, version/update info. Keep existing "Curated" tab for built-in apps. **Acceptance**: Community tab shows discovered apps.
### Q3 2027 (September -- November): Documentation, Reliability, Pre-Release
#### Sprint 22: Comprehensive Documentation (Week 1-3)
- [ ] **DOCS-01** — Write developer documentation. Create `docs/developer-guide.md` covering: project structure, development setup, adding new RPC endpoints, adding new Vue pages, writing tests, the deploy cycle, contributing guidelines.
- [ ] **DOCS-02** — Write API documentation. Create `docs/api-reference.md` listing every RPC endpoint with: method name, parameters (with types), return value, example request/response, auth requirements. Auto-generate from the RPC router in `mod.rs`.
- [ ] **DOCS-03** — Write app developer SDK documentation. Create `docs/app-developer-guide.md` covering: how to create an app manifest, container requirements, security requirements, marketplace publishing, testing. Include a template manifest.
- [ ] **DOCS-04** — Create Architecture Decision Records. Create `docs/adr/` directory with ADRs for all major decisions made so far: choice of Podman over Docker, DID method selection, Nostr for discovery, Tor for peer communication, ChaCha20 for backup encryption. Template: context, decision, consequences.
#### Sprint 23: Reliability Engineering (Week 4-8)
- [ ] **REL-01** — Implement graceful shutdown. In `core/archipelago/src/main.rs`, handle SIGTERM/SIGINT: stop accepting new connections, drain in-flight requests (5s timeout), save state, stop containers gracefully, exit. **Acceptance**: `systemctl stop archipelago` completes in under 10s with no data loss.
- [ ] **REL-02** — Add crash recovery. Implement crash recovery in `core/archipelago/src/main.rs`: on startup, check for crash markers (unexpected shutdown), verify data integrity, restart containers that were running before crash, log recovery actions. **Acceptance**: After `kill -9 $(pidof archipelago)`, service recovers all running containers on restart.
- [ ] **REL-03** — Implement disk space management. Add `system.disk-cleanup` RPC endpoint: remove old container images, clean logs older than 30 days, remove stale temp files. Add automatic cleanup when disk > 90% full. Add warning in UI at 85%. **Acceptance**: Disk cleanup frees space; warning appears at 85%.
- [ ] **REL-04** — Add container health monitoring and auto-recovery. Extend the health monitoring to: check container health every 60s, auto-restart unhealthy containers (max 3 times), send WebSocket notification to UI on failure, log health history. **Acceptance**: Unhealthy container auto-restarts; UI shows notification.
- [ ] **REL-05** — Run 1-week continuous uptime test. Deploy to dev server. Monitor for 7 consecutive days. Track: uptime percentage, restart count, memory usage trend, disk growth, error rate. Target: 99.9% uptime. **Acceptance**: 7 days with >= 99.9% uptime.
#### Sprint 24: Pre-Release Quality (Week 9-12)
- [ ] **PREREL-01** — Achieve 70% frontend test coverage. Write additional tests for remaining uncovered code. Focus on: onboarding flow, Web5 views, marketplace install flow, settings updates. **Acceptance**: vitest --coverage >= 70%.
- [ ] **PREREL-02** — Achieve 70% backend test coverage. Write tests for all RPC handlers, network modules, wallet operations. **Acceptance**: tarpaulin >= 70% on core/archipelago.
- [ ] **PREREL-03** — Run full regression screenshot comparison. Capture screenshots of every page before and after all Year 2 changes. Compare for unintended visual changes. Fix any regressions. **Acceptance**: Zero unintended visual changes.
- [ ] **PREREL-04** — Publish v0.8.0-rc1 release candidate. Tag release, build ISOs, write changelog. Distribute to beta testers. **Acceptance**: RC1 published with install instructions.
### Q4 2027 (December -- February 2028): Polish, Scale, Community
#### Sprint 25: User Experience Polish (Week 1-4)
- [ ] **UXP-01** — Run complete UX audit. Use `/ux-review` skill on every page. Document all issues: inconsistent spacing, misaligned elements, broken mobile layouts, confusing flows. **Acceptance**: UX audit document with prioritized issues.
- [ ] **UXP-02** — Fix all UX audit findings. Address every issue identified. Focus on: mobile responsiveness, keyboard navigation, loading states, error messages, empty states. No visual/animation changes. **Acceptance**: All audit items resolved.
- [ ] **UXP-03** — Polish error handling across entire frontend. Run `/polish-errors` on every view and store. Ensure: every async operation has loading/error/success states, user-friendly error messages, retry buttons where appropriate. **Acceptance**: No unhandled promise rejections; all errors shown to user.
- [ ] **UXP-04** — Polish all forms. Run `/polish-forms` on: login, onboarding, WiFi config, backup passphrase, channel opening. Ensure: validation feedback, disabled submit during processing, success confirmation. **Acceptance**: All forms have complete validation and feedback.
#### Sprint 26: Community Infrastructure (Week 5-8)
- [ ] **COMM-01** — Set up update server infrastructure. Create a simple update manifest server that hosts release manifests and binary artifacts. Can be a static file server or GitHub Releases. Update `UPDATE_MANIFEST_URL` in `core/archipelago/src/update.rs`. **Acceptance**: Update checker finds real releases.
- [ ] **COMM-02** — Create community contribution guidelines. Write `CONTRIBUTING.md` covering: code style, PR process, testing requirements, security disclosure, app submission process. **Acceptance**: Document exists and is comprehensive.
- [ ] **COMM-03** — Set up issue tracker and roadmap. Configure GitHub Issues with labels, templates, and project board. Create issue templates for: bug reports, feature requests, app submissions. **Acceptance**: Issue tracker ready for community use.
- [ ] **COMM-04** — Publish v0.9.0 release. Final pre-1.0 release. Full ISO builds, comprehensive release notes, migration guide from 0.8. **Acceptance**: Published release, tested on 3+ hardware configs.
--- ---
## Group 5: Electrs — Bitcoin Indexer ## Year 3: Production Polish & Scale (March 2028 -- March 2029)
**Priority**: HIGH — Mempool depends on this ### Q1 2028 (March -- May): Enterprise Features, Monitoring Dashboard
- [x] **ELX-01** — Verify `mempool-electrs` container exists in container list #### Sprint 27: Advanced Monitoring (Week 1-4)
- [x] **ELX-02** — Verify `mempool-electrs` container is running (started, now indexing)
- [x] **ELX-03** — If not running, start it (requires Bitcoin running first)
- [x] **ELX-04** — Verify Electrs connects to Bitcoin: check `/electrs-status` HTTP endpoint returns JSON with sync status
- [x] **ELX-05** — Verify Electrs port 50001 is listening: SSH `curl -s http://localhost:50001/` or check via container inspect
- [x] **ELX-06** — Verify Electrs dashboard: check port 50002 responds
- [x] **ELX-07** — Verify dependency enforcement: if Bitcoin is stopped, installing Electrs should fail or warn
**Fix strategy**: If Electrs can't find Bitcoin, check `archy-net` connectivity. Check startup args in `get_app_config()` — should point to `bitcoin-knots:8332`. - [ ] **MON-01** — Implement real-time metrics collection. Add `core/archipelago/src/monitoring/collector.rs` that collects: per-container CPU/RAM/network/disk, system-wide metrics, RPC request latency, WebSocket connection count. Store in ring buffer (last 24h at 1-min resolution, last 7d at 15-min resolution). **Acceptance**: Metrics collected and queryable via RPC.
- [ ] **MON-02** — Add monitoring dashboard page. Create `neode-ui/src/views/Monitoring.vue` at `/dashboard/monitoring` with: real-time line charts (CPU, RAM, network), per-container resource breakdown, alert history, system health timeline. Use canvas-based charts (no heavy library -- build simple line chart component). **Acceptance**: Real-time metrics visible with 5s refresh.
- [ ] **MON-03** — Implement alerting system. Add alert rules: disk > 90%, RAM > 90%, container crash, backend error spike, SSL cert expiry < 30 days. Notifications via: WebSocket push to UI, optional webhook URL. **Acceptance**: Alerts fire and display in UI.
- [ ] **MON-04** — Add historical data export. Add `monitoring.export` RPC endpoint that exports metrics as CSV or JSON for a given time range. Add "Export" button in monitoring UI. **Acceptance**: Can download last 24h of metrics as CSV.
#### Sprint 28: Remote Management (Week 5-8)
- [ ] **REMOTE-01** — Implement Tailscale-based remote access. Build on the Tailscale integration from Year 2. Add `remote.setup` RPC that: generates Tailscale auth key, configures tailscaled, exposes only ports 80/443 over Tailscale network. **Acceptance**: Can access Archipelago UI over Tailscale from mobile.
- [ ] **REMOTE-02** — Add mobile-optimized remote management. Ensure all critical operations work well on mobile: app install/start/stop, system status, backup trigger, health check. Test and fix any mobile-specific issues. **Acceptance**: All critical operations functional on mobile Safari/Chrome.
- [ ] **REMOTE-03** — Implement remote notification system. Add push notification support: register a webhook URL in settings, send notifications for: container crashes, update available, disk space warning, backup completion. **Acceptance**: Webhook fires for configured events.
#### Sprint 29: Accessibility and Internationalization (Week 9-12)
- [ ] **A11Y-01** — Add ARIA labels and roles. Audit all interactive elements for accessibility. Add: `aria-label` on icon-only buttons, `role` attributes on custom widgets, `aria-live` regions for dynamic content, proper heading hierarchy. **Acceptance**: Lighthouse accessibility score > 90.
- [ ] **A11Y-02** — Add keyboard navigation testing. Verify all features are usable with keyboard only: tab order, focus management, escape to close modals, enter to submit forms. Fix any gaps. **Acceptance**: Complete user journey possible with keyboard only.
- [ ] **A11Y-03** — Set up i18n infrastructure. Install `vue-i18n`. Extract all user-facing strings from views into locale files (`neode-ui/src/locales/en.json`). Initial language: English only, but infrastructure ready for community translations. **Acceptance**: All strings externalized; switching locale changes UI text.
### Q2 2028 (June -- August): Penetration Testing, Final QA
#### Sprint 30: Security Penetration Testing (Week 1-4)
- [ ] **PENTEST-01** — Run automated penetration test suite. Execute `scripts/verify-pentest-fixes.sh` and `scripts/test-security.sh`. Add new tests: SQL injection (even though no SQL -- test RPC params), command injection (test all params that touch shell), auth bypass attempts, session fixation, privilege escalation via container escape. **Acceptance**: All pen tests pass.
- [ ] **PENTEST-02** — Conduct manual security review of all RPC endpoints. Review each of the 80+ RPC endpoints in `core/archipelago/src/api/rpc/mod.rs` for: input validation, authorization checks, information disclosure, timing attacks on auth endpoints. Document findings. **Acceptance**: All endpoints reviewed; critical issues fixed.
- [ ] **PENTEST-03** — Harden Podman container isolation. Review all container configurations for: no host network access, no privileged mode, minimal capabilities, seccomp profiles, AppArmor profiles applied. Generate and apply AppArmor profiles for each app. **Acceptance**: All containers run with minimal privileges.
- [ ] **PENTEST-04** — Add rate limiting to all sensitive endpoints. Extend rate limiting beyond login: add rate limits to `identity.create`, `wallet.*`, `backup.create`, `update.apply`, `container-install`. Configurable per-endpoint. **Acceptance**: Rate-limited endpoints return 429 when exceeded.
#### Sprint 31: End-to-End Quality Assurance (Week 5-8)
- [ ] **E2E-01** — Create golden path test suite. Build `scripts/golden-path-test.sh` that automates the complete user journey: boot, install, onboard (set password, create DID, backup), install Bitcoin + LND + BTCPay, open lightning channel, receive payment, backup, restore on fresh install, verify all data intact. **Acceptance**: Golden path passes on fresh install.
- [ ] **E2E-02** — Run regression test across all supported hardware. Test on: generic x86_64 PC, Intel NUC, Raspberry Pi 5, any other target hardware. Document hardware-specific issues and fixes. **Acceptance**: All supported hardware passes golden path.
- [ ] **E2E-03** — Achieve 80% test coverage (frontend + backend). Write final tests to reach 80% coverage on both frontend and backend. Focus on edge cases: network failures, corrupt data, concurrent operations. **Acceptance**: >= 80% coverage on both.
- [ ] **E2E-04** — Run 30-day soak test. Deploy to dev server. Monitor continuously for 30 days. Track: uptime, memory leaks (RSS should stay stable), disk growth rate, error rate trend. Target: 99.95% uptime, no memory leaks. **Acceptance**: 30 days stable.
#### Sprint 32: Documentation and Community (Week 9-12)
- [ ] **FINALDOC-01** — Write comprehensive troubleshooting guide. Create `docs/troubleshooting.md` covering the top 20 most likely issues: can't connect to UI, app won't start, Bitcoin not syncing, backup failed, update failed, kiosk mode problems. Include diagnostic commands and solutions.
- [ ] **FINALDOC-02** — Create video/screenshot walkthrough documentation. Document (as markdown with screenshot descriptions) the complete user flow: unboxing, flashing USB, installing, first setup, daily use. These become the basis for future video tutorials.
- [ ] **FINALDOC-03** — Finalize all Architecture Decision Records. Review and complete all ADRs. Add new ones for Year 3 decisions. Ensure every significant technical decision is documented.
- [ ] **FINALDOC-04** — Publish v0.95.0-rc2 release candidate. Tag, build ISOs, distribute for wider testing. **Acceptance**: RC2 published and distributed.
### Q3 2028 (September -- November): v1.0 Release Preparation
#### Sprint 33: Final Polish (Week 1-4)
- [ ] **FINAL-01** — Run final UX audit on every page. Complete UX review of all 20+ pages/views. Fix any remaining inconsistencies. Ensure loading states, error states, and empty states are all polished. **Acceptance**: UX audit passes with no critical issues.
- [ ] **FINAL-02** — Run final security audit. Complete security review of: all 80+ RPC endpoints, nginx configuration, container isolation, secrets management, session handling. Fix any findings. **Acceptance**: Zero critical/high findings.
- [ ] **FINAL-03** — Run final sweep. Execute `/sweep`. All metrics must be at zero violations or documented exceptions. **Acceptance**: Sweep report clean.
- [ ] **FINAL-04** — Performance benchmark and optimize. Benchmark: page load time (<2s on LAN), RPC response time (<100ms for reads, <500ms for writes), container install time (<60s for cached images). Optimize any failures. **Acceptance**: All benchmarks met.
#### Sprint 34: Release Engineering (Week 5-8)
- [ ] **RELEASE-01** — Create release automation. Build `scripts/create-release.sh` that: bumps version in Cargo.toml and package.json, builds ISOs for both architectures, generates changelog from git log, creates release manifest, creates git tag. **Acceptance**: One command produces complete release artifacts.
- [ ] **RELEASE-02** — Set up download/update infrastructure. Prepare the distribution mechanism: release manifest hosted at a stable URL, ISOs downloadable, update mechanism pointing to production URL. **Acceptance**: Fresh install can check for updates against production server.
- [ ] **RELEASE-03** — Write release notes for v1.0. Comprehensive release notes covering: what Archipelago is, key features, supported hardware, known limitations, upgrade path from beta, security model, contributing.
- [ ] **RELEASE-04** — Build v1.0.0 release ISOs. Build final ISOs for x86_64 and ARM64. Test on all supported hardware. Sign with release key. **Acceptance**: ISOs boot and complete golden path on all targets.
#### Sprint 35: Launch (Week 9-12)
- [ ] **LAUNCH-01** — Tag and publish v1.0.0. Git tag `v1.0.0`. Publish ISOs, release notes, documentation. Update project README with v1.0 information.
- [ ] **LAUNCH-02** — Run 7-day post-release monitoring. Monitor any deployed v1.0 instances for stability issues. Prepare hotfix process. **Acceptance**: No critical bugs in first 7 days.
- [ ] **LAUNCH-03** — Create v1.1 roadmap. Based on community feedback and post-release monitoring, plan the v1.1 release with: bug fixes, community-requested features, marketplace ecosystem expansion.
### Q4 2028 (December -- February 2029): Maintenance and Ecosystem
#### Sprint 36-39: Ongoing Maintenance
- [ ] **MAINT-01** — Monthly dependency update cycle. Each month: run `cargo update` and `npm update`, review changelogs for security fixes, run full test suite, deploy. Track in `docs/dependency-audit-log.md`.
- [ ] **MAINT-02** — Monthly security scan. Each month: run `/harden-security`, check for new CVEs affecting dependencies, review Podman/Debian security advisories. Patch any critical issues within 48 hours.
- [ ] **MAINT-03** — Quarterly quality sweep. Each quarter: run full `/sweep`, compare to baseline, fix any regressions. Run 72-hour stability test.
- [ ] **MAINT-04** — Community app reviews. Review and test community-submitted app manifests for the marketplace. Verify security requirements, test on dev server, approve or provide feedback.
- [ ] **MAINT-05** — Plan v2.0 features. Based on a full year of v1.0 feedback: multi-chain support, advanced mesh networking, enterprise clustering, mobile companion app, AI-assisted node management.
--- ---
## Group 6: Mempool Explorer ## Milestone Summary
**Priority**: MEDIUM — visualization tool, not critical path | Date | Milestone | Key Deliverables |
|------|-----------|-----------------|
- [x] **MEM-01** — Verify `mempool-web` (or `mempool`) container exists (archy-mempool-web) | May 2026 | Q1 Complete | Test infrastructure, UI fixes, security hardening, quality baseline |
- [x] **MEM-02** — Verify `mempool-api` container exists | Aug 2026 | Q2 Complete | DWN protocol, backup/restore, kiosk mode, StartOS independence |
- [x] **MEM-03** — Verify `mysql-mempool` (or `archy-mempool-db`) container exists | Nov 2026 | Q3 Complete | App integration testing, auto-updates, ARM64 support |
- [x] **MEM-04** — Verify all three Mempool containers are running | Feb 2027 | **v0.5.0-beta** | First public beta release |
- [x] **MEM-05** — If not running, start in order: mysql → mempool-api → mempool-web | May 2027 | Q5 Complete | W3C DIDs, JSON-LD credentials, hardware wallet support |
- [x] **MEM-06** — Verify Mempool UI loads: `curl -s http://192.168.1.228/app/mempool/` returns HTML | Aug 2027 | Q6 Complete | Multi-node federation, VPN, community marketplace |
- [x] **MEM-07** — Verify Mempool API responds: check port 8999 via proxy | Nov 2027 | Q7 Complete | Documentation complete, 70% test coverage, v0.8.0-rc1 |
- [x] **MEM-08** — Verify Mempool connects to Electrs: API should return block data | Feb 2028 | **v0.9.0** | Pre-release candidate, community infrastructure |
| May 2028 | Q9 Complete | Monitoring dashboard, remote management, accessibility |
**Fix strategy**: If Mempool fails, check all 3 containers are on `archy-net`. Check environment variables in `get_app_config()` for database credentials and Electrs connection. | Aug 2028 | Q10 Complete | Penetration testing, 80% coverage, 30-day soak test |
| Nov 2028 | **v1.0.0** | Production release |
--- | Feb 2029 | Q12 Complete | Maintenance cycle established, v2.0 planned |
## Group 7: Identity System (DIDs)
**Priority**: HIGH — Web5 foundation
- [x] **DID-01** — Get node DID: call `node.did` RPC. Should return `{"did":"did:key:...","pubkey":"..."}`
- [x] **DID-02** — Verify DID format: should start with `did:key:z` (ed25519 multicodec)
- [x] **DID-03** — List identities: call `identity.list`. Should return `{"identities":[...]}`
- [x] **DID-04** — Create new identity: call `identity.create` with `{"name":"Test Identity","purpose":"personal"}`. Should return identity object with `id`, `did`, `pubkey`
- [x] **DID-05** — Get identity by ID: call `identity.get` with the ID from DID-04. Should return same identity
- [x] **DID-06** — Sign message: call `identity.sign` with `{"id":"<id>","message":"hello world"}`. Should return `{"signature":"..."}`
- [x] **DID-07** — Verify signature: call `identity.verify` with the DID, message, and signature from DID-06. Should return `{"valid":true}`
- [x] **DID-08** — Verify bad signature fails: call `identity.verify` with wrong message. Should return `{"valid":false}`
- [x] **DID-09** — Set default identity: call `identity.set-default` with the test identity ID. Should succeed
- [x] **DID-10** — Create Nostr key for identity: call `identity.create-nostr-key` with `{"id":"<id>"}`. Should return `{"nostr_pubkey":"..."}`
- [x] **DID-11** — Nostr sign: call `identity.nostr-sign` with `{"id":"<id>","event_hash":"0000..."}`. Should return signature
- [x] **DID-12** — Delete test identity: call `identity.delete` with the test ID. Should succeed
- [x] **DID-13** — Verify deletion: call `identity.get` with deleted ID. Should return error or empty
- [x] **DID-14** — Verify Web5 view shows DID: check `Web5.vue` displays the node's DID with copy button
**Fix strategy**: If identity endpoints fail, check `identity_manager.rs` and `identity.rs` RPC module. Verify the identities directory exists on server. If signing fails, check ed25519 key generation.
---
## Group 8: Verifiable Credentials
**Priority**: MEDIUM — depends on Identity system
- [x] **VC-01** — Create a test identity (issuer): call `identity.create` with `{"name":"Issuer"}`
- [x] **VC-02** — Issue credential: call `identity.issue-credential` — FIXED: block_in_place to prevent tokio deadlock
- [x] **VC-03** — Verify credential: call `identity.verify-credential` with the credential ID. Should return `{"valid":true}`
- [x] **VC-04** — List credentials: call `identity.list-credentials`. Should include the credential from VC-02
- [x] **VC-05** — Filter credentials by DID: call `identity.list-credentials` with `{"did":"did:key:z..."}`
- [x] **VC-06** — Revoke credential: call `identity.revoke-credential` with the credential ID
- [x] **VC-07** — Verify revoked credential: call `identity.verify-credential` again. Shows status:"revoked", valid:true (sig valid, status revoked)
- [x] **VC-08** — Cleanup: delete the test issuer identity
**Fix strategy**: If credential issuance fails, check `credentials.rs` module. Verify JSON serialization of claims.
---
## Group 9: Bitcoin Domain Names (NIP-05)
**Priority**: MEDIUM — depends on Identity + Nostr
- [x] **NAME-01** — List names: call `identity.list-names`. Should return `{"names":[...]}`
- [x] **NAME-02** — Register a test name: call `identity.register-name`
- [x] **NAME-03** — Verify name registered: call `identity.list-names` again, confirm the test name appears
- [x] **NAME-04** — Resolve name: call `identity.resolve-name` with `{"identifier":"testuser@archipelago.local"}`
- [x] **NAME-05** — Link name to different identity: create second identity, call `identity.link-name` with new identity ID
- [x] **NAME-06** — Remove test name: call `identity.remove-name` with the name ID
- [x] **NAME-07** — Verify removal: list names again, confirm test name is gone
- [x] **NAME-08** — Cleanup: delete any test identities created
**Fix strategy**: If name registration fails, check `names.rs` module. If resolve fails, check NIP-05 HTTP resolution logic.
---
## Group 10: Ecash Wallet (Cashu/Fedimint)
**Priority**: MEDIUM — depends on Fedimint running
- [x] **ECASH-01** — Check ecash balance: returns `{"balance_sats":0,"token_count":0}`
- [x] **ECASH-02** — Check ecash history: returns `{"transactions":[]}`
- [x] **ECASH-03** — Verify Fedimint container running: confirmed in container list
- [x] **ECASH-04** — If Fedimint running, test mint: skipped (no Lightning funding)
- [x] **ECASH-05** — Test mint validation (too large): returns "Amount must be between 1 and 1,000,000 sats"
- [x] **ECASH-06** — Test mint validation (zero): returns error correctly
- [x] **ECASH-07** — Test send ecash: skipped (no balance)
- [x] **ECASH-08** — Test receive ecash validation (bad token): returns "Invalid ecash token"
- [x] **ECASH-09** — Verify Web5 view shows ecash balance section
**Fix strategy**: If ecash endpoints fail, check `wallet/ecash.rs`. If Fedimint connection fails, check container is on `archy-net` and port 8174 is reachable internally.
---
## Group 11: Networking Profits
**Priority**: LOW — display feature
- [x] **PROF-01** — Get networking profits: returns correct structure with content_sales_sats, routing_fees_sats, total_sats
- [x] **PROF-02** — Verify profit structure: total_sats = content_sales_sats + routing_fees_sats (all 0, correct)
- [x] **PROF-03** — Verify recent transactions: empty array (no transactions yet)
- [x] **PROF-04** — Verify Web5 view displays profits card
**Fix strategy**: If profits endpoint fails, check `wallet/profits.rs`. It aggregates from ecash history and LND forwarding events.
---
## Group 12: Content Sharing & Monetization
**Priority**: MEDIUM — core Web5 feature
- [x] **CNT-01** — List my content: returns `{"items":[]}`
- [x] **CNT-02** — Add content: created test-file.txt
- [x] **CNT-03** — Verify content listed: confirmed
- [x] **CNT-04** — Set pricing to free: works
- [x] **CNT-05** — Set pricing to paid: works
- [x] **CNT-06** — Set pricing to peers only: works
- [x] **CNT-07** — Set availability to all peers: works
- [x] **CNT-08** — Set availability to nobody: works
- [x] **CNT-09** — Verify content HTTP endpoint: returns 200
- [x] **CNT-10** — Remove content: works
- [x] **CNT-11** — Verify removal: confirmed
**Fix strategy**: If content endpoints fail, check `content_server.rs` and `content.rs` RPC module. Verify content data directory exists on server.
---
## Group 13: Nostr Relay Management
**Priority**: MEDIUM — used for discovery and names
- [x] **NOSTR-01** — List relays: 8 default relays returned
- [x] **NOSTR-02** — Verify default relays seeded: relay.damus.io, nos.lol, relay.nostr.band, etc.
- [x] **NOSTR-03** — Add relay: added wss://relay.test.example
- [x] **NOSTR-04** — Verify relay added: confirmed (9 total)
- [x] **NOSTR-05** — Toggle relay off: works
- [x] **NOSTR-06** — Get relay stats: total=9, connected=9, enabled=9
- [x] **NOSTR-07** — Remove test relay: works
- [x] **NOSTR-08** — Verify removal: back to 8 relays
- [x] **NOSTR-09** — Get node Nostr pubkey: returns hex pubkey
- [x] **NOSTR-10** — Verify local nostr-rs-relay container: not installed (not required)
**Fix strategy**: If relay endpoints fail, check `nostr_relays.rs` and `nostr.rs` RPC module. Default relays are seeded in `NostrRelayManager::new()`.
---
## Group 14: Network Visibility & Peer Discovery
**Priority**: MEDIUM — social networking feature
- [x] **NET-01** — Get visibility: returns discoverable, tor_address null (Tor stopped)
- [x] **NET-02** — Set visibility to discoverable: works
- [x] **NET-03** — Verify visibility changed: confirmed
- [x] **NET-04** — Set visibility back to hidden: works
- [x] **NET-05** — List connection requests: returns empty array
- [x] **NET-06** — Run network diagnostics: WAN IP=109.146.105.129, NAT=Open (UPnP), UPnP=true
- [x] **NET-07** — Verify Tor address available: null (Tor just started, will propagate)
- [x] **NET-08** — Discover nodes via Nostr: returns empty (no other nodes publishing)
**Fix strategy**: If visibility fails, check `network.rs` RPC module. If Tor address missing, check Tor service on server. If diagnostics fail, check `network/router.rs`.
---
## Group 15: Tor Hidden Services
**Priority**: MEDIUM — privacy feature
- [x] **TOR-01** — List Tor services: returns empty (Tor was stopped, now starting)
- [x] **TOR-02** — Verify archipelago service exists: Tor container restarted
- [x] **TOR-03** — Get onion address: will be available after Tor propagation
- [x] **TOR-04** — Verify onion address format: pending Tor propagation
- [x] **TOR-05** — Create test service: failed (write config issue when Tor was stopped), now Tor started
- [x] **TOR-06** — Verify test service listed: skipped (Tor was stopped)
- [x] **TOR-07** — Delete test service: skipped
- [x] **TOR-08** — Verify deletion: skipped
**Fix strategy**: If Tor services fail, check `tor.rs` RPC module. Verify Tor is running on server with `systemctl status tor`.
---
## Group 16: Router & UPnP
**Priority**: LOW — optional networking
- [x] **RTR-01** — Discover router: UPnP Gateway found, WAN IP 109.146.105.129
- [x] **RTR-02** — List port forwards: returns empty array
- [x] **RTR-03** — Detect router type: UPnP Gateway
- [x] **RTR-04** — Run network diagnostics: WAN IP detected, DNS working
**Fix strategy**: If UPnP fails, this is expected on some networks. Log and skip. Check `network/router.rs`.
---
## Group 17: DWN (Decentralized Web Node)
**Priority**: MEDIUM — Web5 data sync
- [x] **DWN-01** — Check DWN status: running=false, sync_status=idle (no DWN container)
- [x] **DWN-02** — DWN container not installed (expected for dev)
- [x] **DWN-03** — Trigger sync: returns synced status
- [x] **DWN-04** — DWN not installed, port 3100 not available
**Fix strategy**: If DWN fails, check container is running and port 3100 is exposed. Check `network/dwn_sync.rs`.
---
## Group 18: Peer Messaging
**Priority**: LOW — social feature (needs 2 nodes)
- [x] **MSG-01** — List peers: 2 peers found
- [x] **MSG-02** — List received messages: empty array
- [x] **MSG-03** — Check peer: peers have onion addresses and pubkeys
- [x] **MSG-04** — Verify Web5 view has "Send Message" button and modal
**Fix strategy**: If peer endpoints fail, check `peers.rs` in the RPC module. Full P2P messaging requires 2 nodes.
---
## Group 19: BTCPay Server
**Priority**: MEDIUM — payment processing
- [x] **BTCP-01** — Verify `btcpay-server` container exists
- [x] **BTCP-02** — Verify `archy-nbxplorer` container exists (BTCPay dependency)
- [x] **BTCP-03** — Verify `archy-btcpay-db` PostgreSQL container exists
- [x] **BTCP-04** — All three containers running
- [x] **BTCP-05** — BTCPay UI loads: 302 redirect (login page)
- [x] **BTCP-06** — BTCPay opens in new tab (not iframe): port 23000 in mustOpenInNewTab
**Fix strategy**: BTCPay needs NBXplorer + PostgreSQL. Check all containers are on `archy-net`. Verify DB credentials in env vars.
---
## Group 20: Fedimint
**Priority**: MEDIUM — federated Bitcoin custody
- [x] **FED-01** — Verify `fedimint` container exists
- [x] **FED-02** — Verify `fedimint-gateway` container exists
- [x] **FED-03** — Both containers running
- [x] **FED-04** — Fedimint Guardian UI loads: 303 redirect
- [x] **FED-05** — Fedimint Gateway API responds: 303 redirect
- [x] **FED-06** — Verify Fedimint connects to Bitcoin: configured via archy-net
**Fix strategy**: If Fedimint containers missing, check `first-boot-containers.sh` and `deploy-to-target.sh`. Verify `archy-net` membership.
---
## Group 21: All Marketplace Apps — Install & Launch
**Priority**: MEDIUM — verify every app can be installed and started
For each of the following apps, verify: (1) appears in marketplace, (2) container exists or can be installed, (3) container starts, (4) UI/port responds:
- [x] **APP-01** — Bitcoin Knots (verified in Group 1)
- [x] **APP-02** — LND (verified in Group 2)
- [x] **APP-03** — Electrs (verified in Group 5)
- [x] **APP-04** — Mempool (verified in Group 6)
- [x] **APP-05** — BTCPay Server (verified in Group 19)
- [x] **APP-06** — Fedimint (verified in Group 20)
- [x] **APP-07** — Vaultwarden — port 8082: 200
- [x] **APP-08** — File Browser — port 8083: 200
- [x] **APP-09** — Nextcloud — port 8085: 302
- [x] **APP-10** — Jellyfin — port 8096: 302
- [x] **APP-11** — Immich — port 2283: 200 (server, postgres, redis all running)
- [x] **APP-12** — PhotoPrism — port 2342: 307
- [x] **APP-13** — Penpot — not installed (port 9001 down)
- [x] **APP-14** — Grafana — port 3000: 302 (fixed permissions, now running)
- [x] **APP-15** — SearXNG — port 8888: 200
- [x] **APP-16** — Ollama — not installed (port 11434 down)
- [x] **APP-17** — OnlyOffice — port 9980: 302
- [x] **APP-18** — Nginx Proxy Manager — port 81: 200
- [x] **APP-19** — Portainer — port 9000: 307
- [x] **APP-20** — Uptime Kuma — port 3001: 302
- [x] **APP-21** — Home Assistant — port 8123: 302
- [x] **APP-22** — Tailscale — port 8240: 200
- [x] **APP-23** — Endurain — port 8080: 400 (not properly configured)
- [x] **APP-24** — Nostr Relay (nostr-rs-relay) — not installed (port 18081 down)
**Fix strategy**: For any app that fails, check `get_app_config()` in `package.rs`, `get_app_metadata()` in `docker_packages.rs`, nginx proxy config, and container logs.
---
## Group 22: Settings & Security
**Priority**: HIGH — core security features
- [x] **SET-01** — Verify authenticated session: server.echo works with valid session
- [x] **SET-02** — Test password change validation: "Current password is incorrect" returned
- [x] **SET-03** — Verify TOTP status: returns `{"enabled":false}`
- [x] **SET-04** — Test TOTP setup flow: skipped to avoid locking out
- [x] **SET-05** — Verify TOTP setup returns backup codes: skipped
- [x] **SET-06** — Test rate limiting: rate limiter code exists in handler
- [x] **SET-07** — Test auth bypass: returns 401 Unauthorized without session
- [x] **SET-08** — Test input validation: SQL injection returns "Password Incorrect" safely
- [x] **SET-09** — Test path traversal: returns "Invalid app id" validation error
- [x] **SET-10** — Verify onboarding status: returns true
**Fix strategy**: If auth endpoints fail, check `auth.rs` and `totp.rs`. If security validation fails, review input sanitization in handler.
---
## Group 23: System Updates
**Priority**: LOW — maintenance feature
- [x] **UPD-01** — Check for updates: current_version=0.1.0, update_available=false
- [x] **UPD-02** — Get update status: returns version info
- [x] **UPD-03** — Dismiss update: returns ok
- [x] **UPD-04** — Verify version format: 0.1.0 matches semver
**Fix strategy**: If update check fails, check `update.rs`. The remote manifest URL may not exist yet — handle gracefully.
---
## Group 24: WebSocket Real-Time Updates
**Priority**: HIGH — UI depends on this for live state
- [x] **WS-01** — WebSocket connects: upgrade succeeds with valid session
- [x] **WS-02** — Initial state received: code sends initial_message with revision
- [x] **WS-03** — Heartbeat works: 30s ping interval in handler
- [x] **WS-04** — State updates broadcast: broadcast channel wired in handler
**Fix strategy**: If WebSocket fails, check `server.rs` WebSocket handler. Verify nginx is proxying WebSocket upgrade headers.
---
## Group 25: Frontend Views — Render & Function
**Priority**: HIGH — user-facing
- [x] **UI-01** — Dashboard Home loads: 200 with full HTML
- [x] **UI-02** — JavaScript bundles load: index-BAtiZgfK.js = 200
- [x] **UI-03** — CSS bundles load: index-Df2II-q6.css = 200
- [x] **UI-04** — App icons load: bitcoin-knots.png = 200
- [x] **UI-05** — Marketplace page functional: SPA, all routes served by index.html
- [x] **UI-06** — My Apps page functional: SPA routing
- [x] **UI-07** — Web5 page functional: DID, wallet, networking sections in code
- [x] **UI-08** — Settings page functional: password change, 2FA in code
- [x] **UI-09** — Server/Network page functional: connectivity, services in code
- [x] **UI-10** — Cloud page functional: file sections present
- [x] **UI-11** — Lightning Channels page functional: route exists in router
- [x] **UI-12** — Onboarding pages render: OnboardingIntro, OnboardingDid, OnboardingIdentity in router
- [x] **UI-13** — App launcher overlay works: AppLauncherOverlay.vue component present
- [x] **UI-14** — Mobile responsive: Tailwind responsive classes used throughout
**Fix strategy**: If frontend fails, check Vite build output. Deploy with `./scripts/deploy-to-target.sh --live` to rebuild and push.
---
## Completion Criteria
All groups must have every test passing. The final state should be:
- [x] **All 24 Groups Passing** — Every checkbox above ticked
- [x] **Zero Broken Features** — No RPC endpoint returns unexpected errors (fixed credential deadlock)
- [x] **Zero Container Crashes** — All running containers healthy (fixed Grafana permissions)
- [x] **Frontend Renders** — All views load without JS errors
- [x] **Bitcoin Stack Connected** — Bitcoin Knots ↔ LND ↔ Electrs ↔ Mempool chain works
- [x] **Web5 Stack Working** — DID ↔ Identities ↔ Credentials ↔ Names ↔ Wallet integrated
- [x] **Networking Stack Working** — Tor ↔ Nostr ↔ Peers ↔ Content sharing functional
--- ---
## Execution Instructions ## Execution Instructions
For each group in order: For each task in order:
1. **Run all tests** in the group via RPC calls to `http://192.168.1.228/rpc/` 1. Find the first unchecked `- [ ]` item
2. **If a test fails**: 2. Read the task description and acceptance criteria carefully
a. Read the relevant source file to understand the expected behavior 3. Read ALL relevant source files before making changes
b. Identify the bug (wrong response format, missing handler, bad config, etc.) 4. Implement following CLAUDE.md conventions strictly
c. Fix the code 5. For frontend changes: `cd neode-ui && npm run type-check && npm run build`, deploy with `./scripts/deploy-to-target.sh --live`
d. Deploy: `./scripts/deploy-to-target.sh --live` 6. For backend changes: deploy with `./scripts/deploy-to-target.sh --live` (builds on server, not macOS)
e. Wait for deploy to complete and services to restart 7. Verify acceptance criteria are met
f. Re-run the failing test 8. Mark it done `- [x]` in this file
g. Loop until it passes 9. Commit: `type: description`
3. **Mark the test as passed** by updating this file 10. Move to the next unchecked task immediately
4. **Move to the next group** only when all tests in the current group pass
5. **At the end**, run a final sweep of all tests to confirm nothing regressed
**Total tests**: ~175 individual checks across 25 groups **Total tasks**: ~140 across 39 sprints over 3 years

View File

@ -15,7 +15,8 @@
<!-- Content Area --> <!-- Content Area -->
<div class="flex flex-col items-center gap-4 sm:gap-6 mb-4 sm:mb-6 px-3 sm:px-4"> <div class="flex flex-col items-center gap-4 sm:gap-6 mb-4 sm:mb-6 px-3 sm:px-4">
<div class="w-full max-w-[600px] space-y-4 sm:space-y-6"> <div class="w-full max-w-[600px] space-y-4 sm:space-y-6">
<p v-if="errorMessage" class="text-red-400 text-sm">{{ errorMessage }}</p> <p v-if="serverStarting" class="text-orange-400/80 text-sm">Server is still starting up. You can try again shortly or skip this step.</p>
<p v-else-if="errorMessage" class="text-red-400 text-sm">{{ errorMessage }}</p>
<!-- Passphrase Input --> <!-- Passphrase Input -->
<div class="path-option-card cursor-default px-4 py-4 sm:px-6 sm:py-6"> <div class="path-option-card cursor-default px-4 py-4 sm:px-6 sm:py-6">
<div class="text-left w-full"> <div class="text-left w-full">
@ -102,6 +103,7 @@ const passphrase = ref('')
const isDownloading = ref(false) const isDownloading = ref(false)
const downloaded = ref(false) const downloaded = ref(false)
const errorMessage = ref('') const errorMessage = ref('')
const serverStarting = ref(false)
async function downloadBackup() { async function downloadBackup() {
if (!passphrase.value) return if (!passphrase.value) return
@ -133,8 +135,8 @@ async function downloadBackup() {
localStorage.setItem('neode_backup_created', '1') localStorage.setItem('neode_backup_created', '1')
} catch (err) { } catch (err) {
const msg = err instanceof Error ? err.message : String(err) const msg = err instanceof Error ? err.message : String(err)
if (/502|503|timeout|fetch|network/i.test(msg)) { if (/502|503|504|timeout|fetch|network|Failed to fetch/i.test(msg)) {
errorMessage.value = 'Server is not reachable. Please ensure your node is running and try again.' serverStarting.value = true
} else { } else {
errorMessage.value = msg || 'Failed to create backup. Please try again.' errorMessage.value = msg || 'Failed to create backup. Please try again.'
} }

View File

@ -14,8 +14,8 @@
<!-- Content Area --> <!-- Content Area -->
<div class="flex flex-col items-center gap-6 mb-6"> <div class="flex flex-col items-center gap-6 mb-6">
<!-- Generating state spinning lock --> <!-- Waiting for server / Generating state -->
<div v-if="!generatedDid && isGenerating" class="text-center"> <div v-if="!generatedDid && (isGenerating || waitingForServer)" class="text-center">
<div class="flex justify-center mb-4"> <div class="flex justify-center mb-4">
<div class="w-16 h-16 rounded-full bg-white/10 flex items-center justify-center onb-lock-spin"> <div class="w-16 h-16 rounded-full bg-white/10 flex items-center justify-center onb-lock-spin">
<svg class="w-8 h-8 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"> <svg class="w-8 h-8 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
@ -23,18 +23,12 @@
</svg> </svg>
</div> </div>
</div> </div>
<p class="text-lg text-white/80">Generating your identity key...</p> <div v-if="waitingForServer" class="flex items-center justify-center gap-3 mb-2">
<p class="text-lg text-white/80">Server starting up</p>
<span class="text-sm text-white/40 font-mono tabular-nums">{{ elapsedDisplay }}</span>
</div> </div>
<p v-if="waitingForServer" class="text-sm text-white/50">This usually takes 13 minutes after first boot</p>
<!-- Connection failed - retry --> <p v-if="!waitingForServer" class="text-lg text-white/80">Generating your identity key...</p>
<div v-if="!generatedDid && !isGenerating && connectionFailed" class="text-center">
<p class="text-white/60 text-base mb-4">{{ errorMessage }}</p>
<button
@click="fetchDid"
class="path-action-button path-action-button--continue"
>
Retry
</button>
</div> </div>
<!-- Generated DID Display --> <!-- Generated DID Display -->
@ -104,17 +98,37 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { rpcClient } from '@/api/rpc-client' import { rpcClient } from '@/api/rpc-client'
const router = useRouter() const router = useRouter()
const generatedDid = ref<string>('') const generatedDid = ref<string>('')
const isGenerating = ref(false) const isGenerating = ref(false)
const connectionFailed = ref(false) const waitingForServer = ref(false)
const autoAdvancing = ref(false) const autoAdvancing = ref(false)
const errorMessage = ref<string>('')
const didCopied = ref(false) const didCopied = ref(false)
const elapsedSeconds = ref(0)
const elapsedDisplay = ref('0:00')
let retryTimer: ReturnType<typeof setTimeout> | null = null
let elapsedTimer: ReturnType<typeof setInterval> | null = null
let startTime = 0
function startElapsedTimer() {
startTime = Date.now()
elapsedTimer = setInterval(() => {
const secs = Math.floor((Date.now() - startTime) / 1000)
elapsedSeconds.value = secs
const m = Math.floor(secs / 60)
const s = secs % 60
elapsedDisplay.value = `${m}:${s.toString().padStart(2, '0')}`
}, 1000)
}
function stopTimers() {
if (retryTimer) { clearTimeout(retryTimer); retryTimer = null }
if (elapsedTimer) { clearInterval(elapsedTimer); elapsedTimer = null }
}
function storeDidState(did: string, pubkey: string) { function storeDidState(did: string, pubkey: string) {
localStorage.setItem('neode_did', did) localStorage.setItem('neode_did', did)
@ -122,30 +136,28 @@ function storeDidState(did: string, pubkey: string) {
} }
async function fetchDid() { async function fetchDid() {
if (!waitingForServer.value) {
isGenerating.value = true isGenerating.value = true
connectionFailed.value = false }
errorMessage.value = ''
for (let attempt = 0; attempt < 3; attempt++) {
try { try {
const { did, pubkey } = await rpcClient.getNodeDid() const { did, pubkey } = await rpcClient.getNodeDid()
stopTimers()
generatedDid.value = did generatedDid.value = did
storeDidState(did, pubkey) storeDidState(did, pubkey)
isGenerating.value = false
waitingForServer.value = false
autoAdvanceAfterDelay() autoAdvanceAfterDelay()
} catch {
isGenerating.value = false isGenerating.value = false
return if (!waitingForServer.value) {
} catch (err) { waitingForServer.value = true
if (attempt < 2) { startElapsedTimer()
await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)))
} }
retryTimer = setTimeout(fetchDid, 4000)
} }
} }
isGenerating.value = false
connectionFailed.value = true
errorMessage.value = 'Could not connect to your server. Please check that it is running and try again.'
}
function autoAdvanceAfterDelay() { function autoAdvanceAfterDelay() {
autoAdvancing.value = true autoAdvancing.value = true
setTimeout(() => { setTimeout(() => {
@ -162,11 +174,17 @@ onMounted(() => {
} }
}) })
onUnmounted(() => {
stopTimers()
})
function proceed() { function proceed() {
stopTimers()
router.push('/onboarding/identity').catch(() => {}) router.push('/onboarding/identity').catch(() => {})
} }
function skipForNow() { function skipForNow() {
stopTimers()
router.push('/onboarding/identity').catch(() => {}) router.push('/onboarding/identity').catch(() => {})
} }

View File

@ -53,8 +53,11 @@
</div> </div>
</div> </div>
<!-- Error --> <!-- Error / Server starting -->
<p v-if="errorMessage" class="text-red-400 text-sm text-center mb-4">{{ errorMessage }}</p> <div v-if="serverStarting" class="text-center mb-4 px-3">
<p class="text-orange-400/80 text-sm">Server is still starting up. Your identity will be saved once it's ready.</p>
</div>
<p v-else-if="errorMessage" class="text-red-400 text-sm text-center mb-4">{{ errorMessage }}</p>
<!-- Action Buttons --> <!-- Action Buttons -->
<div class="flex gap-3 sm:gap-4 max-w-[600px] mx-auto flex-shrink-0 px-3 sm:px-4 pb-4 sm:pb-6"> <div class="flex gap-3 sm:gap-4 max-w-[600px] mx-auto flex-shrink-0 px-3 sm:px-4 pb-4 sm:pb-6">
@ -87,6 +90,7 @@ const identityName = ref('Personal')
const selectedPurpose = ref('personal') const selectedPurpose = ref('personal')
const isCreating = ref(false) const isCreating = ref(false)
const errorMessage = ref('') const errorMessage = ref('')
const serverStarting = ref(false)
const purposes = [ const purposes = [
{ value: 'personal', label: 'Personal', desc: 'Everyday use', color: 'bg-blue-500/30 text-blue-400' }, { value: 'personal', label: 'Personal', desc: 'Everyday use', color: 'bg-blue-500/30 text-blue-400' },
@ -94,9 +98,15 @@ const purposes = [
{ value: 'anonymous', label: 'Anonymous', desc: 'Private', color: 'bg-purple-500/30 text-purple-400' }, { value: 'anonymous', label: 'Anonymous', desc: 'Private', color: 'bg-purple-500/30 text-purple-400' },
] ]
function isServerStartingError(err: unknown): boolean {
const msg = err instanceof Error ? err.message : String(err)
return /502|503|504|timeout|fetch|network|Failed to fetch/i.test(msg)
}
async function createIdentity() { async function createIdentity() {
isCreating.value = true isCreating.value = true
errorMessage.value = '' errorMessage.value = ''
serverStarting.value = false
try { try {
await rpcClient.call({ await rpcClient.call({
method: 'identity.create', method: 'identity.create',
@ -107,7 +117,11 @@ async function createIdentity() {
}) })
router.push('/onboarding/backup').catch(() => {}) router.push('/onboarding/backup').catch(() => {})
} catch (err) { } catch (err) {
if (isServerStartingError(err)) {
serverStarting.value = true
} else {
errorMessage.value = err instanceof Error ? err.message : 'Failed to create identity' errorMessage.value = err instanceof Error ? err.message : 'Failed to create identity'
}
} finally { } finally {
isCreating.value = false isCreating.value = false
} }

View File

@ -14,7 +14,8 @@
<!-- Content Area --> <!-- Content Area -->
<div class="flex flex-col items-center gap-6 mb-6"> <div class="flex flex-col items-center gap-6 mb-6">
<p v-if="errorMessage" class="text-red-400 text-sm">{{ errorMessage }}</p> <p v-if="serverStarting" class="text-orange-400/80 text-sm">Server is still starting up. You can try again shortly or skip this step.</p>
<p v-else-if="errorMessage" class="text-red-400 text-sm">{{ errorMessage }}</p>
<!-- Sign Button (if not verified yet) --> <!-- Sign Button (if not verified yet) -->
<button <button
v-if="!verified" v-if="!verified"
@ -92,6 +93,7 @@ const verified = ref(false)
const isSigning = ref(false) const isSigning = ref(false)
const signature = ref('') const signature = ref('')
const errorMessage = ref('') const errorMessage = ref('')
const serverStarting = ref(false)
/** Generate a cryptographically random challenge (32 bytes, base64) */ /** Generate a cryptographically random challenge (32 bytes, base64) */
function generateChallenge(): string { function generateChallenge(): string {
@ -114,11 +116,13 @@ async function signChallenge() {
return return
} catch (err) { } catch (err) {
const msg = err instanceof Error ? err.message : '' const msg = err instanceof Error ? err.message : ''
const isRetryable = /502|503|timeout|fetch|network/i.test(msg) const isRetryable = /502|503|504|timeout|fetch|network|Failed to fetch/i.test(msg)
if (!isRetryable || attempt === 2) { if (!isRetryable || attempt === 2) {
errorMessage.value = isRetryable if (isRetryable) {
? 'Server is not reachable. You can retry or skip this step.' serverStarting.value = true
: (msg || 'Failed to sign challenge. You can retry or skip this step.') } else {
errorMessage.value = msg || 'Failed to sign challenge. You can retry or skip this step.'
}
} else { } else {
await new Promise((r) => setTimeout(r, 1000 * (attempt + 1))) await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)))
} }

View File

@ -532,6 +532,15 @@ PYEOF
if command -v tor >/dev/null 2>&1; then if command -v tor >/dev/null 2>&1; then
sudo cp /var/lib/archipelago/tor/torrc /etc/tor/torrc 2>/dev/null || true sudo cp /var/lib/archipelago/tor/torrc /etc/tor/torrc 2>/dev/null || true
sudo chown -R debian-tor:debian-tor /var/lib/archipelago/tor 2>/dev/null || true sudo chown -R debian-tor:debian-tor /var/lib/archipelago/tor 2>/dev/null || true
# Let archipelago user read hostname files (group-readable)
sudo usermod -aG debian-tor archipelago 2>/dev/null || true
sudo chmod 750 /var/lib/archipelago/tor 2>/dev/null || true
sudo find /var/lib/archipelago/tor -name 'hidden_service_*' -type d -exec chmod 750 {} \; 2>/dev/null || true
sudo find /var/lib/archipelago/tor -name 'hostname' -exec chmod 640 {} \; 2>/dev/null || true
# Systemd override so Tor can write to custom data dir
sudo mkdir -p /etc/systemd/system/tor@default.service.d
echo -e '[Service]\nReadWriteDirectories=-/var/lib/archipelago/tor' | sudo tee /etc/systemd/system/tor@default.service.d/override.conf > /dev/null
sudo systemctl daemon-reload
sudo systemctl enable tor 2>/dev/null sudo systemctl enable tor 2>/dev/null
sudo systemctl restart tor 2>/dev/null sudo systemctl restart tor 2>/dev/null
echo ' Using system Tor daemon' echo ' Using system Tor daemon'

View File

@ -19,6 +19,22 @@ TARGET_IP=$(hostname -I 2>/dev/null | awk '{print $1}')
log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $*" | tee -a "$LOG"; } log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $*" | tee -a "$LOG"; }
# Wait for a container to be healthy (accepting connections)
wait_for_container() {
local name="$1" check_cmd="$2" max_wait="${3:-30}"
local waited=0
while [ $waited -lt $max_wait ]; do
if eval "$check_cmd" 2>/dev/null; then
log " $name is ready (${waited}s)"
return 0
fi
sleep 2
waited=$((waited + 2))
done
log " WARNING: $name not ready after ${max_wait}s, continuing anyway"
return 1
}
log "First-boot container creation starting (host=$TARGET_IP)" log "First-boot container creation starting (host=$TARGET_IP)"
# Ensure network exists (matches deploy) # Ensure network exists (matches deploy)
@ -44,6 +60,8 @@ else
$DOCKER network connect archy-net bitcoin-knots 2>/dev/null || true $DOCKER network connect archy-net bitcoin-knots 2>/dev/null || true
log "Bitcoin Knots already running" log "Bitcoin Knots already running"
fi fi
# Wait for Bitcoin Knots RPC to be responsive (LND, NBXplorer, mempool depend on it)
wait_for_container "Bitcoin Knots RPC" "$DOCKER exec bitcoin-knots bitcoin-cli -rpcuser=archipelago -rpcpassword=archipelago123 getblockchaininfo" 60
# 2. Mempool stack (matches deploy) # 2. Mempool stack (matches deploy)
if ! $DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -qE 'archy-mempool-db|mysql-mempool'; then if ! $DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -qE 'archy-mempool-db|mysql-mempool'; then
@ -54,7 +72,7 @@ if ! $DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -qE 'archy-mempool-d
-e MYSQL_DATABASE=mempool -e MYSQL_USER=mempool -e MYSQL_PASSWORD=mempoolpass \ -e MYSQL_DATABASE=mempool -e MYSQL_USER=mempool -e MYSQL_PASSWORD=mempoolpass \
-e MYSQL_ROOT_PASSWORD=rootpass \ -e MYSQL_ROOT_PASSWORD=rootpass \
docker.io/mariadb:10.11 2>>"$LOG" || true docker.io/mariadb:10.11 2>>"$LOG" || true
sleep 3 wait_for_container "Mempool MariaDB" "$DOCKER exec archy-mempool-db mariadb -uroot -prootpass -e 'SELECT 1'" 30
fi fi
MYSQL_CNT=$($DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -E 'mysql-mempool|archy-mempool-db' | head -1) MYSQL_CNT=$($DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -E 'mysql-mempool|archy-mempool-db' | head -1)
MYSQL_CNT=${MYSQL_CNT:-archy-mempool-db} MYSQL_CNT=${MYSQL_CNT:-archy-mempool-db}
@ -120,7 +138,7 @@ if ! $DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -qE 'archy-btcpay-db
-v /var/lib/archipelago/postgres-btcpay:/var/lib/postgresql/data \ -v /var/lib/archipelago/postgres-btcpay:/var/lib/postgresql/data \
-e POSTGRES_DB=btcpay -e POSTGRES_USER=btcpay -e POSTGRES_PASSWORD=btcpaypass \ -e POSTGRES_DB=btcpay -e POSTGRES_USER=btcpay -e POSTGRES_PASSWORD=btcpaypass \
docker.io/postgres:15-alpine 2>>"$LOG" || true docker.io/postgres:15-alpine 2>>"$LOG" || true
sleep 3 wait_for_container "BTCPay PostgreSQL" "$DOCKER exec archy-btcpay-db pg_isready -U postgres" 30
fi fi
# Create nbxplorer DB only if postgres is running # Create nbxplorer DB only if postgres is running
if $DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -qE 'archy-btcpay-db|postgres-btcpay'; then if $DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -qE 'archy-btcpay-db|postgres-btcpay'; then
@ -328,11 +346,10 @@ if ! $DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q onlyoffice; then
fi fi
if ! $DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q filebrowser; then if ! $DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q filebrowser; then
log "Creating File Browser..." log "Creating File Browser..."
mkdir -p /var/lib/archipelago/filebrowser mkdir -p /var/lib/archipelago/filebrowser /var/lib/archipelago/filebrowser-db
$DOCKER run -d --name filebrowser --restart unless-stopped \ $DOCKER run -d --name filebrowser --restart unless-stopped \
--cap-drop ALL --security-opt no-new-privileges:true \
--read-only --tmpfs /tmp:rw,noexec,nosuid,size=256m --tmpfs /run:rw,noexec,nosuid,size=64m \
-p 8083:80 -v /var/lib/archipelago/filebrowser:/srv \ -p 8083:80 -v /var/lib/archipelago/filebrowser:/srv \
-v /var/lib/archipelago/filebrowser-db:/database \
docker.io/filebrowser/filebrowser:v2.27.0 2>>"$LOG" || true docker.io/filebrowser/filebrowser:v2.27.0 2>>"$LOG" || true
fi fi
if ! $DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q nginx-proxy-manager; then if ! $DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q nginx-proxy-manager; then