Phase 1a — Gradient Removal: - Replaced all gradient-button/gradient-card with glass-button/path-option-card - Removed banned gradient CSS classes Phase 1b — Security Hardening: - SecretsManager: AES-256-GCM encryption (core/security) - electrs_status: credentials from env vars instead of hardcoded - port_manager: RwLock proper error handling (no unwrap) - Pinned all 11 :latest manifest images to specific versions - parmanode converter: pinned inferred image versions Phase 1c — Code Quality: - Split rpc.rs (1795 lines) into 6 handler modules (auth, node, container, package, peers) - Removed sideload code (UI, store, RPC client, 3 doc files) - Fixed body background flash on logout/refresh - Replaced 30 TypeScript `any` types with proper types - Deleted HelloWorld.vue, removed TODO comments - Added set -euo pipefail to all shell scripts - Made deploy script verbose with timestamps and elapsed time Also adds: - CLAUDE.md project guide - docs/three-mode-ui-design.md — design spec for Easy/Pro/Chat UI modes - OnlineStatusPill component Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 KiB
CLAUDE.md — Archipelago (Archy) Project Guide
Project Overview
Archipelago is a Bitcoin Node OS — a bootable, self-sovereign personal server you flash to USB, install on hardware, and manage via a web UI. Similar to Umbrel/Start9/RaspiBlitz but custom-built with production-grade security.
Stack: Rust backend + Vue 3 (Composition API) + TypeScript (strict) + Vite 7 + Tailwind CSS + Pinia + Podman Target OS: Debian 12 (Bookworm) — x86_64 and ARM64 Current version: 0.1.0
Quick Reference
# Frontend local dev (mock backend on :5959, Vite on :8100)
cd neode-ui && npm start
# Deploy to live server (frontend + backend + restart services)
./scripts/deploy-to-target.sh --live
# Deploy to both servers
./scripts/deploy-to-target.sh --both
# Frontend build (outputs to web/dist/neode-ui/)
cd neode-ui && npm run build
# Type-check frontend
cd neode-ui && npm run type-check
# Rust checks (run on dev server, NOT macOS)
cargo clippy --all-targets --all-features
cargo fmt --all
cargo test --all-features
Dev server: http://192.168.1.228 | Local frontend: http://localhost:8100 (password: password123)
Architecture
Debian 12 (Bookworm)
├── Podman (rootless containers)
├── Nginx (port 80 → proxies /rpc/, /ws/, /health to backend)
├── Rust Backend (core/) — binary on port 5678
│ ├── core/archipelago/ — Main binary, RPC endpoints
│ ├── core/container/ — PodmanClient, manifest parser, dependency resolver, health monitor
│ ├── core/security/ — AppArmor profiles, secrets manager, Cosign image verifier
│ ├── core/performance/ — Resource manager
│ └── core/parmanode/ — Parmanode compatibility layer
└── Vue.js UI (neode-ui/)
├── src/api/ — RPC client (rpc-client.ts), WebSocket, container client
├── src/stores/ — Pinia stores
├── src/views/ — Page components
├── src/components/ — Reusable components
├── src/router/ — Vue Router
├── src/types/ — TypeScript type definitions
└── src/style.css — Global styles + Tailwind utilities
Data Paths (Server)
- App data:
/var/lib/archipelago/{app-id}/ - Secrets:
/var/lib/archipelago/secrets/{app-id}/(encrypted) - Frontend:
/opt/archipelago/web-ui/ - Backend binary:
/usr/local/bin/archipelago - Systemd service:
/etc/systemd/system/archipelago.service - Nginx config:
/etc/nginx/sites-available/archipelago
CRITICAL Workflow Rules
1. NEVER Build Rust on macOS for Linux
Always rsync source to the Linux dev server and build there. Building on macOS and copying the binary causes Exec format errors.
# Deploy does this automatically:
./scripts/deploy-to-target.sh --live
2. Always Deploy After Changes
After editing code (frontend, backend, scripts, or configs), deploy to the live server. Do not leave deployment to the user.
3. Frontend Build Output Path
Frontend builds to web/dist/neode-ui/ — NOT neode-ui/dist/.
4. Deploy-Test-Fix Loop
- Make the change
- Deploy with
./scripts/deploy-to-target.sh --live - Test at http://192.168.1.228
- If broken, fix and redeploy — repeat until working
- End loop only when everything works
5. SSH Access
- Primary:
archipelago@192.168.1.228— password:EwPDR8q45l0Upx@ - Secondary:
archipelago@192.168.1.198 - Credentials stored in gitignored
scripts/deploy-config.sh
sshpass -p 'EwPDR8q45l0Upx@' ssh -o StrictHostKeyChecking=no archipelago@192.168.1.228
Frontend Rules (Vue.js + TypeScript)
Component Standards
- Always
<script setup lang="ts">— never Options API, never plain JS - Pinia for all state management — focused single-purpose stores
- TypeScript strict mode — no
any, useunknownor proper types - Export types from dedicated
.types.tsfiles - Use type guards for runtime type checking
Styling — Global Classes Only
- ALWAYS create global utility classes in
neode-ui/src/style.css - NEVER use inline Tailwind classes directly in components
- Use semantic class names:
.glass-card,.glass-button,.gradient-button,.path-option-card
API Client Rules
- Use
@/api/rpc-client.tsfor RPC calls,@/api/container-client.tsfor containers - NEVER hardcode API endpoints — use environment variables
- Handle loading states, error states, retry logic for all async operations
CSS Class Hierarchy
| Class | Use | Hover |
|---|---|---|
.path-option-card |
Section containers, interactive cards (Settings-style) | Lifts -2px |
.glass-card |
Content containers, modals, panels | No |
.info-card |
Status badges, metric displays | No |
.info-card-button |
Action buttons inside info sections | Lifts, brightens |
bg-black/20 rounded-xl border border-white/10 |
Info sub-cards inside sections | No |
bg-white/5 |
Simple read-only info rows | No |
.glass-button |
ALL buttons (primary and secondary) | Subtle brighten |
.path-action-button |
Large action buttons (Logout, Continue) | Lifts -2px |
BANNED Classes — Do NOT Use
.gradient-button— REMOVED. Use.glass-buttoninstead. The gradient style breaks the clean glass aesthetic..gradient-card/.gradient-card-dark— REMOVED. Use.glass-cardor.path-option-cardinstead.
Design Tokens
- Font: Avenir Next (primary), Montserrat (
font-archipelago) - Spacing: 4px grid system, 16px default padding
- Glassmorphism:
background: rgba(0,0,0,0.60),backdrop-filter: blur(24px),inset 0 1px 0 rgba(255,255,255,0.22) - Transitions:
all 0.3s easestandard,translateY(-2px)hover,translateY(1px)active - Accent orange (Bitcoin):
#fb923c—#f59e0b - Green (success):
#4ade80| Red (danger):#ef4444| Blue (info):#3b82f6 - Text:
rgba(255,255,255,0.9)primary,rgba(255,255,255,0.6-0.7)muted
Tailwind Custom Values
- Blur:
backdrop-blur-glass(18px),backdrop-blur-glass-strong(24px) - Colors:
glass-dark(0,0,0,0.35),glass-darker(0,0,0,0.6),glass-border(255,255,255,0.18) - Shadows:
shadow-glass,shadow-glass-inset
Backend Rules (Rust)
Error Handling
- No
unwrap()orexpect()in production code — use?operator thiserrorfor library error types,anyhowfor application errors- Custom error types per module:
{module}::Error - Include context:
.context("What failed and why")
RPC Endpoints
- Use
rpc_toolkit::commandmacro for all endpoints - Use
#[context] ctx: RpcContextfor context - Return
Result<T, Error>— validate all inputs before processing
Async & Runtime
tokioruntime only — never mix with other async runtimes- Set timeouts on all external operations
- Use
select!for racing futures with timeouts - Handle shutdown gracefully with cancellation tokens
Code Organization
- New modules in
core/{module-name}/, add tocore/Cargo.tomlmembers snake_casefor all modules/files- Run
cargo clippy --all-targets --all-featuresandcargo fmt --allbefore commits
Logging
- Use
tracingfor structured logging — neverprintln! - Never log secrets, passwords, keys, or tokens
- Include context:
tracing::info!(user_id = %id, "Action")
Container & Security
App Manifests
- All manifests in
apps/{app-id}/manifest.yml - Follow spec in
docs/app-manifest-spec.md - Use
archipelago_container::PodmanClient— NEVER call Docker directly
Security Requirements (Non-Negotiable)
- ALWAYS
readonly_root: trueunless explicitly needed - ALWAYS drop all capabilities, add only required ones
- ALWAYS run as non-root user (UID > 1000)
- ALWAYS
no-new-privileges: true - NEVER use
latesttag — pin specific image versions - NEVER hardcode secrets — use
core/security/secrets_manager.rs
App Icons
Single source of truth: neode-ui/public/assets/img/app-icons/
Naming: {app-id}.{png|webp|svg} — do not duplicate elsewhere.
Code Quality
- Zero compiler warnings (Rust and TypeScript)
- Zero linter errors (clippy, eslint)
- Functions under 50 lines, single responsibility
- Comment WHY not WHAT — code should be self-documenting
- Remove dead code entirely — never comment it out
- No
TODO/FIXMEin commits — fix now or create issues - Workspace-relative paths only — NEVER hardcode
/Users/dorian/...
Git Conventions
Commit Format
type: description
Types: feat:, fix:, docs:, refactor:, test:, chore:, perf:
Rules
- Atomic commits — one logical change per commit
mainbranch always production-ready- Feature branches:
feature/description, bug fixes:fix/description - Never commit secrets,
.envfiles, or credentials - Tag releases:
v1.2.3(SemVer)
App Integration Checklist
When adding or fixing apps:
- Test the app UI loads on its configured port
- Auto-connect dependencies (Bitcoin RPC, LND, etc.) — apps must work out of the box
- Ensure
get_app_config()incore/archipelago/src/api/rpc.rshas correct env vars - Most apps launch in iframe; BTCPay (23000) and Home Assistant (8123) open in new tab (X-Frame-Options)
ISO Build
Build on the target server (has all dependencies):
ssh archipelago@192.168.1.228
cd ~/archy/image-recipe
sudo ./build-auto-installer-iso.sh
# Result: results/archipelago-auto-installer-*.iso
After testing on live server, always update ISO build to include changes. Sync system configs:
archipelago.service→image-recipe/configs/nginx-archipelago.conf→image-recipe/configs/
Key Documentation
docs/architecture.md— System architecturedocs/current-state.md— Current development phasedocs/development-setup.md— Local dev setupdocs/app-manifest-spec.md— YAML manifest specBUILD-GUIDE.md— ISO build guideDEPLOYMENT.md— Deployment detailsCHANGELOG.md— Version history