From e8a0e1af1955dd7a52640be25d7fe5451eb60213 Mon Sep 17 00:00:00 2001 From: Dorian Date: Sun, 8 Mar 2026 08:06:52 +0000 Subject: [PATCH] feat: add Ollama proxy timeouts, SSH key migration, polish skills, and demo content - Update all skill SSH commands from sshpass to key-based auth (~/.ssh/archipelago-deploy) - Add proxy_connect_timeout 120s to nginx Ollama location blocks - Add new polish/sweep skills for overnight automation - Add demo content (documents, photos) for demo stack - Add .ssh/ to .gitignore Co-Authored-By: Claude Opus 4.6 --- .claude/plans/plan.md | 514 ++++++++++++++++++ .claude/skills/build-iso/SKILL.md | 4 +- .claude/skills/deploy-both/SKILL.md | 4 +- .claude/skills/deploy/SKILL.md | 2 +- .claude/skills/diagnose/SKILL.md | 2 +- .claude/skills/lint/SKILL.md | 2 +- .claude/skills/polish-backend/SKILL.md | 151 +++++ .claude/skills/polish-deploy/SKILL.md | 176 ++++++ .claude/skills/polish-errors/SKILL.md | 82 +++ .claude/skills/polish-forms/SKILL.md | 120 ++++ .claude/skills/polish-loading/SKILL.md | 83 +++ .claude/skills/polish-security/SKILL.md | 157 ++++++ .claude/skills/polish-websocket/SKILL.md | 167 ++++++ .claude/skills/polish/SKILL.md | 104 ++++ .claude/skills/server-logs/SKILL.md | 2 +- .claude/skills/sweep/SKILL.md | 105 ++++ .claude/skills/sync-configs/SKILL.md | 4 +- .claude/skills/test/SKILL.md | 2 +- .gitignore | 3 + demo/content/documents/backup-log.json | 39 ++ .../documents/bitcoin-whitepaper-notes.md | 28 + demo/content/documents/lightning-channels.csv | 8 + .../content/documents/node-setup-checklist.md | 31 ++ .../documents/sovereignty-manifesto.txt | 37 ++ .../photos/bitcoin-conference-2024.svg | 11 + demo/content/photos/home-server-build.svg | 11 + .../lightning-network-visualization.svg | 11 + demo/content/photos/node-rack-setup.svg | 11 + demo/content/photos/sunset-from-balcony.svg | 11 + 29 files changed, 1871 insertions(+), 11 deletions(-) create mode 100644 .claude/plans/plan.md create mode 100644 .claude/skills/polish-backend/SKILL.md create mode 100644 .claude/skills/polish-deploy/SKILL.md create mode 100644 .claude/skills/polish-errors/SKILL.md create mode 100644 .claude/skills/polish-forms/SKILL.md create mode 100644 .claude/skills/polish-loading/SKILL.md create mode 100644 .claude/skills/polish-security/SKILL.md create mode 100644 .claude/skills/polish-websocket/SKILL.md create mode 100644 .claude/skills/polish/SKILL.md create mode 100644 .claude/skills/sweep/SKILL.md create mode 100644 demo/content/documents/backup-log.json create mode 100644 demo/content/documents/bitcoin-whitepaper-notes.md create mode 100644 demo/content/documents/lightning-channels.csv create mode 100644 demo/content/documents/node-setup-checklist.md create mode 100644 demo/content/documents/sovereignty-manifesto.txt create mode 100644 demo/content/photos/bitcoin-conference-2024.svg create mode 100644 demo/content/photos/home-server-build.svg create mode 100644 demo/content/photos/lightning-network-visualization.svg create mode 100644 demo/content/photos/node-rack-setup.svg create mode 100644 demo/content/photos/sunset-from-balcony.svg diff --git a/.claude/plans/plan.md b/.claude/plans/plan.md new file mode 100644 index 00000000..32224aaa --- /dev/null +++ b/.claude/plans/plan.md @@ -0,0 +1,514 @@ +# Archipelago Production Polish Plan + +**Duration**: 8 weeks (March 10 – May 4, 2026) +**Goal**: Zero new features. Every existing feature polished to flawless production quality. +**Philosophy**: The iPhone moment — everything just works, feels inevitable, no rough edges. + +## SSH Access + +All remote commands use SSH key auth (password auth is disabled): +```bash +ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 +``` +Never use `sshpass`. The deploy script handles this automatically via `SSH_KEY`. + +--- + +## Audit Summary + +Full codebase audit completed March 8, 2026. Findings: + +| Layer | Critical | High | Medium | Low | +|-------|----------|------|--------|-----| +| Frontend (Vue/TS) | 4 | 6 | 10 | 4 | +| Backend (Rust) | 6 | 6 | 6 | 7 | +| Infrastructure | 5 | 6 | 7 | 3 | +| UX Flows | 4 | 4 | 6 | 3 | +| **Total** | **19** | **22** | **29** | **17** | + +--- + +## Skills Required + +### Existing Skills (14) +`deploy`, `deploy-both`, `diagnose`, `check-server`, `frontend-dev`, `sync-configs`, `build-iso`, `server-logs`, `add-app`, `harden`, `test`, `lint`, `ux-review`, `refactor` + +### New Skills (9) +| Skill | Purpose | +|-------|---------| +| `polish` | Main orchestrator — reads this plan, detects week, executes tasks | +| `polish-errors` | Fix silent error handling, add user-facing error states | +| `polish-loading` | Add skeleton loaders, loading indicators, empty states | +| `polish-forms` | Input validation, trimming, real-time feedback | +| `polish-backend` | Fix unwrap/expect, add timeouts, connection pooling | +| `polish-deploy` | Add rollback, health checks, pre-deploy validation | +| `polish-security` | Systemd hardening, nginx CSP, secrets management | +| `polish-websocket` | Reconnection UX, connection status indicator, heartbeat | +| `sweep` | Full automated quality sweep: lint + type-check + verify fixes | + +--- + +## Week 1: Silent Failures & Error Handling (March 10–16) + +**Theme**: Nothing fails silently. Every error is visible, actionable, recoverable. + +### Tasks + +#### 1.1 Frontend: Kill all silent catch blocks +- **Files**: Settings.vue, Web5.vue, router/index.ts, Apps.vue, OnboardingIntro.vue +- **Action**: Replace 21+ `.catch(() => {})` patterns with proper error handling +- **Pattern**: Log to console in dev, show toast/inline error to user in prod +- **Acceptance**: Zero `.catch(() => {})` in codebase (grep confirms) +- **Skill**: `/polish-errors` + +#### 1.2 Frontend: Remove all console.log from production +- **Files**: stores/app.ts (15+), api/websocket.ts (12+) +- **Action**: Replace with conditional dev-only logging or remove +- **Pattern**: `if (import.meta.env.DEV) console.log(...)` or remove entirely +- **Acceptance**: Zero `console.log` outside of dev guards (grep confirms) +- **Skill**: `/lint` + +#### 1.3 Backend: Fix all unwrap/expect in handler.rs +- **Files**: core/archipelago/src/api/handler.rs (11 unwraps) +- **Action**: Replace `.unwrap()` on Response builders with `.map_err()` and `?` +- **Acceptance**: Zero `unwrap()` in handler.rs +- **Skill**: `/polish-backend` + +#### 1.4 Backend: Fix unwrap/expect across all production paths +- **Files**: main.rs, identity.rs, totp.rs, rpc/mod.rs, image_verifier.rs +- **Action**: Audit all 32 `.unwrap()`/`.expect()` calls, replace with `?` or `.context()` +- **Acceptance**: Zero unwrap/expect outside of test modules +- **Skill**: `/polish-backend` + +#### 1.5 Backend: Hardcoded Bitcoin RPC credentials +- **Files**: core/archipelago/src/api/rpc/bitcoin.rs:89 +- **Action**: Move `archipelago/archipelago123` to env var or secrets manager +- **Pattern**: `std::env::var("ARCHIPELAGO_BITCOIN_RPC_USER").unwrap_or("archipelago".into())` +- **Acceptance**: No hardcoded credentials in Rust source + +#### 1.6 Deploy & verify +- Run `/lint` to confirm zero violations +- Run `/deploy` to live server +- Run `/check-server` to verify health +- Manual spot-check: trigger errors in UI, confirm they're visible + +--- + +## Week 2: Loading States & Visual Feedback (March 17–23) + +**Theme**: The user always knows what's happening. No blank screens, no mystery waits. + +### Tasks + +#### 2.1 Add skeleton loaders to all async views +- **Files**: Apps.vue, AppDetails.vue, Marketplace.vue, Cloud.vue, Server.vue, Settings.vue +- **Action**: Create `SkeletonLoader.vue` component, add to every view that fetches data +- **Pattern**: Show skeleton immediately, swap to real content on load +- **Acceptance**: Every view shows placeholder content during load +- **Skill**: `/polish-loading` + +#### 2.2 Add timeout warnings to long operations +- **Files**: Login.vue (server startup), Marketplace.vue (app install) +- **Action**: After 15s show "Taking longer than expected...", after 30s show troubleshoot options +- **Acceptance**: No operation silently hangs + +#### 2.3 Fix Start/Stop button state mismatch +- **Files**: Apps.vue, AppDetails.vue, ContainerApps.vue +- **Action**: Button reflects actual backend state, not a fixed 5s timer +- **Pattern**: Poll backend every 2s during state transition, update button immediately on response +- **Acceptance**: Button state always matches container state within 3s + +#### 2.4 Connection status indicator +- **Files**: Create `ConnectionStatus.vue`, integrate into App.vue header +- **Action**: Show green/amber/red dot based on WebSocket connection state +- **Pattern**: Use `wsClient.isConnected()` — green=connected, amber=reconnecting, red=disconnected +- **Acceptance**: User always knows if they're connected +- **Skill**: `/polish-websocket` + +#### 2.5 Fix OnlineStatusPill to use real data +- **Files**: components/OnlineStatusPill.vue +- **Action**: Connect to actual WebSocket state instead of hardcoded "Online" +- **Acceptance**: Pill reflects real connection state + +#### 2.6 Empty states for all views +- **Files**: Apps.vue, Cloud.vue, ContainerApps.vue +- **Action**: When no data, show helpful message with CTA (e.g., "No apps installed — Browse Marketplace") +- **Acceptance**: Every view handles the zero-data case gracefully + +#### 2.7 Deploy & verify +- `/deploy` then `/check-server` +- Test: disconnect network, observe status indicator +- Test: slow network (throttle), observe skeleton loaders +- Test: fresh account with no apps, observe empty states + +--- + +## Week 3: Form Validation & Input Quality (March 24–30) + +**Theme**: Every input feels responsive, validated, impossible to misuse. + +### Tasks + +#### 3.1 Real-time password validation +- **Files**: Login.vue (password setup), Settings.vue (password change) +- **Action**: Show inline validation as user types: length check, match check, strength meter +- **Pattern**: Debounced validation on input, green checkmark / red X per rule +- **Acceptance**: User sees validation state before clicking submit +- **Skill**: `/polish-forms` + +#### 3.2 TOTP input improvements +- **Files**: Login.vue (TOTP verify step) +- **Action**: Auto-submit on 6 digits, show session countdown timer, trim whitespace +- **Pattern**: `watch(code, () => { if (code.length === 6) submit() })` +- **Acceptance**: TOTP flow is fast and clear, session timeout is visible + +#### 3.3 Input trimming on all forms +- **Files**: Login.vue, Settings.vue, any form input +- **Action**: `.trim()` all text inputs before submission +- **Acceptance**: Leading/trailing whitespace never causes failures + +#### 3.4 Disable submit buttons during operations +- **Files**: Settings.vue (password change), Login.vue (login), Marketplace.vue (install) +- **Action**: Add `:disabled="isSubmitting"` to all action buttons +- **Pattern**: Button shows spinner + disabled state during async operation +- **Acceptance**: No button can be double-clicked during an operation + +#### 3.5 Error message consistency +- **Files**: All views with error messages +- **Action**: Create `formatError()` utility that normalizes error messages +- **Pattern**: Network errors -> "Can't reach server", Auth errors -> "Session expired", Server errors -> "Something went wrong" +- **Acceptance**: Error messages are user-friendly, never show raw error strings + +#### 3.6 Deploy & verify +- Test every form: login, password change, TOTP setup, app install +- Try invalid inputs, verify feedback is immediate and clear + +--- + +## Week 4: Backend Robustness (March 31 – April 6) + +**Theme**: The backend never crashes, never hangs, handles every edge case. + +### Tasks + +#### 4.1 Add timeouts to all container operations +- **Files**: core/archipelago/src/container/dev_orchestrator.rs +- **Action**: Wrap all podman calls with `tokio::time::timeout(Duration::from_secs(30), ...)` +- **Acceptance**: No container operation can hang indefinitely + +#### 4.2 Add timeouts to all external HTTP calls +- **Files**: bitcoin.rs, handler.rs (LND proxy) +- **Action**: Explicit `reqwest::Client` with timeout, not default +- **Pattern**: Reuse a single `Client` stored in `RpcHandler` state +- **Acceptance**: Every HTTP call has an explicit timeout + +#### 4.3 Connection pooling for Bitcoin RPC +- **Files**: core/archipelago/src/api/rpc/bitcoin.rs +- **Action**: Store `reqwest::Client` in `RpcHandler`, reuse across requests +- **Acceptance**: One client instance, connection pooled + +#### 4.4 Fix all clippy warnings +- **Action**: Run `cargo clippy --all-targets --all-features` on dev server, fix all 10 warnings +- **Warnings**: `should_implement_trait`, `get_first`, `assign_op_pattern`, `wildcard_in_or_patterns`, `redundant_field_names`, `unused_import`, `ptr_arg`, `very_complex_type`, `if_else_collapse`, `io::Error::other` +- **Acceptance**: `cargo clippy` returns zero warnings +- **Skill**: `/lint` + +#### 4.5 Rate limiting on unauthenticated endpoints +- **Files**: core/archipelago/src/api/handler.rs +- **Action**: Add per-IP rate limiting to `/archipelago/node-message` and `/electrs-status` +- **Pattern**: In-memory rate limiter with 60 req/min per IP +- **Acceptance**: Endpoints return 429 when rate exceeded + +#### 4.6 Consistent error codes and messages +- **Files**: All RPC endpoints +- **Action**: Define error code constants, consistent capitalization +- **Pattern**: `const ERR_AUTH: i32 = -1001;` etc. +- **Acceptance**: All error responses use defined constants + +#### 4.7 Remove dead code +- **Files**: identity.rs (unused field, unused methods), auth.rs (dead_code allows) +- **Action**: Remove `identity_dir` field, remove unused `verify()` and `did_key()` methods, remove `#[allow(dead_code)]` and verify usage +- **Acceptance**: Zero `#[allow(dead_code)]` outside of generated code + +#### 4.8 Replace println/eprintln with tracing +- **Files**: core/startos/src/* (23+ instances) +- **Action**: Replace `println!` -> `tracing::info!`, `eprintln!` -> `tracing::warn!` +- **Acceptance**: Zero `println!` / `eprintln!` in non-test code + +#### 4.9 Deploy & verify +- `/deploy` then `/check-server` then `/diagnose` +- Test: kill Bitcoin container, verify backend doesn't crash +- Test: flood unauthenticated endpoint, verify rate limiting +- Test: restart backend, verify graceful startup + +--- + +## Week 5: WebSocket & Real-Time Quality (April 7–13) + +**Theme**: Real-time updates are bulletproof. Connection issues are transparent to the user. + +### Tasks + +#### 5.1 WebSocket reconnection UX +- **Files**: api/websocket.ts, App.vue +- **Action**: After max reconnect attempts, show persistent banner "Connection lost. Click to retry." +- **Pattern**: Don't silently give up after 10 attempts +- **Acceptance**: User always has a path to reconnect +- **Skill**: `/polish-websocket` + +#### 5.2 WebSocket heartbeat improvement +- **Files**: api/websocket.ts +- **Action**: Send ping every 30s, expect pong within 5s, reconnect if missed +- **Acceptance**: Stale connections detected within 35s, not 60s + +#### 5.3 RPC client session detection +- **Files**: api/rpc-client.ts +- **Action**: On 401/403 response, redirect to login page instead of showing generic error +- **Pattern**: `if (status === 401) { router.push('/login'); return; }` +- **Acceptance**: Expired sessions redirect to login immediately + +#### 5.4 Message queuing during reconnection +- **Files**: api/rpc-client.ts, api/websocket.ts +- **Action**: If WebSocket is down, queue state-update subscriptions, replay on reconnect +- **Pattern**: Don't lose container state updates during brief disconnects +- **Acceptance**: State is consistent after reconnection without page refresh + +#### 5.5 WebSocket race condition fix +- **Files**: stores/app.ts, api/websocket.ts +- **Action**: Fix duplicate listener issue on rapid reconnect (`isWsSubscribed` flag) +- **Pattern**: Use a Set of listener IDs, deduplicate on registration +- **Acceptance**: No duplicate event handlers after reconnect cycles + +#### 5.6 Deploy & verify +- Test: kill backend, observe frontend reconnection behavior +- Test: toggle wifi, observe status indicator + reconnection +- Test: let session expire, verify redirect to login + +--- + +## Week 6: Deployment & Infrastructure Hardening (April 14–20) + +**Theme**: Deployments are safe, reversible, and verified. Infrastructure is production-grade. + +### Tasks + +#### 6.1 Deploy script: add rollback capability +- **Files**: scripts/deploy-to-target.sh +- **Action**: Before overwriting binary/frontend, backup to `.backup` suffix +- **Pattern**: On health check failure after restart, restore from backup +- **Acceptance**: Failed deploy auto-restores previous working version +- **Skill**: `/polish-deploy` + +#### 6.2 Deploy script: pre-deploy sanity checks +- **Files**: scripts/deploy-to-target.sh +- **Action**: Check disk space (2GB min), verify SSH key exists, verify target dir exists +- **Acceptance**: Deploy fails early with clear message if preconditions not met + +#### 6.3 Deploy script: post-deploy health verification +- **Files**: scripts/deploy-to-target.sh +- **Action**: After restart, poll `/health` endpoint for 30s. If no 200, trigger rollback +- **Acceptance**: Every deploy is verified healthy before declaring success + +#### 6.4 Deploy script: deployment locking +- **Files**: scripts/deploy-to-target.sh +- **Action**: Use flock to prevent concurrent deploys +- **Acceptance**: Second simultaneous deploy fails immediately with message + +#### 6.5 First-boot script: add error handling +- **Files**: scripts/first-boot-containers.sh +- **Action**: Add `set -e`, verify each container starts before creating dependents +- **Acceptance**: If Bitcoin fails, Mempool is not attempted + +#### 6.6 Systemd service hardening +- **Files**: image-recipe/configs/archipelago.service +- **Action**: Add `PrivateTmp=yes`, `NoNewPrivileges=true`, `ProtectSystem=strict`, `ProtectHome=yes`, `SystemCallFilter=@system-service` +- **Acceptance**: Service runs with minimal privileges +- **Skill**: `/harden` + +#### 6.7 Nginx security headers +- **Files**: image-recipe/configs/nginx-archipelago.conf +- **Action**: Add HSTS, fix CSP (remove unsafe-inline), add rate limiting zones, custom log format that strips tokens +- **Acceptance**: Security headers pass Mozilla Observatory scan + +#### 6.8 Nginx config: test before reload +- **Files**: scripts/deploy-to-target.sh +- **Action**: `nginx -t` failure should abort deploy and restore backup config +- **Acceptance**: Invalid nginx config never goes live + +#### 6.9 Deploy & verify +- Test: deploy with intentionally broken binary, verify rollback +- Test: deploy with invalid nginx config, verify rollback +- Test: concurrent deploy attempt, verify lock +- Run `/diagnose` full check + +--- + +## Week 7: Accessibility, Polish & Edge Cases (April 21–27) + +**Theme**: Every interaction is crisp. Keyboard users, slow networks, edge cases — all handled. + +### Tasks + +#### 7.1 ARIA labels on all interactive elements +- **Files**: All views and components +- **Action**: Add `aria-label` to buttons, links, form inputs that lack visible labels +- **Pattern**: ` +``` + +## Input Trimming + +All text inputs should be trimmed before submission: +```typescript +const trimmed = password.value.trim() +``` + +## Error Message Consistency + +Create or use a `formatError` utility: +```typescript +function formatError(err: unknown): string { + if (err instanceof Error) { + if (err.message.includes('fetch') || err.message.includes('network')) + return 'Unable to reach server. Check your connection.' + if (err.message.includes('401') || err.message.includes('unauthorized')) + return 'Session expired. Please log in again.' + return err.message + } + return 'Something went wrong. Please try again.' +} +``` + +## Verification + +For each form: +- [ ] Real-time validation shows feedback as user types +- [ ] Submit button disabled during operation +- [ ] Submit button disabled when validation fails +- [ ] Inputs trimmed before submission +- [ ] Error messages are user-friendly (no raw error strings) +- [ ] Success feedback shown after completion + +## Deploy After Fixes + +```bash +./scripts/deploy-to-target.sh --live +``` + +Test each form with: valid input, invalid input, empty input, whitespace-only input, rapid double-click on submit. diff --git a/.claude/skills/polish-loading/SKILL.md b/.claude/skills/polish-loading/SKILL.md new file mode 100644 index 00000000..9158862c --- /dev/null +++ b/.claude/skills/polish-loading/SKILL.md @@ -0,0 +1,83 @@ +# Skill: Polish Loading States + +Add skeleton loaders, loading indicators, timeout warnings, and empty states to all async views. No view should ever show a blank screen. + +## Skeleton Loader Component + +Create or use a `SkeletonLoader.vue` component with the glassmorphism style: +- Background: `bg-white/5` with shimmer animation +- Rounded corners matching the card it replaces +- Animate with CSS `@keyframes shimmer` (translate gradient left to right) +- Must use global classes from style.css, not inline Tailwind + +## Views to Fix + +For EACH view in `neode-ui/src/views/`, verify these states exist: + +### 1. Loading State +- Show skeleton placeholders immediately on mount +- Pattern: + ```vue + + ``` + +### 2. Empty State +- When data loads but is empty (zero items) +- Show helpful message with CTA +- Pattern: + ```vue +
+

No apps installed yet

+ Browse Marketplace +
+ ``` + +### 3. Timeout Warning +- After 15 seconds of loading, show "Taking longer than expected..." +- After 30 seconds, show troubleshooting options +- Pattern: + ```typescript + const loadingTooLong = ref(false) + let timeout: ReturnType + + onMounted(() => { + timeout = setTimeout(() => { loadingTooLong.value = true }, 15000) + }) + + watch(isLoading, (val) => { if (!val) clearTimeout(timeout) }) + ``` + +## Priority Views (must have all 3 states) + +1. **Apps.vue** — app grid skeleton, "No apps installed" empty state +2. **AppDetails.vue** — detail card skeleton, loading indicator +3. **Marketplace.vue** — app card grid skeleton, "Loading apps..." with timeout +4. **Dashboard.vue** — metric card skeletons +5. **Cloud.vue** — file list skeleton, "No files" empty state +6. **Settings.vue** — settings section skeleton +7. **Server.vue** — server info skeleton + +## Verification + +For each view, confirm: +- [ ] `isLoading` ref exists and is set properly +- [ ] Template has `v-if="isLoading"` skeleton section +- [ ] Template has empty state for zero-data case +- [ ] Loading timeout warning after 15s +- [ ] Skeleton uses global classes, not inline Tailwind + +## Deploy After Fixes + +Always deploy and verify on live server: +```bash +./scripts/deploy-to-target.sh --live +``` + +Test by throttling network in browser DevTools to observe loading states. diff --git a/.claude/skills/polish-security/SKILL.md b/.claude/skills/polish-security/SKILL.md new file mode 100644 index 00000000..b709e701 --- /dev/null +++ b/.claude/skills/polish-security/SKILL.md @@ -0,0 +1,157 @@ +# Skill: Polish Security + +Security hardening pass for systemd, nginx, secrets management, and rate limiting. + +## 1. Systemd Service Hardening + +File: `image-recipe/configs/archipelago.service` + +Add these directives to the `[Service]` section: +```ini +PrivateTmp=yes +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=yes +ReadWritePaths=/var/lib/archipelago +SystemCallFilter=@system-service +SystemCallFilter=~@privileged @resources +``` + +After editing, sync to server and verify: +```bash +ssh archipelago@192.168.1.228 "sudo systemd-analyze security archipelago" +``` + +## 2. Nginx Security Headers + +File: `image-recipe/configs/nginx-archipelago.conf` + +### Add HSTS (HTTPS block only): +```nginx +add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; +``` + +### Fix CSP (remove unsafe-inline): +Replace: +```nginx +add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' ws: wss:; frame-src 'self' http://localhost:* http://192.168.*:*;" always; +``` + +With CSP that uses nonces or hashes for inline scripts/styles. If inline scripts can't be removed yet, document which ones and plan their removal. + +### Add rate limiting zones: +```nginx +# In http block: +limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m; + +# On login/auth endpoints: +limit_req zone=auth burst=3 nodelay; + +# On API endpoints: +limit_req zone=api burst=50 nodelay; +``` + +### Custom log format (strip tokens): +```nginx +log_format no_tokens '$remote_addr - $remote_user [$time_local] "$request_method $uri $server_protocol" $status $body_bytes_sent "$http_referer"'; +access_log /var/log/nginx/archipelago_access.log no_tokens; +``` + +## 3. Secrets Management + +### Remove hardcoded RPC credentials from scripts +File: `scripts/deploy-to-target.sh` + +Replace: +```bash +-e CORE_RPC_USERNAME=archipelago -e CORE_RPC_PASSWORD=archipelago123 +``` + +With: +```bash +-e CORE_RPC_USERNAME=archipelago -e CORE_RPC_PASSWORD=$(cat /var/lib/archipelago/secrets/bitcoin-rpc-pass) +``` + +### Generate secrets on first boot +File: `scripts/first-boot-containers.sh` + +Add at the top: +```bash +SECRETS_DIR="/var/lib/archipelago/secrets" +mkdir -p "$SECRETS_DIR" +chmod 700 "$SECRETS_DIR" + +# Generate Bitcoin RPC password if not exists +if [ ! -f "$SECRETS_DIR/bitcoin-rpc-pass" ]; then + openssl rand -base64 24 > "$SECRETS_DIR/bitcoin-rpc-pass" + chmod 600 "$SECRETS_DIR/bitcoin-rpc-pass" +fi +``` + +### Remove hardcoded credentials from Rust backend +File: `core/archipelago/src/api/rpc/bitcoin.rs` + +Replace: +```rust +.basic_auth("archipelago", Some("archipelago123")) +``` + +With: +```rust +let rpc_user = std::env::var("ARCHIPELAGO_BITCOIN_RPC_USER").unwrap_or_else(|_| "archipelago".into()); +let rpc_pass = std::env::var("ARCHIPELAGO_BITCOIN_RPC_PASS").unwrap_or_else(|_| "archipelago123".into()); +.basic_auth(&rpc_user, Some(&rpc_pass)) +``` + +## 4. Rate Limiting on Backend + +File: `core/archipelago/src/api/handler.rs` + +Add rate limiting to unauthenticated endpoints: +- `/archipelago/node-message` — 10 req/min per IP +- `/electrs-status` — 30 req/min per IP + +Use an in-memory `HashMap` with cleanup on access. + +## 5. SSH Hardening + +File: `scripts/deploy-to-target.sh` + +Replace: +```bash +SSH_OPTS="-o StrictHostKeyChecking=no" +``` + +With: +```bash +SSH_OPTS="-o StrictHostKeyChecking=accept-new" +``` + +And add SSH key validation: +```bash +if [ ! -f "$SSH_KEY" ]; then + echo "ERROR: SSH key not found at $SSH_KEY" + exit 1 +fi +``` + +## Verification Checklist + +- [ ] `systemd-analyze security archipelago` score < 5.0 (lower is better) +- [ ] Nginx headers pass: `curl -I http://192.168.1.228 | grep -i 'strict-transport\|content-security\|x-frame'` +- [ ] No hardcoded passwords in scripts: `grep -rn 'archipelago123' scripts/ core/` +- [ ] Rate limiting works: rapid-fire requests get 429 +- [ ] SSH key required (no password fallback) + +## Deploy + +After changes, sync configs and deploy: +```bash +./scripts/deploy-to-target.sh --live +``` + +Then sync to ISO recipe: +```bash +# Run /sync-configs skill +``` diff --git a/.claude/skills/polish-websocket/SKILL.md b/.claude/skills/polish-websocket/SKILL.md new file mode 100644 index 00000000..969b3d8c --- /dev/null +++ b/.claude/skills/polish-websocket/SKILL.md @@ -0,0 +1,167 @@ +# Skill: Polish WebSocket & Real-Time + +Improve WebSocket reliability, reconnection UX, heartbeat, session timeout detection, and connection status indicators. + +## 1. Connection Status Indicator + +### Create or update connection status display +- **Location**: App.vue header or create ConnectionStatus.vue component +- **States**: Connected (green), Reconnecting (amber pulse), Disconnected (red) +- **Data source**: `wsClient.isConnected()` from websocket.ts +- **Style**: Use existing design tokens, small dot + text in header area + +```vue +
+
+ + {{ isConnected ? '' : isReconnecting ? 'Reconnecting...' : 'Offline' }} + +
+``` + +### Fix OnlineStatusPill.vue +- Connect to actual WebSocket state instead of hardcoded "Online" +- Use the app store's connection state + +## 2. Reconnection UX + +### Don't silently give up +File: `api/websocket.ts` + +After max reconnect attempts (currently 10), instead of silently stopping: +- Set a `permanentlyDisconnected` flag +- Emit event that App.vue listens to +- Show persistent banner: "Connection lost. Click to retry." or "Refresh page to reconnect." + +```typescript +if (this.reconnectAttempts >= this.maxReconnectAttempts) { + this.shouldReconnect = false + this.notifyConnectionState(false) + // Emit permanent disconnect event + this.onPermanentDisconnect?.() +} +``` + +### Allow manual reconnect +Add a `forceReconnect()` method that resets attempt counter and tries again: +```typescript +forceReconnect() { + this.reconnectAttempts = 0 + this.shouldReconnect = true + this.connect() +} +``` + +## 3. Heartbeat Improvement + +File: `api/websocket.ts` + +Current: 60-second stale detection (passive). +Target: 30-second active ping with 5-second pong timeout. + +```typescript +private startHeartbeat() { + this.heartbeatInterval = setInterval(() => { + if (this.ws?.readyState === WebSocket.OPEN) { + this.ws.send(JSON.stringify({ type: 'ping' })) + this.pongTimeout = setTimeout(() => { + // No pong received — connection is dead + this.ws?.close() + this.handleReconnect() + }, 5000) + } + }, 30000) +} + +// In message handler: +if (data.type === 'pong') { + clearTimeout(this.pongTimeout) + return +} +``` + +Note: Backend must respond to `ping` with `pong`. Check handler.rs WebSocket handler. + +## 4. Session Timeout Detection + +File: `api/rpc-client.ts` + +When RPC returns 401 or 403: +```typescript +if (response.status === 401 || response.status === 403) { + // Session expired — redirect to login + window.location.href = '/login' + return +} +``` + +This should be in the base `call()` method so it applies to all RPC calls. + +## 5. Fix Race Condition on Reconnect + +File: `stores/app.ts` or `api/websocket.ts` + +Problem: `isWsSubscribed` flag doesn't prevent duplicate listeners on rapid reconnect. + +Fix: Use listener deduplication: +```typescript +private listeners = new Map>() + +subscribe(event: string, callback: Function) { + if (!this.listeners.has(event)) { + this.listeners.set(event, new Set()) + } + this.listeners.get(event)!.add(callback) +} +``` + +Or simpler: remove all listeners before reconnect, then re-add: +```typescript +onReconnect() { + // Clear old subscriptions + this.removeAllListeners() + // Re-subscribe + this.setupSubscriptions() +} +``` + +## 6. Message Queuing During Disconnect + +When WebSocket is down, queue subscription requests: +```typescript +private pendingSubscriptions: Array<() => void> = [] + +subscribe(event: string, callback: Function) { + if (this.ws?.readyState !== WebSocket.OPEN) { + this.pendingSubscriptions.push(() => this.subscribe(event, callback)) + return + } + // Normal subscribe logic +} + +onReconnected() { + // Replay pending subscriptions + const pending = [...this.pendingSubscriptions] + this.pendingSubscriptions = [] + pending.forEach(fn => fn()) +} +``` + +## Verification + +1. **Kill backend** → frontend shows "Disconnected" → restart backend → frontend reconnects and shows "Connected" +2. **Toggle wifi** → status indicator updates → wifi back → auto-reconnect +3. **Wait for session timeout** → next RPC call redirects to login +4. **Rapid reconnect** → no duplicate event handlers (check with DevTools) +5. **Leave tab in background** → come back → status is accurate + +## Deploy + +```bash +./scripts/deploy-to-target.sh --live +``` + +Test with browser DevTools Network tab to observe WebSocket frames. diff --git a/.claude/skills/polish/SKILL.md b/.claude/skills/polish/SKILL.md new file mode 100644 index 00000000..902a61a6 --- /dev/null +++ b/.claude/skills/polish/SKILL.md @@ -0,0 +1,104 @@ +# Skill: Production Polish (Overnight Orchestrator) + +Main entry point for the Archipelago production polish plan. Reads `plan.md` at the project root, determines the current week based on today's date, and executes the tasks for that week. + +## How It Works + +1. Read `plan.md` from the project root +2. Determine the current week from the schedule: + - Week 1: March 10–16 — Silent Failures & Error Handling + - Week 2: March 17–23 — Loading States & Visual Feedback + - Week 3: March 24–30 — Form Validation & Input Quality + - Week 4: March 31 – April 6 — Backend Robustness + - Week 5: April 7–13 — WebSocket & Real-Time Quality + - Week 6: April 14–20 — Deployment & Infrastructure Hardening + - Week 7: April 21–27 — Accessibility, Polish & Edge Cases + - Week 8: April 28 – May 4 — Integration Testing, Final Sweep & ISO +3. Execute tasks for the current week, in order +4. After completing tasks, run `/sweep` to verify +5. Deploy and verify with `/deploy` then `/check-server` + +## Execution Flow + +### Step 1: Read the plan +``` +Read plan.md and find the current week's section +``` + +### Step 2: Check what's already done +Run the verification checks for the current week's tasks. For example in Week 1: +- Count remaining `.catch(() => {})` patterns +- Count remaining `console.log` outside dev guards +- Count remaining `unwrap()` in backend production code +- Check if hardcoded credentials still exist + +### Step 3: Work on the next incomplete task +Pick the first task in the current week that still has violations (hasn't met its acceptance criteria). Fix violations one file at a time: +1. Read the file +2. Apply the fix following the pattern described in the task +3. Verify the fix compiles/type-checks +4. Move to the next violation + +### Step 4: Verify after each batch of fixes +After fixing all violations for a task: +- Frontend: `cd neode-ui && npx vue-tsc --noEmit` +- Backend: `ssh archipelago@192.168.1.228 "cd ~/archy && cargo check"` +- Run the task's specific acceptance grep/check + +### Step 5: Deploy when a task is complete +When all violations for a task are fixed and verified: +```bash +./scripts/deploy-to-target.sh --live +``` +Then verify: +```bash +ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 "systemctl is-active archipelago && curl -s http://localhost:5678/health" +``` + +### Step 6: Move to the next task +Repeat Steps 3-5 for the next incomplete task in the current week. + +### Step 7: When all tasks are done +Run `/sweep` for a full quality report. If clean, the week is complete. + +## Rules + +- **Never change functionality** — only improve quality of existing code +- **Never change the design** — use existing glassmorphism classes, color tokens, and layout patterns +- **Always deploy after changes** — don't leave undeployed code +- **Always verify after deploy** — check server health +- **Build Rust on the dev server** — never compile Rust on macOS +- **Commit after each completed task** — atomic commits with `fix:` or `refactor:` prefix +- **If something breaks, revert** — don't push forward with broken code + +## Arguments + +If `$ARGUMENTS` is provided: +- `week N` — Force execution of week N regardless of date +- `task N.M` — Execute only task N.M (e.g., `task 1.3`) +- `status` — Show completion status for all weeks without executing +- `sweep` — Run sweep only, no fixes + +## Example Usage + +``` +/polish # Auto-detect week, work on next incomplete task +/polish week 1 # Force Week 1 tasks +/polish task 1.3 # Work on just task 1.3 +/polish status # Show what's done and what's left +/polish sweep # Just run the quality sweep +``` + +## For Overnight TUI + +Launch with: +``` +/loop 30m /polish +``` + +Each 30-minute cycle: +1. Checks current week +2. Finds next incomplete task +3. Fixes as many violations as possible in the time available +4. Deploys and verifies +5. Reports progress diff --git a/.claude/skills/server-logs/SKILL.md b/.claude/skills/server-logs/SKILL.md index 99901f8d..9df06f86 100644 --- a/.claude/skills/server-logs/SKILL.md +++ b/.claude/skills/server-logs/SKILL.md @@ -5,7 +5,7 @@ allowed-tools: Bash argument-hint: "[backend|nginx|container-name]" --- -View logs from the Archipelago server (192.168.1.228). Use `sshpass -p 'EwPDR8q45l0Upx@' ssh -o StrictHostKeyChecking=no archipelago@192.168.1.228` for all commands. +View logs from the Archipelago server (192.168.1.228). Use `ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228` for all commands. If $ARGUMENTS is provided, show logs for that specific service. Otherwise, show backend logs by default. diff --git a/.claude/skills/sweep/SKILL.md b/.claude/skills/sweep/SKILL.md new file mode 100644 index 00000000..d8f800a8 --- /dev/null +++ b/.claude/skills/sweep/SKILL.md @@ -0,0 +1,105 @@ +# Skill: Quality Sweep + +Full automated quality sweep across the entire codebase. Detects regressions, violations, and quality issues. This is the overnight watchdog. + +Run all checks below sequentially. For each check, use the Grep tool (not bash grep) for local file scanning, and Bash for remote/build commands. Report a summary at the end. + +## Checks + +### 1. TypeScript Type Check +Run in bash: +```bash +cd /Users/dorian/Projects/archy/neode-ui && npx vue-tsc --noEmit 2>&1 | tail -20 +``` +PASS = zero errors. Count any errors found. + +### 2. Frontend Violations +Use the Grep tool to scan `neode-ui/src/` for each pattern. Count matches for each: + +**Silent catch blocks** — pattern: `catch\s*\(\s*\)\s*=>?\s*\{\s*\}` or `\.catch\(\(\)\s*=>\s*\{\}` in `*.vue` and `*.ts` files + +**console.log in prod** — pattern: `console\.(log|warn|error)` in `*.vue` and `*.ts` files. Exclude lines containing `import.meta.env.DEV` or `// dev-only` + +**any type usage** — pattern: `:\s*any[^a-zA-Z]|as\s+any[^a-zA-Z]` in `*.vue` and `*.ts` files. Exclude `.d.ts` files + +**TODO/FIXME/HACK** — pattern: `TODO|FIXME|HACK|XXX` in `*.vue` and `*.ts` files + +**Banned CSS classes** — pattern: `gradient-button|gradient-card` in `*.vue` files + +### 3. Backend Violations (via SSH) +Run in bash: +```bash +ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 " + echo '--- unwrap/expect ---' + grep -rn 'unwrap()\|\.expect(' ~/archy/core/archipelago/src/ ~/archy/core/container/src/ ~/archy/core/security/src/ --include='*.rs' | grep -v test | grep -v '_test.rs' | grep -v target/ | wc -l + + echo '--- println/eprintln ---' + grep -rn 'println!\|eprintln!' ~/archy/core/ --include='*.rs' | grep -v test | grep -v target/ | wc -l + + echo '--- TODO/FIXME ---' + grep -rn 'TODO\|FIXME\|HACK' ~/archy/core/ --include='*.rs' | grep -v target/ | wc -l +" +``` + +### 4. Hardcoded Credentials +Use Grep tool locally — pattern: `archipelago123|password123` in `core/` and `scripts/` directories, excluding `target/`, `node_modules/`, and `deploy-config.sh` + +### 5. Server Health +Run in bash: +```bash +ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 " + echo 'service:' \$(systemctl is-active archipelago) + echo 'health:' \$(curl -s -o /dev/null -w '%{http_code}' http://localhost:5678/health) + echo 'containers:' \$(podman ps -q 2>/dev/null | wc -l || docker ps -q | wc -l) + echo 'errors:' \$(journalctl -u archipelago --since '1 hour ago' --no-pager -p err 2>/dev/null | wc -l) + echo 'disk:' \$(df -h / | tail -1 | awk '{print \$5}') +" +``` + +### 6. Frontend Build +Run in bash: +```bash +cd /Users/dorian/Projects/archy/neode-ui && npm run build 2>&1 | tail -5 +``` +PASS = exit code 0. + +## Report Format + +After all checks, output a summary exactly like this: + +``` +=== SWEEP REPORT === + +TypeScript: PASS/FAIL (N errors) +Silent catches: PASS/FAIL (N) +Console.log: PASS/FAIL (N) +Any types: PASS/FAIL (N) +TODOs: PASS/FAIL (N) +Banned classes: PASS/FAIL (N) +Backend unwrap: PASS/FAIL (N) +Backend println: PASS/FAIL (N) +Hardcoded creds: PASS/FAIL (N) +Server health: PASS/FAIL +Frontend build: PASS/FAIL + +Total violations: N +``` + +PASS = zero violations for that check. FAIL = one or more. + +## Auto-Fix Rules + +Safe to auto-fix without asking: +- `cargo fmt --all` on dev server (formatting only) +- Trailing whitespace removal +- Import ordering + +Do NOT auto-fix (flag for review): +- Error handling changes +- Logic or behavior changes +- Anything in core/ Rust files beyond formatting + +## Reference + +Full plan with weekly task breakdown: `plan.md` (project root) +Current week's focus determines which violations are highest priority. diff --git a/.claude/skills/sync-configs/SKILL.md b/.claude/skills/sync-configs/SKILL.md index b027f118..b1fa9d79 100644 --- a/.claude/skills/sync-configs/SKILL.md +++ b/.claude/skills/sync-configs/SKILL.md @@ -11,12 +11,12 @@ Sync system configuration files from the live server back to the repo for ISO bu 1. **Capture systemd service**: ```bash - sshpass -p 'EwPDR8q45l0Upx@' ssh -o StrictHostKeyChecking=no archipelago@192.168.1.228 'sudo cat /etc/systemd/system/archipelago.service' > image-recipe/configs/archipelago.service + ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 'sudo cat /etc/systemd/system/archipelago.service' > image-recipe/configs/archipelago.service ``` 2. **Capture nginx config**: ```bash - sshpass -p 'EwPDR8q45l0Upx@' ssh -o StrictHostKeyChecking=no archipelago@192.168.1.228 'sudo cat /etc/nginx/sites-available/archipelago' > image-recipe/configs/nginx-archipelago.conf + ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 'sudo cat /etc/nginx/sites-available/archipelago' > image-recipe/configs/nginx-archipelago.conf ``` 3. **Capture any custom scripts** in `/opt/archipelago/scripts/` if they've changed. diff --git a/.claude/skills/test/SKILL.md b/.claude/skills/test/SKILL.md index cb5e1176..4ec00fab 100644 --- a/.claude/skills/test/SKILL.md +++ b/.claude/skills/test/SKILL.md @@ -13,7 +13,7 @@ Run or create tests for $ARGUMENTS. ### Run existing tests ```bash # On dev server (never build Rust on macOS) -sshpass -p 'EwPDR8q45l0Upx@' ssh -o StrictHostKeyChecking=no archipelago@192.168.1.228 \ +ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 \ 'source ~/.cargo/env && cd ~/archy/core && cargo test --all-features 2>&1' ``` diff --git a/.gitignore b/.gitignore index 4995b7f5..366500ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# SSH keys (sandbox copies) +.ssh/ + # Rust build output target/ **/target/ diff --git a/demo/content/documents/backup-log.json b/demo/content/documents/backup-log.json new file mode 100644 index 00000000..b2e7df96 --- /dev/null +++ b/demo/content/documents/backup-log.json @@ -0,0 +1,39 @@ +{ + "backups": [ + { + "id": "bkp-2025-03-01", + "timestamp": "2025-03-01T02:00:00Z", + "type": "full", + "apps": ["bitcoin-knots", "lnd", "mempool", "nextcloud"], + "size_mb": 2340, + "status": "success", + "encrypted": true, + "destination": "local-usb" + }, + { + "id": "bkp-2025-02-15", + "timestamp": "2025-02-15T02:00:00Z", + "type": "incremental", + "apps": ["bitcoin-knots", "lnd"], + "size_mb": 156, + "status": "success", + "encrypted": true, + "destination": "local-usb" + }, + { + "id": "bkp-2025-02-01", + "timestamp": "2025-02-01T02:00:00Z", + "type": "full", + "apps": ["bitcoin-knots", "lnd", "mempool", "nextcloud", "vaultwarden"], + "size_mb": 2890, + "status": "success", + "encrypted": true, + "destination": "local-usb" + } + ], + "schedule": { + "full": "1st of month, 02:00 UTC", + "incremental": "15th of month, 02:00 UTC" + }, + "retention": "6 months" +} diff --git a/demo/content/documents/bitcoin-whitepaper-notes.md b/demo/content/documents/bitcoin-whitepaper-notes.md new file mode 100644 index 00000000..6ac6d97c --- /dev/null +++ b/demo/content/documents/bitcoin-whitepaper-notes.md @@ -0,0 +1,28 @@ +# Bitcoin Whitepaper Notes + +## Key Concepts + +### Peer-to-Peer Electronic Cash +- No trusted third party needed +- Double-spending solved via proof-of-work +- Longest chain = truth + +### Proof of Work +- SHA-256 based hashing +- Difficulty adjusts every 2016 blocks (~2 weeks) +- Incentive: block reward + transaction fees + +### Network +- Nodes broadcast transactions +- Miners collect into blocks +- Blocks are chained via hash references + +## My Thoughts +- The 21M supply cap is genius - digital scarcity +- Lightning Network solves the scaling concern +- Self-custody is the whole point + +## Reading List +- [ ] Mastering Bitcoin - Andreas Antonopoulos +- [ ] The Bitcoin Standard - Saifedean Ammous +- [ ] Programming Bitcoin - Jimmy Song diff --git a/demo/content/documents/lightning-channels.csv b/demo/content/documents/lightning-channels.csv new file mode 100644 index 00000000..d00daa9a --- /dev/null +++ b/demo/content/documents/lightning-channels.csv @@ -0,0 +1,8 @@ +channel_id,peer_alias,capacity_sats,local_balance,remote_balance,status,opened_date +ch_001,ACINQ,5000000,2450000,2550000,active,2024-11-15 +ch_002,WalletOfSatoshi,2000000,1200000,800000,active,2024-12-01 +ch_003,Voltage,10000000,4500000,5500000,active,2024-12-20 +ch_004,Kraken,3000000,1800000,1200000,active,2025-01-05 +ch_005,BitcoinBeach,1000000,500000,500000,active,2025-01-22 +ch_006,LNBig,8000000,3200000,4800000,active,2025-02-10 +ch_007,Breez,1500000,900000,600000,inactive,2025-02-28 diff --git a/demo/content/documents/node-setup-checklist.md b/demo/content/documents/node-setup-checklist.md new file mode 100644 index 00000000..c38a85b1 --- /dev/null +++ b/demo/content/documents/node-setup-checklist.md @@ -0,0 +1,31 @@ +# Archipelago Node Setup Checklist + +## Hardware +- [x] Intel NUC / Mini PC (16GB RAM minimum) +- [x] 2TB NVMe SSD (for Bitcoin blockchain) +- [x] USB drive for Archipelago installer +- [x] Ethernet cable (recommended over WiFi) + +## Initial Setup +- [x] Flash Archipelago ISO to USB +- [x] Boot from USB, follow installer +- [x] Set admin password +- [x] Connect to local network + +## Core Apps +- [x] Bitcoin Knots - Full node validation +- [x] LND - Lightning Network +- [x] Mempool Explorer - Block/tx visualizer +- [ ] BTCPay Server - Payment processing +- [ ] Fedimint - Federated mint + +## Security +- [x] Enable 2FA (TOTP) +- [x] Set up Tor hidden services +- [ ] Configure Tailscale VPN for remote access +- [ ] Back up node identity (DID) + +## Monitoring +- [ ] Set up Grafana dashboards +- [ ] Configure Uptime Kuma alerts +- [ ] Review system logs weekly diff --git a/demo/content/documents/sovereignty-manifesto.txt b/demo/content/documents/sovereignty-manifesto.txt new file mode 100644 index 00000000..0d33e4ca --- /dev/null +++ b/demo/content/documents/sovereignty-manifesto.txt @@ -0,0 +1,37 @@ +THE SOVEREIGNTY MANIFESTO +========================= + +We hold these truths to be self-evident: + +1. Your data belongs to you. + Not to corporations. Not to governments. Not to platforms. + To you. + +2. Your money should be uncensorable. + No one should have the power to freeze your funds, + reverse your transactions, or inflate away your savings. + +3. Your communications should be private. + End-to-end encryption is a right, not a feature. + Metadata collection is surveillance. + +4. Your compute should be sovereign. + Running your own server is not paranoia. + It's digital self-reliance. + +5. Your identity should be self-issued. + You don't need permission from a corporation + to prove who you are. + +--- + +The tools exist. Bitcoin. Lightning. Tor. Nostr. +Self-hosted infrastructure. Open source software. + +The question isn't whether it's possible. +The question is whether you'll do it. + +Run your own node. +Hold your own keys. +Own your own data. +Be sovereign. diff --git a/demo/content/photos/bitcoin-conference-2024.svg b/demo/content/photos/bitcoin-conference-2024.svg new file mode 100644 index 00000000..72b7143e --- /dev/null +++ b/demo/content/photos/bitcoin-conference-2024.svg @@ -0,0 +1,11 @@ + + + + + + + + + bitcoin-conference-2024 + Demo Photo Placeholder + diff --git a/demo/content/photos/home-server-build.svg b/demo/content/photos/home-server-build.svg new file mode 100644 index 00000000..b0d22fd3 --- /dev/null +++ b/demo/content/photos/home-server-build.svg @@ -0,0 +1,11 @@ + + + + + + + + + home-server-build + Demo Photo Placeholder + diff --git a/demo/content/photos/lightning-network-visualization.svg b/demo/content/photos/lightning-network-visualization.svg new file mode 100644 index 00000000..3750fae7 --- /dev/null +++ b/demo/content/photos/lightning-network-visualization.svg @@ -0,0 +1,11 @@ + + + + + + + + + lightning-network-visualization + Demo Photo Placeholder + diff --git a/demo/content/photos/node-rack-setup.svg b/demo/content/photos/node-rack-setup.svg new file mode 100644 index 00000000..090e8c6b --- /dev/null +++ b/demo/content/photos/node-rack-setup.svg @@ -0,0 +1,11 @@ + + + + + + + + + node-rack-setup + Demo Photo Placeholder + diff --git a/demo/content/photos/sunset-from-balcony.svg b/demo/content/photos/sunset-from-balcony.svg new file mode 100644 index 00000000..eb1ec82f --- /dev/null +++ b/demo/content/photos/sunset-from-balcony.svg @@ -0,0 +1,11 @@ + + + + + + + + + sunset-from-balcony + Demo Photo Placeholder +