127 Commits

Author SHA1 Message Date
Dorian
9db55b0b34 feat: container orchestration, branding overhaul, onboarding logging
Container orchestration:
- Health monitor with crash recovery and auto-restart
- Doctor service (periodic health checks via systemd timer)
- Reconcile service (desired-state convergence)
- Stack-aware install/uninstall with dependency tracking

Branding:
- Custom GRUB background (designer artwork, 1024x768)
- ISOLINUX boot menu: centered, orange accents, clean labels
- Terminal banners: adaptive width, basic ANSI colors, fits 80-col
- Removed auto-generated splash scripts (designer provides assets)
- GRUB theme: lowercase branding

Frontend:
- 401 handler clears localStorage immediately (prevents cascade)

Backend:
- Onboarding/auth logging ([onboarding] tag in journalctl)
- Cookie Secure flag logging for debugging HTTP/HTTPS issues

ISO fixes:
- Install log saved before unmount (was silently failing)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 11:34:29 +00:00
Dorian
782a4a62d5 fix: cookies Secure flag based on X-Forwarded-Proto, not dev_mode
Secure flag on session cookies broke HTTP LAN access — browsers refuse
to send Secure cookies over plain HTTP, causing 401 redirect loop.

Fix: check X-Forwarded-Proto header. Only set Secure when request came
over HTTPS. HTTP on LAN works, HTTPS still gets Secure cookies.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 11:34:29 +00:00
Dorian
39857c775a fix: onboarding auth, stale CI build, autocomplete attrs
- Add identity.create + server.echo to UNAUTHENTICATED_METHODS
- Clear web/dist before frontend build to prevent stale artifacts
- Add autocomplete attrs to login inputs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:19:51 +00:00
Dorian
f940b4562a fix: filebrowser port bind, CSRF in tests, console-setup, auto-test scope
FileBrowser crash fix:
- Add --cap-add=NET_BIND_SERVICE (port 80 needs it with --cap-drop=ALL)
- Add --cap-add=DAC_OVERRIDE for rootless volume access
- Both in first-boot script and backend config.rs

Test script fixes:
- Extract csrf_token cookie and send as X-CSRF-Token header on RPC calls
- Add --phase1-only flag for safe install-only checks (no side effects)
- Auto-test service uses --phase1-only so it doesn't steal onboarding

Install fixes:
- Pre-create ~/.local/share/containers (ReadWritePaths mount namespace error)
- Fix console-setup.service: add After=tmp.mount + ExecStartPre mkdir /tmp

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 17:17:18 +00:00
Dorian
127a36c5c8 fix: production onboarding, CI tests, container security, keyboard nav
Install & Onboarding:
- Remove DEV_MODE=true from production ISO service file (auto-created
  users, skipped password setup)
- Auto-install no longer overwrites rootfs service file with bad template
- Login.vue always checks auth.isSetup — shows password creation form
  on fresh install without requiring dev build flag
- Deploy image-versions.sh to /opt/archipelago/scripts/ on installed nodes
- First-boot-containers sources image-versions.sh, runs podman as
  archipelago user (rootless), enables linger + podman.socket
- Correct volume ownership (100000:100000 for rootless UID mapping)

Container Security:
- FileBrowser: add --cap-add=DAC_OVERRIDE for rootless podman volume access
- FileBrowser: add --read-only, /data volume for database, proper cmd args
- First-boot script matches backend config (security hardening + health check)

CI Pipeline:
- Add vue-tsc type check + vitest run to build-iso.yml (runs every push)
- Add post-install-tests.yml workflow (workflow_dispatch, SSH to target)
- Build report: set +eo pipefail, fix rootfs path, add || true guards
- Bundle run-post-install-tests.sh into ISO

E2E Test Suite (scripts/run-post-install-tests.sh):
- Phase 1: Install verification (files, services, podman, linger, DEV_MODE check)
- Phase 2: Onboarding flow (auth.isSetup, auth.setup, login, DID, complete)
- Phase 3: Container lifecycle (install 3 apps via package.install RPC,
  verify running, stop, verify stopped, restart, verify running, health)
- Phase 4: Log verification (first-boot log, diagnostics, journal errors)
- Correct package.install params: {"id", "dockerImage"}

Frontend:
- Fix backdrop-filter tab-switch bug (keep animations paused during rebuild)
- Dashboard glitch animations paused during tab-hidden
- Gamepad nav: auto-focus first container on route change
- Tab roving: Left/Right on role="tab" cycles and activates sibling tabs
- ContainerApps: data-controller-launch on running app cards
- 515 tests passing (fixed 30 broken, added 19 new keyboard nav tests)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:16:57 +00:00
Dorian
320c9f5b19 fix: container install flow, filebrowser auth, AppCard enrichment
- Fix .198-style fresh installs: systemd service ExecStartPre creates
  /run/user/1000, enable podman.socket, chmod 644 /etc/hosts
- Filebrowser: add /data volume for database (fixes read-only crash),
  secure auth with random password via backend RPC (no more admin/admin)
- AppCard: enrich installing state with marketplace metadata (icon,
  title, description, tier badge, author, version)
- Registry: btcpayserver 1.13.5 → 1.13.7, images mirrored
- ReadWritePaths: add home container paths for rootless podman

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:32:54 +00:00
Dorian
ae13c0dad2 feat: migrate all container images to Archipelago app registry
All container image references now pull from 80.71.235.15:3000/archipelago/
instead of Docker Hub and ghcr.io. image-versions.sh is the single source
of truth; all scripts use $*_IMAGE variables instead of hardcoded refs.

Files updated:
- scripts/image-versions.sh: central ARCHY_REGISTRY variable
- core/*/config.rs: registry whitelist includes app registry
- core/*/stacks.rs: Immich + Penpot stack images
- scripts/{first-boot,deploy-to-target,container-specs}.sh: use variables
- docker/*/Dockerfile: nginx base image from registry
- image-recipe/: ISO build, podman config, menu script
- scripts/{container-doctor,deploy-bitcoin-knots,fix-indeedhub,validate-app-manifest}.sh

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 14:06:21 +00:00
Dorian
08bb2c80d4 feat: LUKS2 encryption, boot sequence fixes, onboarding auth, CI/CD
- LUKS2 full-partition encryption for /var/lib/archipelago/ (TASK-42)
  4-partition layout: BIOS + EFI + root (30GB) + encrypted data
  AES-256-XTS with AES-NI detection, ChaCha20 fallback for ARM
  Auto-unlock via crypttab + random key file

- Fix EFI boot errors: remove shim-signed, clean shim artifacts
- Fix first-boot sequence: always show boot animation before onboarding
- Fix stale localStorage causing login instead of onboarding (BUG-47)

- Add auth.setup + auth.isSetup RPC handlers for password on clean install
- Add onboarding methods to UNAUTHENTICATED_METHODS (DID sign 403 fix)

- FileBrowser bundled in unbundled ISO, fix auto-login Secure cookie (BUG-46)
- Kiosk mode: xorg/chromium in rootfs, toggle script, MOTD instructions

- Add Gitea Actions CI/CD workflow for automatic ISO builds

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 09:12:16 +00:00
Dorian
0e0c97c203 feat: architecture review fixes, self-update system, CI pipeline, supply chain hardening
Architecture review (all P0+P1 issues now fixed):
- Add 10s timeout to 6 bare Nostr client.connect() calls
- Pin all 12 crypto deps to exact versions from Cargo.lock
- Pin all 15 floating container image tags to exact patch versions
- Add CI pipeline (cargo fmt + clippy + tests, frontend type-check + build)

Self-update system (git.tx1138.com):
- scripts/self-update.sh: pull, build, install, restart with rollback
- systemd timer checks daily at 3 AM
- update.check RPC does git-based checks when repo is present
- update.git-apply RPC triggers self-update from UI
- Default update URL changed from GitHub to git.tx1138.com
- Git added to ISO package list for fresh installs

Documentation:
- CHANGELOG v1.3.1 with all changes
- README updated (version, update system section)
- BETA-PROGRESS session #6 logged
- architecture-review.html: 4 issues marked FIXED, 8/12 refactoring done

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 15:52:26 +00:00
Dorian
13e4a738be bug fixing and deploy and build diagnostics 2026-03-22 03:30:21 +00:00
Dorian
77f550fb5e refactor: split package.rs, mod.rs, listener.rs, and lnd.rs into focused submodules
- R35: Split package.rs (1794 lines) into package/{mod,config,validation,lifecycle}.rs
- R36: Split mesh/listener.rs (1799 lines) into listener/{mod,session,frames,decode,dispatch,bitcoin}.rs
- R37: Split rpc/mod.rs into mod.rs + dispatcher.rs, middleware.rs, response.rs (54% reduction)
- R38: Split lnd.rs (1064 lines) into lnd/{mod,info,channels,wallet,payments}.rs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 02:26:28 +00:00
Dorian
f3976ba03a refactor: centralize constants, eliminate unwraps, remove dead code, resolve TODOs
- R13+R16: Replace .expect() with .context()? in main.rs and identity.rs
- R17+R18+R19: Fix unwrap() calls in helpers and js-engine
- R20+R21: Remove #[allow(dead_code)] annotations and delete truly dead code
- R22-R26: Create constants.rs module, replace 21 hardcoded values across 12 files
- R28+R29: LND/DWN timeouts already present — verified
- R30-R33: Remove TODO comments, implement marketplace payment check

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 01:54:35 +00:00
Dorian
5c3a3ffa8e fix: systemd resource limits, Tor rotation transition, unwrap elimination, RPC timeouts
- I2: Add MemoryMax=4G, LimitNOFILE=65535, TasksMax=2048 to systemd service
- I3: Tor rotation keeps old service for 1h transition before cleanup
- R14: Replace .parse().unwrap() with .unwrap_or(localhost) in rate limiter
- R15: Replace 7 unwrap/expect in mesh protocol with proper error propagation
- R27: Add 10s timeouts to mesh Bitcoin RPC calls

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 01:46:40 +00:00
Dorian
4d17c60da7 refactor: replace blocking std::fs and TCP I/O with async tokio equivalents
- R6: Convert 6 std::fs calls in session.rs to tokio::fs async
- R7: Convert std::fs::read_to_string in docker_packages.rs to async
- R8: Convert 3 std::fs calls in port_allocator.rs to async, switch to tokio::sync::Mutex
- R9+R10+R11: Fix blocking I/O in node_message.rs and nostr_discovery.rs
- R12: Convert electrs_status.rs from sync TCP to async tokio::net with 5s timeouts
- R4+R5: Spawn periodic cleanup tasks for endpoint and login rate limiters

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 01:21:08 +00:00
Dorian
c299199d37 fix: add health RPC handler, Nostr connect timeouts, atomic backup restore, nginx rate limits
- R1: Add health RPC endpoint with crash recovery status, uptime, and version
- R2: Wrap all 5 Nostr client.connect() calls in 10s timeout
- R3: Make backup restore atomic with staging dir and rollback on failure
- I1: Add rate limiting, body size, and proxy timeouts to unauthenticated nginx endpoints

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 01:02:16 +00:00
Dorian
196682f2f2 fix: LND and ElectrumX Tor onion address resolution
- lnd.rs: check tor-hostnames readable copy, then /var/lib/tor/, then
  legacy /var/lib/archipelago/tor/ with sudo fallback for each
- electrs_status.rs: same multi-path resolution for ElectrumX onion
- Both servers: created /var/lib/archipelago/tor-hostnames/ with readable
  copies of onion addresses (avoids sudo on every API call)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 12:31:30 +00:00
Dorian
b31148a8b7 fix: rpcauth credentials, reboot survival, system Tor for all containers
- Bitcoin RPC: switch to rpcauth (salted hash in bitcoin.conf, no plaintext
  in config or CLI). Password stable across reboots/restarts/deploys.
- Remove daily-reboot-test.sh cron on both servers
- Enable podman-restart.service for container auto-start after reboot
- System Tor: SocksPort 0.0.0.0:9050 with SocksPolicy for container access
- LND: tor.socks=host.containers.internal:9050 (system Tor, not container)
- Bitcoin: -proxy=host.containers.internal:9050 for Tor outbound
- bitcoin_rpc.rs: reads from secrets file, cached, stable credentials
- package.rs: dynamic rpc_user/rpc_pass, rpcauth hash generation
- network.rs: fix missing send_to_peer args (mesh encryption update)
- first-boot-containers.sh: rpcauth generation, system Tor config
- deploy-to-target.sh: rpcauth credentials, LND config migration
- Mesh: encrypted channel message support (ChaCha20-Poly1305 updates)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 11:56:20 +00:00
Dorian
b4d204d1d6 feat: reboot button in Settings with password confirmation
- system.reboot RPC endpoint requires password re-verification
- Uses systemd path unit pattern (tor-helper.sh) for privilege escalation
- 2-second delay before reboot to allow RPC response to reach client
- Clean UI: password input modal, loading state, error feedback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 10:48:06 +00:00
Dorian
9b6adfc42d feat: E2E encrypted Tor channel messages (ChaCha20-Poly1305)
Messages between federated nodes are now end-to-end encrypted:
- X25519 ECDH key agreement from existing ed25519 node identities
- HKDF-SHA256 key derivation with domain separation
- ChaCha20-Poly1305 authenticated encryption per message
- Random 12-byte nonce per message via OsRng (CSPRNG)
- Graceful fallback to plaintext if encryption fails
- Receiver auto-detects encrypted vs plaintext messages

The Tor transport was already encrypted (onion routing), this adds
application-layer E2E encryption so even a compromised receiving
backend can't read messages without the node's private key.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 09:04:43 +00:00
Dorian
f0a403b224 fix: persistent Tor channel messages, bulletproof Tor after deploys
- Messages persisted to disk (messages.json) — survive restarts
- Sent messages stored on backend via node-store-sent RPC
- Message deduplication (same pubkey + message within 30s)
- Max 200 messages in circular buffer
- Direction field (sent/received) for proper UI display
- Container doctor: prefer system Tor, remove archy-tor container
- Deploy torrc generator: read from tor-config/services.json,
  web apps map port 80→local port for clean .onion URLs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 08:26:40 +00:00
Dorian
fc1120338d fix: Tor management system, bug fixes, federation name sync
Major changes:
- Full Tor hidden service management via systemd path unit pattern
  (tor-helper.sh + archipelago-tor-helper.path/service) — respects
  NoNewPrivileges=yes, no sudo needed from backend
- Container doctor: prefer system Tor over container, remove archy-tor
- Deploy script: fix torrc generation (read correct services.json path),
  web apps map port 80→local port, enable both tor and tor@default
- Federation: server rename pushes name to peers via background sync
- Server name: fix root-owned file, optimistic store update
- Mesh: local echo for sent messages, sendingArch loading state
- Web5: Message button → Mesh redirect, node name lookup in messages
- PeerFiles: show DID not onion in header
- Connected Nodes: flex-1 instead of fixed max-h
- Toast notifications route to Mesh
- Deploy script: fix single-quote syntax in SSH block

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 02:59:29 +00:00
Dorian
c5417640a2 feat: Lightning channel backup, Web5 mobile tab active, file path fix
Task 14: Lightning Channel Backup
- New lnd.export-channel-backup RPC — exports SCB (Static Channel Backup)
- Settings UI: "Lightning Channel Backup" section with export + copy
- Returns base64 backup data, channel count, timestamp

Web5 mobile tab active state
- Fixed combined tab matching for Web5: includes /web5, /federation, /mesh routes
- Previously only matched /cloud and /server (wrong branch)

Content file path fix
- Allow forward slashes in filenames for subdirectories (Music/song.mp3)
- Still block .., \, null bytes, hidden files, absolute paths

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 21:47:18 +00:00
Dorian
93aaeb4abe fix: node names not DIDs, file sharing path validation, sync results
- nodeName() shows friendly "Node-XXXX" instead of truncated DID
- nodeNameFromDid() for sync results lookup
- Map labels use node names
- Content filename validation: allow / for subdirectories (Music/song.mp3)
  but still block .., \, null bytes, hidden files, absolute paths
- Increased filename max length to 512 for paths with subdirectories

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 20:35:41 +00:00
Dorian
1a138c0409 feat: DID rotation + federation peer notification (Part 3)
- node.rotate-did: generates new Ed25519 keypair, signs rotation proof
  with old key, overwrites identity files, requires password
- federation.notify-did-change: broadcasts rotation proof to all
  trusted/observer peers over Tor
- federation.peer-did-changed: receiving side verifies rotation proof
  against known pubkey before updating peer's DID
- Rate-limited: 3/600s for rotation, 5/60s for peer notification
- Signature verification uses ed25519_dalek (constant-time)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 19:27:16 +00:00
Dorian
f8794791f3 feat: DID persistence + federation node names in sync
Part 1 — DID Persistence:
- Deploy script creates /var/lib/archipelago/identity/ directory
- First-boot script creates identity dir with proper ownership
- Identity load now logs pubkey to confirm persistence across restarts

Part 2 — Node Names:
- NodeStateSnapshot includes node_name field
- build_local_state() passes server name to sync responses
- update_node_state() stores peer's announced name on the FederatedNode
- Names propagate automatically during federation.sync-state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 19:19:13 +00:00
Dorian
f20f0650cf feat: Discover view, Fleet dashboard, MeshMap, type fixes
- New Discover.vue (app store redesign)
- Fleet.vue dashboard for .228
- MeshMap.vue component
- Fixed Discover.vue type errors (unused var, type predicate)
- Various UI updates (Apps, Dashboard, Marketplace, Mesh, Web5)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 16:12:01 +00:00
Dorian
e574b6dd18 fix: SameSite=Strict → Lax for session cookies (fixes iframe fetch)
SameSite=Strict prevents cookies from being sent when iframe content
(like the LND UI at /app/lnd/) fetches endpoints on the parent origin
(/lnd-connect-info). Lax still protects against CSRF on POST requests
but allows same-site GET navigations and fetches from iframes.

This was the root cause of "Failed to fetch" on LND Connect.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:30:58 +00:00
Dorian
6033199864 fix: remove backend auth check on /lnd-connect-info (nginx validates session)
Backend is bound to 127.0.0.1 — only nginx can reach it.
Nginx checks cookie_session presence. Adding backend auth broke
the LND UI iframe fetch because the session validation was too
strict for the cross-proxy cookie flow. The nginx layer is the
correct auth gate for this endpoint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:20:44 +00:00
Dorian
aabeb2e679 security: add is_authenticated check to /lnd-connect-info backend handler (AUTH-011)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:58:16 +00:00
Dorian
1a74a930f7 security+feat: v1.3.0 — pentest remediation, container reliability, UI overhaul
Security (33 pentest findings addressed):
- CRITICAL: backend binds 127.0.0.1, path traversal in tor.rs/dwn fixed
- HIGH: federation requires signatures, XSS login redirect, RBAC viewer restricted
- HIGH: tar slip prevention, S3 SSRF validation, backup ID validation
- MEDIUM: remember-me random secret, TOTP session rotation, password re-auth
- LOW: CSP unsafe-inline removed, CORS dev-only, onion/webhook validation

Container reliability:
- Memory limits on all 37 containers (OOM prevention)
- Exited vs stopped state distinction with health-aware status badges
- Crash recovery coordination (no more restart cascade)
- User-stopped tracking survives reboots
- Tiered boot recovery (databases → core → services → apps)

UI:
- Wallet TransactionsModal, health-aware app status badges
- Restart button on containers, exited/crashed red state
- Mesh view overhaul, glass button updates, BaseModal/ToggleSwitch
- Apps sticky header removed, dev faucet, mutable mock wallet

Infrastructure:
- LND REST port 8080 exposed over Tor (LND Connect fix)
- Nginx cookie_session fix, deploy script Tor config updated
- Dev environment: podman auto-start, boot mode simulation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 12:44:31 +00:00
Dorian
41ab499698 feat(TASK-12): periodic telemetry reporter — 15min interval, collector POST
Background task spawned on server startup: every 15 minutes, checks opt-in
status, builds anonymous health report (node ID hash, version, uptime,
CPU/RAM/disk %, container states, recent alerts), saves to disk, and POSTs
to TELEMETRY_COLLECTOR_URL env var if configured. Non-fatal on failure.

Fixed FiredAlert field references (kind not rule_type, timestamp not
fired_at) in both monitoring and analytics modules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 23:36:57 +00:00
Dorian
165972e75c feat(TASK-12): beta telemetry — report endpoint + settings toggle
Backend: telemetry.report RPC builds anonymous health report with node ID
(SHA-256 hash of pubkey, truncated), version, uptime, container states,
CPU/RAM, federation peers, and recent alerts. Saves latest report to disk.
Requires analytics opt-in (existing analytics.enable/disable flow).

Frontend: "Beta Telemetry" section in Settings with enable/disable toggle.
Shows what data is and isn't collected. Mock backend handles all analytics
and telemetry RPCs.

Privacy: No wallet data, no private keys, no DIDs, no IP addresses.
Node identified by truncated hash only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 23:14:47 +00:00
Dorian
b7edada7fe chore: health endpoint JSON, BETA-PROGRESS updated to ~55%
Health endpoint now returns JSON with version and service status instead
of plain "OK". Updated BETA-PROGRESS.md: BUG-1 done, TASK-8 done (12/12
+ code audit), FEATURE-4 at ~80%, overall at ~55%. Added session #5 log.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:57:29 +00:00
Dorian
7bbd8f889a security: RBAC viewer role, identity label length, error sanitization
- RBAC: Viewer role changed from prefix "system." to explicit allowlist
  of safe read-only methods. Prevents Viewer access to system.factory-reset,
  system.shutdown, system.reboot, system.disk-cleanup.
- identity.create: Name/label param now enforces max 100 chars.
- sanitize_error_message: Changed from contains() to starts_with() for
  prefix matching, preventing internal errors that happen to contain
  user-facing keywords from leaking through.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:37:08 +00:00
Dorian
41ff1021ad fix: BUG-1 CSRF, TASK-8 H2/H3/H4, BUG-20/37/40/41 — 7 bugs fixed
BUG-1 (P0): CSRF tokens now HMAC-derived from session token instead of
random — survives backend restarts, eliminates cookie/header race conditions.
Frontend retries 403s as belt-and-suspenders.

TASK-8 H2: federation.peer-joined verifies ed25519 signature on join messages.
TASK-8 H3: federation.peer-address-changed requires signed proof from known peer.
TASK-8 H4: Rust backend default bind 0.0.0.0 → 127.0.0.1 (nginx proxies all).

BUG-20: ElectrumX index estimate string fixed from ~55GB to ~130GB.
BUG-37: App card Start/Stop buttons split into loading vs interactive states
        to prevent WebSocket state flicker during container scans.
BUG-40: Uninstall modal uses Teleport to body with z-[3000] for full overlay.
BUG-41: Uninstalling overlay on card + optimistic store removal.

Updated MASTER_PLAN.md and BETA-PROGRESS.md to reflect all completed work.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:05:21 +00:00
Dorian
27f205f38a security(TASK-8): fix 8 pentest findings — C1/C3/H1/M1/M2/L2
CRITICAL:
- C1: /lnd-connect-info now requires session auth, CORS wildcard removed
- C3: DEV_MODE removed from production service file (dev override only)

HIGH:
- H1: node-message endpoint now verifies ed25519 signatures when
  provided, logs warning for unsigned messages

MEDIUM:
- M1: content.add rejects filenames containing ".." (path traversal)
- M2: NIP-07 postMessage responses use specific origin instead of '*'

LOW:
- L2: Onion validation now enforces strict v3 format (56 base32 chars
  + ".onion", exactly 62 chars, no colons)

Previously fixed: C2 (RPC creds generated per-install from secrets)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 19:45:10 +00:00
Dorian
870ff095d8 feat: rootless podman, session hardening, boot stability, sidebar fix
Rootless podman migration (TASK-11):
- Remove sudo from all podman calls in PodmanClient + 8 backend files
- Remove sudo from all podman/docker calls in deploy script
- Restore full systemd security hardening: NoNewPrivileges,
  RestrictAddressFamilies, MemoryDenyWriteExecute, RestrictRealtime,
  RestrictNamespaces, RestrictSUIDSGID, SystemCallFilter, ProtectSystem=strict
- Enable loginctl linger for rootless container persistence
- Remove Ollama from auto-deploy (marketplace-only)

Session & auth hardening:
- Increase MAX_CONCURRENT_SESSIONS 20→50 (prevents eviction storms)
- Debounced 401 redirect in rpc-client.ts (prevents redirect storms)

Boot stability:
- optimize-debian.sh: adds chrony, swap, removes policy-rc.d
- deploy script: pre-restart chrony + swap setup
- ISO build: chrony package, swap file creation
- BootScreen: no longer clears localStorage (prevents splash replay)
- RootRedirect: sole owner of localStorage clearing on server ready

UI fixes:
- Sidebar opacity default changed from 0→visible (fixes missing sidebar
  after page-persistence login without entrance animation)
- Console.log/error wrapped in import.meta.env.DEV guards
- Remove unused route import from RootRedirect

Beta tracking:
- CLAUDE.md: beta freeze protocol added
- MASTER_PLAN.md: TASK-11, TASK-17, phase structure
- BETA-PROGRESS.md: initial tracking doc
- Tagged v1.2.0-alpha.1 as pre-rootless baseline

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 13:53:27 +00:00
Dorian
dd8e8e9e4f fix: Phase 3 — command injection, unwrap/expect panics, unsigned image acceptance
- VPN key gen: replaced sh -c with format string (command injection) with
  safe stdin piping to wg pubkey
- Secrets manager: replaced .unwrap() on path.parent() with proper error
- Tor proxy: replaced .expect("valid proxy") with continue on error
- Image verifier: added require_signatures flag, strict mode rejects
  unsigned images and missing cosign binary

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 00:45:15 +00:00
Dorian
c005dc9a22 feat: Phase 2 — systemd sandboxing, Bitcoin RPC localhost binding, Tailscale deprivilege
- Service runs as unprivileged `archipelago` user instead of root
- Added systemd sandboxing: ProtectSystem=strict, NoNewPrivileges, PrivateTmp,
  MemoryDenyWriteExecute, RestrictNamespaces, SystemCallFilter
- Bitcoin RPC rpcallowip restricted to localhost + Podman subnet (10.88.0.0/16)
- Tailscale container: removed --privileged, uses cap-drop ALL + cap-add NET_ADMIN/NET_RAW

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 00:42:29 +00:00
Dorian
809a976960 feat: Phase 1 — per-installation credential generation, eliminate hardcoded passwords
Generate unique random passwords at first boot for Bitcoin RPC, all database
services (mempool, btcpay, immich, penpot, mysql-root), and Fedimint gateway.
Credentials stored in /var/lib/archipelago/secrets/ with 600 permissions.

Scripts: first-boot-containers.sh, deploy-to-target.sh, deploy-bitcoin-knots.sh,
container-doctor.sh all read from secrets files instead of hardcoded values.

Rust backend: new bitcoin_rpc module reads password from secrets file, env var,
or dev fallback. All .basic_auth() calls and container config strings now use
the shared credential reader instead of hardcoded "archipelago123".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 00:39:52 +00:00
Dorian
f273816405 feat: v1.2.0-alpha — E2E encrypted mesh relay, steganography, relay status polling
Phase 5 mesh networking:
- E2E encrypted TX relay (X25519 + ChaCha20-Poly1305) — non-Archy nodes
  relay encrypted blobs transparently via Meshcore native routing
- Steganographic encoding modes (WeatherStation, SensorNetwork) — traffic
  looks like sensor data on the wire, 0xAA marker, configurable per-node
- Pre-flight Bitcoin Core health check on relay node — specific error codes
  (bitcoin_unreachable, bitcoin_syncing, tx_rejected) instead of generic fails
- mesh.relay-status RPC endpoint — frontend polls for relay result every 3s
- On-Chain / Lightning tabs in Off-Grid Bitcoin panel
- Archy Peers vs Mesh Broadcast relay mode selector
- Mesh view fills viewport (no page scroll), internal panel scrolling
- Version bump to 1.2.0-alpha

Also includes: deploy hardening, container fixes, IndeedHub updates,
boot screen, dashboard improvements, MASTER_PLAN task tracking

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 23:56:37 +00:00
Dorian
d1ac098edb feat: Phase 4 — off-grid Bitcoin relay, block headers, dead man's switch
- Typed message dispatch in listener (BlockHeader, TxRelay, LightningRelay, Alert, TxConfirmation)
- Base64 encoding for binary payloads over LoRa (fixes NUL byte truncation)
- Compact block header announcements (88 bytes, fits 160-byte LoRa limit)
- Block header announcer: internet nodes auto-announce new blocks to Archy peers
- TX relay: mesh-only nodes can broadcast transactions via internet-connected peers
- Confirmation tracking: relay node monitors 1/3, 2/3, 3/3 confirmations, sends updates back
- Dead man's switch background task with configurable interval and signed alert broadcast
- 6 new RPC endpoints: relay-tx, block-headers, relay-lightning, deadman-status/configure/checkin
- lnd.create-raw-tx: create signed TX without broadcasting (for mesh relay)
- Web5 wallet: offline detection + "Send via mesh?" prompt with auto relay + confirmation polling
- Mesh.vue: Off-Grid Bitcoin tab, Dead Man tab, Send Bitcoin/Lightning buttons
- TX/Lightning relay sends only to Archy peers (not broadcast to all devices)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 15:51:56 +00:00
Dorian
f504f08cd4 feat: Phase 3 Week 4 — mesh RPC endpoints for typed messages + session management
Backend (6 new RPC endpoints):
- mesh.send-invoice: create Lightning invoice, send bolt11 to mesh peer
- mesh.send-coordinate: send GPS coordinates (integer microdegrees)
- mesh.send-alert: send signed emergency alert (with optional GPS)
- mesh.outbox: list pending store-and-forward messages
- mesh.session-status: get Double Ratchet session info per peer
- mesh.rotate-prekeys: force X3DH prekey rotation

Mock backend: matching dev mode responses for all 6 new endpoints

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 02:23:30 +00:00
Dorian
f42ff45475 fix: resolve merge conflicts and compile errors for transport layer
- Resolve stash conflicts in Cargo.toml, rpc/mod.rs, AppDetails.vue, Apps.vue
- Fix ScopedIp conversion in LAN transport (mdns-sd compatibility)
- Fix String vs &str in transport RPC send handler
- Remove duplicate mod transport declaration
- Remove stale mesh.discover route (replaced by mesh.peers/messages/send)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 00:34:37 +00:00
Dorian
32f89fa8d5 backup commit 2026-03-17 00:03:08 +00:00
Dorian
57f3416d60 fix: Tor toggle tries systemd before container restart
The toggle handler only tried `podman restart archy-tor` which fails
on servers running Tor as a systemd service. Now tries
`systemctl restart tor` first (like the rotation handler already does),
falling back to container restart.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 17:41:32 +00:00
Dorian
30164fd12a feat: bitcoin-ui CSS fix, HTTPS proxy support, deploy script improvements
Bitcoin UI:
- Replace cdn.tailwindcss.com with locally bundled tailwind.css (CSP blocks external scripts)
- Make all asset paths relative for nginx proxy compatibility
- Add bitcoin-ui build/deploy to deploy-to-target.sh (was missing entirely)
- Use --network host (bitcoin-ui proxies Bitcoin RPC at 127.0.0.1:8332)

HTTPS mixed content fix:
- Add HTTPS_PROXY_PATHS in AppSession.vue — when parent page is HTTPS,
  iframe loads through nginx proxy instead of direct HTTP port
- Prevents browser blocking HTTP iframes inside HTTPS pages
- All Tailscale servers use HTTPS, this was breaking all app iframes

Deploy & first-boot improvements:
- first-boot-containers.sh auto-detects disk size for pruning vs txindex
- first-boot-containers.sh checks fallback source path for UI containers
- Added mempool-electrs to APP_PORTS mapping
- ElectrumX container creation in first-boot
- Podman doctor/fix/uptime skills added

Also includes: session persistence, identity management, LND transactions,
ElectrumX status UI, nostr-provider improvements, Web5 enhancements

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 12:58:35 +00:00
Dorian
cc270bcf34 fix: use c.name not c.names in factory reset
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 05:21:32 +00:00
Dorian
7b9fa08493 fix: use PodmanClient::new() in factory reset handler
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 05:20:15 +00:00
Dorian
c545b79b65 feat: factory reset, backup restore, auto-identity creation
- system.factory-reset RPC: wipes user data, preserves images/node_key
- Factory Reset button in Settings with confirmation modal
- backup.restore-identity RPC: decrypts and restores DID key
- Restore from Backup panel in OnboardingIntro first screen
- Auto-create default identity with Nostr key on boot if none exist

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 05:18:12 +00:00