feat(wallet,content,seed): Fedimint dual-ecash, paid content streaming, seed ceremony
- Fedimint ecash alongside Cashu: fedimint-clientd (fmcd) HTTP bridge, fedimint_client, fedimint RPC, wallet wiring - Paid peer content: content invoices + streaming content server + content RPCs - Seed-phrase ceremony/reveal RPCs and CLI ceremony tool - LND wallet, mesh status/messaging, app-stack (netbird HTTPS), and decoupled-update wiring; Fedimint Client core app in catalog Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
c10f2ac22e
commit
bd567cd165
@@ -44,6 +44,11 @@ pub struct ApiHandler {
|
||||
session_store: SessionStore,
|
||||
/// Broadcast channel for relaying companion app input to remote browsers.
|
||||
input_relay_tx: broadcast::Sender<String>,
|
||||
/// Reverse broadcast channel: the kiosk browser publishes "open this URL
|
||||
/// externally" requests here, and the companion (phone) socket forwards them
|
||||
/// to the phone's default browser. Lets "open in external browser" apps —
|
||||
/// which the kiosk can't usefully open itself — launch on the controller.
|
||||
external_open_tx: broadcast::Sender<String>,
|
||||
/// Content-addressed blob store for attachments shared over mesh/federation.
|
||||
blob_store: Arc<BlobStore>,
|
||||
/// Our own node pubkey (hex) — used to self-sign debug/test capabilities.
|
||||
@@ -71,6 +76,7 @@ impl ApiHandler {
|
||||
.await?,
|
||||
);
|
||||
let (input_relay_tx, _) = broadcast::channel(64);
|
||||
let (external_open_tx, _) = broadcast::channel(16);
|
||||
|
||||
// Derive a blob-store capability key from the node's Ed25519 signing
|
||||
// key. SHA-256 domain-separated so rotating the identity rotates
|
||||
@@ -100,6 +106,7 @@ impl ApiHandler {
|
||||
metrics_store,
|
||||
session_store,
|
||||
input_relay_tx,
|
||||
external_open_tx,
|
||||
blob_store,
|
||||
self_pubkey_hex,
|
||||
})
|
||||
@@ -356,7 +363,12 @@ impl ApiHandler {
|
||||
tracing::warn!("401 WebSocket /ws/remote-input — session invalid or missing");
|
||||
return Ok(Self::unauthorized());
|
||||
}
|
||||
return Self::handle_remote_input(req, self.input_relay_tx.clone()).await;
|
||||
return Self::handle_remote_input(
|
||||
req,
|
||||
self.input_relay_tx.clone(),
|
||||
self.external_open_tx.subscribe(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
// Remote relay WebSocket — browser receives companion input events
|
||||
@@ -365,7 +377,12 @@ impl ApiHandler {
|
||||
tracing::warn!("401 WebSocket /ws/remote-relay — session invalid or missing");
|
||||
return Ok(Self::unauthorized());
|
||||
}
|
||||
return Self::handle_remote_relay(req, self.input_relay_tx.subscribe()).await;
|
||||
return Self::handle_remote_relay(
|
||||
req,
|
||||
self.input_relay_tx.subscribe(),
|
||||
self.external_open_tx.clone(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
// Convert body to bytes for non-WS routes
|
||||
@@ -480,6 +497,22 @@ impl ApiHandler {
|
||||
Self::handle_content_preview(p, &self.config).await
|
||||
}
|
||||
|
||||
// Lightning-invoice peer-file sale (#46): mint invoice / poll settlement
|
||||
(Method::GET, p) if p.starts_with("/content/") && p.ends_with("/invoice") => {
|
||||
self.handle_content_invoice(p).await
|
||||
}
|
||||
(Method::GET, p) if p.starts_with("/content/") && p.contains("/invoice-status/") => {
|
||||
self.handle_content_invoice_status(p).await
|
||||
}
|
||||
|
||||
// On-chain peer-file sale (#46): issue address / poll for payment
|
||||
(Method::GET, p) if p.starts_with("/content/") && p.contains("/onchain-status/") => {
|
||||
self.handle_content_onchain_status(p).await
|
||||
}
|
||||
(Method::GET, p) if p.starts_with("/content/") && p.ends_with("/onchain") => {
|
||||
self.handle_content_onchain(p).await
|
||||
}
|
||||
|
||||
// Content serving — peers access shared content over Tor (no session auth)
|
||||
(Method::GET, p) if p.starts_with("/content/") => {
|
||||
Self::handle_content_request(p, &headers, &self.config).await
|
||||
|
||||
Reference in New Issue
Block a user