Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fcde975
test(network): require bounded BiDi loopback TCP connection
seonghobae Aug 19, 2026
aef0ac7
test(network): format BiDi TCP RED contract
seonghobae Aug 19, 2026
1de304b
feat(network): connect exact BiDi loopback transport
seonghobae Aug 19, 2026
ccb7d31
feat(network): export bounded BiDi TCP transport
seonghobae Aug 19, 2026
b4ce10d
refactor(network): isolate BiDi transport errors
seonghobae Aug 19, 2026
8ed075c
test(network): isolate BiDi transport resilience coverage
seonghobae Aug 19, 2026
ec66b21
refactor(network): keep BiDi transport boundary focused
seonghobae Aug 19, 2026
755d626
test(network): apply canonical BiDi resilience formatting
seonghobae Aug 19, 2026
fd91248
fix(network): remove unused BiDi error imports
seonghobae Aug 19, 2026
975e2e2
docs(changelog): record bounded BiDi TCP transport
seonghobae Aug 19, 2026
c491464
test(network): require consumable BiDi TCP evidence
seonghobae Aug 19, 2026
6aab73c
feat(network): hand off verified BiDi TCP evidence
seonghobae Aug 19, 2026
18703be
docs(changelog): record BiDi TCP evidence handoff
seonghobae Aug 19, 2026
11a533d
test(network): require bounded BiDi WebSocket opening request
seonghobae Aug 19, 2026
9803303
feat(network): bind BiDi WebSocket opening request
seonghobae Aug 19, 2026
8b8f664
test(network): cover BiDi handshake diagnostics
seonghobae Aug 19, 2026
73f2fbd
test(network): close BiDi handshake branch coverage
seonghobae Aug 19, 2026
d39d0d3
test(network): retain BiDi WebSocket client key
seonghobae Aug 19, 2026
ccfc641
fix(network): retain BiDi WebSocket client key
seonghobae Aug 19, 2026
e441948
merge: carry ChromeDriver session compatibility into BiDi TCP transport
seonghobae Aug 21, 2026
b036e47
merge: carry ChromeDriver session compatibility into BiDi handshake r…
seonghobae Aug 21, 2026
30da73a
test(network): redact WebSocket client nonce debug
seonghobae Aug 22, 2026
6922dd9
fix(network): redact WebSocket client nonce debug
seonghobae Aug 22, 2026
e1ad7f4
Merge pull request #193 from ContextualWisdomLab/feat/webdriver-bidi-…
seonghobae Aug 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ All notable changes to OriginWeave are documented in this file. The format follo

### Added

- Bounded WebDriver BiDi loopback TCP transport that consumes one exact no-DNS connect target, retries only explicitly recoverable local transport failures within repository timeout and attempt ceilings, exposes the stream only after operating-system peer inspection and exact peer verification, supports a consuming handoff of the original stream with typed credential-free peer/session/TLS and bounded-attempt evidence, preserves typed causal errors, and performs no DNS, proxy/PAC, process authentication, TLS, WebSocket, BiDi message, browser-action, or Agent-authority step.
- Exact WebDriver BiDi socket-peer verification that consumes an approved no-DNS connect target, requires the observed IP address and port to match exactly, preserves the TLS requirement and exact correlated session id, and remains inert metadata that does not authenticate an OS process, does not negotiate TLS, perform a WebSocket handshake, or grant Agent authority.
- Explicit no-DNS WebDriver BiDi loopback connection targets that derive exact IPv4/IPv6 loopback `SocketAddr` metadata from a session-correlated endpoint, reject `localhost` as requiring separately trusted name resolution, preserve the TLS requirement and exact session id, perform no socket I/O, and grant no Agent authority.
- Rust workspace for independently reusable core, policy, destination, network, TLS, resource, and evidence modules.
Expand Down
20 changes: 17 additions & 3 deletions crates/originweave-network/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
//! Direct-only policy-bound TCP connection authority for OriginWeave.
//!
//! The crate consumes a validated connection plan, opens one exact socket
//! address without hostname resolution or proxy inheritance, verifies the
//! operating-system peer, and emits credential-free evidence.
//! The crate consumes validated connection plans, opens exact socket addresses
//! without hostname resolution or proxy inheritance, verifies operating-system
//! peers before exposing transport I/O, and emits credential-free evidence.
//! It also bridges a session-correlated WebDriver BiDi loopback target from
//! `originweave-core` into one bounded exact TCP connection and can bind an inert
//! RFC 6455 opening request to an already-verified plain BiDi stream without
//! granting browser, WebSocket, TLS, policy, or Agent authority.

#![forbid(unsafe_code)]
#![deny(missing_docs)]

mod connection;
mod webdriver_bidi_connection;
mod webdriver_bidi_websocket_handshake;

pub use connection::{
ConnectionPlan, DirectTcpConnection, MAX_CONNECT_TIMEOUT, MAX_CONNECTION_ATTEMPTS,
NetworkError, SocketConnectionEvidence,
};
pub use webdriver_bidi_connection::{
WebDriverBiDiTcpConnection, WebDriverBiDiTcpConnectionError,
WebDriverBiDiTcpConnectionEvidence, WebDriverBiDiTcpConnectionPlan,
};
pub use webdriver_bidi_websocket_handshake::{
WebDriverBiDiWebSocketClientKey, WebDriverBiDiWebSocketHandshakeError,
WebDriverBiDiWebSocketHandshakePlan,
};
254 changes: 254 additions & 0 deletions crates/originweave-network/src/webdriver_bidi_connection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
use std::{
io,
net::{SocketAddr, TcpStream},
time::Duration,
};

use originweave_core::{VerifiedWebDriverBiDiSocketPeer, WebDriverBiDiWebSocketConnectTarget};

use crate::connection::{MAX_CONNECT_TIMEOUT, MAX_CONNECTION_ATTEMPTS};

mod error;

pub use error::WebDriverBiDiTcpConnectionError;

#[cfg(test)]
mod tests;

fn is_retryable_connect_error(kind: io::ErrorKind) -> bool {
matches!(
kind,
io::ErrorKind::TimedOut
| io::ErrorKind::ConnectionRefused
| io::ErrorKind::ConnectionReset
| io::ErrorKind::ConnectionAborted
| io::ErrorKind::Interrupted
)
}

/// Single-use authority to open one exact WebDriver BiDi loopback TCP destination.
///
/// The plan consumes a session-correlated, no-DNS [`WebDriverBiDiWebSocketConnectTarget`]
/// produced by `originweave-core`. It applies the same bounded per-attempt timeout and retry
/// ceilings as the general direct-network connector, opens only the exact [`SocketAddr`] carried by
/// that target, and does not expose the stream until the operating system's observed peer has been
/// verified by the consumed target.
///
/// This boundary performs no DNS lookup, proxy or PAC routing, Chromium/ChromeDriver process
/// authentication, TLS negotiation, WebSocket upgrade, BiDi framing, browser policy decision, or
/// Agent-authority grant.
#[derive(Debug)]
pub struct WebDriverBiDiTcpConnectionPlan {
target: WebDriverBiDiWebSocketConnectTarget,
connect_timeout: Duration,
maximum_attempts: u8,
}

impl WebDriverBiDiTcpConnectionPlan {
/// Validate one bounded exact-loopback connection plan without performing network I/O.
pub fn new(
target: WebDriverBiDiWebSocketConnectTarget,
connect_timeout: Duration,
maximum_attempts: u8,
) -> Result<Self, WebDriverBiDiTcpConnectionError> {
if connect_timeout.is_zero() || connect_timeout > MAX_CONNECT_TIMEOUT {
return Err(WebDriverBiDiTcpConnectionError::InvalidConnectTimeout {
connect_timeout,
maximum_timeout: MAX_CONNECT_TIMEOUT,
});
}
if maximum_attempts == 0 || maximum_attempts > MAX_CONNECTION_ATTEMPTS {
return Err(WebDriverBiDiTcpConnectionError::InvalidAttemptCount {
attempt_count: maximum_attempts,
maximum_attempts: MAX_CONNECTION_ATTEMPTS,
});
}

Ok(Self {
target,
connect_timeout,
maximum_attempts,
})
}

/// Open the exact approved loopback socket and expose it only after peer verification.
///
/// Retry is limited to transport errors that can occur transiently while a local browser driver
/// listener is becoming ready. Peer-inspection and peer-mismatch failures are integrity failures
/// and therefore fail closed without retry or fallback.
pub fn connect(self) -> Result<WebDriverBiDiTcpConnection, WebDriverBiDiTcpConnectionError> {
self.connect_with(&SystemWebDriverBiDiConnector)
}

fn connect_with(
self,
connector: &dyn WebDriverBiDiSocketConnector,
) -> Result<WebDriverBiDiTcpConnection, WebDriverBiDiTcpConnectionError> {
let socket_address = self.target.socket_addr();
let connect_timeout = self.connect_timeout;
let maximum_attempts = self.maximum_attempts;
let target = self.target;
let mut attempt_number = 1;

loop {
match connector.connect_timeout(&socket_address, connect_timeout) {
Ok(stream) => {
let observed_peer = connector.peer_addr(&stream).map_err(|source| {
WebDriverBiDiTcpConnectionError::PeerInspectionFailed {
socket_address,
attempt_number,
source,
}
})?;
let verified_peer =
target
.verify_connected_peer(observed_peer)
.map_err(|source| WebDriverBiDiTcpConnectionError::PeerMismatch {
attempt_number,
source,
})?;
return Ok(WebDriverBiDiTcpConnection {
stream,
verified_peer,
attempt_number,
connect_timeout,
});
}
Err(source)
if is_retryable_connect_error(source.kind())
&& attempt_number < maximum_attempts =>
{
attempt_number += 1;
}
Err(source) => {
if source.kind() == io::ErrorKind::TimedOut {
return Err(WebDriverBiDiTcpConnectionError::ConnectionTimedOut {
socket_address,
attempt_count: attempt_number,
connect_timeout,
source,
});
}
return Err(WebDriverBiDiTcpConnectionError::ConnectionFailed {
socket_address,
attempt_count: attempt_number,
source,
});
}
}
}
}
}

trait WebDriverBiDiSocketConnector {
fn connect_timeout(
&self,
socket_address: &SocketAddr,
timeout: Duration,
) -> io::Result<TcpStream>;

fn peer_addr(&self, stream: &TcpStream) -> io::Result<SocketAddr>;
}

struct SystemWebDriverBiDiConnector;

impl WebDriverBiDiSocketConnector for SystemWebDriverBiDiConnector {
fn connect_timeout(
&self,
socket_address: &SocketAddr,
timeout: Duration,
) -> io::Result<TcpStream> {
TcpStream::connect_timeout(socket_address, timeout)
}

fn peer_addr(&self, stream: &TcpStream) -> io::Result<SocketAddr> {
stream.peer_addr()
}
}

/// Established WebDriver BiDi TCP stream whose observed peer matched the approved target exactly.
///
/// This wrapper proves only exact transport-destination equality for one bounded connection. The
/// caller must still establish any required TLS channel, complete a WebSocket handshake, bind the
/// transport to the expected browser process/session, and pass separate action-policy checks.
#[derive(Debug)]
pub struct WebDriverBiDiTcpConnection {
stream: TcpStream,
verified_peer: VerifiedWebDriverBiDiSocketPeer,
attempt_number: u8,
connect_timeout: Duration,
}

impl WebDriverBiDiTcpConnection {
/// Borrow the verified TCP stream.
#[must_use]
pub const fn stream(&self) -> &TcpStream {
&self.stream
}

/// Borrow the session-correlated exact peer evidence consumed by this connection.
#[must_use]
pub const fn verified_peer(&self) -> &VerifiedWebDriverBiDiSocketPeer {
&self.verified_peer
}

/// Return the one-based bounded attempt on which the connection succeeded.
#[must_use]
pub const fn attempt_number(&self) -> u8 {
self.attempt_number
}

/// Return the per-attempt timeout applied while establishing this connection.
#[must_use]
pub const fn connect_timeout(&self) -> Duration {
self.connect_timeout
}

/// Consume the wrapper into the original verified stream and credential-free transport evidence.
///
/// This handoff does not clone the socket or create reusable connection authority. The returned
/// evidence records only the already-verified peer plus bounded connection-attempt metadata; it
/// does not authenticate a browser process, establish TLS, complete WebSocket framing, or grant
/// browser or Agent authority.
#[must_use]
pub fn into_parts(self) -> (TcpStream, WebDriverBiDiTcpConnectionEvidence) {
let evidence = WebDriverBiDiTcpConnectionEvidence {
verified_peer: self.verified_peer,
attempt_number: self.attempt_number,
connect_timeout: self.connect_timeout,
};
(self.stream, evidence)
}
}

/// Credential-free evidence retained when a verified WebDriver BiDi TCP stream is consumed.
///
/// This value records exact peer/session/TLS-requirement metadata inherited from the consumed
/// no-DNS target together with the successful bounded attempt and per-attempt timeout. It is
/// transport evidence only and grants no process, TLS, WebSocket, browser-action, or Agent authority.
#[derive(Debug)]
pub struct WebDriverBiDiTcpConnectionEvidence {
verified_peer: VerifiedWebDriverBiDiSocketPeer,
attempt_number: u8,
connect_timeout: Duration,
}

impl WebDriverBiDiTcpConnectionEvidence {
/// Borrow the exact session-correlated peer verified before stream exposure.
#[must_use]
pub const fn verified_peer(&self) -> &VerifiedWebDriverBiDiSocketPeer {
&self.verified_peer
}

/// Return the one-based bounded attempt on which the connection succeeded.
#[must_use]
pub const fn attempt_number(&self) -> u8 {
self.attempt_number
}

/// Return the per-attempt timeout applied while establishing the connection.
#[must_use]
pub const fn connect_timeout(&self) -> Duration {
self.connect_timeout
}
}
Loading
Loading