Upgrade Iroh native backend foundation
This commit is contained in:
parent
b042149173
commit
9d46dd4d0d
11 changed files with 1588 additions and 712 deletions
1991
Cargo.lock
generated
1991
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
10
Cargo.toml
10
Cargo.toml
|
|
@ -42,13 +42,19 @@ blake3 = "1"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
clap = { version = "4", features = ["derive", "env"] }
|
clap = { version = "4", features = ["derive", "env"] }
|
||||||
directories = "5"
|
directories = "5"
|
||||||
|
# Compatibility pins for iroh 0.95's ed25519-dalek prerelease dependency.
|
||||||
|
ed25519 = "=3.0.0-rc.1"
|
||||||
ed25519-dalek = { version = "2", features = ["rand_core"] }
|
ed25519-dalek = { version = "2", features = ["rand_core"] }
|
||||||
|
pkcs8 = "=0.11.0-rc.11"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
hexane = "=0.1.5"
|
hexane = "=0.1.5"
|
||||||
iroh = { version = "0.90.0", features = ["discovery-local-network"] }
|
iroh = { version = "0.95.1", features = ["discovery-local-network"] }
|
||||||
n0-watcher = "0.2"
|
iroh-blobs = "0.97.0"
|
||||||
|
iroh-docs = "0.95.0"
|
||||||
|
iroh-gossip = "0.95.0"
|
||||||
postcard = { version = "1", features = ["alloc"] }
|
postcard = { version = "1", features = ["alloc"] }
|
||||||
|
rand = "0.9"
|
||||||
rand_core = { version = "0.6", features = ["getrandom"] }
|
rand_core = { version = "0.6", features = ["getrandom"] }
|
||||||
rusqlite = { version = "0.32", features = ["bundled"] }
|
rusqlite = { version = "0.32", features = ["bundled"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
|
|
||||||
13
README.md
13
README.md
|
|
@ -198,13 +198,12 @@ has `cas.fetch` on `resource:cas:local`, and the caller verifies that the bytes
|
||||||
hash to the requested BLAKE3 CAS hash before storing them locally. Successful
|
hash to the requested BLAKE3 CAS hash before storing them locally. Successful
|
||||||
fetches record the serving peer as a local provider, visible with
|
fetches record the serving peer as a local provider, visible with
|
||||||
`geth cas providers <hash>`. This is the bootstrap transfer path; future work
|
`geth cas providers <hash>`. This is the bootstrap transfer path; future work
|
||||||
will move provider/fetch behavior to `iroh-blobs`. `geth status` currently
|
will move provider/fetch behavior to `iroh-blobs`. `geth-iroh` is now pinned to
|
||||||
reports the native backend blocker for CAS, KV, and pubsub: the daemon endpoint
|
`iroh 0.95.1` and compiles the native backend libraries `iroh-blobs 0.97.0`,
|
||||||
is pinned to `iroh 0.90.0`, while the Rust-1.85-compatible backend crates
|
`iroh-docs 0.95.0`, and `iroh-gossip 0.95.0` against the same daemon-owned
|
||||||
resolved from crates.io are `iroh-blobs 0.97.0`, `iroh-docs 0.95.0`, and
|
endpoint generation. `geth status` reports those backends as ready to wire; the
|
||||||
`iroh-gossip 0.95.0`, all of which require `iroh 0.95`. These cannot be wired
|
module implementations still use the explicit bootstrap control path until the
|
||||||
to the daemon-owned endpoint until the endpoint wrapper is upgraded in one
|
module-specific migrations replace it.
|
||||||
coordinated step.
|
|
||||||
Remote resource commands that accept `--bearer-secret` can also authorize with a
|
Remote resource commands that accept `--bearer-secret` can also authorize with a
|
||||||
resource-scoped bearer proof generated from the private bearer token returned at
|
resource-scoped bearer proof generated from the private bearer token returned at
|
||||||
creation time. The persisted auth log stores a public bearer id and token
|
creation time. The persisted auth log stores a public bearer id and token
|
||||||
|
|
|
||||||
|
|
@ -1534,10 +1534,12 @@ fn print_response(response: ControlResponse, json: bool) -> Result<()> {
|
||||||
backend.target_version,
|
backend.target_version,
|
||||||
backend.status
|
backend.status
|
||||||
);
|
);
|
||||||
println!(
|
if !backend.blocker.is_empty() {
|
||||||
"native backend {} blocker: {}",
|
println!(
|
||||||
backend.module, backend.blocker
|
"native backend {} note: {}",
|
||||||
);
|
backend.module, backend.blocker
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ControlResponse::NodeId(node) => {
|
ControlResponse::NodeId(node) => {
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,13 @@ license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hex.workspace = true
|
hex.workspace = true
|
||||||
|
ed25519.workspace = true
|
||||||
|
pkcs8.workspace = true
|
||||||
iroh.workspace = true
|
iroh.workspace = true
|
||||||
n0-watcher.workspace = true
|
iroh-blobs.workspace = true
|
||||||
rand_core.workspace = true
|
iroh-docs.workspace = true
|
||||||
|
iroh-gossip.workspace = true
|
||||||
|
rand.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::net::{SocketAddrV4, SocketAddrV6};
|
use std::net::{SocketAddrV4, SocketAddrV6};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
pub const ALPN_CONTROL: &[u8] = b"/geth/control/1";
|
pub const ALPN_CONTROL: &[u8] = b"/geth/control/1";
|
||||||
pub const ALPN_KV: &[u8] = b"/geth/kv/1";
|
pub const ALPN_KV: &[u8] = b"/geth/kv/1";
|
||||||
|
|
@ -13,6 +12,11 @@ pub const ALPN_DB: &[u8] = b"/geth/db/1";
|
||||||
pub const ALPN_DOCUMENT: &[u8] = b"/geth/document/1";
|
pub const ALPN_DOCUMENT: &[u8] = b"/geth/document/1";
|
||||||
pub const ALPN_SSH_PROXY: &[u8] = b"/geth/ssh-proxy/1";
|
pub const ALPN_SSH_PROXY: &[u8] = b"/geth/ssh-proxy/1";
|
||||||
|
|
||||||
|
pub const IROH_VERSION: &str = "0.95.1";
|
||||||
|
pub const IROH_BLOBS_VERSION: &str = "0.97.0";
|
||||||
|
pub const IROH_DOCS_VERSION: &str = "0.95.0";
|
||||||
|
pub const IROH_GOSSIP_VERSION: &str = "0.95.0";
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct GethIrohConfig {
|
pub struct GethIrohConfig {
|
||||||
pub secret_key_path: PathBuf,
|
pub secret_key_path: PathBuf,
|
||||||
|
|
@ -157,6 +161,38 @@ pub fn default_protocol_descriptors() -> Vec<ProtocolDescriptor> {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct NativeIrohLibrary {
|
||||||
|
pub module: String,
|
||||||
|
pub crate_name: String,
|
||||||
|
pub crate_version: String,
|
||||||
|
pub alpn: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn native_iroh_libraries() -> Vec<NativeIrohLibrary> {
|
||||||
|
vec![
|
||||||
|
NativeIrohLibrary {
|
||||||
|
module: "cas".to_owned(),
|
||||||
|
crate_name: "iroh-blobs".to_owned(),
|
||||||
|
crate_version: IROH_BLOBS_VERSION.to_owned(),
|
||||||
|
alpn: display_alpn(iroh_blobs::ALPN),
|
||||||
|
},
|
||||||
|
NativeIrohLibrary {
|
||||||
|
module: "kv".to_owned(),
|
||||||
|
crate_name: "iroh-docs".to_owned(),
|
||||||
|
crate_version: IROH_DOCS_VERSION.to_owned(),
|
||||||
|
alpn: display_alpn(iroh_docs::ALPN),
|
||||||
|
},
|
||||||
|
NativeIrohLibrary {
|
||||||
|
module: "pubsub".to_owned(),
|
||||||
|
crate_name: "iroh-gossip".to_owned(),
|
||||||
|
crate_version: IROH_GOSSIP_VERSION.to_owned(),
|
||||||
|
alpn: display_alpn(iroh_gossip::ALPN),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum GethRelayMode {
|
pub enum GethRelayMode {
|
||||||
|
|
@ -218,7 +254,7 @@ impl GethIrohEndpoint {
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn node_id(&self) -> String {
|
pub fn node_id(&self) -> String {
|
||||||
self.endpoint.node_id().to_string()
|
self.endpoint.id().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
@ -227,19 +263,12 @@ impl GethIrohEndpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn node_addr_snapshot(&self) -> Result<GethNodeAddr, IrohError> {
|
pub async fn node_addr_snapshot(&self) -> Result<GethNodeAddr, IrohError> {
|
||||||
use n0_watcher::Watcher;
|
let endpoint_addr = self.endpoint.addr();
|
||||||
|
|
||||||
let mut watcher = self.endpoint.node_addr();
|
|
||||||
let node_addr = tokio::time::timeout(Duration::from_secs(2), watcher.initialized())
|
|
||||||
.await
|
|
||||||
.map_err(|_| IrohError::NodeAddrTimeout)?
|
|
||||||
.map_err(|error| IrohError::NodeAddrUnavailable(error.to_string()))?;
|
|
||||||
Ok(GethNodeAddr {
|
Ok(GethNodeAddr {
|
||||||
endpoint_id: node_addr.node_id.to_string(),
|
endpoint_id: endpoint_addr.id.to_string(),
|
||||||
relay_url: node_addr.relay_url.map(|url| url.to_string()),
|
relay_url: endpoint_addr.relay_urls().next().map(ToString::to_string),
|
||||||
direct_addresses: node_addr
|
direct_addresses: endpoint_addr
|
||||||
.direct_addresses
|
.ip_addrs()
|
||||||
.into_iter()
|
|
||||||
.map(|addr| addr.to_string())
|
.map(|addr| addr.to_string())
|
||||||
.collect(),
|
.collect(),
|
||||||
})
|
})
|
||||||
|
|
@ -265,7 +294,7 @@ pub async fn start_endpoint(config: &GethIrohConfig) -> Result<GethIrohEndpoint,
|
||||||
.alpns(config.alpns.clone());
|
.alpns(config.alpns.clone());
|
||||||
|
|
||||||
if config.local_discovery {
|
if config.local_discovery {
|
||||||
builder = builder.discovery_local_network();
|
builder = builder.discovery(iroh::discovery::mdns::MdnsDiscovery::builder());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(bind_ipv4) = config.bind_ipv4 {
|
if let Some(bind_ipv4) = config.bind_ipv4 {
|
||||||
|
|
@ -278,12 +307,17 @@ pub async fn start_endpoint(config: &GethIrohConfig) -> Result<GethIrohEndpoint,
|
||||||
let endpoint = builder.bind().await.map_err(IrohError::from)?;
|
let endpoint = builder.bind().await.map_err(IrohError::from)?;
|
||||||
let status = EndpointStatus {
|
let status = EndpointStatus {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
endpoint_id: Some(endpoint.node_id().to_string()),
|
endpoint_id: Some(endpoint.id().to_string()),
|
||||||
relay_mode: config.relay_mode.label(),
|
relay_mode: config.relay_mode.label(),
|
||||||
local_discovery: config.local_discovery,
|
local_discovery: config.local_discovery,
|
||||||
note: format!(
|
note: format!(
|
||||||
"Iroh endpoint started with iroh 0.90.0; relay mode: {:?}; local discovery: {}",
|
"Iroh endpoint started with iroh {}; relay mode: {:?}; local discovery: {}; native libraries: iroh-blobs {}, iroh-docs {}, iroh-gossip {}",
|
||||||
config.relay_mode, config.local_discovery
|
IROH_VERSION,
|
||||||
|
config.relay_mode,
|
||||||
|
config.local_discovery,
|
||||||
|
IROH_BLOBS_VERSION,
|
||||||
|
IROH_DOCS_VERSION,
|
||||||
|
IROH_GOSSIP_VERSION
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
Ok(GethIrohEndpoint { endpoint, status })
|
Ok(GethIrohEndpoint { endpoint, status })
|
||||||
|
|
@ -297,7 +331,7 @@ pub fn load_or_create_secret_key(path: &Path) -> Result<iroh::SecretKey, IrohErr
|
||||||
if let Some(parent) = path.parent() {
|
if let Some(parent) = path.parent() {
|
||||||
std::fs::create_dir_all(parent)?;
|
std::fs::create_dir_all(parent)?;
|
||||||
}
|
}
|
||||||
let secret_key = iroh::SecretKey::generate(rand_core::OsRng);
|
let secret_key = iroh::SecretKey::generate(&mut rand::rng());
|
||||||
let tmp = path.with_extension("tmp");
|
let tmp = path.with_extension("tmp");
|
||||||
std::fs::write(&tmp, hex::encode(secret_key.to_bytes()))?;
|
std::fs::write(&tmp, hex::encode(secret_key.to_bytes()))?;
|
||||||
std::fs::rename(tmp, path)?;
|
std::fs::rename(tmp, path)?;
|
||||||
|
|
@ -389,6 +423,30 @@ mod tests {
|
||||||
assert_eq!(alpns.len(), 8);
|
assert_eq!(alpns.len(), 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn native_iroh_libraries_are_pinned_and_expose_alpns() {
|
||||||
|
let libraries = native_iroh_libraries();
|
||||||
|
assert_eq!(libraries.len(), 3);
|
||||||
|
assert!(libraries.iter().any(|library| {
|
||||||
|
library.module == "cas"
|
||||||
|
&& library.crate_name == "iroh-blobs"
|
||||||
|
&& library.crate_version == IROH_BLOBS_VERSION
|
||||||
|
&& library.alpn == "/iroh-bytes/4"
|
||||||
|
}));
|
||||||
|
assert!(libraries.iter().any(|library| {
|
||||||
|
library.module == "kv"
|
||||||
|
&& library.crate_name == "iroh-docs"
|
||||||
|
&& library.crate_version == IROH_DOCS_VERSION
|
||||||
|
&& library.alpn == "/iroh-sync/1"
|
||||||
|
}));
|
||||||
|
assert!(libraries.iter().any(|library| {
|
||||||
|
library.module == "pubsub"
|
||||||
|
&& library.crate_name == "iroh-gossip"
|
||||||
|
&& library.crate_version == IROH_GOSSIP_VERSION
|
||||||
|
&& library.alpn == "/iroh-gossip/1"
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default_router_registers_all_protocols() {
|
fn default_router_registers_all_protocols() {
|
||||||
let router = default_protocol_router();
|
let router = default_protocol_router();
|
||||||
|
|
|
||||||
|
|
@ -1045,10 +1045,7 @@ async fn peer_ping(node: &LocalNode, peer_node: &str) -> Result<ControlResponse,
|
||||||
.connect(node_addr, geth_iroh::ALPN_CONTROL)
|
.connect(node_addr, geth_iroh::ALPN_CONTROL)
|
||||||
.await
|
.await
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
||||||
let alpn = conn
|
let alpn = display_alpn(conn.alpn());
|
||||||
.alpn()
|
|
||||||
.map(display_alpn)
|
|
||||||
.unwrap_or_else(|| "unknown".to_owned());
|
|
||||||
let (mut send, mut recv) = conn
|
let (mut send, mut recv) = conn
|
||||||
.open_bi()
|
.open_bi()
|
||||||
.await
|
.await
|
||||||
|
|
@ -2178,14 +2175,16 @@ async fn handle_local_pipe_tcp_stream(
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
||||||
remote_send
|
remote_send
|
||||||
.finish()
|
.finish()
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))
|
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
||||||
|
Ok::<(), NodeError>(())
|
||||||
};
|
};
|
||||||
let mut remote_recv = remote_recv;
|
let mut remote_recv = remote_recv;
|
||||||
let download = async {
|
let download = async {
|
||||||
tokio::io::copy(&mut remote_recv, &mut local_write)
|
tokio::io::copy(&mut remote_recv, &mut local_write)
|
||||||
.await
|
.await
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
||||||
local_write.shutdown().await.map_err(NodeError::from)
|
local_write.shutdown().await.map_err(NodeError::from)?;
|
||||||
|
Ok::<(), NodeError>(())
|
||||||
};
|
};
|
||||||
tokio::try_join!(upload, download)?;
|
tokio::try_join!(upload, download)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -2301,14 +2300,16 @@ async fn handle_local_pipe_unix_stream(
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
||||||
remote_send
|
remote_send
|
||||||
.finish()
|
.finish()
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))
|
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
||||||
|
Ok::<(), NodeError>(())
|
||||||
};
|
};
|
||||||
let mut remote_recv = remote_recv;
|
let mut remote_recv = remote_recv;
|
||||||
let download = async {
|
let download = async {
|
||||||
tokio::io::copy(&mut remote_recv, &mut local_write)
|
tokio::io::copy(&mut remote_recv, &mut local_write)
|
||||||
.await
|
.await
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
||||||
local_write.shutdown().await.map_err(NodeError::from)
|
local_write.shutdown().await.map_err(NodeError::from)?;
|
||||||
|
Ok::<(), NodeError>(())
|
||||||
};
|
};
|
||||||
tokio::try_join!(upload, download)?;
|
tokio::try_join!(upload, download)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -2511,14 +2512,16 @@ async fn handle_local_ssh_proxy_stream(
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
||||||
remote_send
|
remote_send
|
||||||
.finish()
|
.finish()
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))
|
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
||||||
|
Ok::<(), NodeError>(())
|
||||||
};
|
};
|
||||||
let mut remote_recv = remote_recv;
|
let mut remote_recv = remote_recv;
|
||||||
let download = async {
|
let download = async {
|
||||||
tokio::io::copy(&mut remote_recv, &mut local_write)
|
tokio::io::copy(&mut remote_recv, &mut local_write)
|
||||||
.await
|
.await
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
||||||
local_write.shutdown().await.map_err(NodeError::from)
|
local_write.shutdown().await.map_err(NodeError::from)?;
|
||||||
|
Ok::<(), NodeError>(())
|
||||||
};
|
};
|
||||||
tokio::try_join!(upload, download)?;
|
tokio::try_join!(upload, download)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -4437,22 +4440,16 @@ async fn handle_iroh_control_connection(
|
||||||
let conn = incoming
|
let conn = incoming
|
||||||
.await
|
.await
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
||||||
let remote_endpoint_id = conn
|
let remote_endpoint_id = conn.remote_id().to_string();
|
||||||
.remote_node_id()
|
let alpn = display_alpn(conn.alpn());
|
||||||
.map(|node_id| node_id.to_string())
|
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
|
||||||
let alpn = conn
|
|
||||||
.alpn()
|
|
||||||
.map(display_alpn)
|
|
||||||
.unwrap_or_else(|| "unknown".to_owned());
|
|
||||||
let (mut send, mut recv) = conn
|
let (mut send, mut recv) = conn
|
||||||
.accept_bi()
|
.accept_bi()
|
||||||
.await
|
.await
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
||||||
if alpn == display_alpn(geth_iroh::ALPN_SSH_PROXY.to_vec()) {
|
if alpn == display_alpn(geth_iroh::ALPN_SSH_PROXY) {
|
||||||
return handle_ssh_proxy_wire_connection(node, remote_endpoint_id, send, recv).await;
|
return handle_ssh_proxy_wire_connection(node, remote_endpoint_id, send, recv).await;
|
||||||
}
|
}
|
||||||
if alpn == display_alpn(geth_iroh::ALPN_PIPE.to_vec()) {
|
if alpn == display_alpn(geth_iroh::ALPN_PIPE) {
|
||||||
return handle_pipe_wire_connection(node, remote_endpoint_id, send, recv).await;
|
return handle_pipe_wire_connection(node, remote_endpoint_id, send, recv).await;
|
||||||
}
|
}
|
||||||
let bytes = recv
|
let bytes = recv
|
||||||
|
|
@ -5946,10 +5943,10 @@ async fn handle_pipe_unix_wire_connection(
|
||||||
|
|
||||||
fn iroh_node_addr_from_candidate(
|
fn iroh_node_addr_from_candidate(
|
||||||
candidate: &EndpointCandidate,
|
candidate: &EndpointCandidate,
|
||||||
) -> Result<iroh::NodeAddr, NodeError> {
|
) -> Result<iroh::EndpointAddr, NodeError> {
|
||||||
let node_id = candidate
|
let endpoint_id = candidate
|
||||||
.endpoint_id
|
.endpoint_id
|
||||||
.parse::<iroh::NodeId>()
|
.parse::<iroh::EndpointId>()
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
|
||||||
let direct_addresses = candidate
|
let direct_addresses = candidate
|
||||||
.direct_addresses
|
.direct_addresses
|
||||||
|
|
@ -5959,7 +5956,10 @@ fn iroh_node_addr_from_candidate(
|
||||||
.map_err(|error| NodeError::IrohPeer(error.to_string()))
|
.map_err(|error| NodeError::IrohPeer(error.to_string()))
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
let mut node_addr = iroh::NodeAddr::new(node_id).with_direct_addresses(direct_addresses);
|
let mut node_addr = iroh::EndpointAddr::new(endpoint_id);
|
||||||
|
for address in direct_addresses {
|
||||||
|
node_addr = node_addr.with_ip_addr(address);
|
||||||
|
}
|
||||||
if let Some(relay_url) = &candidate.relay_url {
|
if let Some(relay_url) = &candidate.relay_url {
|
||||||
node_addr = node_addr.with_relay_url(
|
node_addr = node_addr.with_relay_url(
|
||||||
relay_url
|
relay_url
|
||||||
|
|
@ -6005,8 +6005,8 @@ fn restricted_admin_shell_output(node: &LocalNode, command: &str) -> Result<Stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_alpn(alpn: Vec<u8>) -> String {
|
fn display_alpn(alpn: &[u8]) -> String {
|
||||||
String::from_utf8_lossy(&alpn).into_owned()
|
String::from_utf8_lossy(alpn).into_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_request(
|
pub fn handle_request(
|
||||||
|
|
@ -7661,33 +7661,21 @@ pub fn handle_request(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn native_backend_statuses() -> Vec<NativeBackendStatus> {
|
fn native_backend_statuses() -> Vec<NativeBackendStatus> {
|
||||||
let blocker = "pinned blocker: current daemon endpoint uses iroh 0.90.0; the Rust-1.85-compatible backend crates resolved from crates.io require iroh 0.95, so they cannot share the daemon-owned endpoint until geth performs a coordinated Iroh endpoint upgrade".to_owned();
|
geth_iroh::native_iroh_libraries()
|
||||||
vec![
|
.into_iter()
|
||||||
NativeBackendStatus {
|
.map(|library| NativeBackendStatus {
|
||||||
module: "cas".to_owned(),
|
module: library.module,
|
||||||
current_backend: "iroh-control-alpn-bootstrap".to_owned(),
|
current_backend: "shared-iroh-endpoint-ready".to_owned(),
|
||||||
target_crate: "iroh-blobs".to_owned(),
|
target_crate: library.crate_name,
|
||||||
target_version: "0.97.0".to_owned(),
|
target_version: library.crate_version,
|
||||||
status: "blocked".to_owned(),
|
status: "ready-to-wire".to_owned(),
|
||||||
blocker: blocker.clone(),
|
blocker: format!(
|
||||||
},
|
"none; native ALPN {} is compiled against the daemon-owned iroh {} endpoint, but the module still needs its bootstrap control-path implementation replaced",
|
||||||
NativeBackendStatus {
|
library.alpn,
|
||||||
module: "kv".to_owned(),
|
geth_iroh::IROH_VERSION
|
||||||
current_backend: "iroh-control-alpn-bootstrap".to_owned(),
|
),
|
||||||
target_crate: "iroh-docs".to_owned(),
|
})
|
||||||
target_version: "0.95.0".to_owned(),
|
.collect()
|
||||||
status: "blocked".to_owned(),
|
|
||||||
blocker: blocker.clone(),
|
|
||||||
},
|
|
||||||
NativeBackendStatus {
|
|
||||||
module: "pubsub".to_owned(),
|
|
||||||
current_backend: "iroh-control-alpn-bootstrap".to_owned(),
|
|
||||||
target_crate: "iroh-gossip".to_owned(),
|
|
||||||
target_version: "0.95.0".to_owned(),
|
|
||||||
status: "blocked".to_owned(),
|
|
||||||
blocker,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stored_resource_to_descriptor(stored: StoredResource) -> Result<ResourceDescriptor, NodeError> {
|
fn stored_resource_to_descriptor(stored: StoredResource) -> Result<ResourceDescriptor, NodeError> {
|
||||||
|
|
|
||||||
|
|
@ -155,13 +155,13 @@ fn geth_status_against_running_daemon() {
|
||||||
assert!(stdout.contains("iroh relay: disabled"));
|
assert!(stdout.contains("iroh relay: disabled"));
|
||||||
assert!(stdout.contains("iroh discovery: local-network disabled"));
|
assert!(stdout.contains("iroh discovery: local-network disabled"));
|
||||||
assert!(stdout.contains(
|
assert!(stdout.contains(
|
||||||
"native backend cas: iroh-control-alpn-bootstrap target iroh-blobs 0.97.0 (blocked)"
|
"native backend cas: shared-iroh-endpoint-ready target iroh-blobs 0.97.0 (ready-to-wire)"
|
||||||
));
|
));
|
||||||
assert!(stdout.contains(
|
assert!(stdout.contains(
|
||||||
"native backend kv: iroh-control-alpn-bootstrap target iroh-docs 0.95.0 (blocked)"
|
"native backend kv: shared-iroh-endpoint-ready target iroh-docs 0.95.0 (ready-to-wire)"
|
||||||
));
|
));
|
||||||
assert!(stdout.contains(
|
assert!(stdout.contains(
|
||||||
"native backend pubsub: iroh-control-alpn-bootstrap target iroh-gossip 0.95.0 (blocked)"
|
"native backend pubsub: shared-iroh-endpoint-ready target iroh-gossip 0.95.0 (ready-to-wire)"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,8 @@ prefix-scoped capabilities.
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
|
|
||||||
The MVP exposes CLI shape and types while deferring iroh-docs API pinning.
|
The prototype exposes CLI shape and durable local KV state. `iroh-docs 0.95.0`
|
||||||
|
is pinned and compiles against the daemon-owned `iroh 0.95.1` endpoint
|
||||||
|
generation, so the remaining work is replacing the bootstrap control-path KV
|
||||||
|
sync with an Iroh Documents namespace implementation and resource-scoped
|
||||||
|
authorization checks around namespace access.
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,7 @@ Remote geth node-to-node communication is Iroh-only. The daemon will own one
|
||||||
shared Iroh endpoint and register module protocols on ALPNs such as
|
shared Iroh endpoint and register module protocols on ALPNs such as
|
||||||
`/geth/cas/1`, `/geth/kv/1`, `/geth/pipe/1`, and `/geth/ssh-proxy/1`.
|
`/geth/cas/1`, `/geth/kv/1`, `/geth/pipe/1`, and `/geth/ssh-proxy/1`.
|
||||||
|
|
||||||
The first pinned Iroh integration uses `iroh = 0.90.0`, because newer
|
The pinned Iroh integration uses `iroh = 0.95.1`. `geth-iroh` wraps
|
||||||
Rust-1.85-compatible candidates in the 0.93-0.95 range failed to compile through
|
|
||||||
a transitive `ed25519-dalek` prerelease dependency. `geth-iroh` wraps
|
|
||||||
`iroh::Endpoint::builder()`, configures geth ALPNs with `Builder::alpns`, uses
|
`iroh::Endpoint::builder()`, configures geth ALPNs with `Builder::alpns`, uses
|
||||||
`Builder::relay_mode`, persists an `iroh::SecretKey` as hex-encoded 32-byte key
|
`Builder::relay_mode`, persists an `iroh::SecretKey` as hex-encoded 32-byte key
|
||||||
material, and shuts down through `Endpoint::close().await`. The default config
|
material, and shuts down through `Endpoint::close().await`. The default config
|
||||||
|
|
@ -30,22 +28,20 @@ uses Iroh's default relay policy; local-only/offline development can set
|
||||||
`relay_map = "<name>"`, validated at config load, and reported in status as
|
`relay_map = "<name>"`, validated at config load, and reported in status as
|
||||||
`custom:<name>` without exposing relay URLs.
|
`custom:<name>` without exposing relay URLs.
|
||||||
|
|
||||||
The native module-backend crates currently available for the intended CAS, KV,
|
The native module-backend crates for the intended CAS, KV, and pubsub
|
||||||
and pubsub replacements are not wired in yet because they require a coordinated
|
replacements now compile against the same endpoint generation:
|
||||||
endpoint upgrade. Crates.io metadata checked during this prototype pass resolved
|
`iroh-blobs 0.97.0`, `iroh-docs 0.95.0`, and `iroh-gossip 0.95.0`. `geth-iroh`
|
||||||
`iroh-blobs 0.97.0`, `iroh-docs 0.95.0`, and `iroh-gossip 0.95.0` as
|
exposes their native ALPNs so module migrations can register handlers without
|
||||||
Rust-1.85-compatible candidates; those crates depend on `iroh 0.95` and cannot
|
creating a second daemon endpoint. `geth status` reports these backends as
|
||||||
share the daemon-owned `iroh 0.90.0` endpoint. Pulling them in beside the
|
ready to wire. The module implementations still use explicit bootstrap
|
||||||
current endpoint would create parallel Iroh stacks and violate the one-endpoint
|
control-ALPN paths until each module is migrated to its native protocol.
|
||||||
daemon invariant. Until the endpoint wrapper upgrades as a unit, `geth status`
|
|
||||||
reports CAS, KV, and pubsub native backends as blocked and the bootstrap
|
|
||||||
control-ALPN paths remain explicit.
|
|
||||||
|
|
||||||
Module ALPNs are registered through `geth-iroh`'s protocol router scaffold. The
|
Module ALPNs are registered through `geth-iroh`'s protocol router scaffold. The
|
||||||
router owns the default protocol descriptors, rejects duplicate ALPN
|
router owns the default protocol descriptors, rejects duplicate ALPN
|
||||||
registrations, and returns explicit unknown-ALPN errors. It does not yet accept
|
registrations, and returns explicit unknown-ALPN errors. The current daemon
|
||||||
or dispatch remote streams; peer authentication and module handlers are later
|
accept loop dispatches geth control, pipe, and SSH-proxy streams directly; the
|
||||||
Phase 1 work.
|
next backend migrations should attach iroh-blobs, iroh-docs, and iroh-gossip
|
||||||
|
handlers to the same endpoint instead of creating parallel endpoints.
|
||||||
|
|
||||||
The target product should use Iroh relay support for practical internet
|
The target product should use Iroh relay support for practical internet
|
||||||
connectivity and mDNS/LAN discovery for local networks. These are connectivity
|
connectivity and mDNS/LAN discovery for local networks. These are connectivity
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ Implementation order:
|
||||||
equivalent.
|
equivalent.
|
||||||
- `[x]` Fallback/stub behavior remains clearly marked where APIs are not yet
|
- `[x]` Fallback/stub behavior remains clearly marked where APIs are not yet
|
||||||
pinned.
|
pinned.
|
||||||
- `[ ]` Upgrade `geth-iroh` from `iroh 0.90.0` to an endpoint version
|
- `[x]` Upgrade `geth-iroh` from `iroh 0.90.0` to an endpoint version
|
||||||
compatible with `iroh-blobs`, `iroh-docs`, and `iroh-gossip` without
|
compatible with `iroh-blobs`, `iroh-docs`, and `iroh-gossip` without
|
||||||
introducing a second daemon endpoint.
|
introducing a second daemon endpoint.
|
||||||
|
|
||||||
|
|
@ -459,8 +459,8 @@ authorization and durable-state boundaries clear.
|
||||||
- `[x]` `geth cas providers <hash>` lists locally known providers.
|
- `[x]` `geth cas providers <hash>` lists locally known providers.
|
||||||
- `[x]` Tests cover local provider metadata storage.
|
- `[x]` Tests cover local provider metadata storage.
|
||||||
- `[ ]` Replace the bootstrap control-ALPN transfer with `iroh-blobs`
|
- `[ ]` Replace the bootstrap control-ALPN transfer with `iroh-blobs`
|
||||||
provider/fetch behavior after the daemon endpoint upgrades to an Iroh
|
provider/fetch behavior using the daemon-owned `iroh 0.95.1` endpoint and
|
||||||
version compatible with `iroh-blobs 0.97.0` or a newer pinned equivalent.
|
pinned `iroh-blobs 0.97.0`.
|
||||||
|
|
||||||
- `[x]` CAS pin and cache policy.
|
- `[x]` CAS pin and cache policy.
|
||||||
Acceptance criteria:
|
Acceptance criteria:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue