commit d2ca795a81de1b4f9c6200f06b38d6723feabc58
parent 2d6e793d38e8b7383bddac60c86fd02046ceec0f
Author: triesap <tyson@radroots.org>
Date: Mon, 24 Aug 2026 17:03:20 +0000
test(rhi): close Unix admin qualification wave
Freeze the final 20-route and 33-model qualification contract against the exact Lib source lock.
Exercise original-wire, version, pagination, idempotency, peer-permission, removed-route, and resource boundaries over the sealed Unix adapter.
Diffstat:
6 files changed, 416 insertions(+), 34 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
@@ -404,13 +404,15 @@
identity rekey/replace vocabulary, and Step 209 alone may claim the complete
20-route/33-model surface. Never expose the shared raw router, listener, JSON
handler, or a caller-selected socket path.
-- Through Step 208, the active and final route sets are exactly seven common
- plus thirteen domain routes. Pagination is bounded to 200 items, query fields
- are closed, authenticated cursors remain bound by the handler to
- route/filter/snapshot, and decoded trade parameters use the exact
- lowercase-hex trade-ID type. Identity rotation remains offline create-new
- plus validated configuration apply and restart; Unix peer admission grants
- no direct SQLite or identity-provider mutation authority.
+- Through Step 209, the active and final route sets are exactly seven common
+ plus thirteen domain routes. The Step 209 wave contract freezes the complete
+ original-wire, version, pagination, idempotency, peer, removed-route, and
+ resource negative matrices against the exact Lib source lock. Pagination is
+ bounded to 200 items, query fields are closed, authenticated cursors remain
+ bound by the handler to route/filter/snapshot, and decoded trade parameters
+ use the exact lowercase-hex trade-ID type. Identity rotation remains offline
+ create-new plus validated configuration apply and restart; Unix peer
+ admission grants no direct SQLite or identity-provider mutation authority.
- Optional TCP operations expose only cached `/livez`, `/readyz`, and
`/metrics`; requests must not perform SQLite, source, relay, DNS, identity,
evidence, or credential probes.
diff --git a/README b/README
@@ -584,10 +584,15 @@ suggested live provider authority. Identity rotation is only offline create-new
envelope plus validated configuration apply and restart. Unix peer authorization
remains a transport admission gate and grants neither direct SQLite nor
identity-provider mutation authority. Step 209 qualifies the complete
-20-route/33-model inventory. The cumulative domain contract is
+20-route/33-model inventory with real Unix-socket original-wire, version,
+pagination, idempotency, peer-permission, removed-route, and resource negative
+matrices plus the exact source-locked shared-transport corpus. The cumulative
+domain contract is
[`admin_domain.v1.json`](contracts/services_hardening/admin_domain.v1.json).
The offline identity correction is
[`admin_identity_offline.v1.json`](contracts/services_hardening/admin_identity_offline.v1.json).
+The completed admin-wave qualification is
+[`admin_wave_qualification.v1.json`](contracts/services_hardening/admin_wave_qualification.v1.json).
## Sealed service-instance paths
diff --git a/contracts/services_hardening/admin_wave_qualification.v1.json b/contracts/services_hardening/admin_wave_qualification.v1.json
@@ -0,0 +1,104 @@
+{
+ "schema": "radroots.rhi.admin-wave-qualification",
+ "schema_version": 1,
+ "contract_version": 1,
+ "step": 209,
+ "wave": "130-b",
+ "service": "rhi",
+ "final_inventory": {
+ "route_count": 20,
+ "model_count": 33,
+ "common_route_count": 7,
+ "domain_route_count": 13,
+ "source": "operator_contract.v1.json"
+ },
+ "component_corpus": [
+ "complete_route_and_model_inventory_matches_the_machine_contract",
+ "strict_response_admission_rejects_duplicates_null_and_noncanonical_bytes",
+ "query_and_nested_type_admission_is_exact_and_bounded",
+ "public_diagnostics_are_source_free_and_content_free",
+ "twenty_active_routes_round_trip_over_the_hardened_unix_boundary",
+ "production_server_projects_exact_validated_admin_limits"
+ ],
+ "live_negative_matrix": {
+ "original_wire": [
+ "duplicate_request_field",
+ "nested_null",
+ "missing_contract_version"
+ ],
+ "version": [
+ "unknown_major_path",
+ "contract_version_zero_every_mutation",
+ "contract_version_two_every_mutation"
+ ],
+ "pagination": [
+ "noncanonical_limit",
+ "just_over_maximum_limit",
+ "duplicate_query_item",
+ "unknown_query_item",
+ "malformed_percent_encoding",
+ "noncanonical_cursor",
+ "handler_rejected_unbound_cursor"
+ ],
+ "idempotency": [
+ "exact_operation_replay_returns_success_without_second_commit",
+ "conflicting_operation_reuse_rejected"
+ ],
+ "peer": [
+ "owner_only_socket_mode",
+ "current_owner_round_trip",
+ "source_locked_linux_uid_or_gid_allow_and_other_deny"
+ ],
+ "removed_sensitive_routes": [
+ "identity_rekey_unavailable",
+ "identity_replace_unavailable"
+ ],
+ "resource": [
+ "request_body_just_over_limit",
+ "response_body_just_over_limit",
+ "page_limit_just_over_maximum",
+ "source_locked_header_query_connection_deadline_and_drain_limits"
+ ]
+ },
+ "source_locked_transport_evidence": {
+ "repository": "https://github.com/radrootslabs/lib",
+ "revision": "21b11e7a5120ea949f7ad0838c746873fc73aac2",
+ "package": "radroots_service_host",
+ "corpus": [
+ "serves_valid_json_with_exact_caller_correlation_and_no_web_headers",
+ "rejects_oversized_and_malformed_json_before_the_handler",
+ "rejects_invalid_mutation_envelopes_duplicates_and_nested_null_before_dispatch",
+ "parameterized_routes_percent_decode_bounded_values_without_service_authority",
+ "caller_correlation_precedes_entropy_and_survives_timeout_handoff",
+ "rejects_http_1_0_before_dispatch",
+ "request_deadline_returns_a_safe_timeout_and_cancels_the_handler_future",
+ "enforces_header_query_response_and_body_correlation_boundaries",
+ "connection_admission_never_exceeds_the_configured_limit",
+ "graceful_cancellation_stops_admission_and_drains_an_active_request",
+ "linux_process_credentials_allow_uid_or_gid_and_deny_otherwise",
+ "exact_positive_boundaries_are_accepted_for_every_field",
+ "zero_and_just_over_maximum_fail_for_every_field"
+ ]
+ },
+ "invariants": {
+ "active_equals_final_inventory": true,
+ "all_models_referenced_exactly": true,
+ "authoritative_commit_owned_by_handler": true,
+ "cursor_authentication_owned_by_handler": true,
+ "peer_authorization_owned_by_shared_transport": true,
+ "adapter_performs_sqlite": false,
+ "adapter_performs_relay_io": false,
+ "adapter_performs_identity_mutation": false,
+ "raw_shared_transport_public": false,
+ "unbounded_resource": false
+ },
+ "deferred": [
+ "rcld_promotion",
+ "parent_pin_alignment",
+ "nix",
+ "oci",
+ "terminal_consumer_convergence",
+ "rcld-rshr-180",
+ "rcld-rshr-190"
+ ]
+}
diff --git a/src/admin_v1.rs b/src/admin_v1.rs
@@ -498,7 +498,7 @@ impl fmt::Display for RhiAdminRouterError {
impl Error for RhiAdminRouterError {}
-/// Opaque RHI v1 router capability through Step 208.
+/// Opaque final RHI v1 router capability through Step 209.
///
/// The underlying shared-host router remains an implementation detail. The
/// later runtime-composition checkpoint consumes this capability without
@@ -627,7 +627,7 @@ impl fmt::Display for RhiAdminServerError {
impl Error for RhiAdminServerError {}
-/// Unbound RHI Unix-admin server through Step 208.
+/// Unbound final RHI Unix-admin server through Step 209.
///
/// Construction projects only the already-admitted Rhi configuration, seals
/// the exact route inventory around the supplied domain handler, and uses the
@@ -679,7 +679,7 @@ impl fmt::Debug for RhiAdminServer {
}
}
-/// Bound RHI Unix-admin server through Step 208.
+/// Bound final RHI Unix-admin server through Step 209.
pub struct RhiBoundAdminServer {
inner: SharedAdminServer,
binding: UnixAdminSocketBinding,
@@ -704,7 +704,7 @@ impl fmt::Debug for RhiBoundAdminServer {
}
}
-/// Registers the final seven common and thirteen domain routes through Step 208.
+/// Registers the final seven common and thirteen domain routes through Step 209.
///
/// Live identity rekey and replace are absent by final offline-only policy.
pub fn build_rhi_admin_router<H>(handler: Arc<H>) -> Result<RhiAdminRouter, RhiAdminRouterError>
@@ -1919,6 +1919,7 @@ mod tests {
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod native {
+ use std::collections::BTreeMap;
use std::fs;
use std::path::Path;
use std::sync::Mutex;
@@ -1931,15 +1932,18 @@ mod tests {
const CONFIG: &str = include_str!("../contracts/services_hardening/config.v1.example.toml");
type FixtureCall = (RhiAdminRoute, Option<String>, Option<String>, Box<[u8]>);
+ type FixtureOperation = (RhiAdminRoute, Box<[u8]>);
struct FixtureHandler {
calls: Mutex<Vec<FixtureCall>>,
+ operations: Mutex<BTreeMap<String, FixtureOperation>>,
}
impl FixtureHandler {
fn new() -> Self {
Self {
calls: Mutex::new(Vec::new()),
+ operations: Mutex::new(BTreeMap::new()),
}
}
}
@@ -1947,11 +1951,6 @@ mod tests {
impl RhiAdminHandler for FixtureHandler {
fn handle<'a>(&'a self, request: RhiAdminRequestDocument) -> RhiAdminFuture<'a> {
Box::pin(async move {
- if request.operation_id() == Some("conflict") {
- return Err(RhiAdminHandlerError::new(
- RhiAdminHandlerErrorKind::OperationIdConflict,
- ));
- }
if request
.model_bytes()
.windows(15)
@@ -1961,6 +1960,23 @@ mod tests {
RhiAdminHandlerErrorKind::InvalidCursor,
));
}
+ if let Some(operation_id) = request.operation_id() {
+ let mut operations = self.operations.lock().expect("operations");
+ if let Some((route, request_bytes)) = operations.get(operation_id) {
+ if *route != request.route()
+ || request_bytes.as_ref() != request.model_bytes()
+ {
+ return Err(RhiAdminHandlerError::new(
+ RhiAdminHandlerErrorKind::OperationIdConflict,
+ ));
+ }
+ return Ok(response_document(request.route()));
+ }
+ operations.insert(
+ operation_id.to_owned(),
+ (request.route(), request.model_bytes().into()),
+ );
+ }
self.calls.lock().expect("calls").push((
request.route(),
request.operation_id().map(str::to_owned),
@@ -2026,23 +2042,29 @@ mod tests {
AdminClientTarget::new(format!("{path}?{}", serializer.finish())).expect("query target")
}
- async fn raw_post(socket: &Path, body: &str) -> String {
+ async fn raw_post(socket: &Path, path: &str, body: &str) -> String {
let mut stream = tokio::net::UnixStream::connect(socket)
.await
.expect("raw connection");
let request = format!(
- "POST /v1/state/backup HTTP/1.1\r\nHost: localhost\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}",
+ "POST {path} HTTP/1.1\r\nHost: localhost\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}",
body.len()
);
- stream
- .write_all(request.as_bytes())
- .await
- .expect("raw request");
+ match stream.write_all(request.as_bytes()).await {
+ Ok(()) => {}
+ Err(error)
+ if matches!(
+ error.kind(),
+ std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::ConnectionReset
+ ) => {}
+ Err(error) => panic!("raw request: {error}"),
+ }
let mut response = Vec::new();
- stream
- .read_to_end(&mut response)
- .await
- .expect("raw response");
+ match stream.read_to_end(&mut response).await {
+ Ok(_) => {}
+ Err(error) if error.kind() == std::io::ErrorKind::ConnectionReset => {}
+ Err(error) => panic!("raw response: {error}"),
+ }
String::from_utf8(response).expect("HTTP response")
}
@@ -2067,6 +2089,16 @@ mod tests {
let client =
AdminClient::new(&socket, AdminTransportLimits::DEFAULT).expect("admin client");
+ use std::os::unix::fs::PermissionsExt as _;
+ assert_eq!(
+ fs::metadata(&socket)
+ .expect("admin socket metadata")
+ .permissions()
+ .mode()
+ & 0o777,
+ 0o600
+ );
+
for (index, route) in RhiAdminRoute::ACTIVE.into_iter().enumerate() {
let request = sample_model(route.request_model());
let target = target_for(route, &request);
@@ -2085,17 +2117,31 @@ mod tests {
.expect("route response model");
}
- let conflict_target =
+ let replay_target =
AdminClientTarget::new("/v1/state/backup").expect("mutation target");
- let conflict_error = client
+ let replay_operation = AdminOperationId::new("operation-5").expect("operation ID");
+ let replay_request = sample_model("state_backup_request_v1");
+ client
.mutate::<_, Value>(
- &conflict_target,
- AdminOperationId::new("conflict").expect("operation ID"),
+ &replay_target,
+ replay_operation.clone(),
None,
- sample_model("state_backup_request_v1"),
+ &replay_request,
)
.await
- .expect_err("operation conflict");
+ .expect("exact operation replay");
+ let mut conflicting_request = replay_request;
+ conflicting_request
+ .as_object_mut()
+ .expect("backup request")
+ .insert(
+ "target_path".to_owned(),
+ Value::String("/tmp/different-backup".to_owned()),
+ );
+ let conflict_error = client
+ .mutate::<_, Value>(&replay_target, replay_operation, None, &conflicting_request)
+ .await
+ .expect_err("conflicting operation reuse");
assert_eq!(
conflict_error
.failure()
@@ -2110,10 +2156,42 @@ mod tests {
r#"{"contract_version":1,"operation_id":"duplicate","request":{"confirmation":"confirm","expected_generation":0,"expected_generation":1,"target_path":"/tmp/backup"}}"#,
r#"{"contract_version":1,"operation_id":"null","request":{"confirmation":"confirm","expected_generation":null,"target_path":"/tmp/backup"}}"#,
] {
- let response = raw_post(&socket, body).await;
+ let response = raw_post(&socket, "/v1/state/backup", body).await;
assert!(response.starts_with("HTTP/1.1 400 "), "{response}");
}
+ for (index, route) in RhiAdminRoute::ACTIVE
+ .into_iter()
+ .filter(|route| route.is_mutation())
+ .enumerate()
+ {
+ let request = sample_model(route.request_model());
+ for version in [0, 2] {
+ let body = serde_json::json!({
+ "contract_version": version,
+ "operation_id": format!("invalid-version-{index}-{version}"),
+ "request": request,
+ })
+ .to_string();
+ let response = raw_post(&socket, route.path(), &body).await;
+ assert!(response.starts_with("HTTP/1.1 400 "), "{response}");
+ }
+ let missing_version = serde_json::json!({
+ "operation_id": format!("missing-version-{index}"),
+ "request": request,
+ })
+ .to_string();
+ let response = raw_post(&socket, route.path(), &missing_version).await;
+ assert!(response.starts_with("HTTP/1.1 400 "), "{response}");
+ }
+
+ let request_limit =
+ usize::try_from(AdminTransportLimits::DEFAULT.request_body_utf8_bytes())
+ .expect("request limit");
+ let oversized = " ".repeat(request_limit + 1);
+ let response = raw_post(&socket, "/v1/state/backup", &oversized).await;
+ assert!(response.starts_with("HTTP/1.1 413 "), "{response}");
+
let domain_target = AdminClientTarget::new("/v1/reconciliation/jobs?limit=201")
.expect("bounded domain route");
assert!(client.get::<Value>(&domain_target).await.is_err());
@@ -2139,6 +2217,8 @@ mod tests {
.expect("trade route target");
assert!(client.get::<Value>(&invalid_trade).await.is_err());
+ assert!(AdminClientTarget::new("/v2/status").is_err());
+
for (index, path) in ["/v1/identity/rekey", "/v1/identity/replace"]
.into_iter()
.enumerate()
diff --git a/tests/package_boundary.rs b/tests/package_boundary.rs
@@ -7,6 +7,8 @@ const ROOT: &str = include_str!("../src/lib.rs");
const ADMIN: &str = include_str!("../src/admin_v1.rs");
const ADMIN_IDENTITY_OFFLINE_CONTRACT: &str =
include_str!("../contracts/services_hardening/admin_identity_offline.v1.json");
+const ADMIN_WAVE_QUALIFICATION_CONTRACT: &str =
+ include_str!("../contracts/services_hardening/admin_wave_qualification.v1.json");
const ADAPTERS: &str = include_str!("../src/adapters/mod.rs");
const NOSTR_ADAPTERS: &str = include_str!("../src/adapters/nostr/mod.rs");
const FEATURES: &str = include_str!("../src/features/mod.rs");
@@ -387,6 +389,12 @@ fn active_admin_boundary_hides_shared_transport_authority() {
assert_eq!(offline["final_inventory"]["route_count"], 20);
assert_eq!(offline["final_inventory"]["model_count"], 33);
assert_eq!(offline["identity_rotation"]["unix_admin_mutation"], false);
+ let qualification: serde_json::Value = serde_json::from_str(ADMIN_WAVE_QUALIFICATION_CONTRACT)
+ .expect("admin wave qualification contract");
+ assert_eq!(qualification["step"], 209);
+ assert_eq!(qualification["wave"], "130-b");
+ assert_eq!(qualification["final_inventory"]["route_count"], 20);
+ assert_eq!(qualification["final_inventory"]["model_count"], 33);
for forbidden in ["IdentityRekey", "IdentityReplace"] {
assert!(!ADMIN.contains(forbidden));
assert!(!PUBLIC_API.contains(forbidden));
@@ -1511,6 +1519,7 @@ fn readme_freezes_the_root_only_boundary_and_exact_baseline() {
"[`admin_common.v1.json`](contracts/services_hardening/admin_common.v1.json)",
"[`admin_domain.v1.json`](contracts/services_hardening/admin_domain.v1.json)",
"[`admin_identity_offline.v1.json`](contracts/services_hardening/admin_identity_offline.v1.json)",
+ "[`admin_wave_qualification.v1.json`](contracts/services_hardening/admin_wave_qualification.v1.json)",
] {
assert!(README.contains(required), "README is missing {required}");
}
diff --git a/tests/services_hardening_admin_wave_qualification.rs b/tests/services_hardening_admin_wave_qualification.rs
@@ -0,0 +1,182 @@
+#![forbid(unsafe_code)]
+
+use std::collections::BTreeSet;
+
+use rhi::RhiAdminRoute;
+use serde_json::{Value, json};
+
+const CONTRACT: &str =
+ include_str!("../contracts/services_hardening/admin_wave_qualification.v1.json");
+const OPERATOR_CONTRACT: &str =
+ include_str!("../contracts/services_hardening/operator_contract.v1.json");
+const ADMIN_SOURCE: &str = include_str!("../src/admin_v1.rs");
+const MANIFEST: &str = include_str!("../Cargo.toml");
+const ROOT_SOURCE: &str = include_str!("../src/lib.rs");
+
+#[test]
+fn step209_machine_contract_freezes_the_complete_admin_wave() {
+ let contract: Value = serde_json::from_str(CONTRACT).expect("qualification contract");
+ let operator: Value = serde_json::from_str(OPERATOR_CONTRACT).expect("operator contract");
+ assert_eq!(contract["schema"], "radroots.rhi.admin-wave-qualification");
+ assert_eq!(contract["schema_version"], 1);
+ assert_eq!(contract["contract_version"], 1);
+ assert_eq!(contract["step"], 209);
+ assert_eq!(contract["wave"], "130-b");
+ assert_eq!(contract["service"], "rhi");
+ assert_eq!(contract["final_inventory"]["route_count"], 20);
+ assert_eq!(contract["final_inventory"]["model_count"], 33);
+ assert_eq!(contract["final_inventory"]["common_route_count"], 7);
+ assert_eq!(contract["final_inventory"]["domain_route_count"], 13);
+
+ let routes = operator["admin"]["routes"].as_array().expect("routes");
+ let models = operator["admin"]["models"].as_object().expect("models");
+ assert_eq!(routes.len(), 20);
+ assert_eq!(models.len(), 33);
+ assert_eq!(RhiAdminRoute::ALL, RhiAdminRoute::ACTIVE);
+ assert_eq!(
+ RhiAdminRoute::COMMON
+ .into_iter()
+ .chain(RhiAdminRoute::DOMAIN)
+ .collect::<Vec<_>>(),
+ RhiAdminRoute::ALL
+ );
+ let referenced = routes
+ .iter()
+ .flat_map(|route| {
+ ["request_model", "response_model"]
+ .map(|field| route[field].as_str().expect("model reference"))
+ })
+ .collect::<BTreeSet<_>>();
+ assert_eq!(
+ referenced,
+ models.keys().map(String::as_str).collect::<BTreeSet<_>>()
+ );
+
+ assert_eq!(
+ contract["live_negative_matrix"],
+ json!({
+ "original_wire": [
+ "duplicate_request_field",
+ "nested_null",
+ "missing_contract_version"
+ ],
+ "version": [
+ "unknown_major_path",
+ "contract_version_zero_every_mutation",
+ "contract_version_two_every_mutation"
+ ],
+ "pagination": [
+ "noncanonical_limit",
+ "just_over_maximum_limit",
+ "duplicate_query_item",
+ "unknown_query_item",
+ "malformed_percent_encoding",
+ "noncanonical_cursor",
+ "handler_rejected_unbound_cursor"
+ ],
+ "idempotency": [
+ "exact_operation_replay_returns_success_without_second_commit",
+ "conflicting_operation_reuse_rejected"
+ ],
+ "peer": [
+ "owner_only_socket_mode",
+ "current_owner_round_trip",
+ "source_locked_linux_uid_or_gid_allow_and_other_deny"
+ ],
+ "removed_sensitive_routes": [
+ "identity_rekey_unavailable",
+ "identity_replace_unavailable"
+ ],
+ "resource": [
+ "request_body_just_over_limit",
+ "response_body_just_over_limit",
+ "page_limit_just_over_maximum",
+ "source_locked_header_query_connection_deadline_and_drain_limits"
+ ]
+ })
+ );
+}
+
+#[test]
+fn qualification_is_executable_source_locked_and_authority_safe() {
+ let contract: Value = serde_json::from_str(CONTRACT).expect("qualification contract");
+ for test in contract["component_corpus"]
+ .as_array()
+ .expect("component corpus")
+ {
+ let test = test.as_str().expect("test name");
+ assert!(
+ ADMIN_SOURCE.contains(test),
+ "missing component test `{test}`"
+ );
+ }
+
+ let revision = contract["source_locked_transport_evidence"]["revision"]
+ .as_str()
+ .expect("Lib revision");
+ assert_eq!(revision.len(), 40);
+ assert!(
+ revision
+ .bytes()
+ .all(|byte| byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase())
+ );
+ assert!(MANIFEST.contains(&format!("rev = \"{revision}\"")));
+ assert!(
+ MANIFEST
+ .contains("radroots_service_host = { git = \"https://github.com/radrootslabs/lib\"")
+ );
+ assert_eq!(
+ contract["source_locked_transport_evidence"]["corpus"],
+ json!([
+ "serves_valid_json_with_exact_caller_correlation_and_no_web_headers",
+ "rejects_oversized_and_malformed_json_before_the_handler",
+ "rejects_invalid_mutation_envelopes_duplicates_and_nested_null_before_dispatch",
+ "parameterized_routes_percent_decode_bounded_values_without_service_authority",
+ "caller_correlation_precedes_entropy_and_survives_timeout_handoff",
+ "rejects_http_1_0_before_dispatch",
+ "request_deadline_returns_a_safe_timeout_and_cancels_the_handler_future",
+ "enforces_header_query_response_and_body_correlation_boundaries",
+ "connection_admission_never_exceeds_the_configured_limit",
+ "graceful_cancellation_stops_admission_and_drains_an_active_request",
+ "linux_process_credentials_allow_uid_or_gid_and_deny_otherwise",
+ "exact_positive_boundaries_are_accepted_for_every_field",
+ "zero_and_just_over_maximum_fail_for_every_field"
+ ])
+ );
+
+ for invariant in [
+ "active_equals_final_inventory",
+ "all_models_referenced_exactly",
+ "authoritative_commit_owned_by_handler",
+ "cursor_authentication_owned_by_handler",
+ "peer_authorization_owned_by_shared_transport",
+ ] {
+ assert_eq!(contract["invariants"][invariant], true, "{invariant}");
+ }
+ for invariant in [
+ "adapter_performs_sqlite",
+ "adapter_performs_relay_io",
+ "adapter_performs_identity_mutation",
+ "raw_shared_transport_public",
+ "unbounded_resource",
+ ] {
+ assert_eq!(contract["invariants"][invariant], false, "{invariant}");
+ }
+ for forbidden in [
+ "IdentityRekey",
+ "IdentityReplace",
+ "pub fn into_inner",
+ "pub fn router",
+ "pub fn listener",
+ "sqlx::",
+ ] {
+ assert!(
+ !ADMIN_SOURCE.contains(forbidden),
+ "forbidden admin surface `{forbidden}`"
+ );
+ assert!(
+ !ROOT_SOURCE.contains(forbidden),
+ "forbidden root surface `{forbidden}`"
+ );
+ }
+}