Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a439c9b
test(core): require OriginWeave protocol version binding
seonghobae Aug 12, 2026
345b5c6
test(core): apply canonical protocol-version formatting
seonghobae Aug 12, 2026
1883288
feat(core): bind browser adapters to protocol version
seonghobae Aug 12, 2026
fb34295
feat(core): export protocol version contract
seonghobae Aug 12, 2026
c4cd417
style(core): apply canonical protocol-version formatting
seonghobae Aug 12, 2026
ea45a91
test(core): exercise runtime protocol-version construction
seonghobae Aug 12, 2026
ac780f0
test(core): require canonical protocol version parsing
seonghobae Aug 12, 2026
eec3f46
feat(core): parse canonical protocol versions
seonghobae Aug 12, 2026
aeb9629
feat(core): export protocol version parse error
seonghobae Aug 12, 2026
ea17243
docs(changelog): record canonical protocol version parsing
seonghobae Aug 12, 2026
66f6ad7
test(core): require exact runtime browser revisions
seonghobae Aug 12, 2026
03f7b18
style(core): apply canonical runtime revision test formatting
seonghobae Aug 12, 2026
e7c3e3e
feat(core): validate browser runtime revisions
seonghobae Aug 12, 2026
4b1ce55
feat(core): export browser runtime revision error
seonghobae Aug 12, 2026
f0fc8f9
style(core): apply canonical runtime revision formatting
seonghobae Aug 12, 2026
8907813
merge: align protocol version binding with current capability require…
seonghobae Aug 15, 2026
9e07a47
merge: align protocol version parsing with current binding
seonghobae Aug 15, 2026
04c4759
merge: align runtime revision checks with current protocol parser
seonghobae Aug 15, 2026
fdaa3db
merge: align protocol version binding with current capability require…
seonghobae Aug 15, 2026
3f2b254
merge: align protocol version parser with current version binding
seonghobae Aug 15, 2026
d5c7bd5
merge: align runtime revision checks with current protocol parser
seonghobae Aug 15, 2026
3a865f5
chore(core): align protocol version binding with current capability r…
seonghobae Aug 17, 2026
5cc01ea
chore(core): align protocol version parsing with current version binding
seonghobae Aug 17, 2026
8f1c022
chore(core): align runtime revision checks with current protocol parser
seonghobae Aug 17, 2026
ae5b77d
chore(core): align protocol version binding with current capability r…
seonghobae Aug 18, 2026
9595e18
chore(core): align protocol parser with current version binding
seonghobae Aug 18, 2026
e183163
chore(core): align runtime revision checks with current protocol parser
seonghobae Aug 18, 2026
9162fa9
Merge pull request #109 from ContextualWisdomLab/feat/originweave-pro…
seonghobae Aug 26, 2026
415b886
docs: record runtime revision boundary
seonghobae Aug 26, 2026
d2249e5
Merge pull request #110 from ContextualWisdomLab/feat/browser-protoco…
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ All notable changes to OriginWeave are documented in this file. The format follo
- Bound explicit extension-to-Agent grants to the exact canonical origin in addition to extension identity, session, and browsing context, so a same-session navigation or port change cannot reuse the grant.
- Rust workspace for independently reusable core, policy, destination, network, TLS, resource, and evidence modules.
- Versioned browser-protocol adapter metadata that distinguishes WebDriver BiDi from pinned CDP, binds bounded adapter/browser revision tokens to an explicit duplicate-free capability set, normalizes capability-set identity independently of caller ordering, and exposes typed fail-closed capability requirements without granting browser, action, network, or secret authority by protocol kind alone.
- Canonical OriginWeave protocol-version parsing for exact `originweave/<major>.<minor>` syntax, with typed fail-closed rejection of malformed, ambiguous, overflowed, or noncanonical serialized generations; parsing does not negotiate compatibility or grant adapter authority.
- Public `require_runtime_revisions` validation that fails closed when caller-supplied runtime protocol or browser revision evidence is malformed or differs from the descriptor's pinned revisions, preserving typed malformed-versus-drift errors without authenticating or attesting the adapter process.
- Canonical HTTPS and loopback-origin boundary with case-normalized schemes and hosts, default-port normalization, IPv4/IPv6 handling, browser-special numeric-host rejection, and explicit malformed-input errors.
- Typed browser actions, capabilities, risk classes, execution modes, robots decisions, secret-delivery contracts, immutable canonical action-intent digests, and intent-bound approval scopes.
- Deterministic fail-closed policy evaluation for untrusted instructions, origin grants, crawler restrictions, execution-mode and purpose consistency, approvals, and brokered secrets.
Expand Down
223 changes: 213 additions & 10 deletions crates/originweave-core/src/browser_protocol.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,94 @@
use std::fmt;
use std::{fmt, str::FromStr};

/// 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)
}
}

impl FromStr for OriginWeaveProtocolVersion {
type Err = OriginWeaveProtocolVersionParseError;

fn from_str(value: &str) -> Result<Self, Self::Err> {
let Some(remainder) = value.strip_prefix("originweave/") else {
return Err(OriginWeaveProtocolVersionParseError::InvalidFormat);
};
let Some((major_text, minor_text)) = remainder.split_once('.') else {
return Err(OriginWeaveProtocolVersionParseError::InvalidFormat);
};
if minor_text.contains('.') {
return Err(OriginWeaveProtocolVersionParseError::InvalidFormat);
}
let Ok(major) = major_text.parse::<u16>() else {
return Err(OriginWeaveProtocolVersionParseError::InvalidFormat);
};
let Ok(minor) = minor_text.parse::<u16>() else {
return Err(OriginWeaveProtocolVersionParseError::InvalidFormat);
};

let version = Self::new(major, minor);
if version.to_string() != value {
return Err(OriginWeaveProtocolVersionParseError::InvalidFormat);
}
Ok(version)
}
Comment thread
seonghobae marked this conversation as resolved.
}

/// Failure to parse a canonical serialized OriginWeave Protocol generation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OriginWeaveProtocolVersionParseError {
/// The value did not use the exact canonical `originweave/<major>.<minor>` syntax.
InvalidFormat,
}

impl fmt::Display for OriginWeaveProtocolVersionParseError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidFormat => formatter.write_str(
"OriginWeave protocol version must use canonical originweave/<major>.<minor> syntax",
),
}
}
}

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

/// 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 +119,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 +135,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,
Comment thread
seonghobae marked this conversation as resolved.
Expand Down Expand Up @@ -85,6 +174,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 +188,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 +224,55 @@ impl BrowserProtocolAdapterDescriptor {
self.capabilities.contains(&capability)
}

/// 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 exact runtime browser-protocol and browser revisions before use.
///
/// The caller must derive both values from the trusted runtime adapter that
/// is about to perform browser work. This deterministic comparison does not
/// authenticate or attest that caller. It only prevents a descriptor pinned
/// to one validated upstream-protocol/browser pair from being silently used
/// when the supplied runtime evidence is malformed or has drifted.
pub fn require_runtime_revisions(
&self,
protocol_revision: &str,
browser_revision: &str,
) -> Result<(), BrowserProtocolRuntimeRequirementError> {
if !metadata_token_is_valid(protocol_revision) {
return Err(BrowserProtocolRuntimeRequirementError::InvalidProtocolRevision);
}
if !metadata_token_is_valid(browser_revision) {
return Err(BrowserProtocolRuntimeRequirementError::InvalidBrowserRevision);
}
if self.protocol_revision != protocol_revision {
return Err(BrowserProtocolRuntimeRequirementError::ProtocolRevisionMismatch);
}
if self.browser_revision != browser_revision {
return Err(BrowserProtocolRuntimeRequirementError::BrowserRevisionMismatch);
}
Ok(())
}

/// Require one explicitly declared adapter capability before later use.
///
/// This method never infers support from the browser protocol family. An
Expand Down Expand Up @@ -174,6 +319,64 @@ fn metadata_token_is_valid(value: &str) -> bool {
&& value.bytes().any(|byte| byte.is_ascii_alphanumeric())
}

/// 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 exact pinned runtime revision evidence from an adapter.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BrowserProtocolRuntimeRequirementError {
/// The runtime upstream-protocol revision token was malformed.
InvalidProtocolRevision,
/// The runtime browser revision token was malformed.
InvalidBrowserRevision,
/// The runtime upstream-protocol revision differs from the pinned descriptor.
ProtocolRevisionMismatch,
/// The runtime browser revision differs from the pinned descriptor.
BrowserRevisionMismatch,
}

impl fmt::Display for BrowserProtocolRuntimeRequirementError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidProtocolRevision => formatter.write_str(
"runtime browser protocol revision must be a bounded ASCII metadata token",
),
Self::InvalidBrowserRevision => formatter
.write_str("runtime browser revision must be a bounded ASCII metadata token"),
Self::ProtocolRevisionMismatch => formatter.write_str(
"runtime browser protocol revision does not match the pinned adapter revision",
),
Self::BrowserRevisionMismatch => formatter.write_str(
"runtime browser revision does not match the pinned adapter browser revision",
),
}
}
}

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

/// Failure to require one browser protocol capability from an adapter.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BrowserProtocolCapabilityRequirementError {
Expand Down
4 changes: 3 additions & 1 deletion crates/originweave-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ mod extension_authority;
pub use browser_protocol::{
BrowserProtocolAdapterDescriptor, BrowserProtocolCapability,
BrowserProtocolCapabilityRequirementError, BrowserProtocolDescriptorError, BrowserProtocolKind,
MAX_BROWSER_PROTOCOL_METADATA_BYTES,
BrowserProtocolRuntimeRequirementError, BrowserProtocolVersionRequirementError,
MAX_BROWSER_PROTOCOL_METADATA_BYTES, OriginWeaveProtocolVersion,
OriginWeaveProtocolVersionParseError,
};
pub use browser_registry::{
BrowserAuthorityRegistry, BrowserRegistryError, MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES,
Expand Down
Loading
Loading