feat: reconcile GLS registry into runtime manifest v2
This commit is contained in:
parent
b25597117c
commit
ca5d6f3f58
7 changed files with 4615 additions and 121 deletions
|
|
@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
const EMBEDDED_REGISTRY: &str = include_str!("../../contracts/gls-runtime-registry.json");
|
||||
const EXPECTED_SCHEMA: &str = "hololake.gls-protocol-runtime-registry/v1";
|
||||
const EXPECTED_SCHEMA: &str = "hololake.gls-runtime-manifest/v2";
|
||||
const EXPECTED_SOURCE_COMMIT: &str = "2598fbfba8caf64c7ab9740a3036c5aab977502e";
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
|
|
@ -15,6 +15,7 @@ struct GlsRuntimeRegistry {
|
|||
schema: String,
|
||||
source: GlsSource,
|
||||
compiler: GlsCompilerBoundary,
|
||||
reconciliation: GlsReconciliation,
|
||||
protocol_count: usize,
|
||||
executable_projection_count: usize,
|
||||
inventoried_not_executable_count: usize,
|
||||
|
|
@ -26,6 +27,14 @@ struct GlsSource {
|
|||
repository: String,
|
||||
commit: String,
|
||||
root: String,
|
||||
authority_files: Vec<GlsAuthorityFile>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
struct GlsAuthorityFile {
|
||||
authority_kind: String,
|
||||
source_path: String,
|
||||
source_sha256: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
|
|
@ -34,18 +43,51 @@ struct GlsCompilerBoundary {
|
|||
arbitrary_protocol_code_allowed: bool,
|
||||
executable_projection_requires_explicit_adapter: bool,
|
||||
unprojected_protocol_behavior: String,
|
||||
dependency_cycles: String,
|
||||
legacy_untyped_dependency_behavior: String,
|
||||
runtime_graph_source: String,
|
||||
runtime_dependency_cycles: String,
|
||||
unknown_protocol: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
struct GlsReconciliation {
|
||||
numbered_protocol_count: usize,
|
||||
protocol_registry_id_count: usize,
|
||||
existing_registered_count: usize,
|
||||
registered_draft_count: usize,
|
||||
registered_draft_not_started_count: usize,
|
||||
legacy_dependency_target_count: usize,
|
||||
dependencies_not_in_protocol_registry: Vec<String>,
|
||||
dependencies_without_numbered_source: Vec<String>,
|
||||
numbered_sources_not_in_protocol_registry: Vec<String>,
|
||||
legacy_dependency_cycles: Vec<Vec<String>>,
|
||||
discovered_unreconciled_count: usize,
|
||||
authority_conflict_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
struct GlsRegistration {
|
||||
state: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
struct GlsDependencyEdge {
|
||||
target: String,
|
||||
edge_kind: String,
|
||||
enters_runtime_graph: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
struct GlsProtocol {
|
||||
id: String,
|
||||
source_sha256: String,
|
||||
registration: GlsRegistration,
|
||||
projection_state: String,
|
||||
adapter: Option<String>,
|
||||
event_kinds: Vec<String>,
|
||||
dependencies: Vec<String>,
|
||||
dependency_edges: Vec<GlsDependencyEdge>,
|
||||
activation_blockers: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
|
|
@ -58,6 +100,14 @@ pub struct GlsProtocolRuntimeSnapshot {
|
|||
pub protocol_count: usize,
|
||||
pub executable_projection_count: usize,
|
||||
pub inventoried_not_executable_count: usize,
|
||||
pub protocol_registry_id_count: usize,
|
||||
pub registered_draft_count: usize,
|
||||
pub registered_draft_not_started_count: usize,
|
||||
pub legacy_dependency_target_count: usize,
|
||||
pub dependency_gap_count: usize,
|
||||
pub legacy_dependency_cycle_count: usize,
|
||||
pub discovered_unreconciled_count: usize,
|
||||
pub authority_conflict_count: usize,
|
||||
pub active_adapters: Vec<String>,
|
||||
pub raw_protocol_text_executed: bool,
|
||||
pub arbitrary_protocol_code_allowed: bool,
|
||||
|
|
@ -76,14 +126,34 @@ fn validate_registry(registry: &GlsRuntimeRegistry) -> Result<(), String> {
|
|||
|| registry.source.repository != "REPO-012"
|
||||
|| registry.source.commit != EXPECTED_SOURCE_COMMIT
|
||||
|| registry.source.root != "gls"
|
||||
|| registry.source.authority_files.len() != 4
|
||||
|| registry.compiler.raw_protocol_text_executed
|
||||
|| registry.compiler.arbitrary_protocol_code_allowed
|
||||
|| !registry
|
||||
.compiler
|
||||
.executable_projection_requires_explicit_adapter
|
||||
|| registry.compiler.unprojected_protocol_behavior != "INVENTORIED_NOT_EXECUTABLE"
|
||||
|| registry.compiler.dependency_cycles != "REJECT"
|
||||
|| registry.compiler.legacy_untyped_dependency_behavior
|
||||
!= "AUDIT_ONLY_BLOCKS_NEW_ACTIVATION"
|
||||
|| registry.compiler.runtime_graph_source != "EXPLICIT_EXECUTABLE_PROJECTIONS_ONLY"
|
||||
|| registry.compiler.runtime_dependency_cycles != "REJECT"
|
||||
|| registry.compiler.unknown_protocol != "FAIL_CLOSED"
|
||||
|| registry.reconciliation.numbered_protocol_count != registry.protocol_count
|
||||
|| registry.reconciliation.protocol_registry_id_count != 52
|
||||
|| registry.reconciliation.existing_registered_count != 19
|
||||
|| registry.reconciliation.registered_draft_count != 33
|
||||
|| registry.reconciliation.registered_draft_not_started_count != 21
|
||||
|| registry.reconciliation.legacy_dependency_target_count != 57
|
||||
|| registry.reconciliation.dependencies_not_in_protocol_registry.len() != 19
|
||||
|| registry.reconciliation.dependencies_without_numbered_source.len() != 24
|
||||
|| registry
|
||||
.reconciliation
|
||||
.numbered_sources_not_in_protocol_registry
|
||||
.len()
|
||||
!= 31
|
||||
|| registry.reconciliation.legacy_dependency_cycles.len() != 3
|
||||
|| registry.reconciliation.discovered_unreconciled_count != 0
|
||||
|| registry.reconciliation.authority_conflict_count != 0
|
||||
|| registry.protocol_count != registry.protocols.len()
|
||||
|| registry.protocol_count
|
||||
!= registry.executable_projection_count + registry.inventoried_not_executable_count
|
||||
|
|
@ -91,6 +161,20 @@ fn validate_registry(registry: &GlsRuntimeRegistry) -> Result<(), String> {
|
|||
return Err("HOLOLAKE_GLS_RUNTIME_BOUNDARY_INVALID".into());
|
||||
}
|
||||
|
||||
let mut authority_kinds = HashSet::new();
|
||||
for authority in ®istry.source.authority_files {
|
||||
if !authority_kinds.insert(authority.authority_kind.as_str())
|
||||
|| !authority.source_path.starts_with("gls/")
|
||||
|| authority.source_sha256.len() != 64
|
||||
|| !authority
|
||||
.source_sha256
|
||||
.chars()
|
||||
.all(|character| character.is_ascii_hexdigit())
|
||||
{
|
||||
return Err("HOLOLAKE_GLS_AUTHORITY_SOURCE_INVALID".into());
|
||||
}
|
||||
}
|
||||
|
||||
let mut ids = HashSet::new();
|
||||
let mut executable = 0;
|
||||
for protocol in ®istry.protocols {
|
||||
|
|
@ -109,6 +193,7 @@ fn validate_registry(registry: &GlsRuntimeRegistry) -> Result<(), String> {
|
|||
executable += 1;
|
||||
if protocol.adapter.as_deref().unwrap_or("").is_empty()
|
||||
|| protocol.event_kinds.is_empty()
|
||||
|| !protocol.activation_blockers.is_empty()
|
||||
{
|
||||
return Err("HOLOLAKE_GLS_EXECUTABLE_ADAPTER_REQUIRED".into());
|
||||
}
|
||||
|
|
@ -123,6 +208,33 @@ fn validate_registry(registry: &GlsRuntimeRegistry) -> Result<(), String> {
|
|||
}
|
||||
_ => return Err("HOLOLAKE_GLS_PROJECTION_STATE_UNKNOWN".into()),
|
||||
}
|
||||
if !matches!(
|
||||
protocol.registration.state.as_str(),
|
||||
"REGISTERED_PROTOCOL_REGISTRY" | "REGISTERED_OTHER_CANONICAL_INDEX"
|
||||
) {
|
||||
return Err("HOLOLAKE_GLS_REGISTRATION_NOT_RECONCILED".into());
|
||||
}
|
||||
for edge in &protocol.dependency_edges {
|
||||
match edge.edge_kind.as_str() {
|
||||
"RUNTIME_REQUIRES" if edge.enters_runtime_graph => {}
|
||||
"LEGACY_UNTYPED_REFERENCE" if !edge.enters_runtime_graph => {}
|
||||
_ => return Err("HOLOLAKE_GLS_DEPENDENCY_EDGE_INVALID".into()),
|
||||
}
|
||||
}
|
||||
let runtime_edges = protocol
|
||||
.dependency_edges
|
||||
.iter()
|
||||
.filter(|edge| edge.enters_runtime_graph)
|
||||
.map(|edge| edge.target.as_str())
|
||||
.collect::<HashSet<_>>();
|
||||
let runtime_dependencies = protocol
|
||||
.dependencies
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.collect::<HashSet<_>>();
|
||||
if runtime_edges != runtime_dependencies {
|
||||
return Err("HOLOLAKE_GLS_RUNTIME_DEPENDENCY_MISMATCH".into());
|
||||
}
|
||||
}
|
||||
if executable != registry.executable_projection_count {
|
||||
return Err("HOLOLAKE_GLS_EXECUTABLE_COUNT_MISMATCH".into());
|
||||
|
|
@ -212,6 +324,19 @@ pub async fn get_gls_protocol_runtime() -> Result<GlsProtocolRuntimeSnapshot, St
|
|||
protocol_count: registry.protocol_count,
|
||||
executable_projection_count: registry.executable_projection_count,
|
||||
inventoried_not_executable_count: registry.inventoried_not_executable_count,
|
||||
protocol_registry_id_count: registry.reconciliation.protocol_registry_id_count,
|
||||
registered_draft_count: registry.reconciliation.registered_draft_count,
|
||||
registered_draft_not_started_count: registry
|
||||
.reconciliation
|
||||
.registered_draft_not_started_count,
|
||||
legacy_dependency_target_count: registry.reconciliation.legacy_dependency_target_count,
|
||||
dependency_gap_count: registry
|
||||
.reconciliation
|
||||
.dependencies_without_numbered_source
|
||||
.len(),
|
||||
legacy_dependency_cycle_count: registry.reconciliation.legacy_dependency_cycles.len(),
|
||||
discovered_unreconciled_count: registry.reconciliation.discovered_unreconciled_count,
|
||||
authority_conflict_count: registry.reconciliation.authority_conflict_count,
|
||||
active_adapters,
|
||||
raw_protocol_text_executed: registry.compiler.raw_protocol_text_executed,
|
||||
arbitrary_protocol_code_allowed: registry.compiler.arbitrary_protocol_code_allowed,
|
||||
|
|
@ -228,6 +353,10 @@ mod tests {
|
|||
let registry = load_registry().unwrap();
|
||||
assert_eq!(registry.protocol_count, 75);
|
||||
assert_eq!(registry.executable_projection_count, 4);
|
||||
assert_eq!(registry.reconciliation.protocol_registry_id_count, 52);
|
||||
assert_eq!(registry.reconciliation.legacy_dependency_target_count, 57);
|
||||
assert_eq!(registry.reconciliation.legacy_dependency_cycles.len(), 3);
|
||||
assert_eq!(registry.reconciliation.authority_conflict_count, 0);
|
||||
assert!(!registry.compiler.raw_protocol_text_executed);
|
||||
assert!(!registry.compiler.arbitrary_protocol_code_allowed);
|
||||
}
|
||||
|
|
@ -249,4 +378,26 @@ mod tests {
|
|||
"HOLOLAKE_GLS_EXECUTABLE_ADAPTER_NOT_REGISTERED"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registration_and_dependency_manifest_tampering_fail_closed() {
|
||||
let mut registration_tamper = load_registry().unwrap();
|
||||
registration_tamper.reconciliation.authority_conflict_count = 1;
|
||||
assert_eq!(
|
||||
validate_registry(®istration_tamper).unwrap_err(),
|
||||
"HOLOLAKE_GLS_RUNTIME_BOUNDARY_INVALID"
|
||||
);
|
||||
|
||||
let mut dependency_tamper = load_registry().unwrap();
|
||||
let numbering = dependency_tamper
|
||||
.protocols
|
||||
.iter_mut()
|
||||
.find(|protocol| protocol.id == "GLS-0253")
|
||||
.unwrap();
|
||||
numbering.dependency_edges[0].edge_kind = "LEGACY_UNTYPED_REFERENCE".into();
|
||||
assert_eq!(
|
||||
validate_registry(&dependency_tamper).unwrap_err(),
|
||||
"HOLOLAKE_GLS_DEPENDENCY_EDGE_INVALID"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue