changes for build for sxsw
This commit is contained in:
parent
dbd4cf59d2
commit
d69c0d6724
84
.claude/memory/iso-build-session-2026-03-10.md
Normal file
84
.claude/memory/iso-build-session-2026-03-10.md
Normal 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
|
||||||
292
.claude/memory/project-plan.md
Normal file
292
.claude/memory/project-plan.md
Normal 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]
|
||||||
30
.claude/memory/unbundled-iso.md
Normal file
30
.claude/memory/unbundled-iso.md
Normal 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`
|
||||||
@ -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:
|
||||||
|
```bash
|
||||||
|
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/
|
||||||
|
```
|
||||||
|
|
||||||
## Build (on target server — recommended)
|
## Build variants
|
||||||
|
|
||||||
|
### Unbundled ISO (recommended for distribution — ~3GB)
|
||||||
|
No pre-bundled container images. Apps install on-demand from Marketplace (requires internet).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 'cd ~/archy/image-recipe && sudo ./build-auto-installer-iso.sh'
|
ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 \
|
||||||
|
'cd ~/archy/image-recipe && sudo ./build-unbundled-iso.sh'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Copy ISO back to Mac
|
Output: `results/archipelago-installer-unbundled-x86_64.iso`
|
||||||
|
|
||||||
|
### Full bundled ISO (~11GB)
|
||||||
|
All container images pre-bundled for offline install.
|
||||||
|
|
||||||
```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-auto-installer-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-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.
|
||||||
|
|||||||
@ -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"
|
||||||
|
|
||||||
echo "╔════════════════════════════════════════════════════════════════╗"
|
if [ "$UNBUNDLED" = "1" ]; then
|
||||||
echo "║ Building Archipelago Auto-Installer ISO (StartOS-like) ║"
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
echo "╚════════════════════════════════════════════════════════════════╝"
|
echo "║ Building Archipelago UNBUNDLED ISO (no pre-loaded apps) ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
|
else
|
||||||
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ Building Archipelago Auto-Installer ISO (StartOS-like) ║"
|
||||||
|
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,11 +727,15 @@ 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)
|
||||||
echo " Creating first-boot container creation service..."
|
# Skip for unbundled builds — no images pre-loaded, users install from Marketplace
|
||||||
if [ -f "$SCRIPT_DIR/../scripts/first-boot-containers.sh" ]; then
|
if [ "$UNBUNDLED" = "1" ]; then
|
||||||
cp "$SCRIPT_DIR/../scripts/first-boot-containers.sh" "$ARCH_DIR/scripts/"
|
echo " Skipping first-boot containers (UNBUNDLED: apps installed from Marketplace)"
|
||||||
chmod +x "$ARCH_DIR/scripts/first-boot-containers.sh"
|
else
|
||||||
cat > "$WORK_DIR/archipelago-first-boot-containers.service" <<'FBCSERVICE'
|
echo " Creating first-boot container creation service..."
|
||||||
|
if [ -f "$SCRIPT_DIR/../scripts/first-boot-containers.sh" ]; then
|
||||||
|
cp "$SCRIPT_DIR/../scripts/first-boot-containers.sh" "$ARCH_DIR/scripts/"
|
||||||
|
chmod +x "$ARCH_DIR/scripts/first-boot-containers.sh"
|
||||||
|
cat > "$WORK_DIR/archipelago-first-boot-containers.service" <<'FBCSERVICE'
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Create core Archipelago containers on first boot
|
Description=Create core Archipelago containers on first boot
|
||||||
After=archipelago-setup-tor.service network-online.target podman.service
|
After=archipelago-setup-tor.service network-online.target podman.service
|
||||||
@ -726,7 +751,8 @@ RemainAfterExit=yes
|
|||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
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
|
||||||
@ -737,19 +763,26 @@ 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)
|
||||||
DOCKER_UI_DIR="$SCRIPT_DIR/../docker"
|
# Skip for unbundled builds
|
||||||
if [ -d "$DOCKER_UI_DIR" ]; then
|
if [ "$UNBUNDLED" != "1" ]; then
|
||||||
echo " Bundling docker UI source files..."
|
DOCKER_UI_DIR="$SCRIPT_DIR/../docker"
|
||||||
mkdir -p "$ARCH_DIR/docker"
|
if [ -d "$DOCKER_UI_DIR" ]; then
|
||||||
for ui_dir in bitcoin-ui lnd-ui electrs-ui; do
|
echo " Bundling docker UI source files..."
|
||||||
if [ -d "$DOCKER_UI_DIR/$ui_dir" ]; then
|
mkdir -p "$ARCH_DIR/docker"
|
||||||
cp -r "$DOCKER_UI_DIR/$ui_dir" "$ARCH_DIR/docker/"
|
for ui_dir in bitcoin-ui lnd-ui electrs-ui; do
|
||||||
echo " ✅ Bundled $ui_dir source"
|
if [ -d "$DOCKER_UI_DIR/$ui_dir" ]; then
|
||||||
fi
|
cp -r "$DOCKER_UI_DIR/$ui_dir" "$ARCH_DIR/docker/"
|
||||||
done
|
echo " ✅ Bundled $ui_dir source"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo " ✅ Container images bundled (including Tor + first-boot)"
|
if [ "$UNBUNDLED" = "1" ]; then
|
||||||
|
echo " ✅ Unbundled build ready (Tor setup included, no container images)"
|
||||||
|
else
|
||||||
|
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..."
|
||||||
|
|
||||||
OUTPUT_ISO="$OUTPUT_DIR/archipelago-installer-x86_64.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"
|
||||||
|
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,25 +1550,45 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "╔════════════════════════════════════════════════════════════════╗"
|
if [ "$UNBUNDLED" = "1" ]; then
|
||||||
echo "║ ✅ AUTO-INSTALLER ISO CREATED! ║"
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
echo "╚════════════════════════════════════════════════════════════════╝"
|
echo "║ ✅ UNBUNDLED AUTO-INSTALLER ISO CREATED! ║"
|
||||||
echo ""
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
echo "📀 Output: $OUTPUT_ISO"
|
echo ""
|
||||||
echo " Size: $(du -h "$OUTPUT_ISO" | cut -f1)"
|
echo "📀 Output: $OUTPUT_ISO"
|
||||||
echo ""
|
echo " Size: $(du -h "$OUTPUT_ISO" | cut -f1)"
|
||||||
echo "🔥 This is a StartOS-like automatic installer!"
|
echo ""
|
||||||
echo ""
|
echo "🔥 Lightweight installer — apps downloaded on-demand!"
|
||||||
echo "Features:"
|
echo ""
|
||||||
echo " • Pre-built system (no internet needed during install)"
|
echo "Features:"
|
||||||
echo " • Auto-detects internal disk"
|
echo " • Pre-built system (no internet needed during install)"
|
||||||
echo " • One-button installation"
|
echo " • Auto-detects internal disk"
|
||||||
echo " • Boots directly to Archipelago after install"
|
echo " • One-button installation"
|
||||||
echo " • Pre-bundled container apps:"
|
echo " • Boots directly to Archipelago after install"
|
||||||
echo " - Bitcoin Knots v29"
|
echo " • NO pre-bundled apps (smaller ISO)"
|
||||||
echo " - LND v0.18.4"
|
echo " • Install any app from the Marketplace (internet required)"
|
||||||
echo " - Home Assistant"
|
echo ""
|
||||||
echo ""
|
else
|
||||||
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ ✅ AUTO-INSTALLER ISO CREATED! ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
echo "📀 Output: $OUTPUT_ISO"
|
||||||
|
echo " Size: $(du -h "$OUTPUT_ISO" | cut -f1)"
|
||||||
|
echo ""
|
||||||
|
echo "🔥 This is a StartOS-like automatic installer!"
|
||||||
|
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 " • Pre-bundled container apps:"
|
||||||
|
echo " - Bitcoin Knots v29"
|
||||||
|
echo " - LND v0.18.4"
|
||||||
|
echo " - Home Assistant"
|
||||||
|
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"
|
||||||
|
|||||||
33
image-recipe/build-unbundled-iso.sh
Executable file
33
image-recipe/build-unbundled-iso.sh
Executable 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" "$@"
|
||||||
881
loop/plan.md
881
loop/plan.md
@ -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
|
||||||
|
|||||||
@ -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.'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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">
|
||||||
</div>
|
<p class="text-lg text-white/80">Server starting up</p>
|
||||||
|
<span class="text-sm text-white/40 font-mono tabular-nums">{{ elapsedDisplay }}</span>
|
||||||
<!-- Connection failed - retry -->
|
</div>
|
||||||
<div v-if="!generatedDid && !isGenerating && connectionFailed" class="text-center">
|
<p v-if="waitingForServer" class="text-sm text-white/50">This usually takes 1–3 minutes after first boot</p>
|
||||||
<p class="text-white/60 text-base mb-4">{{ errorMessage }}</p>
|
<p v-if="!waitingForServer" class="text-lg text-white/80">Generating your identity key...</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,28 +136,26 @@ function storeDidState(did: string, pubkey: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function fetchDid() {
|
async function fetchDid() {
|
||||||
isGenerating.value = true
|
if (!waitingForServer.value) {
|
||||||
connectionFailed.value = false
|
isGenerating.value = true
|
||||||
errorMessage.value = ''
|
|
||||||
|
|
||||||
for (let attempt = 0; attempt < 3; attempt++) {
|
|
||||||
try {
|
|
||||||
const { did, pubkey } = await rpcClient.getNodeDid()
|
|
||||||
generatedDid.value = did
|
|
||||||
storeDidState(did, pubkey)
|
|
||||||
autoAdvanceAfterDelay()
|
|
||||||
isGenerating.value = false
|
|
||||||
return
|
|
||||||
} catch (err) {
|
|
||||||
if (attempt < 2) {
|
|
||||||
await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isGenerating.value = false
|
try {
|
||||||
connectionFailed.value = true
|
const { did, pubkey } = await rpcClient.getNodeDid()
|
||||||
errorMessage.value = 'Could not connect to your server. Please check that it is running and try again.'
|
stopTimers()
|
||||||
|
generatedDid.value = did
|
||||||
|
storeDidState(did, pubkey)
|
||||||
|
isGenerating.value = false
|
||||||
|
waitingForServer.value = false
|
||||||
|
autoAdvanceAfterDelay()
|
||||||
|
} catch {
|
||||||
|
isGenerating.value = false
|
||||||
|
if (!waitingForServer.value) {
|
||||||
|
waitingForServer.value = true
|
||||||
|
startElapsedTimer()
|
||||||
|
}
|
||||||
|
retryTimer = setTimeout(fetchDid, 4000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function autoAdvanceAfterDelay() {
|
function autoAdvanceAfterDelay() {
|
||||||
@ -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(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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) {
|
||||||
errorMessage.value = err instanceof Error ? err.message : 'Failed to create identity'
|
if (isServerStartingError(err)) {
|
||||||
|
serverStarting.value = true
|
||||||
|
} else {
|
||||||
|
errorMessage.value = err instanceof Error ? err.message : 'Failed to create identity'
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
isCreating.value = false
|
isCreating.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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'
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user