Skip to content

feat: P2P relay + Nostr + Endpoints settings (all 6 phases)#4

Open
CyberAshven wants to merge 25 commits into
masterfrom
feature/p2p-relay
Open

feat: P2P relay + Nostr + Endpoints settings (all 6 phases)#4
CyberAshven wants to merge 25 commits into
masterfrom
feature/p2p-relay

Conversation

@CyberAshven

Copy link
Copy Markdown
Collaborator

Summary

  • Phase 1 (fully implemented): libp2p relay, Kademlia DHT, Noise encryption, store-and-forward queue (7-day TTL), built-in seed relay list
  • Phases 2-6 (scaffolded): onion routing, cover traffic, Nostr transport fallback (ephemeral keys + NIP-44), Android/iOS, relay bundle updater, BLE/LoRa/I2P mesh
  • Endpoints Settings: Fulcrum nodes, Nostr relays, Cauldron/Thorchain/Meta inputs — green terminal UI, RESET/CANCEL/SAVE
  • Relay Settings: Gupaxx-style green/red dots for local relay + remote peers, p2pd path, Start/Stop, bundle update button
  • SDK: relay + nostr modules (10 total)
  • README: updated architecture diagram + P2P Relay section

Test plan

  • Relay Node panel shows live seed relay ping dots
  • Start/Stop relay changes dot
  • Endpoints panel saves and resets
  • relay_daemon.py starts
  • sdk npm run build succeeds

ABLA and others added 20 commits March 16, 2026 00:31
- gui_bridge.py: read `models_path` from qubes-config.json to let users
  store models on any drive/path (e.g. D:\MyModels). Priority order:
  QUBES_MODELS_DIR env var > qubes-config.json models_path > auto-detect.
  Sets QUBES_MODELS_DIR so all subsystems see the same base path.

- audio/audio_manager.py: _load_config_from_env now resolves all model
  paths (piper, whisper, qwen3-tts) relative to QUBES_MODELS_DIR instead
  of the hardcoded ~/.qubes/models default.

- audio/audio_manager.py: _get_qwen3_provider now auto-triggers
  Qwen3ModelDownloader.start_download() when the selected variant is not
  yet on disk, so the model downloads automatically in the background
  instead of silently failing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…S models

- Add pull_ollama_model Rust command: streams NDJSON progress from Ollama API,
  emits ollama-pull-progress Tauri events to frontend
- Add check_local_tts_models Rust command: delegates to Python sidecar
- Add update_local_tts_models Rust command: delegates to Python sidecar
- Add check-local-tts-models Python handler: checks HF_HOME for Kokoro,
  sentence-transformers, and Whisper model directories
- Add update-local-tts-models Python handler: re-pulls models from HuggingFace
  using snapshot_download with QUBES_MODELS_DIR awareness
- Add 📦 Local Models collapsible section in SettingsTab after GPU Acceleration:
  - Lists installed Ollama models with per-model Update button
  - Update All Ollama Models button with live progress bar
  - Shows Kokoro TTS, Sentence Transformers, Whisper install status
  - Re-download Voice Models button

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Merge custom QUBES_MODELS_DIR support (our branch) with master's
auto-download improvements. Combined result:
- Priority: custom _custom_models_dir config > frozen auto-detect
- Always mkdir HF_HOME so models auto-download on first use
- Sets QUBES_MODELS_DIR in both paths

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Balance UI:
- Remove TotalBalanceWidget from header title bar
- Add Total Balance widget in QubeManagerTab toolbar (next to + New Qube):
  - Shows total BCH across all qubes (gray when 0, accent when has balance)
  - Refresh button (↻) refreshes all qube balances
  - Sweep All button (↓ Sweep) — disabled/gray at 0, active when balance > 0
  - Inline sweep-all input: enter destination address, confirm, sweeps all qubes with balance

Card Flip Sweep:
- Add ↓ Sweep Funds button on blockchain flip side per qube
  - Gray + disabled when balance = 0
  - Active (accent color) when balance > 0
  - Inline input: enter bitcoincash:q... address, confirm, sweeps that qube only
  - Shows ✓/✗ result, updates cached balance to 0 on success

Backend:
- Add sweep-qube-wallet Python command (calls orchestrator._sweep_before_delete)
- Add sweep_qube_wallet Rust invoke command

TTS Fix:
- Re-applied to local D:\Qubes binary (torch/nn/__init__.py pre-imports functional+init before modules)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ilds

- Loosen Tauri crate version constraints to "2" so cargo resolves latest
  compatible versions (fixes version mismatch with npm packages)
- Add pnpm-lock.yaml for reproducible frontend dependency installs
- Add .npmrc with allow-build=esbuild for Vite/Rollup native bindings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eration

The PyInstaller bundle strips torch.distributed, torch.futures, torch.rpc
and their submodules. TTS (Kokoro) and GPU acceleration code paths import
these at runtime, causing startup failures:
- No module named 'torch.distributed'
- cannot import name 'Future' from 'torch.futures'
- cannot import name 'nn' from partially initialized module 'torch'

Fixes:
- torch/distributed/__init__.py: import hook that auto-stubs any
  torch.distributed.* submodule import; pre-builds Join/Joinable/JoinHook
  as real classes so nn.parallel.distributed can subclass them
- torch/futures/__init__.py: minimal Future class with full interface
- torch/rpc/__init__.py: stub
- torch/nn/__init__.py: pre-import functional+init before modules to
  break circular import chain

Apply patches/ files to D:\Qubes\qubes-backend\_internal\torch\ after
any Qubes update that overwrites the bundle.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…layout

- sidecar_server.py: add _handle_check_local_tts_models and
  _handle_update_local_tts_models handlers + POSITIONAL_ARG_NAMES entries.
  Fixes "Unknown command: update-local-tts-models (no method on GUIBridge)"
  error — commands now route through sidecar server instead of stale main() elif chain.
- SettingsTab.tsx: add Download New Model input (model:tag + Download button)
  below Ollama model list. Reuses existing handlePullModel / pullProgress state.
- QubeManagerTab.tsx: move Total Balance widget after "+ New Qube" button;
  restructure widget with label+refresh row, balance row, full-width Sweep All button.
- Bump version 0.9.0 → 0.9.1 across package.json, tauri.conf.json, Cargo.toml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Migration period ended — wallet authentication is now required for:
- Backup (export_account_backup): added wallet_sig param to Rust command
  and UI; backup button disabled until wallet signed
- Restore local file: Restore button disabled until wallet signed
- Restore from IPFS: Restore button disabled until wallet signed
- LoginScreen: both restore flows require wallet sig before proceeding

Login, auto-anchor, and session operations remain password-only (unchanged).
Labels updated: "Optional: Wallet 2FA" → "Required: Wallet 2FA" everywhere.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Every sync was uploading a new file without deleting old ones, causing
duplicate accumulation (e.g. JARVIS backed up 15+ times). Added
_pinata_unpin_old() helper that queries Pinata pinList for the filename,
then DELETEs all old CIDs except the newly uploaded one. Called after
both per-Qube (sync_qube_to_ipfs_backup) and account-level
(export_account_backup_ipfs) uploads. Each Qube/account now keeps
exactly one backup in Pinata.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-bound

Added peek_backup_manifest command (Rust + sidecar pre-bridge handler) that
reads manifest.json from a .qube-backup ZIP without decrypting anything,
returning wallet_bound flag. Frontend now:
- Peeks manifest when file is selected in restore modal (QubeManagerTab + LoginScreen)
- Shows "Required: Wallet 2FA" if wallet_bound=True, "Wallet 2FA" otherwise
- Blocks Restore button only if wallet_bound=True and no sig provided
- Auto-anchor backups (wallet_bound=False) remain fully restorable without wallet
- Manual backups (wallet_bound=True) still require wallet sig

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove wallet sig requirement from backup (export) flow
- Always require wallet sig at restore (local + IPFS) — no conditional
- Remove peek_backup_manifest command and pre-bridge handler
- Remove wallet_bound conditional logic from QubeManagerTab + LoginScreen
- Remove backupWalletSig/backupWcAddress/isSigningBackupWc/restoreWalletBound states

Security model: file + password alone cannot restore — wallet ownership
required at restore time. Backup creation needs no wallet sig.
Auto-anchor unaffected (creates backups, never restores).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- LoginScreen: fix text "Required for all restores" (not backups)
- LoginScreen: add Copy URL button below QR code
- QubeManagerTab restore: inline connect flow (no longer says "connect wallet first")
- QubeManagerTab restore: shows QR code + Copy URL while waiting for scan
- Works for both wallet-connected and wallet-disconnected users

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… new owner)

After a Qube is received via wallet transfer and imported, automatically
trigger IPFS backup so it gets anchored to the recipient's own Pinata account.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Import from wallet now shows 3 clear steps with animated progress bar:
1. Downloading & decrypting from IPFS (per-Qube progress)
2. Uploading to recipient's own Pinata backup (inline, not background)
3. Done — modal auto-closes after 1.2s

IPFS upload is awaited (not silent) so user sees full progress.
Modal stays open until everything is safely backed up.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After decrypting each Qube from a backup, verify the restoring wallet
currently owns that Qube's NFT on-chain before writing any files.

Closes the "old backup" loophole: if Van's NFT was transferred away,
an old backup file still containing Van's data will now skip it
(skipped_count++) instead of silently restoring it.

- gui_bridge.py: import_account_backup + import_account_backup_ipfs
  accept wallet_address; build NFTVerifier once before loop; skip any
  Qube whose category_id the wallet no longer holds; unminted Qubes
  (no nft_metadata) are restored unconditionally
- lib.rs: import_account_backup + import_account_backup_ipfs accept
  wallet_address: Option<String> and forward it via secrets dict
- QubeManagerTab.tsx: passes restoreWcAddress to both restore invokes
- LoginScreen.tsx: passes wcAddress to both restore invokes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- IPFS restore modal now has 3 tabs: Pinata Account | Direct CID | Scan Wallet
  - Scan Wallet: connects WalletConnect, calls scan_wallet with wallet address,
    lists found Qubes (with already_imported flag), imports each via
    import_from_wallet with master password + 3-step progress bar
  - Both QubeManagerTab (logged-in restore) and LoginScreen (account recovery)
- Renamed "Restore from File" → "Restore from Local File" throughout LoginScreen
- Copy URL button was already present in walletConnectBlock — rebuild exposes it

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Connect Wallet button now uses GlassButton (same as restore modals inside app)
- Copy URL button now shows '✓ Copied!' feedback for 2s after click
- Adds wcCopied state to walletConnectBlock

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- gui_bridge.py: background anchor subprocess now checks auto_sync_ipfs_on_anchor
  preference and calls sync_qube_to_ipfs_backup after each successful anchor
  (pin new CID, unpin old — no Pinata bloat)
- config/user_preferences.py: default both IPFS sync options to True, 15 min interval
- SettingsTab.tsx: restructure into two clear sections (BCH Anchor + IPFS Backup)
  with timing shown on every row; threshold shows estimated message count;
  interval dropdown always visible (grayed when periodic disabled); 5 min option added

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 1 (fully implemented):
- network/relay_list.py — built-in BitFaced seed relays (Electron Cash pattern)
  Pre-loaded with well-known BCH Fulcrum nodes and Nostr relays
- network/relay_node.py — RelayNodeManager (wraps libp2p p2pd, store-forward)
- network/store_forward.py — JSON-backed offline message queue (7-day retention)
- network/bundle_manager.py — relay bundle path management (all in Qubes dir)
- 11 new Rust commands in lib.rs (relay + endpoints)
- 12 new Python commands in gui_bridge.py (relay + endpoints)
- RelayPreferences + EndpointPreferences in config/user_preferences.py

Phases 2-6 (scaffolded):
- network/onion.py — 2-hop onion router stub (Phase 2)
- network/cover_traffic.py — uniform-size cover traffic stub (Phase 2)
- network/nostr_transport.py — NIP-44 + ephemeral keys stub (Phase 3, BitChat-style)
- Phase 4 (mobile), Phase 5 (bundle update), Phase 6 (BLE/LoRa): TODOs in place

Settings UI (SettingsTab.tsx):
- 📡 Relay Node accordion — local relay start/stop with live green/red
  status dot (Gupaxx-style), p2pd binary path selector, remote relay
  status dots (seed + custom), relay bundle update button
- 🔌 Endpoints accordion — green terminal-style UI matching reference
  screenshot: Fulcrum/Electrum nodes, Nostr relays (textareas, one URL
  per line), Cauldron indexer, Thorchain Midgard, Meta/Icon service,
  RESET/CANCEL/SAVE buttons, pre-loaded with well-known defaults

SDK (@qubesai/sdk):
- sdk/src/relay/ — createRelayNode, sendMessage, onMessage, types (Phase 1)
- sdk/src/nostr/ — deriveSessionKeypair, nostrSend, nostrListen stubs (Phase 3)
- sdk/package.json — ./relay and ./nostr export entries added (10 modules total)

README: updated architecture diagram with relay stack, P2P Relay section,
SDK module count 8 → 10, relay daemon dev commands

relay/: standalone relay daemon for community operators
  (npm run relay:start / relay:dev / relay:test)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@CyberAshven CyberAshven reopened this Mar 16, 2026
@CyberAshven CyberAshven marked this pull request as draft March 16, 2026 23:16
ABLA and others added 4 commits March 17, 2026 02:24
…s, auto-load defaults

- Merge Relay Node and Endpoints into one "Endpoints" accordion panel
  (same style as API Keys section — one GlassCard, one toggle)
- Remove Thorchain Midgard, Cauldron Indexer, Meta/Icon Service
  (not relevant to P2P relay; can be added to a DeFi section later)
- Pre-fill Fulcrum/Electrum and Nostr relay textareas with well-known
  defaults on initial render — user never sees empty fields
- Load endpoint prefs on mount (useEffect) instead of only on panel open
- Show seed relay names statically with "start relay to ping" hint
  instead of "Open the relay to see seed relay status"
- Relay status dot appears in the Endpoints panel header

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… lib

- check_endpoints(): uses installed websockets>=14.1 for real WSS handshake
  checks (not just TCP) — connects to ALL Fulcrum + Nostr endpoints in parallel
- Add check_endpoints Tauri command + dispatch case
- UI: X/Y counter next to each section header (green=all up, yellow=partial,
  red=none), per-line status dots appear after ping, "↻ ping all" button
- Editing a textarea clears stale status so user re-pings after changes
- Nostr defaults trimmed to 4 well-known relays (damus, nos.lol, nostr.band,
  snort.social) — user confirmed these are the good ones
- Remove 4 extra Nostr defaults from user_preferences.py to match

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- bundle_manager.py now resolves relay bundle to {qubes_root}/relay/
  matching the D:\Qubes\ollama\ pattern — one clear folder per component
- _qubes_root_dir() detects install root from PyInstaller executable path
- p2pd.exe lookup: relay/ → _internal/ → system PATH
- Created D:\Qubes\relay\ with README.txt explaining what to place there
- Settings → Endpoints → Update Bundle will manage this folder

To activate relay: drop p2pd.exe into D:\Qubes\relay\

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…date, mobile)

Phase 2 — Privacy Layer:
- network/onion.py: Real 3-layer ECIES onion routing (ephemeral ECDH + AES-256-GCM)
  Outer→R1, Middle→R2, Inner→dest. 512-byte uniform packet padding.
- network/cover_traffic.py: 1Hz dummy packet emission to random online peers.
  Cover traffic failure never blocks real message delivery.
- network/relay_node.py: Onion wrap before DHT publish when 2+ peer pubkeys known.

Phase 3 — Nostr Integration:
- network/nostr_transport.py: Full NIP-44 v2 implementation.
  Pure Python secp256k1 (no native deps). BIP-340 Schnorr signing.
  XChaCha20-Poly1305 encryption (ChaCha20 fallback). WebSocket send+listen.
  HKDF-derived ephemeral keypairs — mathematically unlinked from BCH identity.
- relay_node.py: Nostr fallback when DHT publish throws. Start/stop lifecycle.

Phase 5 — Bundle Update:
- network/bundle_manager.py: check_for_update() fetches manifest via aiohttp.
  update_bundle(): download, SHA-256 verify, secp256k1 sig verify, atomic swap.
  Backup-and-restore on failure. Makes p2pd executable on non-Windows.

Phase 6 — BLE Transport:
- network/ble_transport.py: Cross-platform BLE driver via bleak.
  GATT service UUID, message fragmentation (512-byte chunks), async receive.
- requirements.txt: Add bleak>=0.21.0 (graceful fallback if not installed).

Phase 4 — Mobile Scaffolding:
- mobile/android/relay_node_android.py: gomobile binding surface documentation.
- mobile/ios/apns_bridge.py: APNS notification bridge scaffold.
- mobile/README.md: Architecture and build instructions for mobile devs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CyberAshven pushed a commit that referenced this pull request Mar 17, 2026
- Remove misplaced torch nn __init__.py from project root (#3)
- Remove dead sweepPassword state variable; always use masterPassword (#4)
- Reuse single aiohttp session for all Pinata unpin calls in _pinata_unpin_old (#7)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Transport layer:
- Add QUIC (/udp/quic) + WebSocket (/tcp/ws) listen addrs to p2pd daemon
- Enable -natPortMap and -autoRelay flags for better NAT traversal
- New network/lora_transport.py — LoRa long-range radio driver stub (Priority 4)
- New network/wifi_direct_transport.py — WiFi-Direct P2P driver stub (Priority 3)
- New network/i2p_transport.py — I2P anonymous overlay driver stub (Priority 5)

BLE mesh (BitChat parity):
- Add BLEServer class to ble_transport.py using bless library
- BLEServer advertises Qubes relay GATT service so nearby devices can
  discover and connect without internet (central + peripheral roles)
- Add bless>=0.2.5 to requirements.txt

p2pd auto-download:
- bundle_manager.ensure_p2pd_binary() downloads correct binary for
  Windows/macOS/Linux on first relay start if p2pd not present
- relay_node.start() calls ensure_p2pd_binary() before spawning daemon

Developer experience:
- Add relay:start / relay:dev / relay:test npm scripts to package.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@CyberAshven CyberAshven marked this pull request as ready for review March 17, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant