lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit 4f3b51fa899a4e91661eef0f5600f98ea7a5b99e
parent 4bd7642555edb7e4b82fb1167555b7ec088e03b6
Author: triesap <tyson@radroots.org>
Date:   Fri, 14 Aug 2026 21:34:57 +0000

contracts: govern sqlx sqlite access

- freeze SQLx as the sole high-level native Rust SQLite library
- bind one SQLx-owned native linkage and the sealed backup adapter
- record the exact temporary RCLD 045 dependency migration inventory
- validate workspace and package dependency ownership across aliases

Diffstat:
Mcontracts/releases/sqlite_runtime.toml | 31++++++++++++++++++++++++++++++-
Mtools/xtask/src/contract.rs | 212+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 238 insertions(+), 5 deletions(-)

diff --git a/contracts/releases/sqlite_runtime.toml b/contracts/releases/sqlite_runtime.toml @@ -1,4 +1,4 @@ -schema_version = 1 +schema_version = 2 [package] name = "libsqlite3-sys" @@ -12,3 +12,32 @@ route = [ "sqlx/sqlite-bundled", "libsqlite3-sys/bundled", ] + +[access] +high_level_library = "sqlx" +native_linkage_owner = "sqlx" +native_linkage_package = "libsqlite3-sys" +maximum_native_linkages = 1 +forbidden_high_level_dependencies = [ + "diesel", + "refinery", + "rusqlite", + "sea-orm", + "sqlite", + "sqlite3", +] + +[sealed_native_adapter] +owner_package = "radroots_service_sqlite" +relative_module = "crates/service_sqlite/src/sqlite_native_backup.rs" +capability = "incremental_online_backup" +status = "planned" + +[migration] +owner = "rcld-rshr-045" +status = "in_progress" +temporary_direct_dependencies = [ + "radroots_geonames:rusqlite", + "radroots_service_sqlite:rusqlite", + "workspace:rusqlite", +] diff --git a/tools/xtask/src/contract.rs b/tools/xtask/src/contract.rs @@ -1595,6 +1595,9 @@ struct SqliteRuntimeContract { schema_version: u32, package: SqliteRuntimePackage, activation: SqliteRuntimeActivation, + access: SqliteRuntimeAccess, + sealed_native_adapter: SqliteRuntimeSealedNativeAdapter, + migration: SqliteRuntimeMigration, } #[derive(Debug, Deserialize)] @@ -1614,6 +1617,33 @@ struct SqliteRuntimeActivation { #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] +struct SqliteRuntimeAccess { + high_level_library: String, + native_linkage_owner: String, + native_linkage_package: String, + maximum_native_linkages: u32, + forbidden_high_level_dependencies: Vec<String>, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct SqliteRuntimeSealedNativeAdapter { + owner_package: String, + relative_module: String, + capability: String, + status: String, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct SqliteRuntimeMigration { + owner: String, + status: String, + temporary_direct_dependencies: Vec<String>, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] struct ReleaseRecordChange { id: String, classification: String, @@ -4128,18 +4158,39 @@ fn validate_sqlite_runtime_contract(workspace_root: &Path) -> Result<(), String> "sqlx/sqlite-bundled", "libsqlite3-sys/bundled", ]; + const FORBIDDEN_HIGH_LEVEL_DEPENDENCIES: [&str; 6] = [ + "diesel", "refinery", "rusqlite", "sea-orm", "sqlite", "sqlite3", + ]; + const TEMPORARY_DIRECT_DEPENDENCIES: [&str; 3] = [ + "radroots_geonames:rusqlite", + "radroots_service_sqlite:rusqlite", + "workspace:rusqlite", + ]; let contract = parse_toml::<SqliteRuntimeContract>( &workspace_root.join(SQLITE_RUNTIME_CONTRACT_RELATIVE), )?; - if contract.schema_version != 1 + if contract.schema_version != 2 || contract.package.name != PACKAGE_NAME || contract.package.version != PACKAGE_VERSION || contract.package.source != PACKAGE_SOURCE || contract.activation.route != ACTIVATION_ROUTE + || contract.access.high_level_library != "sqlx" + || contract.access.native_linkage_owner != "sqlx" + || contract.access.native_linkage_package != PACKAGE_NAME + || contract.access.maximum_native_linkages != 1 + || contract.access.forbidden_high_level_dependencies != FORBIDDEN_HIGH_LEVEL_DEPENDENCIES + || contract.sealed_native_adapter.owner_package != "radroots_service_sqlite" + || contract.sealed_native_adapter.relative_module + != "crates/service_sqlite/src/sqlite_native_backup.rs" + || contract.sealed_native_adapter.capability != "incremental_online_backup" + || contract.sealed_native_adapter.status != "planned" + || contract.migration.owner != "rcld-rshr-045" + || contract.migration.status != "in_progress" + || contract.migration.temporary_direct_dependencies != TEMPORARY_DIRECT_DEPENDENCIES { return Err(format!( - "{SQLITE_RUNTIME_CONTRACT_RELATIVE} must govern the exact crates.io bundled SQLite runtime identity and activation route" + "{SQLITE_RUNTIME_CONTRACT_RELATIVE} must govern the exact SQLx access, native SQLite linkage, sealed-adapter, and RCLD-045 migration policy" )); } if contract.package.checksum.len() != 64 @@ -4171,10 +4222,22 @@ fn validate_sqlite_runtime_contract(workspace_root: &Path) -> Result<(), String> )); } + let native_linkages = lock + .package + .iter() + .filter(|package| package.name == contract.access.native_linkage_package) + .count(); + if native_linkages != contract.access.maximum_native_linkages as usize { + return Err(format!( + "Cargo.lock must contain exactly {} {} native linkage", + contract.access.maximum_native_linkages, contract.access.native_linkage_package + )); + } + let workspace = parse_toml::<WorkspaceCargoManifest>(&workspace_root.join("Cargo.toml"))?; - for member in workspace.workspace.members { + for member in &workspace.workspace.members { let package = - parse_toml::<PackageCargoManifest>(&workspace_root.join(&member).join("Cargo.toml"))?; + parse_toml::<PackageCargoManifest>(&workspace_root.join(member).join("Cargo.toml"))?; if package.package.name == PACKAGE_NAME { return Err(format!( "workspace member {member} must not vendor registry package {PACKAGE_NAME}" @@ -4182,6 +4245,34 @@ fn validate_sqlite_runtime_contract(workspace_root: &Path) -> Result<(), String> } } + let actual_temporary = sqlite_forbidden_direct_dependencies( + workspace_root, + &workspace.workspace.members, + &contract.access.forbidden_high_level_dependencies, + )?; + let expected_temporary = contract + .migration + .temporary_direct_dependencies + .iter() + .cloned() + .collect::<BTreeSet<_>>(); + if actual_temporary != expected_temporary { + return Err(format!( + "{SQLITE_RUNTIME_CONTRACT_RELATIVE} temporary direct dependency inventory is stale: expected {expected_temporary:?}, found {actual_temporary:?}" + )); + } + + let direct_native_dependencies = sqlite_direct_dependency_owners( + workspace_root, + &workspace.workspace.members, + PACKAGE_NAME, + )?; + if !direct_native_dependencies.is_empty() { + return Err(format!( + "direct {PACKAGE_NAME} access is forbidden while the sealed native adapter is planned: {direct_native_dependencies:?}" + )); + } + let storage_sqlite = fs::read_to_string(workspace_root.join("crates/storage_sqlite/Cargo.toml")) .map_err(|error| format!("read crates/storage_sqlite/Cargo.toml: {error}"))?; @@ -4207,6 +4298,90 @@ fn validate_sqlite_runtime_contract(workspace_root: &Path) -> Result<(), String> Ok(()) } +fn sqlite_forbidden_direct_dependencies( + workspace_root: &Path, + members: &[String], + forbidden: &[String], +) -> Result<BTreeSet<String>, String> { + let forbidden = forbidden + .iter() + .map(String::as_str) + .collect::<BTreeSet<_>>(); + sqlite_direct_dependencies_matching(workspace_root, members, |dependency| { + forbidden.contains(dependency) + }) +} + +fn sqlite_direct_dependency_owners( + workspace_root: &Path, + members: &[String], + dependency: &str, +) -> Result<BTreeSet<String>, String> { + sqlite_direct_dependencies_matching(workspace_root, members, |candidate| { + candidate == dependency + }) +} + +fn sqlite_direct_dependencies_matching( + workspace_root: &Path, + members: &[String], + matches: impl Fn(&str) -> bool, +) -> Result<BTreeSet<String>, String> { + let mut found = BTreeSet::new(); + let workspace_manifest = parse_toml::<toml::Value>(&workspace_root.join("Cargo.toml"))?; + if let Some(dependencies) = workspace_manifest + .get("workspace") + .and_then(|workspace| workspace.get("dependencies")) + .and_then(toml::Value::as_table) + { + collect_matching_dependencies("workspace", dependencies, &matches, &mut found); + } + + for member in members { + let path = workspace_root.join(member).join("Cargo.toml"); + let manifest = parse_toml::<toml::Value>(&path)?; + let owner = manifest + .get("package") + .and_then(|package| package.get("name")) + .and_then(toml::Value::as_str) + .ok_or_else(|| format!("{} package.name is required", path.display()))?; + for section in ["dependencies", "dev-dependencies", "build-dependencies"] { + if let Some(dependencies) = manifest.get(section).and_then(toml::Value::as_table) { + collect_matching_dependencies(owner, dependencies, &matches, &mut found); + } + } + if let Some(targets) = manifest.get("target").and_then(toml::Value::as_table) { + for target in targets.values().filter_map(toml::Value::as_table) { + for section in ["dependencies", "dev-dependencies", "build-dependencies"] { + if let Some(dependencies) = target.get(section).and_then(toml::Value::as_table) + { + collect_matching_dependencies(owner, dependencies, &matches, &mut found); + } + } + } + } + } + Ok(found) +} + +fn collect_matching_dependencies( + owner: &str, + dependencies: &toml::map::Map<String, toml::Value>, + matches: &impl Fn(&str) -> bool, + found: &mut BTreeSet<String>, +) { + for (alias, specification) in dependencies { + let package = specification + .as_table() + .and_then(|table| table.get("package")) + .and_then(toml::Value::as_str) + .unwrap_or(alias); + if matches(package) { + found.insert(format!("{owner}:{package}")); + } + } +} + fn validate_changelog_release_notes( workspace_root: &Path, contract_version: &str, @@ -9327,6 +9502,35 @@ mod tests { .expect("canonical workspace root") } + #[test] + fn sqlite_runtime_policy_matches_the_exact_migration_inventory() { + validate_sqlite_runtime_contract(&workspace_root()) + .expect("current SQLx and SQLite migration policy must validate"); + } + + #[test] + fn sqlite_dependency_inventory_uses_the_selected_package_not_its_alias() { + let manifest = toml::from_str::<toml::Value>( + r#"[dependencies] +database = { package = "rusqlite", version = "1" } +sqlx = { version = "1" } +"#, + ) + .expect("dependency fixture"); + let dependencies = manifest + .get("dependencies") + .and_then(toml::Value::as_table) + .expect("dependency table"); + let mut found = BTreeSet::new(); + collect_matching_dependencies( + "fixture", + dependencies, + &|dependency| dependency == "rusqlite", + &mut found, + ); + assert_eq!(found, BTreeSet::from(["fixture:rusqlite".to_string()])); + } + fn validate_generic_contract_bundle(bundle: &ContractBundle) -> Result<(), String> { validate_contract_bundle_with_release_policy_override_and_profile( bundle,