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>
277 lines
10 KiB
Markdown
277 lines
10 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
# 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.
|
|
|
|
```bash
|
|
# 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`
|
|
|
|
```bash
|
|
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::PodmanClient` — **NEVER** 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:
|
|
|
|
1. Test the app UI loads on its configured port
|
|
2. Auto-connect dependencies (Bitcoin RPC, LND, etc.) — apps must work out of the box
|
|
3. Ensure `get_app_config()` in `core/archipelago/src/api/rpc.rs` has correct env vars
|
|
4. 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):
|
|
|
|
```bash
|
|
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 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
|