- UserRole enum: Admin (full), Viewer (read-only), AppUser (minimal)
- can_access() method checks RPC method against role permissions
- Role field on User struct with serde default (backward-compatible)
- Viewer: read system/federation/DWN/identity/backup/container status
- AppUser: system.stats, node.did, container list, password change
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New RPC endpoints:
- analytics.get-status: Check if analytics opted in
- analytics.enable/disable: Toggle opt-in
- analytics.get-snapshot: Anonymous aggregate data (version, app count,
hardware tier, CPU cores, RAM, federation peers)
No personal data: no DIDs, no IPs, no secrets. Strictly opt-in.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New RPC endpoints:
- backup.upload-s3: Upload encrypted backup to any S3-compatible endpoint
- backup.download-s3: Download backup from S3 to local storage
Supports MinIO, Backblaze B2, Wasabi via basic auth + S3 API.
Backups are AES-256-GCM encrypted before upload.
Rate-limited at 3 requests per 10 minutes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: sd_notify::notify(true, ...) cleared NOTIFY_SOCKET env var,
so watchdog pings never reached systemd. Backend killed every 60s.
Fixes:
- Change sd_notify::notify first param to false (keep socket)
- Increase WatchdogSec from 60 to 300 (5min) for crash recovery
- Add TimeoutStartSec=300 for slow container startups
- Adjust watchdog ping interval to 120s
This was causing 47 restarts/day on .198 and blocking REBOOT-03,
FLEET-03, FLEET-04, VC-04.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Simplify DHT encoding: use JSON instead of DNS packets (drop simple-dns)
- Fix mainline crate API: SigningKey takes 32 bytes, get_mutable returns Result
- Add missing dht_did field to IdentityRecord constructor
- Store DID Document as JSON in DHT (DNS encoding deferred)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- federation.list-nodes now includes vc_verified: bool per node
- True when a non-revoked FederationTrustCredential exists for the peer DID
- Integrates with VC-02's automatic VC issuance on federation join
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Issue W3C VC (type FederationTrustCredential) when joining federation
- Claims: federationPeer=true, establishedAt=timestamp
- Signed with node Ed25519 identity key
- Runs in background task (non-blocking)
- Stored via credentials system for later verification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add dht_did field to IdentityRecord (optional, serde-compatible)
- Add prefer_dht_did param to identity.issue-credential RPC
- When true and dht_did is set, uses did:dht as VC issuer
- Credential system already format-agnostic for any DID type
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- PERF-01: Move crash recovery to background tokio task so health
endpoint is available immediately on startup
- PERF-04: Add ResponseCache with 5s TTL for system.stats and
federation.list-nodes. Reduces CPU for frequent polling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Crash recovery (check_for_crash + recover_containers +
start_stopped_containers) now runs in a background tokio task.
The health endpoint is available immediately on startup instead of
blocking for 260+ seconds while containers restart sequentially.
This directly fixes the .198 boot recovery timeout issue where the
backend took 260s to become healthy after restart.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add tier: "" to all AppMetadata match arms (was missing from 30+ arms)
- Use std::thread::available_parallelism() instead of num_cpus crate
- Remove unused num_cpus dependency
- Fix unused variable warning in health_monitor.rs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
get_app_tier() classifies all apps:
- core: Bitcoin, LND, Electrs, Mempool, BTCPay, DWN, FileBrowser
- recommended: Fedimint, Grafana, Vaultwarden, Kuma, SearXNG, etc.
- optional: everything else
Tier field added to Manifest struct (data_model.rs) and exposed
via WebSocket package data for frontend tier badges.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MemoryTracker in health_monitor.rs tracks per-container RSS every 5 min.
Warns when a container's memory grows >50% over tracking period.
Parses podman stats output (GiB/MiB/KiB formats).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MEM-01: OOM kill detection via dmesg checks every 5 minutes
MEM-03: Disk growth rate tracking (288 samples over 24h), warns at >1GB/day
MEM-04: Systemd watchdog (WatchdogSec=60, sd_notify::Watchdog every 30s)
Service Type=notify for proper startup notification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add US-10 backup/restore test section to test-cross-node.sh
- Test cycle: create → list → verify → delete, 10 iterations × 2 nodes
- Increase backup.create rate limit from 3/600 to 10/600 (still conservative)
- Increase backup.restore rate limit from 2/600 to 5/600
- Clean up 21K+ stale DWN test messages on both servers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Make dwn.sync endpoint async: spawns background task, returns immediately
- Add 90s overall timeout to sync_with_peers via tokio::time::timeout
- Deduplicate peer onion addresses before syncing
- Batch message pushes (50 per request) instead of one-at-a-time over Tor
- Add 15s connect_timeout to Tor SOCKS5 client
- Cap local message query to 200 messages per sync
- Fix DWN HTTP handler to process ALL messages in batch (was only first)
- Add recordId deduplication in handler to prevent duplicate imports
- Update test script to poll dwn.status for sync completion
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bump version to 1.1.0 in Cargo.toml and package.json.
Add comprehensive CHANGELOG.md entry covering all v1.1.0 features:
NIP-07 iframe signing, file sharing across nodes, DWN multi-node sync,
node visualization map, Tor address rotation, boot container recovery,
and full monitoring/testing suite.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Added start_stopped_containers() to crash_recovery.rs that starts all
exited/created containers on backend startup, fixing the issue where
containers didn't come back after clean reboot (PID marker removed by
systemd stop). Created test-failure-recovery.sh covering 5 failure
scenarios: container crash, backend restart, Tor restart, full reboot,
and Tor traffic block (UPTIME-02).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
read_onion_address() now checks tor-hostnames readable cache first,
clears cache before wait_for_hostname, updates it after rotation.
Rotation restarts system Tor (not just archy-tor container). Created
test-tor-rotation.sh with 10 automated checks (INSTALL-03).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Added node.nostr-sign RPC that uses the node-level Nostr key (matching
getPublicKey), fixing pubkey mismatch where identity.nostr-sign used a
different key. Updated appLauncher to call node.nostr-sign. Added
nostr_sign_hash() to nostr_discovery.rs. Created test-nip07.sh with
11 automated checks (INSTALL-02).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- DWN sync now uses federation node list instead of old peer list
- Fix sync URL to use port 80 (nginx) instead of 5678 (direct backend)
- DWN /dwn endpoint now accessible without auth for peer sync
- Support both message formats: {message:{}} and {messages:[{}]}
- Replace request["message"] with unified message variable
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New PeerFiles.vue view shows federated peers and their shared catalogs
- Peer Files card in Cloud.vue shows when federation peers exist
- New content.download-peer RPC fetches content from peer via Tor
- Route: /dashboard/cloud/peers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- PeersOnly access now checks X-Federation-DID header against known federation nodes
- Specific availability restricts content to named peer DIDs only
- Anonymous/unknown DID requests get 403 Forbidden
- Free content remains accessible to everyone
- Paid content still returns 402 with price info
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add /content and /dwn proxy locations to nginx config (both HTTP and HTTPS)
so peer requests reach the backend instead of the SPA catch-all
- Update content_file_path() to check FileBrowser data dir as fallback when
files aren't in the dedicated content/files/ directory
- Populate size_bytes from actual file metadata in content.add
- Filter out availability:nobody items from the public catalog endpoint
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add tor-hostnames fallback for reading onion addresses when system Tor
owns hidden_service directories (permissions 700)
- Exempt federation.peer-joined, federation.get-state, and
federation.peer-address-changed from auth/CSRF (inter-node RPC)
- Set up system Tor with AppArmor overrides on archipelago-2 and 3
- All 3 servers federated and syncing successfully
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After rotation, spawns background task that publishes updated .onion to
Nostr relays and sends federation.peer-address-changed RPC to all peers
over Tor. Peers update their nodes.json with the new address.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
tor.rotate-service: renames hidden service dir, restarts Tor, waits
for new hostname. Old dir kept for 24h transition.
tor.cleanup-rotated: removes expired old service directories.
tor.toggle-app: enable/disable Tor access per app with service dir
management and container restart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: identity.nostr-encrypt-nip04, identity.nostr-decrypt-nip04,
identity.nostr-encrypt-nip44, identity.nostr-decrypt-nip44 endpoints
with auto-resolve to default identity. Frontend: appLauncher routes
nip04.* and nip44.* postMessage calls to backend RPC.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Every new identity now gets both Ed25519 (DID) and secp256k1 (Nostr)
keys from creation. The create() method calls create_nostr_key()
automatically, so identity.create RPC always returns nostr_pubkey
and nostr_npub fields.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DiskUsage and ContainerCrash alerts now fire webhooks via
send_webhook() after pushing WebSocket notifications. Added
data_dir parameter to spawn_metrics_collector for webhook config
access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Health checks, auto-restarts, and WebSocket notifications now run
unconditionally. Previously the entire health loop was gated on
webhook config, so fresh installs (webhooks disabled) got zero
container monitoring. Webhook HTTP delivery is now fire-and-forget
after the notification is pushed to the UI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Added new dependencies: `adler2`, `crc32fast`, `flate2`, `miniz_oxide`, and `libredox`.
- Updated existing dependencies: `tokio-rustls` to version 0.26.4 and `filetime` to version 0.2.27.
- Removed the `backup.rs` file as it is no longer needed.
- Introduced tests for configuration and credential management.
- Enhanced the `identity` module to generate W3C compliant DID documents.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The manifest field was validated but never applied to the podman create
command. Now passes --security-opt no-new-privileges=true for all containers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace sh -c echo with tokio::fs::write for bitcoin.conf generation
- Add client_max_body_size 1m to /rpc/ in both HTTP and HTTPS nginx blocks
- Document full audit findings in docs/security-audit-2026-03-11.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Webhook module with HTTP delivery, HMAC-SHA256 signing, and event
filtering. RPC handlers for get-config, configure, and test endpoints.
Settings page gains webhook configuration section with URL, secret,
event toggles, and test button.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>