Skip to content
167 changes: 158 additions & 9 deletions crates/originweave-core/src/browser_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,45 @@ use std::fmt;
/// Maximum UTF-8 byte length for browser protocol adapter metadata tokens.
pub const MAX_BROWSER_PROTOCOL_METADATA_BYTES: usize = 128;

/// One OriginWeave Protocol generation.
///
/// This value identifies the OriginWeave contract spoken by an adapter. It is
/// deliberately independent from the upstream WebDriver BiDi/CDP revision and
/// from the browser build. Constructing a version does not make that version
/// supported; callers must compare it with the exact version required by the
/// surrounding OriginWeave protocol boundary.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OriginWeaveProtocolVersion {
major: u16,
minor: u16,
}

impl OriginWeaveProtocolVersion {
/// Construct an OriginWeave Protocol generation identifier.
#[must_use]
pub const fn new(major: u16, minor: u16) -> Self {
Self { major, minor }
}

/// Return the protocol major version.
#[must_use]
pub const fn major(self) -> u16 {
self.major
}

/// Return the protocol minor version.
#[must_use]
pub const fn minor(self) -> u16 {
self.minor
}
}

impl fmt::Display for OriginWeaveProtocolVersion {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "originweave/{}.{}", self.major, self.minor)
}
}

/// Browser automation protocol family used by one versioned adapter.
///
/// The protocol family is descriptive metadata only. Selecting a kind does not
Expand Down Expand Up @@ -33,12 +72,13 @@ pub enum BrowserProtocolCapability {
///
/// This value is deliberately not browser authority. It contains no browser
/// session, context, origin, node handle, action grant, credential, or network
/// permission. Higher layers may use it to fail closed when a required adapter
/// capability is absent, while all OriginWeave authority remains separately
/// validated.
/// permission. Higher layers may use it to fail closed when the adapter targets
/// the wrong OriginWeave Protocol generation or lacks a required browser
/// capability, while all OriginWeave authority remains separately validated.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BrowserProtocolAdapterDescriptor {
kind: BrowserProtocolKind,
originweave_protocol_version: OriginWeaveProtocolVersion,
adapter_version: String,
protocol_revision: String,
browser_revision: String,
Expand All @@ -48,14 +88,16 @@ pub struct BrowserProtocolAdapterDescriptor {
impl BrowserProtocolAdapterDescriptor {
/// Construct one explicit adapter descriptor.
///
/// Adapter version, upstream protocol revision, and browser revision are
/// separate bounded ASCII metadata tokens. This prevents an OriginWeave
/// adapter release from being mistaken for the WebDriver BiDi/CDP revision
/// or the pinned browser build it was validated against. The declared
/// capability list must be non-empty and duplicate-free and is normalized
/// into one stable order so caller ordering cannot change descriptor identity.
/// The OriginWeave Protocol generation, adapter version, upstream protocol
/// revision, and browser revision are distinct metadata. This prevents an
/// OriginWeave contract version from being mistaken for the WebDriver
/// BiDi/CDP revision or the pinned browser build it was validated against.
/// The declared capability list must be non-empty and duplicate-free and is
/// normalized into one stable order so caller ordering cannot change
/// descriptor identity.
pub fn new(
kind: BrowserProtocolKind,
originweave_protocol_version: OriginWeaveProtocolVersion,
adapter_version: &str,
protocol_revision: &str,
browser_revision: &str,
Expand Down Expand Up @@ -85,6 +127,7 @@ impl BrowserProtocolAdapterDescriptor {

Ok(Self {
kind,
originweave_protocol_version,
adapter_version: adapter_version.to_owned(),
protocol_revision: protocol_revision.to_owned(),
browser_revision: browser_revision.to_owned(),
Expand All @@ -98,6 +141,12 @@ impl BrowserProtocolAdapterDescriptor {
self.kind
}

/// Return the exact OriginWeave Protocol generation implemented by this adapter.
#[must_use]
pub const fn originweave_protocol_version(&self) -> OriginWeaveProtocolVersion {
self.originweave_protocol_version
}

/// Return the bounded OriginWeave adapter-version metadata token.
#[must_use]
pub fn adapter_version(&self) -> &str {
Expand Down Expand Up @@ -128,6 +177,47 @@ impl BrowserProtocolAdapterDescriptor {
self.capabilities.contains(&capability)
}

/// Require one exact browser protocol family before later adapter use.
///
/// This boundary never treats WebDriver BiDi and Chrome DevTools Protocol
/// as interchangeable. A kind mismatch fails closed with a typed error and
/// does not select or authorize another protocol family as a fallback.
pub fn require_kind(
&self,
required: BrowserProtocolKind,
) -> Result<(), BrowserProtocolKindRequirementError> {
if self.kind == required {
Ok(())
} else {
Err(BrowserProtocolKindRequirementError::ProtocolKindMismatch {
required,
actual: self.kind,
})
}
}

/// Require one exact OriginWeave Protocol generation before later adapter use.
///
/// Pre-alpha compatibility is deliberately exact at this boundary. A caller
/// may add a separately reviewed compatibility transform later, but this
/// descriptor never silently treats a different major or minor generation
/// as equivalent.
pub fn require_originweave_protocol_version(
&self,
required: OriginWeaveProtocolVersion,
) -> Result<(), BrowserProtocolVersionRequirementError> {
if self.originweave_protocol_version == required {
Ok(())
} else {
Err(
BrowserProtocolVersionRequirementError::ProtocolVersionMismatch {
required,
actual: self.originweave_protocol_version,
},
)
}
}

/// Require one explicitly declared adapter capability before later use.
///
/// This method never infers support from the browser protocol family. An
Expand Down Expand Up @@ -155,6 +245,13 @@ const fn capability_rank(capability: BrowserProtocolCapability) -> u8 {
}
}

fn protocol_kind_name(kind: BrowserProtocolKind) -> &'static str {
match kind {
BrowserProtocolKind::WebDriverBiDi => "webdriver-bidi",
BrowserProtocolKind::ChromeDevToolsProtocol => "chrome-devtools-protocol",
}
}

fn capability_name(capability: BrowserProtocolCapability) -> &'static str {
match capability {
BrowserProtocolCapability::Navigation => "navigation",
Expand All @@ -174,6 +271,58 @@ fn metadata_token_is_valid(value: &str) -> bool {
&& value.bytes().any(|byte| byte.is_ascii_alphanumeric())
}

/// Failure to require one exact browser protocol family from an adapter.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BrowserProtocolKindRequirementError {
/// The adapter uses a different browser protocol family than required.
ProtocolKindMismatch {
/// Exact browser protocol family required by the caller.
required: BrowserProtocolKind,
/// Exact browser protocol family declared by the adapter.
actual: BrowserProtocolKind,
},
}

impl fmt::Display for BrowserProtocolKindRequirementError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ProtocolKindMismatch { required, actual } => write!(
formatter,
"browser protocol adapter uses {} but {} is required",
protocol_kind_name(*actual),
protocol_kind_name(*required)
),
}
}
}

impl std::error::Error for BrowserProtocolKindRequirementError {}

/// Failure to require one exact OriginWeave Protocol generation from an adapter.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BrowserProtocolVersionRequirementError {
/// The adapter targets a different OriginWeave Protocol generation.
ProtocolVersionMismatch {
/// Exact OriginWeave Protocol generation required by the caller.
required: OriginWeaveProtocolVersion,
/// Exact OriginWeave Protocol generation declared by the adapter.
actual: OriginWeaveProtocolVersion,
},
}

impl fmt::Display for BrowserProtocolVersionRequirementError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ProtocolVersionMismatch { required, actual } => write!(
formatter,
"browser protocol adapter targets {actual} but {required} is required"
),
}
}
}

impl std::error::Error for BrowserProtocolVersionRequirementError {}

/// Failure to require one browser protocol capability from an adapter.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BrowserProtocolCapabilityRequirementError {
Expand Down
3 changes: 2 additions & 1 deletion crates/originweave-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ mod extension_authority;
pub use browser_protocol::{
BrowserProtocolAdapterDescriptor, BrowserProtocolCapability,
BrowserProtocolCapabilityRequirementError, BrowserProtocolDescriptorError, BrowserProtocolKind,
MAX_BROWSER_PROTOCOL_METADATA_BYTES,
BrowserProtocolKindRequirementError, BrowserProtocolVersionRequirementError,
MAX_BROWSER_PROTOCOL_METADATA_BYTES, OriginWeaveProtocolVersion,
};
pub use browser_registry::{
BrowserAuthorityRegistry, BrowserRegistryError, MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES,
Expand Down
7 changes: 4 additions & 3 deletions crates/originweave-core/src/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ pub use core_contracts::{
AuthorityExtensionAgentGrant as ExtensionAgentGrant, BrowserAuthorityRegistry,
BrowserProtocolAdapterDescriptor, BrowserProtocolCapability,
BrowserProtocolCapabilityRequirementError, BrowserProtocolDescriptorError, BrowserProtocolKind,
BrowserProtocolKindRequirementError, BrowserProtocolVersionRequirementError,
BrowserRegistryError, BrowserSessionId, BrowsingContextId, Capability, DocumentEpoch,
ExecutionPurpose, ExtensionAgentCapability, ExtensionId, ExtensionIdError, InstructionSource,
MAX_BROWSER_PROTOCOL_METADATA_BYTES, MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES, NodeHandleError,
Origin, OriginError, PolicyContext, RegistryObservedNodeHandle as ObservedNodeHandle,
RiskClass, RobotsDecision, SecretDelivery, SessionMode,
evaluate_extension_authority_access as evaluate_extension_access,
Origin, OriginError, OriginWeaveProtocolVersion, PolicyContext,
RegistryObservedNodeHandle as ObservedNodeHandle, RiskClass, RobotsDecision, SecretDelivery,
SessionMode, evaluate_extension_authority_access as evaluate_extension_access,
};

/// Stateless MCP routing validation that maps only explicit tools to typed actions.
Expand Down
Loading
Loading