Protocol v2: trusted-peer push, truthful partials, split downloads
Three capabilities the iroh-blobs 0.103 line makes possible:
- Push (new API surface, protocol v2): `Push { hash, node_id }` hands
fully-present content to a trusted peer, unprompted. Consent is
mutual — the sender pushes only to peers it trusts, and the receiver
(which now accepts connections unconditionally and gates per request)
admits pushes only from peers *it* trusts, deferring with RateLimited
while metered. An accepted push is pinned by the receiver (default
policy, format inferred from the request ranges) once its transfer
completes, and surfaces as a PushReceived event. Because QUIC writes
are fire-and-forget, the sender confirms delivery by observing the
receiver's bitfields (root + last hashseq child) before replying —
Pushed { bytes } means verified received, not merely sent. New
varde-ctl `push` command.
- Truthful partial presence: Status/List report bytes actually present
and verified for partial blobs, from the store's bitfields via
observe, instead of the old "0 until complete".
- Split downloads: multi-provider fetches stripe one request across
providers (SplitStrategy::Split) instead of trying them serially.
Trusted peers may observe bitfields even when serving is disabled or
metered — bitfields are metadata, and push confirmation rides on them.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
ad69f7d884
commit
4033c0ffab
@@ -125,6 +125,29 @@ impl Daemon {
|
||||
}
|
||||
});
|
||||
|
||||
// Inbound pushes: content a trusted peer handed us, fully
|
||||
// verified. Pin it (default policy) so it survives gc, and tell
|
||||
// subscribers where it came from.
|
||||
if let Some(mut pushes) = daemon.transfer.take_pushes() {
|
||||
let weak = Arc::downgrade(&daemon);
|
||||
tokio::spawn(async move {
|
||||
while let Some(push) = pushes.recv().await {
|
||||
let Some(daemon) = weak.upgrade() else { break };
|
||||
let hex = push.hash.to_hex().to_string();
|
||||
let from = push.from.clone().unwrap_or_default();
|
||||
info!(hash = %hex, from = %from, "push received, pinning");
|
||||
if let Err(e) = daemon.meta.record_format(&hex, push.format) {
|
||||
warn!(hash = %hex, error = %e, "recording pushed format");
|
||||
}
|
||||
if let Err(e) = daemon.meta.pin(&hex, varde_proto::PinPolicy::default()) {
|
||||
warn!(hash = %hex, error = %e, "pinning pushed content");
|
||||
}
|
||||
daemon.recompute_open_set().await;
|
||||
let _ = daemon.events.send(Event::PushReceived { hash: hex, from });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Discovery: raw mdns sightings feed presence tracking, and are
|
||||
// re-emitted as signed announcements through the LanDiscovery
|
||||
// provider — the same seam a future gossip provider plugs into.
|
||||
@@ -513,6 +536,35 @@ impl Daemon {
|
||||
Request::PeerList {} => Ok(ResponseData::Peers {
|
||||
peers: self.present_trusted_peers(),
|
||||
}),
|
||||
Request::Push { hash, node_id } => {
|
||||
let parsed = parse_hash(&hash)?;
|
||||
let peer: iroh::EndpointId = node_id
|
||||
.parse()
|
||||
.map_err(|e| OpError::InvalidArgument(format!("invalid node id: {e}")))?;
|
||||
// Consent, sending side: we only push to peers we trust.
|
||||
// (The receiver independently requires that it trusts us.)
|
||||
if !self.meta.is_trusted(&peer.to_string()) {
|
||||
return Err(OpError::InvalidArgument(format!(
|
||||
"{peer} is not a trusted peer; trust it before pushing"
|
||||
)));
|
||||
}
|
||||
let (_, _, complete) = self.store.presence(parsed).await?;
|
||||
if !complete {
|
||||
return Err(OpError::NotFound(format!(
|
||||
"{} is not fully present; cannot push",
|
||||
parsed.to_hex()
|
||||
)));
|
||||
}
|
||||
let content = HashAndFormat {
|
||||
hash: parsed,
|
||||
format: blob_format(self.meta.format_of(&parsed.to_hex())),
|
||||
};
|
||||
let bytes = self.transfer.push_to(peer, content).await?;
|
||||
Ok(ResponseData::Pushed {
|
||||
hash: parsed.to_hex().to_string(),
|
||||
bytes,
|
||||
})
|
||||
}
|
||||
Request::Subscribe {} => Ok(ResponseData::Done {}),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user