feat(peer)!: cut over to authenticated catalog sharing
Replace address-only trust and pushed peer state with installation identities, SPKI-pinned QUIC, candidate-only discovery, and bounded responder-owned protocol-8 pulls. The runtime now owns each network generation and all admitted work through shutdown. Add exact bundled content identities, reproducible manifest publishing, capability-confined downloads, streaming BLAKE3 verification, quarantine and retry, and crash-recoverable download and install transactions. Ship generated fixture catalogs and fail closed when production manifests are absent. The Tauri backend exposes durable sharing policy, redacted identity state, and attempt-keyed transfer snapshots. Frontend consumption follows in the next commit. Repository-wide test certificates and protocol-7 paths are removed. BREAKING CHANGE: peers must use protocol 8 and exact catalog content artifacts; protocol-7 frames and shared-certificate identities are no longer accepted. Test Plan: - `just test` -- passed on the completed stack (708 workspace tests) - `just clippy` -- passed on the completed stack - `just build` -- passed with fixture catalogs on the completed stack - `just catalog-check-production` -- failed closed because the external production manifest corpus is absent - `git diff --cached --check` -- passed
This commit is contained in:
128 files changed
+51759
-10784
No files matched your search
@@ -3,10 +3,10 @@
|
||||
use std::{collections::HashMap, net::SocketAddr, time::Duration};
|
||||
|
||||
use lanspread_mdns::{DaemonEvent, LANSPREAD_SERVICE_TYPE, MdnsAdvertiser, MdnsMonitor};
|
||||
use lanspread_proto::PROTOCOL_VERSION;
|
||||
use lanspread_proto::{PROTOCOL_VERSION, PeerId};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::{context::PeerCtx, network::select_advertise_ip};
|
||||
use crate::{context::PeerCtx, network::select_advertise_ip, scoped_blocking::scoped_blocking};
|
||||
|
||||
pub(super) async fn start_mdns_advertiser(
|
||||
ctx: &PeerCtx,
|
||||
@@ -21,26 +21,29 @@ pub(super) async fn start_mdns_advertiser(
|
||||
*guard = Some(advertise_addr);
|
||||
}
|
||||
|
||||
let peer_id = ctx.peer_id.as_ref().clone();
|
||||
let peer_id = ctx.peer_id;
|
||||
let hostname = gethostname::gethostname().to_string_lossy().into_owned();
|
||||
let advertised_name = advertised_service_name(&hostname, &peer_id);
|
||||
let advertised_name = advertised_service_name(&hostname, peer_id);
|
||||
let monitor_name = advertised_name.clone();
|
||||
let properties = advertisement_properties(ctx, &hostname, &peer_id).await;
|
||||
let properties = advertisement_properties(&hostname, peer_id);
|
||||
|
||||
let mdns = tokio::task::spawn_blocking(move || {
|
||||
let mdns = scoped_blocking(move || {
|
||||
MdnsAdvertiser::new(
|
||||
LANSPREAD_SERVICE_TYPE,
|
||||
&advertised_name,
|
||||
advertise_addr,
|
||||
Some(properties),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
})?;
|
||||
|
||||
log::info!("Registered mDNS service with name: {monitor_name}");
|
||||
Ok(mdns)
|
||||
}
|
||||
|
||||
pub(super) fn close_mdns_advertiser(advertiser: MdnsAdvertiser) -> eyre::Result<()> {
|
||||
scoped_blocking(move || advertiser.close())
|
||||
}
|
||||
|
||||
pub(super) async fn monitor_mdns_events(monitor: MdnsMonitor, shutdown: CancellationToken) {
|
||||
loop {
|
||||
let event = tokio::select! {
|
||||
@@ -67,7 +70,8 @@ pub(super) async fn monitor_mdns_events(monitor: MdnsMonitor, shutdown: Cancella
|
||||
}
|
||||
}
|
||||
|
||||
fn advertised_service_name(hostname: &str, peer_id: &str) -> String {
|
||||
fn advertised_service_name(hostname: &str, peer_id: PeerId) -> String {
|
||||
let peer_id = peer_id.to_string();
|
||||
let max_hostname_len = 63usize.saturating_sub(peer_id.len() + 1);
|
||||
let truncated_hostname = if hostname.len() > max_hostname_len {
|
||||
hostname.get(..max_hostname_len).unwrap_or(hostname)
|
||||
@@ -76,27 +80,16 @@ fn advertised_service_name(hostname: &str, peer_id: &str) -> String {
|
||||
};
|
||||
|
||||
if truncated_hostname.is_empty() {
|
||||
peer_id.to_string()
|
||||
peer_id
|
||||
} else {
|
||||
format!("{truncated_hostname}-{peer_id}")
|
||||
}
|
||||
}
|
||||
|
||||
async fn advertisement_properties(
|
||||
ctx: &PeerCtx,
|
||||
hostname: &str,
|
||||
peer_id: &str,
|
||||
) -> HashMap<String, String> {
|
||||
let (library_rev, library_digest) = {
|
||||
let library_guard = ctx.local_library.read().await;
|
||||
(library_guard.revision, library_guard.digest)
|
||||
};
|
||||
|
||||
fn advertisement_properties(hostname: &str, peer_id: PeerId) -> HashMap<String, String> {
|
||||
let mut properties = HashMap::new();
|
||||
properties.insert("peer_id".to_string(), peer_id.to_string());
|
||||
properties.insert("proto_ver".to_string(), PROTOCOL_VERSION.to_string());
|
||||
properties.insert("library_rev".to_string(), library_rev.to_string());
|
||||
properties.insert("library_digest".to_string(), library_digest.to_string());
|
||||
if !hostname.is_empty() {
|
||||
properties.insert("hostname".to_string(), hostname.to_string());
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user