Commit Graph

178 Commits

Author SHA1 Message Date
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
f292ebf63e fix: ElectrumX status uses headers.subscribe (returns height correctly)
The previous blockchain.numblocks.subscribe call returned data in a
format the parser couldn't extract height from. headers.subscribe
returns {height: N, hex: "..."} which is properly parsed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 13:51:03 +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
aad98dec08 fix: telemetry reporter field name cpu_percent, add type annotation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 23:53:17 +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
adcc3fddc7 security: migrate bcrypt→Argon2id, random Bitcoin RPC password
Password hashing migrated from bcrypt to Argon2id (m=64MiB, t=3, p=4).
Transparent upgrade: on successful bcrypt login, re-hashes with Argon2id
and persists. New signups and password changes use Argon2id directly.
Unifies crypto stack — Argon2id was already used for TOTP and backup KDF.

Bitcoin RPC password: no longer falls back to hardcoded "archipelago123".
On first boot, generates a random 32-char hex password from CSPRNG,
saves to /var/lib/archipelago/secrets/bitcoin-rpc-password with 0600
permissions. Existing installs with secrets file are unaffected.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:41:23 +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
25ad68ac4c fix: BUG-33 CPU threshold, TASK-27 tab icons, TASK-36 iframe errors
- BUG-33: CPU load alert threshold increased from 2x to 4x core count
  (8→16 on 4-core machine) to reduce false alerts during container ops
- TASK-27: Launch buttons for new-tab apps now show external link icon
  (BTCPay, Grafana, PhotoPrism, Portainer, OnlyOffice, etc.)
- TASK-36: Iframe error screen now distinguishes between X-Frame-Options
  blocked vs container not reachable, with appropriate messaging

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 19:24:52 +00:00
Dorian
dd5ab6b10a fix(TASK-26): Rename fedimintd to "Fedimint Guardian"
Added fedimintd to the metadata map with title "Fedimint Guardian"
and description clarifying it's the federation consensus node.
Shares the fedimint.png icon.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:56:45 +00:00
Dorian
f54206d231 fix(BUG-20): ElectrumX shows index size instead of "Building..."
When ElectrumX is indexing and can't accept TCP connections, the UI
now shows the actual index size (e.g. "126.9 GB") in the Indexed
Height field instead of a generic "Building..." label. Also shows
the size in the status message for better progress visibility.

Updated estimated full index size from 55GB to 130GB (2026 mainnet).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:50:33 +00:00
Dorian
9f90c2cc91 fix: Fedimint Guardian UI on port 8175 (not 8174 API)
Fedimintd serves JSON-RPC API on 8174 and Guardian web UI on 8175.
Updated all port mappings: frontend AppSession, nginx HTTP/HTTPS
proxies, PodmanClient static map.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:31:07 +00:00
Dorian
00eebfbb3d fix: import PodmanClient for lan_address_for fallback
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:35:12 +00:00
Dorian
a6f2e6743f fix: use PodmanClient::lan_address_for as static fallback for port mapping
Dynamic port extraction from container bindings, falling back to the
static PodmanClient address map for apps without port bindings (e.g.
host-network containers).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:32:39 +00:00
Dorian
0c5b7db4a2 fix: dynamic port detection + electrumx sync + rootless infra
Backend:
- Remove most hardcoded port overrides from docker_packages.rs, use
  dynamic port extraction from actual container bindings with fallback
  to static map in PodmanClient
- Fix OnlyOffice (8044), NginxPM (8181), Fedimint (8174) port mappings
- Remove Tailscale fake web UI port (no web UI)
- ElectrumX: detect "Connection reset" as syncing state (not error)

Deploy script:
- Auto-configure sysctl unprivileged_port_start=80 for rootless
- Auto-enable loginctl linger for container persistence
- Auto-enable podman.socket for Portainer

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:29:03 +00:00
Dorian
fef7e8cb24 fix: ElectrumX sync detection + rootless podman infrastructure
- ElectrumX status: detect "Connection reset" as syncing (not error)
  by using case-insensitive check on connect/reset/refused
- Deploy script: auto-configure rootless podman prerequisites
  (sysctl unprivileged ports >= 80, loginctl linger, podman socket)
- Marketplace: sort installed apps to bottom of list

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:07:09 +00:00
Dorian
cc8a6fd4d8 fix: container state mapping + marketplace install aliases
- Created containers now show as "stopped" not "starting" (fixes
  ollama/tailscale perpetual "starting" state)
- Comprehensive INSTALLED_ALIASES map: fedimint, electrumx, grafana,
  jellyfin, vaultwarden, searxng, homeassistant, photoprism, lnd,
  filebrowser, tailscale, ollama — prevents marketplace showing
  "Install" for already-installed containers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 15:18:43 +00:00
Dorian
0c8dd582fa fix: rootless podman scanning — relax namespace/syscall restrictions
RestrictNamespaces and SystemCallFilter block rootless podman from
creating user namespaces needed for container isolation. Removed these
along with RestrictSUIDSGID (implied by NoNewPrivileges). ProtectHome
set to no (rootless podman needs ~/.local/share/containers writable).

Remaining active protections: NoNewPrivileges, ProtectSystem=strict,
ReadWritePaths, RestrictAddressFamilies, MemoryDenyWriteExecute,
RestrictRealtime, SystemCallArchitectures=native.

Also reduced initial scan delay from 15s to 3s for faster container
visibility after boot, and removed Ollama from auto-deploy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 14:22:00 +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
6a56d4972d fix: prevent install buttons showing before first container scan
Added containers_scanned flag to StatusInfo in the data model. Starts
false, set to true after the first Podman scan completes (~15s after
boot). Marketplace now shows a shimmer "Checking..." indicator on app
buttons until the scan finishes, preventing users from accidentally
re-installing apps that are already present but not yet enumerated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:46:38 +00:00
Dorian
3187d1ad28 fix: remove doubled -alpha-alpha version suffix
CARGO_PKG_VERSION already contains -alpha from Cargo.toml, so the
format!("{}-alpha", ...) was producing 1.2.0-alpha-alpha. Use the
Cargo version directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:28:28 +00:00
Dorian
7278397209 fix: bulletproof mesh serial connection — PrivateDevices, auto-detect fallback, backoff
Root cause: systemd PrivateDevices=yes hid /dev/ttyUSB* from the service,
preventing .198 from connecting to its Heltec V3 after the security hardening.

Changes:
- Set PrivateDevices=no in systemd service (serial access needs physical devices;
  other hardening layers remain: NoNewPrivileges, ProtectSystem, RestrictNamespaces)
- Add SupplementaryGroups=dialout for explicit serial permissions
- Add fallback auto-detect when configured serial path fails to open
- Add exponential backoff on reconnect (5s→60s cap) to reduce log spam
- Add pre-open device existence check with actionable error messages
- Add udev rule (99-mesh-radio.rules) for stable /dev/mesh-radio symlink
- Add /dev/mesh-radio to serial candidate list (checked first)
- Add Connect button per detected device in Mesh UI
- Deploy udev rule to both servers and ISO build
- Fix FEDI_HASH unbound variable in deploy script
- Fix deploy binary step to handle hung service stop gracefully

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:50:13 +00:00
Dorian
d341585bed fix: Phase 8 — mesh hardening: atomic writes, unwrap elimination, GPS opt-out
- Ratchet state: atomic write via tmp + rename to prevent corruption on crash
- Block header decode: replaced .unwrap() with proper error handling on
  untrusted network data (was a crash vector from malicious peers)
- Shutdown channel: replaced .unwrap() with .ok_or_else() error propagation
- Dead man's switch GPS: default changed to opt-out (auto_include_gps=false)
- Alert signature verification: already covered by Phase 4 envelope checks

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 01:04:19 +00:00
Dorian
36a33f3575 fix: Phase 7 — key zeroization, OsRng, checked arithmetic, TOTP rate limits
- SecretsManager: raw key stored in Zeroizing<[u8; 32]>, auto-zeroed on drop
- SecretsManager: replaced thread_rng with OsRng (CSPRNG) for nonces
- Remember-me secret: derived from machine-id via SHA-256 (deterministic, no
  plaintext key storage)
- Bitcoin ecash balance: uses checked_add with u64::MAX saturation on overflow
- TOTP setup/confirm: added to EndpointRateLimiter (3 and 5 per 5min)
- AppId validation and Tor service name validation already existed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 01:00:57 +00:00
Dorian
5853b6a065 feat: Phase 4 — mesh authentication, envelope signature verification, TX validation
- Identity announcements: verify Ed25519 key validity and X25519 consistency
- Envelope signatures: verify Ed25519 signatures on signed messages, drop invalid
- Block header validation: height range, hash length, timestamp sanity checks
- TX relay validation: hex validity, size bounds, version check before broadcast
- Rate limiter struct for per-peer relay operations
- Message sequence number field (seq) added to TypedEnvelope for ordering

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 00:49:38 +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
c6f1894e10 feat: Phase 3-4 Weeks 5+6 — off-grid Bitcoin ops + emergency alert system
Bitcoin relay (mesh/bitcoin_relay.rs):
- BlockHeaderCache: stores latest block headers from internet peers for SPV
- RelayTracker: tracks in-flight TX and Lightning relay requests
- Builder functions: block header announcements (Ed25519 signed),
  TX relay request/response, Lightning invoice relay/response
- All amounts as u64 sats, never float
- 4 unit tests

Emergency alerts (mesh/alerts.rs):
- AlertConfig: dead man switch settings, GPS, emergency contacts
- DeadManSwitch: background timer, auto-trigger after configurable interval
  (default 6h), signed alert broadcast with GPS coordinates
- check_in() resets timer, is_triggered() checks elapsed time
- GPS as integer microdegrees (Coordinate type from message_types)
- Disk persistence for config
- 4 unit tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 02:26:07 +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
c5c3dc856b feat: Phase 3 Week 3 — typed messages + store-and-forward outbox
- Create mesh/message_types.rs: typed message envelope system
  - MeshMessageType enum: Text, Alert, Invoice, PsbtHash, Coordinate,
    PrekeyBundle, SessionInit, BlockHeader, TxRelay, LightningRelay
  - TypedEnvelope: CBOR wire format with 0x02 prefix, optional Ed25519 sig
  - Payload types: AlertPayload (with AlertType enum), InvoicePayload
    (sats as u64), Coordinate (integer microdegrees, no float),
    PsbtHashPayload, BlockHeaderPayload, TxRelayPayload, LightningRelayPayload
  - Signed envelope creation + verification for alerts/block headers
  - 8 unit tests

- Create mesh/outbox.rs: store-and-forward message queue
  - PendingMessage with TTL (24h default), retry count, relay hops (max 3)
  - MeshOutbox: persistent VecDeque, max 200 messages, expiry, relay support
  - Disk persistence to mesh-outbox.json
  - 6 unit tests: enqueue, deliver, expire, persistence, max size, relay hops

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 02:08:58 +00:00
Dorian
6c23360522 feat: add per-peer ratchet session manager with disk persistence
- Create mesh/session.rs: SessionManager for Double Ratchet state lifecycle
  - Lazy-loads sessions from disk on first message
  - Saves after every encrypt/decrypt (chain key advancement)
  - Per-DID storage at {data_dir}/ratchet/{sha256(did)}.json
  - Session info API for RPC status reporting
  - Zeroize on drop for all key material
- Tests: store+load roundtrip, encrypt/decrypt through manager, session removal

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 01:54:26 +00:00
Dorian
e60ac99b12 feat: Phase 3 Week 2 — Double Ratchet protocol for forward-secret mesh messaging
- Create mesh/ratchet.rs: full Signal-style Double Ratchet implementation
  - DH ratchet with X25519 ephemeral keypairs per step
  - Symmetric-key ratchet via HKDF-SHA256 chain derivation
  - Per-message ChaCha20-Poly1305 encryption with derived message keys
  - Out-of-order delivery via skipped message key cache (max 100)
  - Forward secrecy: old keys zeroized on ratchet step
  - Wire format: 40B header + nonce + ciphertext + tag
- Tests: full conversation, out-of-order, forward secrecy, wire format,
  long conversation (50 messages alternating), message roundtrip

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 01:50:22 +00:00
Dorian
37a591618d feat: Phase 3 Week 1 — X3DH key agreement + HKDF foundation
- Add hkdf = "0.12" dependency for Double Ratchet key derivation
- Extend mesh/crypto.rs with hkdf_sha256, hkdf_sha256_32, hkdf_sha256_64,
  and generate_x25519_ephemeral() for DH ratchet steps
- Create mesh/x3dh.rs: full X3DH key agreement protocol
  - PrekeyBundle generation with Ed25519-signed prekeys
  - 3-way (or 4-way) ECDH → HKDF-SHA256 → root key
  - Initiator and responder sides derive identical root key
  - CBOR encoding for mesh transmission
  - Bundle signature verification
  - 5 unit tests: generate+verify, both-sides-same-key,
    without-one-time-prekey, cbor-roundtrip, tamper-detection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 01:28:35 +00:00
Dorian
7867ac1931 feat: complete Phase 2 transport layer — off-grid mode, transport icons, federation sync
- Add off-grid (mesh only) toggle to Mesh.vue with orange OFF-GRID banner
- Add per-peer transport indicator in Federation.vue (mesh/lan/tor icons)
- Add sync_with_peer_via_transport() for CBOR delta sync via transport router
- Fetch transport store on mount in both Mesh and Federation views

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 00:45:37 +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