archy/CLAUDE.md
Dorian d3f0f1192e docs: expand app integration checklist with full file-by-file guide
Replaces the brief 4-item checklist with a comprehensive file-level
checklist covering backend, frontend, nginx, deploy, first-boot, and
ISO build. Ensures no file is missed when adding or modifying apps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 02:32:15 +00:00

12 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

  1. Make the change
  2. Deploy with ./scripts/deploy-to-target.sh --live
  3. Test at http://192.168.1.228
  4. If broken, fix and redeploy — repeat until working
  5. 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, use unknown or proper types
  • Export types from dedicated .types.ts files
  • 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.ts for RPC calls, @/api/container-client.ts for 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-button instead. The gradient style breaks the clean glass aesthetic.
  • .gradient-card / .gradient-card-dark — REMOVED. Use .glass-card or .path-option-card instead.

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 ease standard, 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() or expect() in production code — use ? operator
  • thiserror for library error types, anyhow for application errors
  • Custom error types per module: {module}::Error
  • Include context: .context("What failed and why")

RPC Endpoints

  • Use rpc_toolkit::command macro for all endpoints
  • Use #[context] ctx: RpcContext for context
  • Return Result<T, Error> — validate all inputs before processing

Async & Runtime

  • tokio runtime 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 to core/Cargo.toml members
  • snake_case for all modules/files
  • Run cargo clippy --all-targets --all-features and cargo fmt --all before commits

Logging

  • Use tracing for structured logging — never println!
  • 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::PodmanClientNEVER call Docker directly

Security Requirements (Non-Negotiable)

  • ALWAYS readonly_root: true unless 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 latest tag — 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/FIXME in 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
  • main branch always production-ready
  • Feature branches: feature/description, bug fixes: fix/description
  • Never commit secrets, .env files, or credentials
  • Tag releases: v1.2.3 (SemVer)

App Integration Checklist

When adding or fixing apps, every file below must be checked. Missing any one causes failures on fresh installs.

Backend (Rust)

  • core/archipelago/src/api/rpc/package.rsget_app_config(): ports, volumes, env vars, custom args
  • core/archipelago/src/api/rpc/package.rsneeds_archy_net: add if app needs container DNS
  • core/archipelago/src/api/rpc/package.rsget_app_capabilities(): add required caps (CHOWN, etc.)
  • core/archipelago/src/api/rpc/package.rs — dependency checks (e.g., electrs requires bitcoin)
  • core/archipelago/src/container/docker_packages.rsget_app_metadata(): title, description, icon, repo
  • core/archipelago/src/container/docker_packages.rs — UI address mapping (e.g., http://localhost:50002)

Frontend (Vue)

  • neode-ui/src/views/Marketplace.vuegetCuratedAppList(): marketplace entry with dockerImage
  • neode-ui/src/stores/appLauncher.ts — port-to-proxy mapping (if app has custom UI port)
  • neode-ui/src/views/AppDetails.vue — route ID mapping (if app ID differs from container name)

Nginx

  • image-recipe/configs/nginx-archipelago.conf/app/{id}/ proxy in HTTP block
  • image-recipe/configs/snippets/archipelago-https-app-proxies.conf/app/{id}/ proxy in HTTPS block
  • Any custom status endpoints (e.g., /electrs-status) proxied before the SPA catch-all

Deploy & First Boot

  • scripts/deploy-to-target.sh — container creation/update logic
  • scripts/first-boot-containers.sh — container created on fresh ISO install
  • Custom UI containers (e.g., electrs-ui): built and started in both deploy and first-boot

ISO Build

  • image-recipe/build-auto-installer-iso.shCAPTURE_PATTERNS: image captured from live server
  • image-recipe/build-auto-installer-iso.shCONTAINER_IMAGES: fallback image pulled from registry
  • image-recipe/build-auto-installer-iso.sh — docker UI source files bundled for build fallback
  • image-recipe/build-auto-installer-iso.sh — installer copies files to target disk

Runtime Verification

  • Test the app UI loads on its configured port
  • Auto-connect dependencies (Bitcoin RPC, LND, etc.) — apps must work out of the box
  • 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.serviceimage-recipe/configs/
  • nginx-archipelago.confimage-recipe/configs/

Key Documentation

  • docs/architecture.md — System architecture
  • docs/current-state.md — Current development phase
  • docs/development-setup.md — Local dev setup
  • docs/app-manifest-spec.md — YAML manifest spec
  • BUILD-GUIDE.md — ISO build guide
  • DEPLOYMENT.md — Deployment details
  • CHANGELOG.md — Version history