Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ All notable changes to OriginWeave are documented in this file. The format follo
- Credential-free sensitive-handle lifecycle evidence binds issuance, exclusive expiry, bounded uses, observed resolution count, and revocation to the exact credential-free `OpaqueHandleOnly` sensitive-access receipt, preserving tenant, actor, task, field set, purpose, destination, classification, policy version, and decision time without storing opaque handle tokens or protected values.
- Credential-free connection and redirect evidence containing canonical addresses, destination classes, target digests, hop numbers, and approved-address counts.
- Credential-free verified TCP evidence containing the logical origin, requested socket, observed peer, destination class, successful attempt number, and per-attempt timeout.
- Standard `Display` and `std::error::Error` contracts for destination, redirect, digest, direct-network, TLS, and resource-budget failures, including preserved destination-policy, rustls, and operating-system sources where applicable.
- Standard `Display` and `std::error::Error` contracts for core origin, destination, redirect, digest, direct-network, TLS, and resource-budget failures, including preserved destination-policy, rustls, and operating-system sources where applicable.
- Real loopback TCP integration proof plus deterministic timeout, refusal, retry, peer-inspection, peer-mismatch, canonicalization, IPv6 metadata, and single-use replay tests.
- Real loopback rustls integration covering trusted DNS SAN, Common-Name fallback rejection, wrong-name and untrusted-root rejection, fixed-time expiry and not-yet-valid failures, exact IPv4 and IPv6 SANs, TLS 1.2/TLS 1.3, required and optional ALPN, and transport-origin binding.
- Cumulative interactive-first RAM, VRAM, batch, local-model, admission, pause, and compositor-pressure mitigation plans, including active-consumer reduction at exact hard limits.
Expand Down
33 changes: 33 additions & 0 deletions crates/originweave-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,39 @@ pub enum OriginError {
InvalidPort,
}

impl fmt::Display for OriginError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MissingScheme => {
formatter.write_str("origin must include a scheme followed by ://")
}
Self::UnsupportedScheme => formatter.write_str("origin scheme must be http or https"),
Self::InsecureRemoteOrigin => formatter.write_str(
"remote HTTP origins are forbidden; use HTTPS or a loopback HTTP origin",
),
Self::MissingAuthority => {
formatter.write_str("origin must include a non-empty authority after the scheme")
}
Self::UserInfoNotAllowed => {
formatter.write_str("origin authority must not contain user information")
}
Self::PathNotAllowed => {
formatter.write_str("origin must not contain a path, query, or fragment")
}
Self::InvalidAuthority => {
formatter.write_str("origin authority is malformed or ambiguous")
}
Self::AmbiguousNumericHost => formatter
.write_str("origin host uses an ambiguous browser-style numeric address spelling"),
Self::InvalidPort => {
formatter.write_str("origin port must be a nonzero numeric value within 1..=65535")
}
}
}
}

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

/// A nonzero identity for one active browser automation session.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BrowserSessionId(u64);
Expand Down
54 changes: 54 additions & 0 deletions crates/originweave-core/tests/origin_error_contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use std::error::Error;

use originweave_core::OriginError;

fn assert_standard_error<T: Error>() {}

#[test]
fn origin_error_exposes_a_stable_standard_error_contract() {
assert_standard_error::<OriginError>();

let cases = [
(
OriginError::MissingScheme,
"origin must include a scheme followed by ://",
),
(
OriginError::UnsupportedScheme,
"origin scheme must be http or https",
),
(
OriginError::InsecureRemoteOrigin,
"remote HTTP origins are forbidden; use HTTPS or a loopback HTTP origin",
),
(
OriginError::MissingAuthority,
"origin must include a non-empty authority after the scheme",
),
(
OriginError::UserInfoNotAllowed,
"origin authority must not contain user information",
),
(
OriginError::PathNotAllowed,
"origin must not contain a path, query, or fragment",
),
(
OriginError::InvalidAuthority,
"origin authority is malformed or ambiguous",
),
(
OriginError::AmbiguousNumericHost,
"origin host uses an ambiguous browser-style numeric address spelling",
),
(
OriginError::InvalidPort,
"origin port must be a nonzero numeric value within 1..=65535",
),
];

for (error, expected) in cases {
assert_eq!(error.to_string(), expected);
assert!(error.source().is_none());
}
}
Loading