refactor!: rebrand wraith to alknet
Rename all crates, CLI commands, constants, type names, doc comments, and documentation from wraith to alknet. Includes wire-protocol changes: ALPN wraith-ssh -> alknet-ssh, reserved destination prefix wraith- -> alknet-, SSH auth username wraith -> alknet.
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
[package]
|
||||
name = "wraith-core"
|
||||
name = "alknet-core"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "Core library for Wraith: pluggable SSH tunnel transport, SOCKS5 proxy, port forwarding, and authentication"
|
||||
description = "Core library for Alknet: pluggable SSH tunnel transport, SOCKS5 proxy, port forwarding, and authentication"
|
||||
repository.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "wraith_core"
|
||||
name = "alknet_core"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
@@ -36,9 +36,9 @@ async-trait = "0.1"
|
||||
ipnetwork = "0.21.1"
|
||||
|
||||
[dev-dependencies]
|
||||
wraith-core = { path = ".", features = ["testutil", "tls", "iroh"] }
|
||||
alknet-core = { path = ".", features = ["testutil", "tls", "iroh"] }
|
||||
tempfile = "3"
|
||||
rcgen = "0.14"
|
||||
rand_core = "0.6"
|
||||
ssh-key = { version = "0.6", features = ["ed25519", "alloc"] }
|
||||
rand = "0.10.1"
|
||||
rand = "0.10.1"
|
||||
@@ -41,14 +41,14 @@ impl std::fmt::Display for TransportMode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Programmatic configuration for a wraith client session.
|
||||
/// Programmatic configuration for an alknet client session.
|
||||
///
|
||||
/// Construct with `ConnectOptions::new(key_source)` and chain builder methods.
|
||||
/// Call `validate()` before passing to `ClientSession::new()`.
|
||||
///
|
||||
/// ```
|
||||
/// use wraith_core::client::{ConnectOptions, TransportMode};
|
||||
/// use wraith_core::auth::keys::KeySource;
|
||||
/// use alknet_core::client::{ConnectOptions, TransportMode};
|
||||
/// use alknet_core::auth::keys::KeySource;
|
||||
///
|
||||
/// let opts = ConnectOptions::new(KeySource::File("/path/to/key".into()))
|
||||
/// .server("example.com:22")
|
||||
@@ -312,7 +312,7 @@ impl<T: Transport> ClientSession<T> {
|
||||
.await;
|
||||
});
|
||||
|
||||
info!("wraith client running: SOCKS5 on {}", socks5_listen);
|
||||
info!("alknet client running: SOCKS5 on {}", socks5_listen);
|
||||
|
||||
#[cfg(unix)]
|
||||
let signal_done = {
|
||||
@@ -439,7 +439,7 @@ impl<T: Transport> ClientSession<T> {
|
||||
fn derive_username() -> String {
|
||||
std::env::var("USER")
|
||||
.or_else(|_| std::env::var("USERNAME"))
|
||||
.unwrap_or_else(|_| "wraith".to_string())
|
||||
.unwrap_or_else(|_| "alknet".to_string())
|
||||
}
|
||||
|
||||
async fn establish_session<T: Transport>(
|
||||
@@ -567,7 +567,7 @@ mod tests {
|
||||
.remote_forward("0.0.0.0:8080:127.0.0.1:3000")
|
||||
.proxy("socks5://127.0.0.1:1080")
|
||||
.iroh_relay("https://relay.example.com")
|
||||
.tls_server_name("wraith.test")
|
||||
.tls_server_name("alknet.test")
|
||||
.insecure(true);
|
||||
|
||||
assert_eq!(opts.server.as_deref(), Some("example.com:22"));
|
||||
@@ -577,7 +577,7 @@ mod tests {
|
||||
assert_eq!(opts.remote_forwards.len(), 1);
|
||||
assert_eq!(opts.proxy.as_deref(), Some("socks5://127.0.0.1:1080"));
|
||||
assert_eq!(opts.iroh_relay.as_deref(), Some("https://relay.example.com"));
|
||||
assert_eq!(opts.tls_server_name.as_deref(), Some("wraith.test"));
|
||||
assert_eq!(opts.tls_server_name.as_deref(), Some("alknet.test"));
|
||||
assert!(opts.insecure);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Error types for wraith-core.
|
||||
//! Error types for alknet-core.
|
||||
//!
|
||||
//! Layered error hierarchy:
|
||||
//! - `TransportError` — connection/handshake/timeout errors (trigger reconnection on client)
|
||||
@@ -1,8 +1,8 @@
|
||||
//! # wraith-core
|
||||
//! # alknet-core
|
||||
//!
|
||||
//! Core library for [Wraith](https://git.alk.dev/alkdev/wraith), a self-hostable SSH-based
|
||||
//! Core library for [Alknet](https://git.alk.dev/alkdev/alknet), a self-hostable SSH-based
|
||||
//! tunnel tool. This crate provides the transport abstraction, SOCKS5 server, port forwarding,
|
||||
//! authentication, and server handler — everything needed to build a wraith client or server
|
||||
//! authentication, and server handler — everything needed to build an alknet client or server
|
||||
//! on top of pluggable transports.
|
||||
//!
|
||||
//! > **Alpha software.** This crate depends on solid libraries (russh, tokio, rustls, iroh)
|
||||
@@ -33,10 +33,10 @@
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::sync::Arc;
|
||||
//! use wraith_core::transport::TcpTransport;
|
||||
//! use wraith_core::client::{ClientSession, ConnectOptions, TransportMode};
|
||||
//! use wraith_core::auth::keys::KeySource;
|
||||
//! use wraith_core::Transport;
|
||||
//! use alknet_core::transport::TcpTransport;
|
||||
//! use alknet_core::client::{ClientSession, ConnectOptions, TransportMode};
|
||||
//! use alknet_core::auth::keys::KeySource;
|
||||
//! use alknet_core::Transport;
|
||||
//!
|
||||
//! #[tokio::main]
|
||||
//! async fn main() -> anyhow::Result<()> {
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Control channel routing for reserved `wraith-*` destinations.
|
||||
//! Control channel routing for reserved `alknet-*` destinations.
|
||||
//!
|
||||
//! SSH channels opened with a destination starting with `wraith-` are intercepted
|
||||
//! SSH channels opened with a destination starting with `alknet-` are intercepted
|
||||
//! by the server and routed to a `ControlChannelHandler` instead of proxied to a
|
||||
//! TCP target. See ADR-018 for the design rationale.
|
||||
|
||||
@@ -9,11 +9,11 @@ use std::io;
|
||||
use async_trait::async_trait;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
pub const WRAITH_CONTROL_DESTINATION: &str = "wraith-control";
|
||||
pub const WRAITH_PREFIX: &str = "wraith-";
|
||||
pub const ALKNET_CONTROL_DESTINATION: &str = "alknet-control";
|
||||
pub const ALKNET_PREFIX: &str = "alknet-";
|
||||
|
||||
pub fn is_reserved_destination(host: &str) -> bool {
|
||||
host.starts_with(WRAITH_PREFIX)
|
||||
host.starts_with(ALKNET_PREFIX)
|
||||
}
|
||||
|
||||
pub trait DuplexStream: AsyncRead + AsyncWrite + Unpin + Send {}
|
||||
@@ -68,21 +68,21 @@ mod tests {
|
||||
use tokio::io::duplex;
|
||||
|
||||
#[test]
|
||||
fn wraith_control_destination_constant() {
|
||||
assert_eq!(WRAITH_CONTROL_DESTINATION, "wraith-control");
|
||||
fn alknet_control_destination_constant() {
|
||||
assert_eq!(ALKNET_CONTROL_DESTINATION, "alknet-control");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wraith_prefix_constant() {
|
||||
assert_eq!(WRAITH_PREFIX, "wraith-");
|
||||
fn alknet_prefix_constant() {
|
||||
assert_eq!(ALKNET_PREFIX, "alknet-");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reserved_destination_detected() {
|
||||
assert!(is_reserved_destination("wraith-control"));
|
||||
assert!(is_reserved_destination("wraith-status"));
|
||||
assert!(is_reserved_destination("wraith-events"));
|
||||
assert!(is_reserved_destination("wraith-"));
|
||||
assert!(is_reserved_destination("alknet-control"));
|
||||
assert!(is_reserved_destination("alknet-status"));
|
||||
assert!(is_reserved_destination("alknet-events"));
|
||||
assert!(is_reserved_destination("alknet-"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -90,17 +90,17 @@ mod tests {
|
||||
assert!(!is_reserved_destination("example.com"));
|
||||
assert!(!is_reserved_destination("localhost"));
|
||||
assert!(!is_reserved_destination("192.168.1.1"));
|
||||
assert!(!is_reserved_destination("wraith.example.com"));
|
||||
assert!(!is_reserved_destination("alknet.example.com"));
|
||||
assert!(!is_reserved_destination(""));
|
||||
assert!(!is_reserved_destination("wrait-control"));
|
||||
assert!(!is_reserved_destination("WRAITH-control"));
|
||||
assert!(!is_reserved_destination("alkne-control"));
|
||||
assert!(!is_reserved_destination("ALKNET-control"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefix_matching_case_sensitive() {
|
||||
assert!(!is_reserved_destination("Wraith-control"));
|
||||
assert!(!is_reserved_destination("WRAITH-control"));
|
||||
assert!(is_reserved_destination("wraith-Control"));
|
||||
assert!(!is_reserved_destination("Alknet-control"));
|
||||
assert!(!is_reserved_destination("ALKNET-control"));
|
||||
assert!(is_reserved_destination("alknet-Control"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -187,6 +187,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn control_channel_destination_matches_prefix() {
|
||||
assert!(is_reserved_destination(WRAITH_CONTROL_DESTINATION));
|
||||
assert!(is_reserved_destination(ALKNET_CONTROL_DESTINATION));
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ use russh::ChannelId;
|
||||
|
||||
use crate::auth::ServerAuthConfig;
|
||||
use crate::server::control_channel::{
|
||||
ControlChannelHandler, ControlChannelRouter, WRAITH_PREFIX,
|
||||
ControlChannelHandler, ControlChannelRouter, ALKNET_PREFIX,
|
||||
};
|
||||
use crate::server::rate_limit::{AuthAttemptLimiter, ConnectionRateLimiter};
|
||||
|
||||
@@ -210,7 +210,7 @@ impl Handler for ServerHandler {
|
||||
originator_port: u32,
|
||||
_session: &mut Session,
|
||||
) -> Result<bool, Self::Error> {
|
||||
if host_to_connect.starts_with(WRAITH_PREFIX) {
|
||||
if host_to_connect.starts_with(ALKNET_PREFIX) {
|
||||
if !self.control_channel_router.has_handler() {
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -576,18 +576,18 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reserved_wraith_destination_routing() {
|
||||
fn reserved_alknet_destination_routing() {
|
||||
use crate::server::control_channel::is_reserved_destination;
|
||||
assert!(is_reserved_destination("wraith-control"));
|
||||
assert!(is_reserved_destination("wraith-status"));
|
||||
assert!(is_reserved_destination("wraith-events"));
|
||||
assert!(is_reserved_destination("alknet-control"));
|
||||
assert!(is_reserved_destination("alknet-status"));
|
||||
assert!(is_reserved_destination("alknet-events"));
|
||||
assert!(!is_reserved_destination("example.com"));
|
||||
assert!(!is_reserved_destination("localhost"));
|
||||
assert!(!is_reserved_destination("wraith.example.com"));
|
||||
assert!(!is_reserved_destination("alknet.example.com"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn server_handler_without_control_handler_rejects_wraith_destinations() {
|
||||
fn server_handler_without_control_handler_rejects_alknet_destinations() {
|
||||
let auth_config = make_empty_auth_config();
|
||||
let handler = make_handler(auth_config, None, None);
|
||||
assert!(!handler.control_channel_router().has_handler());
|
||||
@@ -5,7 +5,7 @@
|
||||
//! auth, connection rate limiting, auth attempt limiting, stealth mode (fake nginx 404),
|
||||
//! and outbound proxy routing (direct/SOCKS5/HTTP CONNECT).
|
||||
//!
|
||||
//! Destination hosts starting with `wraith-` are reserved for internal use (control channel, ADR-018).
|
||||
//! Destination hosts starting with `alknet-` are reserved for internal use (control channel, ADR-018).
|
||||
|
||||
pub mod channel_proxy;
|
||||
pub mod control_channel;
|
||||
@@ -16,8 +16,8 @@ pub mod stealth;
|
||||
|
||||
pub use channel_proxy::{connect_outbound, proxy_channel};
|
||||
pub use control_channel::{
|
||||
ControlChannelHandler, ControlChannelRouter, DuplexStream, WRAITH_CONTROL_DESTINATION,
|
||||
WRAITH_PREFIX, is_reserved_destination,
|
||||
ControlChannelHandler, ControlChannelRouter, DuplexStream, ALKNET_CONTROL_DESTINATION,
|
||||
ALKNET_PREFIX, is_reserved_destination,
|
||||
};
|
||||
pub use handler::{ProxyConfig, ProxyMode, ServerHandler, TransportKind};
|
||||
pub use rate_limit::{AuthAttemptLimiter, ConnectionRateLimiter};
|
||||
@@ -40,14 +40,14 @@ impl std::fmt::Display for ServeTransportMode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Programmatic configuration for a wraith server.
|
||||
/// Programmatic configuration for an alknet server.
|
||||
///
|
||||
/// Construct with `ServeOptions::new(key_source)` and chain builder methods.
|
||||
/// Call `validate()` before passing to `Server::new()`.
|
||||
///
|
||||
/// ```
|
||||
/// use wraith_core::server::{ServeOptions, ServeTransportMode};
|
||||
/// use wraith_core::auth::keys::KeySource;
|
||||
/// use alknet_core::server::{ServeOptions, ServeTransportMode};
|
||||
/// use alknet_core::auth::keys::KeySource;
|
||||
///
|
||||
/// let opts = ServeOptions::new(KeySource::File("/path/to/host_key".into()))
|
||||
/// .transport_mode(ServeTransportMode::Tcp)
|
||||
@@ -221,7 +221,7 @@ struct ActiveSession {
|
||||
join: tokio::task::JoinHandle<()>,
|
||||
}
|
||||
|
||||
/// The wraith SSH server.
|
||||
/// The alknet SSH server.
|
||||
///
|
||||
/// Accepts connections over any `TransportAcceptor`, authenticates via Ed25519 keys
|
||||
/// or certificate authority, and proxies `direct-tcpip` channels to their targets.
|
||||
@@ -331,13 +331,13 @@ impl Server {
|
||||
|
||||
if self.transport_mode == ServeTransportMode::Iroh {
|
||||
if let Some(id) = endpoint_info {
|
||||
info!("wraith server running: transport=iroh endpoint_id={}", id);
|
||||
info!("alknet server running: transport=iroh endpoint_id={}", id);
|
||||
} else {
|
||||
info!("wraith server running: transport=iroh");
|
||||
info!("alknet server running: transport=iroh");
|
||||
}
|
||||
} else {
|
||||
info!(
|
||||
"wraith server running: transport={} listen={}",
|
||||
"alknet server running: transport={} listen={}",
|
||||
self.transport_mode, self.listen_addr
|
||||
);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use tokio::io;
|
||||
|
||||
use super::{Transport, TransportAcceptor, TransportInfo, TransportKind};
|
||||
|
||||
pub const ALPN: &[u8] = b"wraith-ssh";
|
||||
pub const ALPN: &[u8] = b"alknet-ssh";
|
||||
const DEFAULT_RELAY_URL: &str = "https://relay.iroh.network/";
|
||||
|
||||
/// A client-side iroh QUIC P2P transport that connects to a remote iroh endpoint.
|
||||
@@ -31,8 +31,8 @@ pub struct IrohTransport {
|
||||
impl IrohTransport {
|
||||
/// Create a new iroh transport with its own dedicated endpoint.
|
||||
///
|
||||
/// The endpoint is created with the `wraith-ssh` ALPN and the provided
|
||||
/// relay URL. Use this when wraith is the only iroh service on this node.
|
||||
/// The endpoint is created with the `alknet-ssh` ALPN and the provided
|
||||
/// relay URL. Use this when alknet is the only iroh service on this node.
|
||||
pub async fn new(
|
||||
node_id: NodeId,
|
||||
relay_url: Option<RelayUrl>,
|
||||
@@ -54,9 +54,9 @@ impl IrohTransport {
|
||||
|
||||
/// Create an iroh transport using an existing shared endpoint.
|
||||
///
|
||||
/// The endpoint must already have the `wraith-ssh` ALPN registered
|
||||
/// The endpoint must already have the `alknet-ssh` ALPN registered
|
||||
/// (typically via [`iroh::protocol::Router::builder`]). This enables
|
||||
/// running wraith alongside iroh-blobs, iroh-gossip, iroh-docs, and
|
||||
/// running alknet alongside iroh-blobs, iroh-gossip, iroh-docs, and
|
||||
/// other protocol handlers on the same QUIC endpoint — one connection
|
||||
/// per peer, multiplexed by ALPN.
|
||||
pub fn from_endpoint(node_id: NodeId, endpoint: Endpoint) -> Self {
|
||||
@@ -102,9 +102,9 @@ impl Transport for IrohTransport {
|
||||
/// [`IrohAcceptor::from_endpoint`] to share an existing iroh `Endpoint`
|
||||
/// with other protocol handlers (blobs, gossip, docs).
|
||||
///
|
||||
/// When using `from_endpoint`, the wraith-ssh ALPN must be registered
|
||||
/// When using `from_endpoint`, the alknet-ssh ALPN must be registered
|
||||
/// via an iroh `Router` that calls `Handler::accept()` on incoming
|
||||
/// connections with the `wraith-ssh` ALPN, then passes the accepted
|
||||
/// connections with the `alknet-ssh` ALPN, then passes the accepted
|
||||
/// bidirectional stream to `russh::server::run_stream()`.
|
||||
pub struct IrohAcceptor {
|
||||
endpoint: Endpoint,
|
||||
@@ -112,9 +112,9 @@ pub struct IrohAcceptor {
|
||||
}
|
||||
|
||||
impl IrohAcceptor {
|
||||
/// Bind a new iroh endpoint with a dedicated `wraith-ssh` ALPN.
|
||||
/// Bind a new iroh endpoint with a dedicated `alknet-ssh` ALPN.
|
||||
///
|
||||
/// Use this when wraith is the only iroh service on this node.
|
||||
/// Use this when alknet is the only iroh service on this node.
|
||||
pub async fn bind(
|
||||
relay_url: Option<RelayUrl>,
|
||||
proxy_url: Option<url::Url>,
|
||||
@@ -135,14 +135,14 @@ impl IrohAcceptor {
|
||||
|
||||
/// Create an iroh acceptor using an existing shared endpoint.
|
||||
///
|
||||
/// The endpoint must already have the `wraith-ssh` ALPN registered
|
||||
/// The endpoint must already have the `alknet-ssh` ALPN registered
|
||||
/// (typically via [`iroh::protocol::Router::builder`]). When using a
|
||||
/// shared endpoint, incoming connections with the `wraith-ssh` ALPN
|
||||
/// shared endpoint, incoming connections with the `alknet-ssh` ALPN
|
||||
/// are routed by the Router to a `ProtocolHandler` that this acceptor
|
||||
/// does not manage — the caller is responsible for bridging the
|
||||
/// Router's `accept()` callback to this acceptor's stream handling.
|
||||
///
|
||||
/// For the standalone case where wraith owns the endpoint, use
|
||||
/// For the standalone case where alknet owns the endpoint, use
|
||||
/// [`IrohAcceptor::bind`] instead, which handles the accept loop
|
||||
/// internally.
|
||||
pub fn from_endpoint(endpoint: Endpoint) -> Self {
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Pluggable transport layer for Wraith.
|
||||
//! Pluggable transport layer for Alknet.
|
||||
//!
|
||||
//! The transport layer produces a duplex byte stream (`AsyncRead + AsyncWrite + Unpin + Send`)
|
||||
//! that SSH consumes. This is the core architectural abstraction — SSH never opens its own
|
||||
@@ -293,9 +293,9 @@ mod tests {
|
||||
fn tls_transport_builder_methods() {
|
||||
let addr: SocketAddr = "1.2.3.4:443".parse().unwrap();
|
||||
let transport = TlsTransport::new(addr)
|
||||
.with_server_name("wraith.test")
|
||||
.with_server_name("alknet.test")
|
||||
.with_insecure(true);
|
||||
assert_eq!(transport.tls_server_name, Some("wraith.test".to_string()));
|
||||
assert_eq!(transport.tls_server_name, Some("alknet.test".to_string()));
|
||||
assert!(transport.insecure);
|
||||
}
|
||||
|
||||
@@ -395,7 +395,7 @@ mod tests {
|
||||
let mut client = transport.connect().await.unwrap();
|
||||
let (mut server, _info) = accept_handle.await.unwrap();
|
||||
|
||||
let msg = b"wraith integration test";
|
||||
let msg = b"alknet integration test";
|
||||
client.write_all(msg).await.unwrap();
|
||||
let mut buf = vec![0u8; msg.len()];
|
||||
server.read_exact(&mut buf).await.unwrap();
|
||||
@@ -1,4 +1,4 @@
|
||||
use wraith_core::testutil::{MockTransport, MockTransportAcceptor, Transport, TransportAcceptor, mock_pair};
|
||||
use alknet_core::testutil::{MockTransport, MockTransportAcceptor, Transport, TransportAcceptor, mock_pair};
|
||||
|
||||
#[tokio::test]
|
||||
async fn mock_transport_connect() {
|
||||
@@ -1,16 +1,16 @@
|
||||
[package]
|
||||
name = "wraith-napi"
|
||||
name = "alknet-napi"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "Node.js native addon for Wraith via napi-rs: connect() and serve() SSH tunnel functions"
|
||||
description = "Node.js native addon for Alknet via napi-rs: connect() and serve() SSH tunnel functions"
|
||||
repository.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
wraith-core = { path = "../wraith-core", features = ["tls", "iroh"] }
|
||||
alknet-core = { path = "../alknet-core", features = ["tls", "iroh"] }
|
||||
napi = { version = "3", features = ["async", "error_anyhow"] }
|
||||
napi-derive = "3"
|
||||
tokio = { version = "1", features = ["io-util", "sync", "rt", "macros", "net", "time", "signal"] }
|
||||
@@ -1,4 +1,4 @@
|
||||
//! NAPI `connect()` function and `WraithStream` type.
|
||||
//! NAPI `connect()` function and `AlknetStream` type.
|
||||
//!
|
||||
//! Opens a single SSH channel as a duplex stream for programmatic use.
|
||||
//! Unlike the CLI client, this does not start a SOCKS5 server or port forwards —
|
||||
@@ -13,15 +13,15 @@ use russh::client;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use wraith_core::auth::client_auth::{ClientAuthConfig, ClientHandler};
|
||||
use wraith_core::auth::keys::KeySource;
|
||||
use wraith_core::transport::{IrohTransport, TcpTransport, TlsTransport, Transport};
|
||||
use alknet_core::auth::client_auth::{ClientAuthConfig, ClientHandler};
|
||||
use alknet_core::auth::keys::KeySource;
|
||||
use alknet_core::transport::{IrohTransport, TcpTransport, TlsTransport, Transport};
|
||||
|
||||
const DEFAULT_HOST: &str = "wraith-control";
|
||||
const DEFAULT_HOST: &str = "alknet-control";
|
||||
const DEFAULT_PORT: u32 = 0;
|
||||
|
||||
#[napi(object)]
|
||||
pub struct WraithConnectOptions {
|
||||
pub struct AlknetConnectOptions {
|
||||
pub server: Option<String>,
|
||||
pub peer: Option<String>,
|
||||
pub transport: String,
|
||||
@@ -53,13 +53,13 @@ fn parse_addr(addr_str: &str) -> Result<SocketAddr> {
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub struct WraithStream {
|
||||
pub struct AlknetStream {
|
||||
read: Arc<Mutex<tokio::io::ReadHalf<russh::ChannelStream<client::Msg>>>>,
|
||||
write: Arc<Mutex<tokio::io::WriteHalf<russh::ChannelStream<client::Msg>>>>,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl WraithStream {
|
||||
impl AlknetStream {
|
||||
#[napi]
|
||||
pub async fn read(&self, size: u32) -> Result<Buffer> {
|
||||
let mut buf = vec![0u8; size as usize];
|
||||
@@ -96,7 +96,7 @@ impl WraithStream {
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub async fn connect(options: WraithConnectOptions) -> Result<WraithStream> {
|
||||
pub async fn connect(options: AlknetConnectOptions) -> Result<AlknetStream> {
|
||||
let key_source = resolve_key_source(&options.identity)?;
|
||||
let auth_config = Arc::new(
|
||||
ClientAuthConfig::from_key_source(key_source)
|
||||
@@ -105,7 +105,7 @@ pub async fn connect(options: WraithConnectOptions) -> Result<WraithStream> {
|
||||
|
||||
let transport_mode = options.transport.to_lowercase();
|
||||
let handler = ClientHandler::from_config(&auth_config);
|
||||
let username = "wraith".to_string();
|
||||
let username = "alknet".to_string();
|
||||
|
||||
let config = Arc::new(client::Config::default());
|
||||
|
||||
@@ -232,7 +232,7 @@ pub async fn connect(options: WraithConnectOptions) -> Result<WraithStream> {
|
||||
let stream = channel.into_stream();
|
||||
let (read_half, write_half) = tokio::io::split(stream);
|
||||
|
||||
Ok(WraithStream {
|
||||
Ok(AlknetStream {
|
||||
read: Arc::new(Mutex::new(read_half)),
|
||||
write: Arc::new(Mutex::new(write_half)),
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
//! # wraith-napi
|
||||
//! # alknet-napi
|
||||
//!
|
||||
//! Node.js native addon for [Wraith](https://git.alk.dev/alkdev/wraith) via napi-rs.
|
||||
//! Node.js native addon for [Alknet](https://git.alk.dev/alkdev/alknet) via napi-rs.
|
||||
//! Exposes `connect()` and `serve()` functions for programmatic SSH tunnel creation.
|
||||
//!
|
||||
//! > **Alpha software.** The NAPI interface may change between versions.
|
||||
@@ -8,7 +8,7 @@
|
||||
//! # Quick example (Node.js)
|
||||
//!
|
||||
//! ```js
|
||||
//! const { connect, serve } = require('wraith-napi');
|
||||
//! const { connect, serve } = require('alknet-napi');
|
||||
//!
|
||||
//! // Client: open a duplex SSH stream
|
||||
//! const stream = await connect({
|
||||
@@ -1,4 +1,4 @@
|
||||
//! NAPI `serve()` function and `WraithServer` type.
|
||||
//! NAPI `serve()` function and `AlknetServer` type.
|
||||
//!
|
||||
//! Starts an SSH server that emits new channel streams via a
|
||||
//! `ThreadsafeFunction` callback. Supports TCP, TLS, and iroh transports.
|
||||
@@ -14,14 +14,14 @@ use russh::Channel;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use wraith_core::auth::keys::KeySource;
|
||||
use wraith_core::auth::server_auth::ServerAuthConfig;
|
||||
use wraith_core::server::rate_limit::{AuthAttemptLimiter, ConnectionRateLimiter};
|
||||
use wraith_core::server::serve::{ServeOptions, ServeTransportMode, Server};
|
||||
use wraith_core::transport::{TcpAcceptor, TransportAcceptor};
|
||||
use alknet_core::auth::keys::KeySource;
|
||||
use alknet_core::auth::server_auth::ServerAuthConfig;
|
||||
use alknet_core::server::rate_limit::{AuthAttemptLimiter, ConnectionRateLimiter};
|
||||
use alknet_core::server::serve::{ServeOptions, ServeTransportMode, Server};
|
||||
use alknet_core::transport::{TcpAcceptor, TransportAcceptor};
|
||||
|
||||
#[napi(object)]
|
||||
pub struct WraithServeOptions {
|
||||
pub struct AlknetServeOptions {
|
||||
pub transport: String,
|
||||
pub host_key: Option<Either<String, Buffer>>,
|
||||
pub authorized_keys: Option<Either<String, Buffer>>,
|
||||
@@ -75,13 +75,13 @@ pub struct ConnectionInfo {
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub struct WraithServerStream {
|
||||
pub struct AlknetServerStream {
|
||||
read: Arc<Mutex<tokio::io::ReadHalf<russh::ChannelStream<server::Msg>>>>,
|
||||
write: Arc<Mutex<tokio::io::WriteHalf<russh::ChannelStream<server::Msg>>>>,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl WraithServerStream {
|
||||
impl AlknetServerStream {
|
||||
#[napi]
|
||||
pub async fn read(&self, size: u32) -> napi::Result<Buffer> {
|
||||
let mut buf = vec![0u8; size as usize];
|
||||
@@ -208,7 +208,7 @@ impl russh::server::Handler for NapiServerHandler {
|
||||
_originator_port: u32,
|
||||
_session: &mut russh::server::Session,
|
||||
) -> std::result::Result<bool, Self::Error> {
|
||||
if host_to_connect.starts_with("wraith-") {
|
||||
if host_to_connect.starts_with("alknet-") {
|
||||
let guard = self.channel_sender.lock().await;
|
||||
if let Some(ref tx) = *guard {
|
||||
let _ = tx.send(channel);
|
||||
@@ -385,7 +385,7 @@ impl russh::server::Handler for NapiServerHandler {
|
||||
type ServerTsfn = ThreadsafeFunction<ConnectionEventWrapper, (), ConnectionEventWrapper>;
|
||||
|
||||
#[napi]
|
||||
pub struct WraithServer {
|
||||
pub struct AlknetServer {
|
||||
shutdown_tx: tokio::sync::watch::Sender<bool>,
|
||||
listen_addr: String,
|
||||
endpoint_id: Option<String>,
|
||||
@@ -393,7 +393,7 @@ pub struct WraithServer {
|
||||
}
|
||||
|
||||
struct ConnectionEventWrapper {
|
||||
stream: WraithServerStream,
|
||||
stream: AlknetServerStream,
|
||||
info: ConnectionInfo,
|
||||
}
|
||||
|
||||
@@ -408,7 +408,7 @@ impl ToNapiValue for ConnectionEventWrapper {
|
||||
"Failed to create object"
|
||||
)?;
|
||||
|
||||
let stream_val = <WraithServerStream as ToNapiValue>::to_napi_value(env, val.stream)?;
|
||||
let stream_val = <AlknetServerStream as ToNapiValue>::to_napi_value(env, val.stream)?;
|
||||
let key_stream = std::ffi::CString::new("stream").unwrap();
|
||||
napi::check_status!(
|
||||
napi::sys::napi_set_named_property(env, raw_obj, key_stream.as_ptr(), stream_val),
|
||||
@@ -439,7 +439,7 @@ impl TypeName for ConnectionEventWrapper {
|
||||
impl ValidateNapiValue for ConnectionEventWrapper {}
|
||||
|
||||
#[napi]
|
||||
impl WraithServer {
|
||||
impl AlknetServer {
|
||||
#[napi]
|
||||
pub async fn close(&self) -> napi::Result<()> {
|
||||
let _ = self.shutdown_tx.send(true);
|
||||
@@ -470,7 +470,7 @@ impl WraithServer {
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub async fn serve(options: WraithServeOptions) -> napi::Result<WraithServer> {
|
||||
pub async fn serve(options: AlknetServeOptions) -> napi::Result<AlknetServer> {
|
||||
let host_key_source = resolve_key_source(&options.host_key, "hostKey")?;
|
||||
let authorized_keys_source = resolve_optional_key_source(&options.authorized_keys);
|
||||
let cert_authority_source = resolve_optional_key_source(&options.cert_authority);
|
||||
@@ -543,7 +543,7 @@ pub async fn serve(options: WraithServeOptions) -> napi::Result<WraithServer> {
|
||||
);
|
||||
|
||||
let private_key =
|
||||
wraith_core::auth::keys::load_private_key(host_key_source).map_err(|e| {
|
||||
alknet_core::auth::keys::load_private_key(host_key_source.clone()).map_err(|e| {
|
||||
napi::Error::new(napi::Status::InvalidArg, format!("host key error: {}", e))
|
||||
})?;
|
||||
|
||||
@@ -573,7 +573,7 @@ pub async fn serve(options: WraithServeOptions) -> napi::Result<WraithServer> {
|
||||
.await;
|
||||
});
|
||||
|
||||
Ok(WraithServer {
|
||||
Ok(AlknetServer {
|
||||
shutdown_tx,
|
||||
listen_addr: actual_listen,
|
||||
endpoint_id: None,
|
||||
@@ -581,7 +581,7 @@ pub async fn serve(options: WraithServeOptions) -> napi::Result<WraithServer> {
|
||||
})
|
||||
}
|
||||
ServeTransportMode::Tls => {
|
||||
use wraith_core::transport::TlsAcceptor;
|
||||
use alknet_core::transport::TlsAcceptor;
|
||||
|
||||
let addr = parse_addr(listen_addr_str)?;
|
||||
|
||||
@@ -654,7 +654,7 @@ pub async fn serve(options: WraithServeOptions) -> napi::Result<WraithServer> {
|
||||
);
|
||||
|
||||
let private_key =
|
||||
wraith_core::auth::keys::load_private_key(host_key_source).map_err(|e| {
|
||||
alknet_core::auth::keys::load_private_key(host_key_source.clone()).map_err(|e| {
|
||||
napi::Error::new(napi::Status::InvalidArg, format!("host key error: {}", e))
|
||||
})?;
|
||||
|
||||
@@ -684,7 +684,7 @@ pub async fn serve(options: WraithServeOptions) -> napi::Result<WraithServer> {
|
||||
.await;
|
||||
});
|
||||
|
||||
Ok(WraithServer {
|
||||
Ok(AlknetServer {
|
||||
shutdown_tx,
|
||||
listen_addr: actual_listen,
|
||||
endpoint_id: None,
|
||||
@@ -692,7 +692,7 @@ pub async fn serve(options: WraithServeOptions) -> napi::Result<WraithServer> {
|
||||
})
|
||||
}
|
||||
ServeTransportMode::Iroh => {
|
||||
use wraith_core::transport::IrohAcceptor;
|
||||
use alknet_core::transport::IrohAcceptor;
|
||||
|
||||
let relay_url: Option<iroh::RelayUrl> = match options.iroh_relay.as_deref() {
|
||||
Some(u) => Some(u.parse().map_err(|e| {
|
||||
@@ -736,7 +736,7 @@ pub async fn serve(options: WraithServeOptions) -> napi::Result<WraithServer> {
|
||||
);
|
||||
|
||||
let private_key =
|
||||
wraith_core::auth::keys::load_private_key(host_key_source).map_err(|e| {
|
||||
alknet_core::auth::keys::load_private_key(host_key_source).map_err(|e| {
|
||||
napi::Error::new(napi::Status::InvalidArg, format!("host key error: {}", e))
|
||||
})?;
|
||||
|
||||
@@ -766,7 +766,7 @@ pub async fn serve(options: WraithServeOptions) -> napi::Result<WraithServer> {
|
||||
.await;
|
||||
});
|
||||
|
||||
Ok(WraithServer {
|
||||
Ok(AlknetServer {
|
||||
shutdown_tx,
|
||||
listen_addr: String::new(),
|
||||
endpoint_id: Some(iroh_endpoint_id),
|
||||
@@ -836,7 +836,7 @@ async fn run_accept_loop<A>(
|
||||
Some(ch) => {
|
||||
let channel_stream = ch.into_stream();
|
||||
let (read_half, write_half) = tokio::io::split(channel_stream);
|
||||
let server_stream = WraithServerStream {
|
||||
let server_stream = AlknetServerStream {
|
||||
read: Arc::new(Mutex::new(read_half)),
|
||||
write: Arc::new(Mutex::new(write_half)),
|
||||
};
|
||||
@@ -1,23 +1,23 @@
|
||||
[package]
|
||||
name = "wraith"
|
||||
name = "alknet"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "CLI binary for Wraith: self-hostable SSH tunnel tool with pluggable transports"
|
||||
description = "CLI binary for Alknet: self-hostable SSH tunnel tool with pluggable transports"
|
||||
repository.workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "wraith"
|
||||
name = "alknet"
|
||||
path = "src/main.rs"
|
||||
|
||||
[features]
|
||||
default = ["tls", "iroh"]
|
||||
tls = ["wraith-core/tls", "dep:rustls-pemfile", "dep:rustls-pki-types"]
|
||||
iroh = ["wraith-core/iroh", "dep:iroh", "dep:url"]
|
||||
acme = ["wraith-core/acme", "dep:rustls-acme", "dep:rustls", "tls"]
|
||||
tls = ["alknet-core/tls", "dep:rustls-pemfile", "dep:rustls-pki-types"]
|
||||
iroh = ["alknet-core/iroh", "dep:iroh", "dep:url"]
|
||||
acme = ["alknet-core/acme", "dep:rustls-acme", "dep:rustls", "tls"]
|
||||
|
||||
[dependencies]
|
||||
wraith-core = { path = "../wraith-core" }
|
||||
alknet-core = { path = "../alknet-core" }
|
||||
clap = { version = "4", features = ["derive", "env"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
anyhow = "1"
|
||||
@@ -1,10 +1,10 @@
|
||||
//! # wraith
|
||||
//! # alknet
|
||||
//!
|
||||
//! CLI binary for [Wraith](https://git.alk.dev/alkdev/wraith), a self-hostable SSH-based tunnel
|
||||
//! tool. Provides `wraith connect` (client) and `wraith serve` (server) subcommands with
|
||||
//! CLI binary for [Alknet](https://git.alk.dev/alkdev/alknet), a self-hostable SSH-based tunnel
|
||||
//! tool. Provides `alknet connect` (client) and `alknet serve` (server) subcommands with
|
||||
//! pluggable transports (TCP, TLS, iroh).
|
||||
//!
|
||||
//! > **Alpha software.** See `wraith-core` for library usage.
|
||||
//! > **Alpha software.** See `alknet-core` for library usage.
|
||||
|
||||
use std::net::SocketAddr;
|
||||
use std::process;
|
||||
@@ -12,18 +12,18 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use clap::{Parser, Subcommand, ValueEnum};
|
||||
use wraith_core::auth::keys::KeySource;
|
||||
use wraith_core::client::{ConnectOptions, TransportMode};
|
||||
use wraith_core::server::{ServeOptions, ServeTransportMode, Server};
|
||||
use alknet_core::auth::keys::KeySource;
|
||||
use alknet_core::client::{ConnectOptions, TransportMode};
|
||||
use alknet_core::server::{ServeOptions, ServeTransportMode, Server};
|
||||
#[cfg(feature = "iroh")]
|
||||
use wraith_core::transport::IrohTransport;
|
||||
use wraith_core::transport::TcpTransport;
|
||||
use alknet_core::transport::IrohTransport;
|
||||
use alknet_core::transport::TcpTransport;
|
||||
#[cfg(feature = "tls")]
|
||||
use wraith_core::transport::TlsTransport;
|
||||
use wraith_core::transport::Transport;
|
||||
use alknet_core::transport::TlsTransport;
|
||||
use alknet_core::transport::Transport;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "wraith", version, about = "Wraith SSH tunnel tool")]
|
||||
#[command(name = "alknet", version, about = "Alknet SSH tunnel tool")]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
@@ -32,13 +32,13 @@ struct Cli {
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
#[command(
|
||||
about = "Connect to a wraith server and start a SOCKS5 proxy / port forwarding session"
|
||||
about = "Connect to an alknet server and start a SOCKS5 proxy / port forwarding session"
|
||||
)]
|
||||
Connect {
|
||||
#[arg(
|
||||
long,
|
||||
help = "TCP/TLS server address (required for tcp/tls transport)",
|
||||
env = "WRAITH_SERVER"
|
||||
env = "ALKNET_SERVER"
|
||||
)]
|
||||
server: Option<String>,
|
||||
|
||||
@@ -51,7 +51,7 @@ enum Commands {
|
||||
#[arg(long, value_enum, default_value = "tcp", help = "Transport mode")]
|
||||
transport: TransportModeArg,
|
||||
|
||||
#[arg(long, help = "SSH private key path", env = "WRAITH_IDENTITY")]
|
||||
#[arg(long, help = "SSH private key path", env = "ALKNET_IDENTITY")]
|
||||
identity: Option<String>,
|
||||
|
||||
#[arg(long, default_value = "127.0.0.1:1080", help = "SOCKS5 listen address")]
|
||||
@@ -76,7 +76,7 @@ enum Commands {
|
||||
insecure: bool,
|
||||
},
|
||||
|
||||
#[command(about = "Start the wraith server (accept SSH connections)")]
|
||||
#[command( about = "Start the alknet server (accept SSH connections)")]
|
||||
Serve {
|
||||
#[arg(long, help = "SSH host key path (required)")]
|
||||
key: String,
|
||||
@@ -263,7 +263,7 @@ async fn run_connect(
|
||||
insecure: bool,
|
||||
) -> Result<()> {
|
||||
let identity_val = identity
|
||||
.ok_or_else(|| anyhow!("--identity is required (or set WRAITH_IDENTITY env var)"))?;
|
||||
.ok_or_else(|| anyhow!("--identity is required (or set ALKNET_IDENTITY env var)"))?;
|
||||
let key_source = KeySource::File(identity_val.into());
|
||||
|
||||
let transport_mode: TransportMode = transport.into();
|
||||
@@ -317,7 +317,7 @@ async fn run_connect(
|
||||
#[cfg(not(feature = "tls"))]
|
||||
{
|
||||
Err(anyhow!(
|
||||
"TLS transport is not available (wraith-core built without 'tls' feature)"
|
||||
"TLS transport is not available (alknet-core built without 'tls' feature)"
|
||||
))
|
||||
}
|
||||
#[cfg(feature = "tls")]
|
||||
@@ -340,7 +340,7 @@ async fn run_connect(
|
||||
#[cfg(not(feature = "iroh"))]
|
||||
{
|
||||
Err(anyhow!(
|
||||
"iroh transport is not available (wraith-core built without 'iroh' feature)"
|
||||
"iroh transport is not available (alknet-core built without 'iroh' feature)"
|
||||
))
|
||||
}
|
||||
#[cfg(feature = "iroh")]
|
||||
@@ -375,7 +375,7 @@ async fn run_connect(
|
||||
}
|
||||
|
||||
async fn connect_and_run<T: Transport>(opts: ConnectOptions, transport: Arc<T>) -> Result<()> {
|
||||
wraith_core::client::ClientSession::new(opts, transport)
|
||||
alknet_core::client::ClientSession::new(opts, transport)
|
||||
.await
|
||||
.map_err(|e| anyhow!("{e}"))?
|
||||
.run()
|
||||
@@ -405,7 +405,7 @@ async fn run_serve(
|
||||
#[cfg(not(feature = "acme"))]
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"ACME support is not available (wraith built without 'acme' feature)"
|
||||
"ACME support is not available (alknet built without 'acme' feature)"
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -454,7 +454,7 @@ async fn run_serve(
|
||||
let addr: SocketAddr = listen
|
||||
.parse()
|
||||
.map_err(|e| anyhow!("invalid listen address: {e}"))?;
|
||||
let acceptor = wraith_core::transport::TcpAcceptor::bind(addr)
|
||||
let acceptor = alknet_core::transport::TcpAcceptor::bind(addr)
|
||||
.await
|
||||
.map_err(|e| anyhow!("bind failed: {e}"))?;
|
||||
server.run(acceptor, None).await.map_err(|e| anyhow!("{e}"))
|
||||
@@ -463,7 +463,7 @@ async fn run_serve(
|
||||
#[cfg(not(feature = "tls"))]
|
||||
{
|
||||
Err(anyhow!(
|
||||
"TLS transport is not available (wraith-core built without 'tls' feature)"
|
||||
"TLS transport is not available (alknet-core built without 'tls' feature)"
|
||||
))
|
||||
}
|
||||
#[cfg(feature = "acme")]
|
||||
@@ -473,11 +473,11 @@ async fn run_serve(
|
||||
.parse()
|
||||
.map_err(|e| anyhow!("invalid listen address: {e}"))?;
|
||||
let provider = Arc::new(
|
||||
wraith_core::transport::AcmeCertProvider::domain(domain)
|
||||
alknet_core::transport::AcmeCertProvider::domain(domain)
|
||||
.with_production_directory(),
|
||||
);
|
||||
let acceptor =
|
||||
wraith_core::transport::AcmeTlsAcceptor::bind_acme(addr, provider)
|
||||
alknet_core::transport::AcmeTlsAcceptor::bind_acme(addr, provider)
|
||||
.await
|
||||
.map_err(|e| anyhow!("ACME bind failed: {e}"))?;
|
||||
return server.run(acceptor, None).await.map_err(|e| anyhow!("{e}"));
|
||||
@@ -506,7 +506,7 @@ async fn run_serve(
|
||||
let key: PrivateKeyDer<'static> = rustls_pemfile::private_key(&mut &key_data[..])
|
||||
.map_err(|e| anyhow!("failed to parse TLS private key: {e}"))?
|
||||
.ok_or_else(|| anyhow!("no private key found in {}", key_path))?;
|
||||
let acceptor = wraith_core::transport::TlsAcceptor::bind(addr, certs, key, None)
|
||||
let acceptor = alknet_core::transport::TlsAcceptor::bind(addr, certs, key, None)
|
||||
.await
|
||||
.map_err(|e| anyhow!("TLS bind failed: {e}"))?;
|
||||
server.run(acceptor, None).await.map_err(|e| anyhow!("{e}"))
|
||||
@@ -516,7 +516,7 @@ async fn run_serve(
|
||||
#[cfg(not(feature = "iroh"))]
|
||||
{
|
||||
Err(anyhow!(
|
||||
"iroh transport is not available (wraith-core built without 'iroh' feature)"
|
||||
"iroh transport is not available (alknet-core built without 'iroh' feature)"
|
||||
))
|
||||
}
|
||||
#[cfg(feature = "iroh")]
|
||||
@@ -533,7 +533,7 @@ async fn run_serve(
|
||||
Some(u) => Some(u.parse().map_err(|e| anyhow!("invalid proxy URL: {e}"))?),
|
||||
None => None,
|
||||
};
|
||||
let acceptor = wraith_core::transport::IrohAcceptor::bind(relay_url, proxy_url)
|
||||
let acceptor = alknet_core::transport::IrohAcceptor::bind(relay_url, proxy_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("iroh bind failed: {e}"))?;
|
||||
let endpoint_id = acceptor.endpoint_id();
|
||||
Reference in New Issue
Block a user