Replace all `podman` CLI shell-outs with HTTP requests to the rootless
Podman API unix socket (/run/user/{UID}/podman/podman.sock).
Benefits:
- No process spawning overhead — direct HTTP over unix socket
- Structured JSON responses — no string parsing fragility
- Proper timeouts on all operations (5s connect, 30s default, 120s create)
- Health check method to verify socket availability
- Restart container as first-class operation
Still uses CLI for:
- Image pulls (streaming operation better suited to CLI)
- Container logs (raw text stream, not JSON)
The Podman socket is rootless (runs as archipelago user), local-only
(unix socket), and already behind our session auth in the backend.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
- 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>
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>
- ShareModal: strip leading / from filepath (was causing "absolute paths not allowed")
- Server.vue: Tor status in Local Network section now uses same source as header
- Both fixes needed for file sharing and Tor to work consistently
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Public Channel:
- "Archipelago" channel in Mesh — broadcasts to all federation peers over Tor
- Shows received messages from all peers with pubkey label
- Auto-polls every 15s for new messages
- Orange-branded channel icon with unread badge
- Send handler routes to Tor broadcast when arch channel is active
FileBrowser Auto-Login:
- All filebrowser-client methods now call ensureAuth() before requests
- Auto-authenticates with default credentials if not logged in
- Fixes "files don't work when FileBrowser hasn't been logged into"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Marketplace picks up in-progress installs from WebSocket store even
if install was started before page was opened. Removed nested .git.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Federation page uses bg-web5.jpg background
- Invite code in full-screen modal with type label (Link/Peer)
- Join modal upgraded to full-screen with backdrop blur
- "Untrusted" renamed to "Blocked" in trust selector
- Your Nodes / Peers containers: max-h-[60vh] with inner scroll
- Server name from Settings shown on DID card + network map
- DID sync between Web5 and Federation on rotation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Web5 loads node DID from backend on mount (authoritative, survives rotation)
- Federation rotation updates localStorage so Web5 picks up new DID
- Cloud peer names: peerDisplayName() "Node-XXXX" instead of raw DID
- Cloud hides onion addresses from peer cards
- Sync timeout increased to 180s with better error message
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
- Onion address shows "Not visible to peers" for non-trusted nodes
- Resource usage and app list only shown for trusted nodes
- Deploy app already gated to trusted only
- Backend should also strip data in get-state (future: TASK)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- DID in glass-card top-right (desktop) / below title (mobile)
- Your Nodes + Peers in two-column grid (lg breakpoint)
- "Remove Dead Nodes" button for unreachable peers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Title: "Federation & Peers"
- Your Node DID moved to top-right header row (desktop), below title (mobile)
- Copy button shows "Copied!" feedback for 2 seconds
- Removed "X federated nodes" from description, added count to section header
- Rotate button compact in header
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- "My Node Identity" card shows DID with copy button
- "Rotate DID" button opens modal with password confirmation
- Rotation generates new keypair, then auto-notifies all federation peers
- Shows success/failure count after notification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
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>
- Task 13: added archy-* prefix containers, mempool-api, UI containers
to SERVICE_NAMES filter — removes empty squares from My Apps grid
- Task 12: App Store card hover changed from white/10 to orange-500/5
with orange border glow (subtle, not severe)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ensures LND Connect works through every deployment path:
- Nginx: CORS $http_origin on /lnd-connect-info (both HTTP+HTTPS)
- Nginx: no cookie gate (backend is 127.0.0.1-only)
- LND UI source: fetch with credentials: 'include'
- Deploy: rebuilds LND UI with --no-cache every deploy
- First-boot: --restart unless-stopped + memory limits on UI containers
- Backend: bound to 127.0.0.1:5678 in systemd service
Root cause was CORS: LND UI on :8081 fetching :80 is cross-origin.
Browser blocked reading the 200 response without CORS headers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The LND UI runs on port 8081 (separate nginx container) but fetches
/lnd-connect-info from port 80. This is cross-origin, so browsers
block reading the response without CORS headers. Added dynamic
Access-Control-Allow-Origin from $http_origin.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
Mandatory rules for all new code based on 33 pentest findings.
Covers: input validation, auth checks, SSRF prevention, session
management, CSP, nginx config, container security, RBAC.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
- New Discover.vue with hero banner, featured sovereignty stack apps,
principle cards, manifesto footer, and full app grid
- Featured apps (Bitcoin Knots, LND, BTCPay, Vaultwarden) with
expanded privacy/sovereignty descriptions
- Discover is first tab in categories bar on App Store pages
- Smart back navigation: detail pages return to Discover when navigated from there
- Category clicks from Discover navigate to Marketplace with category pre-selected
- Cypherpunk aesthetic: terminal tags, scanline overlays, gradient accents,
animated Bitcoin orange headings
- Global CSS classes: discover-hero, discover-terminal-tag, discover-featured-card,
discover-principle-card, discover-manifesto
- Route added: /dashboard/discover
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
ReceiveBitcoinModal was missing QR code generation that Web5.vue has.
Added canvas refs + qrcode rendering for both on-chain (bitcoin: URI)
and lightning (lightning: URI) receive flows. Matches Web5 pattern.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LND was crash-looping because lnd.conf had 127.0.0.1:8332 (container
loopback, not reachable) and the old hardcoded password. Deploy script
now detects stale values and patches them to bitcoin-knots:8332 with
the current secrets file password. Fixes address generation failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
TASK-17: Deploy script auto-tags successful clean deploys with next
alpha version number. Skips if commit already tagged or working tree
is dirty.
BUG-3: Updated IndeedHub submodule — removed dead nostrConfig with
hardcoded ws://localhost:7777 that caused WebSocket reconnection spam
in browser console. Relay detection via relay.ts (auto-detect /relay
proxy) is the active path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Tests expected router.push but panel mode (now default) uses panelAppId
store state instead. Updated assertions to check panelAppId. Fixed
BTCPay app ID from 'btcpay' to 'btcpay-server'. All 515 tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
Replaced single hardcoded release note with scrollable history of all
alpha releases (alpha.1 through alpha.9). Each release has version badge,
date, and categorized highlights. Inner container scrolls independently
with max-height 85vh. Current release highlighted with orange badge,
older releases in muted style with left border timeline.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
- 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>
TASK-31: Cleaned up Apps page nav header structure (tabs + categories + search).
TASK-38: Added Bitcoin Core sync progress gauge to homepage System Stats card —
shows sync percentage, block height, and green/orange color coding. Only
appears when Bitcoin is running. Grid expands to 4 columns when visible.
Updated MASTER_PLAN.md — cleaned up completed sections, moved done items.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Switch docker-compose from regtest to signet, add standalone testnet stack
(docker-compose.testnet.yml) with Bitcoin+LND+ThunderHub+Fedimint. Mock
backend now auto-detects Podman/Docker sockets and includes full LND/Lightning
RPC mocks. Dev scripts refactored with boot mode, testnet option, and macOS
EAGAIN fix for port cleanup. Added dev faucet button to Home.vue.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
Boot screen (BootScreen.vue) is already fully production-integrated:
- RootRedirect health checks → shows boot screen if server down
- Polls /rpc/v1 until healthy → transitions to login/onboarding
- Kiosk launcher loads browser immediately, boot screen handles wait
- All audio/icon assets deployed to /opt/archipelago/web-ui/
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mobile mesh had overflow:hidden inherited from desktop layout,
preventing scrolling. Added overflow:visible override for mobile.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mobile mesh view uses 0 top padding so the Dashboard's mobileTabPaddingTop
takes effect correctly (pushes content below fixed tab bar).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Mesh: remove display:flex from .mesh-header CSS that overrode
Tailwind hidden class, causing title/peers to show on mobile
- Federation: add title={did} on node name for hover tooltip
- Cloud: add title={did} on peer name for hover tooltip
- Both already show node.name when available, DID as fallback
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reverted inline-style sticky header. The hack used hardcoded rgba
background that didn't match across screens and shifted position
between tabs. Will implement properly with a shared layout component.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
My Apps/App Store/Services tabs, category filters, and search bar
now stay fixed at the top on scroll using sticky positioning with
glass-blur background. Applied to both Apps.vue and Marketplace.vue
desktop views.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reordered receive method tabs from [Lightning, On-Chain, Ecash] to
[On-Chain, Lightning, Ecash] in both ReceiveBitcoinModal and Web5
view. Default selection changed to 'onchain'.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mobile mesh view had padding: 0 causing glass cards to go edge-to-edge.
Added 12px padding for consistent gutters.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
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>
Extended INSTALLED_ALIASES to cover all container name variants so
marketplace correctly shows "Already Installed" for every deployed app.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Correct off-by-one in UID mapping: container UID N → host UID
(100000 + N - 1), not (100000 + N)
- Deploy script auto-fixes UID ownership on every deploy
- Bitcoin UI nginx uses __BITCOIN_RPC_AUTH__ placeholder injected
from secrets at deploy time
- container rules updated for rootless podman architecture
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Bitcoin UI nginx: use __BITCOIN_RPC_AUTH__ placeholder, injected at
deploy time from secrets file (fixes auth prompt regression)
- Deploy script: sed-replaces placeholder with real base64 RPC creds
before building bitcoin-ui Docker image
- Container state: "created" → "stopped" (not "starting") so ollama/
tailscale show correctly
- Comprehensive INSTALLED_ALIASES for marketplace
All container credentials now flow from secrets files through the
deploy script. Manual container recreation is no longer needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add automatic UID mapping fix to deploy script: uses sudo chown to
set host UIDs matching rootless podman's subuid mapping (container
UID 0→100000, 70→100070, 101→100101, 472→100472, 999→100999)
- Fix rpcallowip: rootless podman uses 10.89.0.0/16 not 10.88.0.0/16,
changed to 0.0.0.0/0 (safe: only accessible via port mapping)
- ProtectHome=no + no PrivateTmp: rootless podman needs shared /tmp
and writable ~/.local/share/containers
All 22 containers now running under rootless podman with working
Bitcoin RPC at block 941163.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
The security hardening (NoNewPrivileges, RestrictAddressFamilies,
MemoryDenyWriteExecute, RestrictRealtime, ProtectSystem=strict) all
blocked podman container management via sudo. These are temporarily
disabled until TASK-11 (rootless podman migration) is complete.
Remaining active protections: ProtectSystem=true (/usr, /boot),
ProtectHome=yes, PrivateTmp=yes, PrivateDevices=no (mesh radio).
Also adds TASK-11 to MASTER_PLAN.md for tracking the rootless podman
migration that will allow re-enabling full security hardening.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
- Add https: to CSP frame-src so external site iframes (BotFights,
484 Kitchen, etc.) load without being blocked by Content-Security-Policy
- Show spinner + "Starting..." on marketplace cards for containers that
are booting up, preventing users from re-installing running apps
- Add spinner to transitional state badges (starting/stopping/installing)
on installed app cards in Apps view
- Add "What's New" button to Settings version card with release notes
modal covering recent highlights in layman-friendly language
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
- Bitcoin Knots: added -proxy=127.0.0.1:9050 for P2P connections through Tor
- LND: enabled tor.active=true, tor.socks, tor.streamisolation in lnd.conf
- Tor setup handled by existing archipelago-setup-tor.service at first boot
- .onion display and Tor toggle already present in Settings UI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
- CSP: removed unsafe-eval, tightened frame-src to self + host ports,
added frame-ancestors, base-uri, form-action directives
- X-Frame-Options: SAMEORIGIN added after proxy_hide_header on all app proxies
- HSTS: max-age=31536000; includeSubDomains on all server blocks
- Rate limiting: 20r/s on /rpc/ with burst=40, 3r/s auth zone
- Added X-DNS-Prefetch-Control, Permissions-Policy payment=() header
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
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>