From 2ee0976c0394033bc9996e89e354369a63563c3b Mon Sep 17 00:00:00 2001 From: bordumb Date: Sat, 15 Aug 2026 06:40:34 +0100 Subject: [PATCH 01/61] fix(safety): close fail-open paths and freeze Rust-owned vocabulary Wave 1 of the v1.0 API hardening effort. See docs/target-state/v1-api-contract.md. Safety: - seal PeerObservation so downstream cannot assert an authenticated peer - deny a bounded budget ceiling when the action requests no budget - stop asserting effect=not-applied for failures that are genuinely possible - harden both stateful budget ledgers Ownership: - rename auths_production_client::RetryClass -> NextCall ("what do I call next") so it stops colliding with auths_errors::RetryClass ("may I retry") - rename VerifierContext -> TrustedContext - rename PlanBuilder::k_of_n -> threshold, matching what both bindings already say Facade: - remove the generic DomainProfile/DomainCommand/DeploymentAction re-export from auths-sdk; it laundered reference machinery into the product path - delete auths-deployment (zero source consumers; recoverable from bbeb654) 705 tests pass, clippy clean, fmt clean. semantic-freeze remains red by design. UNSIGNED: auths-sign needs an interactive passphrase and the claude-release agent env is absent on this host. Re-sign before pushing. Co-Authored-By: Claude Opus 5 --- Cargo.lock | 10 +- Cargo.toml | 2 - architecture.toml | 1 - architecture/dependency-graph.dot | 4 +- architecture/dependency-graph.json | 53 +- bindings/python/src/authoring.rs | 8 +- .../examples/generate-node-vectors.rs | 4 +- bindings/wasm/auths-proof-wasm/src/lib.rs | 8 +- compliance.toml | 21 +- core/crates/auths-author/src/lib.rs | 18 +- core/crates/auths-codec/src/decode.rs | 18 +- core/crates/auths-codec/src/encode.rs | 10 +- core/crates/auths-codec/src/hash.rs | 6 +- core/crates/auths-model/src/lib.rs | 8 +- core/crates/auths-proof/src/lib.rs | 6 +- core/crates/auths-verifier/src/lib.rs | 174 +- core/testkit/auths-testkit/src/conformance.rs | 8 +- core/testkit/auths-testkit/src/lib.rs | 28 +- demos/github-issue/src/fixture.rs | 4 +- demos/kubernetes-rollout/src/fixture.rs | 4 +- demos/radicle-issue/src/fixture.rs | 4 +- demos/rest-api-authorization/src/fixture.rs | 4 +- demos/stripe-payment-common/src/fixture.rs | 6 +- demos/stripe-refund/src/fixture.rs | 4 +- demos/testkit/auths-apps-testkit/src/lib.rs | 43 +- .../PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md | 1016 ++++++++++++ docs/target-state/v1-api-contract.md | 527 ++++++ .../v1-api-review-findings-bindings.md | 194 +++ .../v1-api-review-findings-phase0-final.md | 304 ++++ docs/target-state/v1-api-review-findings.md | 1477 +++++++++++++++++ product/README.md | 1 - product/config/auths-config/src/lib.rs | 18 +- product/docs/developer-integration.md | 2 +- .../integrations/auths-deployment/Cargo.toml | 16 - .../integrations/auths-deployment/src/lib.rs | 178 -- .../integrations/auths-github/src/ports.rs | 2 +- .../auths-operations/src/explanation.rs | 4 +- .../operations/auths-operations/src/lib.rs | 6 +- .../profiles/auths-profile-domains/Cargo.toml | 3 + .../profiles/auths-profile-domains/src/lib.rs | 45 +- product/receipts/auths-receipts/src/lib.rs | 8 +- .../runtime/auths-kernel-runtime/src/lib.rs | 12 +- product/runtime/auths-node/src/api.rs | 18 +- product/runtime/auths-node/src/profiles.rs | 172 +- product/runtime/auths-node/src/sandbox.rs | 25 +- .../auths-production-client/src/lib.rs | 277 +++- product/runtime/auths-runtime/src/lib.rs | 383 ++++- product/sdk/auths-sdk/src/lib.rs | 14 +- product/spec/v1/deployment-action.md | 2 +- product/stores/auths-stores/src/lib.rs | 46 +- .../docs-bundle/auths-docs-bundle-v1.tar.zst | Bin 0 -> 5080 bytes release/docs-bundle/contract.json | 478 ++++++ release/docs-bundle/manifest.json | 37 + release/docs-bundle/public-docs-report.json | 41 + release/docs-bundle/public-topology.json | 32 + release/docs-bundle/python-public-api.txt | 148 ++ release/docs-bundle/runtime-facts.json | 17 + release/docs-bundle/typescript-public-api.txt | 176 ++ scripts/delete-local-branches-except-main.sh | 30 + xtask/src/fixtures.rs | 16 +- 60 files changed, 5657 insertions(+), 524 deletions(-) create mode 100644 docs/target-state/PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md create mode 100644 docs/target-state/v1-api-contract.md create mode 100644 docs/target-state/v1-api-review-findings-bindings.md create mode 100644 docs/target-state/v1-api-review-findings-phase0-final.md create mode 100644 docs/target-state/v1-api-review-findings.md delete mode 100644 product/integrations/auths-deployment/Cargo.toml delete mode 100644 product/integrations/auths-deployment/src/lib.rs create mode 100644 release/docs-bundle/auths-docs-bundle-v1.tar.zst create mode 100644 release/docs-bundle/contract.json create mode 100644 release/docs-bundle/manifest.json create mode 100644 release/docs-bundle/public-docs-report.json create mode 100644 release/docs-bundle/public-topology.json create mode 100644 release/docs-bundle/python-public-api.txt create mode 100644 release/docs-bundle/runtime-facts.json create mode 100644 release/docs-bundle/typescript-public-api.txt create mode 100755 scripts/delete-local-branches-except-main.sh diff --git a/Cargo.lock b/Cargo.lock index 2e3ee918..b6acb7c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -451,15 +451,6 @@ dependencies = [ "sha2 0.10.9", ] -[[package]] -name = "auths-deployment" -version = "1.0.0-rc.1" -dependencies = [ - "auths-enforcement", - "auths-sdk", - "thiserror 2.0.19", -] - [[package]] name = "auths-did-keri" version = "1.0.0-rc.1" @@ -1024,6 +1015,7 @@ dependencies = [ "auths-model", "auths-profile-api", "auths-receipts", + "auths-registries", "auths-verifier", "hex", "serde", diff --git a/Cargo.toml b/Cargo.toml index d39b11cf..403df624 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,6 @@ members = [ "product/policy/auths-bounded-policy", "product/policy/auths-bounded-policy/fuzz", "product/integrations/auths-enforcement", - "product/integrations/auths-deployment", "product/integrations/auths-evidence-assemblers", "product/integrations/auths-custody", "product/integrations/auths-custody-aws-kms", @@ -187,7 +186,6 @@ auths-profile-kit = { version = "1.0.0-rc.1", path = "product/sdk/auths-profile- auths-errors = { version = "1.0.0-rc.1", path = "product/errors/auths-errors", default-features = false } auths-bounded-policy = { version = "1.0.0-rc.1", path = "product/policy/auths-bounded-policy", default-features = false } auths-enforcement = { version = "1.0.0-rc.1", path = "product/integrations/auths-enforcement" } -auths-deployment = { version = "1.0.0-rc.1", path = "product/integrations/auths-deployment" } auths-resolver-did-web = { version = "1.0.0-rc.1", path = "product/integrations/auths-resolver-did-web" } auths-evidence-assemblers = { version = "1.0.0-rc.1", path = "product/integrations/auths-evidence-assemblers" } auths-custody = { version = "1.0.0-rc.1", path = "product/integrations/auths-custody" } diff --git a/architecture.toml b/architecture.toml index 09dba310..ee2cb093 100644 --- a/architecture.toml +++ b/architecture.toml @@ -116,7 +116,6 @@ auths-config = "product" auths-custody = "product" auths-custody-aws-kms = "product" auths-custody-pkcs11 = "product" -auths-deployment = "product" auths-did-keri = "core" auths-did-key = "core" auths-did-web = "core" diff --git a/architecture/dependency-graph.dot b/architecture/dependency-graph.dot index 2fe03ea2..7a869e73 100644 --- a/architecture/dependency-graph.dot +++ b/architecture/dependency-graph.dot @@ -22,7 +22,6 @@ digraph auths_architecture { "auths-custody" [group="product"]; "auths-custody-aws-kms" [group="product"]; "auths-custody-pkcs11" [group="product"]; - "auths-deployment" [group="product"]; "auths-did-keri" [group="core"]; "auths-did-key" [group="core"]; "auths-did-web" [group="core"]; @@ -176,8 +175,6 @@ digraph auths_architecture { "auths-custody-aws-kms" -> "auths-model" [label="normal"]; "auths-custody-pkcs11" -> "auths-custody" [label="normal"]; "auths-custody-pkcs11" -> "auths-model" [label="normal"]; - "auths-deployment" -> "auths-enforcement" [label="normal"]; - "auths-deployment" -> "auths-sdk" [label="normal"]; "auths-did-keri" -> "auths-codec" [label="dev"]; "auths-did-keri" -> "auths-model" [label="normal"]; "auths-did-keri" -> "auths-ports" [label="normal"]; @@ -334,6 +331,7 @@ digraph auths_architecture { "auths-profile-domains" -> "auths-model" [label="normal"]; "auths-profile-domains" -> "auths-profile-api" [label="normal"]; "auths-profile-domains" -> "auths-receipts" [label="normal"]; + "auths-profile-domains" -> "auths-registries" [label="dev"]; "auths-profile-domains" -> "auths-verifier" [label="normal"]; "auths-profile-kit" -> "auths-codec" [label="normal"]; "auths-profile-kit" -> "auths-model" [label="normal"]; diff --git a/architecture/dependency-graph.json b/architecture/dependency-graph.json index 9c38e666..e16ddb82 100644 --- a/architecture/dependency-graph.json +++ b/architecture/dependency-graph.json @@ -111,11 +111,6 @@ "layer": "product", "path": "product/integrations/auths-custody-pkcs11" }, - { - "name": "auths-deployment", - "layer": "product", - "path": "product/integrations/auths-deployment" - }, { "name": "auths-did-keri", "layer": "core", @@ -2127,42 +2122,6 @@ "default_features": false, "features": [] }, - { - "source": "auths-deployment", - "source_layer": "product", - "target": "auths-enforcement", - "target_layer": "product", - "scope": "internal", - "kind": "normal", - "target_condition": null, - "optional": false, - "default_features": true, - "features": [] - }, - { - "source": "auths-deployment", - "source_layer": "product", - "target": "auths-sdk", - "target_layer": "product", - "scope": "internal", - "kind": "normal", - "target_condition": null, - "optional": false, - "default_features": true, - "features": [] - }, - { - "source": "auths-deployment", - "source_layer": "product", - "target": "thiserror", - "target_layer": null, - "scope": "external", - "kind": "normal", - "target_condition": null, - "optional": false, - "default_features": false, - "features": [] - }, { "source": "auths-did-keri", "source_layer": "core", @@ -6628,6 +6587,18 @@ "default_features": true, "features": [] }, + { + "source": "auths-profile-domains", + "source_layer": "product", + "target": "auths-registries", + "target_layer": "core", + "scope": "internal", + "kind": "dev", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, { "source": "auths-profile-domains", "source_layer": "product", diff --git a/bindings/python/src/authoring.rs b/bindings/python/src/authoring.rs index d3538848..5b2a67b9 100644 --- a/bindings/python/src/authoring.rs +++ b/bindings/python/src/authoring.rs @@ -21,8 +21,8 @@ use auths_model::{ PrincipalStatusSnapshot, PrincipalStatusStatement, ProfileId, ProfileRef, ProofRef, PurposeId, ResourceId, SignatureBytes, SignatureDescriptor, SignatureSuiteId, SignedAction, SignedGrant, SignedGrantStatus, SignedPrincipalStatus, StatusMethodId, StatusPolicy, StatusSnapshotId, - StatusTrustRule, Timestamp, TrustAnchor, TrustAnchorId, ValidityWindow, VerificationMethod, - VerifierConfigurationId, VerifierContext, VerifierLimits, + StatusTrustRule, Timestamp, TrustAnchor, TrustAnchorId, TrustedContext, ValidityWindow, + VerificationMethod, VerifierConfigurationId, VerifierLimits, }; use auths_ports::{PrincipalMethod, SignatureSuite}; use auths_profile_api::ActionProfile; @@ -628,7 +628,7 @@ impl PyAuthorizationPlanBuilder { members: Vec>, ) -> PyResult { build_plan(py, members, |builder, values| { - builder.k_of_n(required, values) + builder.threshold(required, values) }) } } @@ -930,7 +930,7 @@ fn status_snapshot( #[pyclass(name = "TrustedContext", frozen, module = "auths._native")] pub struct PyTrustedContext { - pub(crate) inner: VerifierContext, + pub(crate) inner: TrustedContext, } #[pymethods] diff --git a/bindings/wasm/auths-proof-wasm/examples/generate-node-vectors.rs b/bindings/wasm/auths-proof-wasm/examples/generate-node-vectors.rs index e3cdf281..12833a53 100644 --- a/bindings/wasm/auths-proof-wasm/examples/generate-node-vectors.rs +++ b/bindings/wasm/auths-proof-wasm/examples/generate-node-vectors.rs @@ -213,7 +213,7 @@ fn write_mcp_workflow_vectors(output: &std::path::Path) -> Result<(), Box &VerifierContext { + pub const fn context(&self) -> &TrustedContext { &self.context } } @@ -198,7 +198,7 @@ impl WorkflowProofBuilder { Ok(()) } - /// Assembles the proof and exact request-bound verifier context. + /// Assembles the proof and exact request-bound trusted context. /// /// # Errors /// @@ -207,7 +207,7 @@ impl WorkflowProofBuilder { &self, action: &SignedAction, canonical: &CanonicalAction, - context: &VerifierContext, + context: &TrustedContext, ) -> Result { let plan = AuthorizationPlan::proof(action.envelope().proof_ref()); let exact_plan = plan_id(&plan)?; @@ -670,12 +670,12 @@ impl<'a> PlanBuilder<'a> { /// # Errors /// /// Returns a typed shape or deployment-limit failure. - pub fn k_of_n( + pub fn threshold( &self, - k: u16, + required: u16, members: Vec, ) -> Result { - self.validate(AuthorizationPlan::k_of_n(k, members)?) + self.validate(AuthorizationPlan::k_of_n(required, members)?) } fn validate(&self, plan: AuthorizationPlan) -> Result { diff --git a/core/crates/auths-codec/src/decode.rs b/core/crates/auths-codec/src/decode.rs index 9b62f6c6..f6f03965 100644 --- a/core/crates/auths-codec/src/decode.rs +++ b/core/crates/auths-codec/src/decode.rs @@ -28,9 +28,9 @@ use auths_model::{ ProtocolVersion, PurposeId, RegistryManifestId, ResourceId, ResourceMatcherId, SignatureBytes, SignatureDescriptor, SignatureEnvelope, SignatureSuiteId, SignedAction, SignedGrant, SignedGrantStatus, SignedPrincipalStatus, StatementRef, StatusMethodId, StatusPolicy, - StatusSnapshotId, StatusTrustRule, Timestamp, TrustAnchor, TrustAnchorId, ValidityWindow, - VerificationCode, VerificationDecision, VerificationMethod, VerificationResources, - VerificationResultDigest, VerificationStage, VerifierConfigurationId, VerifierContext, + StatusSnapshotId, StatusTrustRule, Timestamp, TrustAnchor, TrustAnchorId, TrustedContext, + ValidityWindow, VerificationCode, VerificationDecision, VerificationMethod, + VerificationResources, VerificationResultDigest, VerificationStage, VerifierConfigurationId, VerifierLimits, }; use minicbor::{Decoder, data::Type}; @@ -1128,7 +1128,7 @@ fn verifier_limits(decoder: &mut V1Decoder<'_>) -> Result) -> Result { +fn context_from(decoder: &mut V1Decoder<'_>) -> Result { map(decoder, 14)?; key(decoder, 0)?; let limits = verifier_limits(decoder)?; @@ -1181,7 +1181,7 @@ fn context_from(decoder: &mut V1Decoder<'_>) -> Result Result Result { +pub fn decode_verifier_context(input: &[u8]) -> Result { if input.len() > auths_model::HARD_MAX_CONTEXT_BYTES { return Err(CodecError::LimitExceeded); } @@ -1835,7 +1835,7 @@ mod tests { .unwrap() } - fn minimal_context() -> VerifierContext { + fn minimal_context() -> TrustedContext { let profile = ProfileRef::new(ProfileId::parse("test").unwrap(), 1).unwrap(); let method = PrincipalMethodId::parse("did-key").unwrap(); let policy_id = AssurancePolicyId::parse("baseline").unwrap(); @@ -1874,7 +1874,7 @@ mod tests { StatusPolicy::ExpiryOnly, ) .unwrap(); - VerifierContext::new( + TrustedContext::new( VerifierConfigurationId::new(identifier(8)), CompositionRequirement::exact(PlanId::new(identifier(4))), vec![anchor], diff --git a/core/crates/auths-codec/src/encode.rs b/core/crates/auths-codec/src/encode.rs index bedf10d1..efbf86f0 100644 --- a/core/crates/auths-codec/src/encode.rs +++ b/core/crates/auths-codec/src/encode.rs @@ -14,8 +14,8 @@ use auths_model::{ PermissionSet, PortableVerificationResult, PrincipalState, PrincipalStatusSnapshot, PrincipalStatusStatement, ProfileRef, ProofBundle, SignatureDescriptor, SignatureEnvelope, SignedAction, SignedGrant, SignedGrantStatus, SignedPrincipalStatus, StatementRef, - StatusPolicy, StatusTrustRule, TrustAnchor, VerificationCode, VerificationDecision, - VerificationResources, VerificationStage, VerifierContext, VerifierLimits, + StatusPolicy, StatusTrustRule, TrustAnchor, TrustedContext, VerificationCode, + VerificationDecision, VerificationResources, VerificationStage, VerifierLimits, }; use minicbor::Encoder; @@ -1463,7 +1463,7 @@ fn encode_composition_requirement( fn encode_verifier_context_to( encoder: &mut V1Encoder, - context: &VerifierContext, + context: &TrustedContext, ) -> Result<(), CodecError> { map(encoder, 14)?; key(encoder, 0)?; @@ -1502,11 +1502,11 @@ fn encode_verifier_context_to( Ok(()) } -/// Encodes the deterministic public verifier-context projection. +/// Encodes the deterministic public trusted-context projection. /// /// # Errors /// /// Returns [`CodecError`] if the context exceeds a protocol encoding bound. -pub fn encode_verifier_context(context: &VerifierContext) -> Result, CodecError> { +pub fn encode_verifier_context(context: &TrustedContext) -> Result, CodecError> { finish(|encoder| encode_verifier_context_to(encoder, context)) } diff --git a/core/crates/auths-codec/src/hash.rs b/core/crates/auths-codec/src/hash.rs index b6068b4d..f4a09bc7 100644 --- a/core/crates/auths-codec/src/hash.rs +++ b/core/crates/auths-codec/src/hash.rs @@ -14,7 +14,7 @@ use auths_model::{ ActionEnvelope, ActionId, AttachmentDigest, ContextDigest, Digest, EvidenceId, EvidenceObject, GrantId, GrantStatement, GrantStatusId, GrantStatusStatement, PROTOCOL_V1, PlanId, PortableVerificationResult, PrincipalStatusId, PrincipalStatusStatement, ProofBundle, - SignatureDescriptor, VerificationResultDigest, VerifierContext, + SignatureDescriptor, TrustedContext, VerificationResultDigest, }; use sha2::{Digest as _, Sha256}; @@ -218,12 +218,12 @@ pub fn proof_digest(bundle: &ProofBundle) -> Result { Ok(raw_sha256(&encode_bundle(bundle)?)) } -/// Derives the deterministic public verifier-context digest. +/// Derives the deterministic public trusted-context digest. /// /// # Errors /// /// Returns [`CodecError`] if deterministic encoding of the context fails. -pub fn context_digest(context: &VerifierContext) -> Result { +pub fn context_digest(context: &TrustedContext) -> Result { Ok(ContextDigest::from_digest(domain_hash( IdentifierType::Context, &encode_verifier_context(context)?, diff --git a/core/crates/auths-model/src/lib.rs b/core/crates/auths-model/src/lib.rs index 7e5ecfe7..ca53fd26 100644 --- a/core/crates/auths-model/src/lib.rs +++ b/core/crates/auths-model/src/lib.rs @@ -3597,7 +3597,7 @@ fn canonical_registry_ids( Ok(identifiers) } -/// Exact immutable registry selection accepted by one verifier context. +/// Exact immutable registry selection accepted by one trusted context. #[derive(Clone, Debug, Eq, PartialEq)] pub struct AcceptedRegistries { manifest_id: RegistryManifestId, @@ -3844,7 +3844,7 @@ impl AcceptedRegistries { } #[derive(Clone, Debug, Eq, PartialEq)] -pub struct VerifierContext { +pub struct TrustedContext { configuration: VerifierConfigurationId, composition: CompositionRequirement, trust_anchors: Vec, @@ -3861,9 +3861,9 @@ pub struct VerifierContext { limits: VerifierLimits, } -impl VerifierContext { +impl TrustedContext { #[allow(clippy::too_many_arguments)] - /// Constructs an explicit, immutable verifier context. + /// Constructs an explicit, immutable trusted context. /// /// # Errors /// diff --git a/core/crates/auths-proof/src/lib.rs b/core/crates/auths-proof/src/lib.rs index 2f08e98f..ffc96509 100644 --- a/core/crates/auths-proof/src/lib.rs +++ b/core/crates/auths-proof/src/lib.rs @@ -41,8 +41,8 @@ pub use auths_verifier::{VerificationOutcome, VerifiedAction}; use auths_codec::{CodecError, decode_verification_result}; use auths_model::{ - CanonicalAction, PortableVerificationResult, Requirement, VerificationCode, - VerificationDecision, VerificationResources, VerificationStage, VerifierContext, + CanonicalAction, PortableVerificationResult, Requirement, TrustedContext, VerificationCode, + VerificationDecision, VerificationResources, VerificationStage, }; use auths_registries::ImmutableRegistries; @@ -99,7 +99,7 @@ impl<'a> Engine<'a> { &self, proof_cbor: &[u8], canonical_action: &CanonicalAction, - context: &VerifierContext, + context: &TrustedContext, ) -> VerificationOutcome { auths_verifier::verify(proof_cbor, canonical_action, context, &self.registries) } diff --git a/core/crates/auths-verifier/src/lib.rs b/core/crates/auths-verifier/src/lib.rs index 83b8e4c8..56dcfcea 100644 --- a/core/crates/auths-verifier/src/lib.rs +++ b/core/crates/auths-verifier/src/lib.rs @@ -26,8 +26,8 @@ use auths_model::{ EvidenceObject, GrantId, GrantStatusId, ParticipantAssurance, ParticipantRole, PlanId, PortableVerificationResult, PrincipalId, PrincipalStatusId, ProofBundle, ProofRef, Requirement, SignatureEnvelope, SignedAction, SignedGrant, StatementRef, StatusPolicy, Timestamp, - TrustAnchor, VerificationCode, VerificationDecision, VerificationResources, VerificationStage, - VerifierConfigurationId, VerifierContext, + TrustAnchor, TrustedContext, VerificationCode, VerificationDecision, VerificationResources, + VerificationStage, VerifierConfigurationId, }; use auths_ports::{ ControlEvidence, ControlPurpose, PrincipalControlError, PrincipalControlInput, ProfileDecision, @@ -345,7 +345,7 @@ impl VerifiedAction { self.proof_digest } - /// Returns the public verifier-context digest. + /// Returns the public trusted-context digest. #[must_use] pub const fn context_digest(&self) -> ContextDigest { self.context_digest @@ -393,7 +393,7 @@ impl VerifiedAction { pub fn verify( proof_bytes: &[u8], canonical_action: &CanonicalAction, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, ) -> VerificationOutcome { verify_internal( @@ -416,7 +416,7 @@ pub fn verify( pub fn verify_explained( proof_bytes: &[u8], canonical_action: &CanonicalAction, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, ) -> Result { let mut trace = TraceCollector::collect(trace::HARD_MAX_TRACE_EVENTS)?; @@ -516,7 +516,7 @@ fn failure_fact_kind(failure: VerificationFailure) -> FactKind { } fn record_configuration_facts( - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, trace: &mut TraceCollector, ) -> (Option, Option) { @@ -579,7 +579,7 @@ fn record_failure( fn verify_internal( proof_bytes: &[u8], canonical_action: &CanonicalAction, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, trace: &mut TraceCollector, ) -> VerificationOutcome { @@ -971,7 +971,7 @@ pub fn verify_v1_sealed( pub fn verify_portable( proof_bytes: &[u8], canonical_action: &CanonicalAction, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, ) -> PortableVerificationResult { verify_portable_sealed(proof_bytes, canonical_action, context, registries).0 @@ -981,7 +981,7 @@ pub fn verify_portable( fn verify_portable_sealed( proof_bytes: &[u8], canonical_action: &CanonicalAction, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, ) -> (PortableVerificationResult, Option>) { let action_bytes = encode_canonical_action(canonical_action).unwrap_or_default(); @@ -1229,7 +1229,7 @@ fn finalize_portable(result: PortableVerificationResult) -> PortableVerification /// or over-limit bytes. pub fn decode_proof( proof_bytes: &[u8], - context: &VerifierContext, + context: &TrustedContext, ) -> Result { let bundle = decode_bundle(proof_bytes, context.limits()).map_err(codec_failure)?; let digest = proof_digest(&bundle).map_err(codec_failure)?; @@ -1247,7 +1247,7 @@ pub fn decode_proof( /// ambiguous, or unused critical references. pub fn resolve_proof( decoded: DecodedProof, - context: &VerifierContext, + context: &TrustedContext, ) -> Result { let bundle = decoded.bundle(); let computed_plan_id = plan_id(bundle.plan()).map_err(codec_failure)?; @@ -1350,7 +1350,7 @@ pub fn resolve_proof( } fn require_expected_plan( - context: &VerifierContext, + context: &TrustedContext, actual: PlanId, ) -> Result<(), VerificationFailure> { if context @@ -1375,7 +1375,7 @@ fn require_expected_plan( /// before the configured maximum can be exceeded. pub fn verify_principal_control( resolved: ResolvedProof, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, ) -> Result { if context.accepted_registries().manifest_id() != registries.manifest_id() { @@ -1481,7 +1481,7 @@ pub fn verify_principal_control( fn verify_status_controls( bundle: &ProofBundle, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, meter: &mut WorkMeter, controls: &mut Vec, @@ -1536,7 +1536,7 @@ fn verify_status_controls( pub fn verify_authority( controlled: ControlVerifiedProof, canonical_action: &CanonicalAction, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, ) -> Result { let mut meter = WorkMeter::from_used(context.limits().max_work_units(), controlled.work_units); @@ -1554,7 +1554,7 @@ pub fn verify_authority( fn verify_authority_measured( controlled: ControlVerifiedProof, canonical_action: &CanonicalAction, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, meter: &mut WorkMeter, diagnostics: &mut AuthorityDiagnostics, @@ -1681,7 +1681,7 @@ fn codec_failure(error: CodecError) -> VerificationFailure { } fn principal_status_records( - context: &VerifierContext, + context: &TrustedContext, ) -> Result, VerificationFailure> { let mut values = Vec::new(); for status in context.principal_status_snapshot().statements() { @@ -1695,7 +1695,7 @@ fn principal_status_records( } fn grant_status_records( - context: &VerifierContext, + context: &TrustedContext, ) -> Result, VerificationFailure> { let mut values = Vec::new(); for status in context.grant_status_snapshot().statements() { @@ -1763,7 +1763,7 @@ fn reject_duplicate_attachments(bundle: &ProofBundle) -> Result<(), Verification fn validate_carried_status( bundle: &ProofBundle, - context: &VerifierContext, + context: &TrustedContext, ) -> Result<(), VerificationFailure> { if bundle.principal_status().iter().any(|carried| { context @@ -1815,7 +1815,7 @@ fn verify_signed( purpose: ControlPurpose, asserted_signing_time: Timestamp, bundle: &ProofBundle, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, meter: &mut WorkMeter, ) -> Result { @@ -1950,7 +1950,7 @@ fn signature_failure(_error: SignatureError) -> VerificationFailure { fn validate_action_binding( controlled: &ControlVerifiedProof, canonical: &CanonicalAction, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, meter: &mut WorkMeter, ) -> Result<(), VerificationFailure> { @@ -2031,7 +2031,7 @@ fn validate_action_binding( fn validate_attachments( bundle: &ProofBundle, canonical: &CanonicalAction, - context: &VerifierContext, + context: &TrustedContext, ) -> Result<(), VerificationFailure> { let descriptors = bundle .actions() @@ -2131,7 +2131,7 @@ fn same_shared_action( fn verify_branch( controlled: &ControlVerifiedProof, proof_ref: ProofRef, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, meter: &mut WorkMeter, ) -> Result< @@ -2193,7 +2193,7 @@ fn verify_branch_from_anchor( chain: &[&SignedGrant], root_control: (&PrincipalId, &ControlEvidence), anchor: &TrustAnchor, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, meter: &mut WorkMeter, ) -> Result<(Vec, Vec), VerificationFailure> { @@ -2343,7 +2343,7 @@ fn participant_report( fn evaluate_extensions( extensions: &auths_model::CriticalExtensions, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, meter: &mut WorkMeter, ) -> Result<(), VerificationFailure> { @@ -2373,7 +2373,7 @@ fn check_principal_status( controlled: &ControlVerifiedProof, policy: &StatusPolicy, principal: &PrincipalId, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, meter: &mut WorkMeter, ) -> Result<(), VerificationFailure> { @@ -2412,7 +2412,7 @@ fn check_grant_status( controlled: &ControlVerifiedProof, policy: &StatusPolicy, grant_id: GrantId, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, meter: &mut WorkMeter, ) -> Result<(), VerificationFailure> { @@ -2477,7 +2477,7 @@ fn validate_resource_constraints( anchor: &TrustAnchor, chain: &[&SignedGrant], action: &SignedAction, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, meter: &mut WorkMeter, ) -> Result<(), VerificationFailure> { @@ -2512,7 +2512,7 @@ fn validate_budget_constraints( anchor: &TrustAnchor, chain: &[&SignedGrant], action: &SignedAction, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, meter: &mut WorkMeter, ) -> Result<(), VerificationFailure> { @@ -2540,7 +2540,17 @@ fn validate_budget_constraints( } parent = child; } - if let (Some(ceiling), Some(requested)) = (parent, action.envelope().requested_budget()) { + if let Some(ceiling) = parent { + // A bounded terminal authority requires a bounded request. An action + // that declares no budget is NOT vacuously covered: it would spend an + // unbounded amount under a ceiling that the verifier could never + // compare against, so it is denied. + let requested = action + .envelope() + .requested_budget() + .ok_or(VerificationFailure::Denied( + DenialReason::BudgetCeilingExceeded, + ))?; let algebra = registries .budget_algebra(context.accepted_registries(), ceiling.algebra()) .ok_or(VerificationFailure::Indeterminate( @@ -2561,7 +2571,7 @@ fn validate_budget_constraints( fn validate_assurance_claims( reports: &[ParticipantAssurance], - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, meter: &mut WorkMeter, ) -> Result<(), VerificationFailure> { @@ -2596,13 +2606,13 @@ mod tests { use auths_model::{ AcceptedRegistries, AssuranceClaimId, AssurancePolicy, AssurancePolicyId, AssuranceQuantifier, AssuranceRequirement, Audience, AudienceSet, AuthorizationPlan, - BundleHeader, CapabilityId, Challenge, ChannelBindingId, CompositionRequirement, - ControlBinding, CriticalExtensions, EvidenceId, EvidenceTypeId, GrantStatusSnapshot, - MediaType, ParticipantRole, Permission, PermissionSet, PrincipalMethodId, - PrincipalStatusSnapshot, ProfileId, ProfilePolicyId, ProfileRef, RegistryManifestId, - ResourceId, SignatureBytes, SignatureDescriptor, SignatureSuiteId, SignedAction, - StatusSnapshotId, Timestamp, TrustAnchorId, ValidityWindow, VerificationMethod, - VerifierLimits, + BudgetCeiling, BundleHeader, CapabilityId, Challenge, ChannelBindingId, + CompositionRequirement, ControlBinding, CriticalExtensions, EvidenceId, EvidenceTypeId, + GrantStatusSnapshot, MediaType, ParticipantRole, Permission, PermissionSet, + PrincipalMethodId, PrincipalStatusSnapshot, ProfileId, ProfilePolicyId, ProfileRef, + RegistryManifestId, ResourceId, SignatureBytes, SignatureDescriptor, SignatureSuiteId, + SignedAction, StatusSnapshotId, Timestamp, TrustAnchorId, ValidityWindow, + VerificationMethod, VerifierLimits, }; use auths_raw_key::{ RAW_KEY_MEDIA_TYPE, RAW_KEY_V1, RawKeyDescriptor, RawKeyMethod, RawKeyType, @@ -2644,11 +2654,26 @@ mod tests { struct Fixture { bytes: Vec, canonical: CanonicalAction, - context: VerifierContext, + context: TrustedContext, } - #[allow(clippy::too_many_lines)] fn target_fixture(mutate_signature: bool) -> Fixture { + target_fixture_with_budget(mutate_signature, None, None) + } + + fn numeric_ceiling(value: u64) -> BudgetCeiling { + BudgetCeiling::new( + auths_model::BudgetAlgebraId::parse("numeric-ceiling-v1").unwrap(), + value, + ) + } + + #[allow(clippy::too_many_lines)] + fn target_fixture_with_budget( + mutate_signature: bool, + anchor_ceiling: Option, + requested_budget: Option, + ) -> Fixture { let signing_key = SigningKey::from_bytes(&[11; 32]); let descriptor = RawKeyDescriptor::new( RawKeyType::Ed25519, @@ -2667,7 +2692,7 @@ mod tests { MediaType::parse("application/vnd.auths.mcp-call.v1+cbor").unwrap(), body.clone(), permission.clone(), - None, + requested_budget.clone(), ) .unwrap(); let proof_ref = ProofRef::new([1; 32]); @@ -2680,7 +2705,7 @@ mod tests { canonical.media_type().clone(), body_digest(&body), permission.clone(), - None, + requested_budget, Audience::parse("mcp://reports").unwrap(), challenge, ValidityWindow::new(Timestamp::new(10), Timestamp::new(20)).unwrap(), @@ -2774,7 +2799,7 @@ mod tests { ], Vec::new(), vec![auths_model::ResourceMatcherId::parse("uri-namespace-v1").unwrap()], - Vec::new(), + vec![auths_model::BudgetAlgebraId::parse("numeric-ceiling-v1").unwrap()], Vec::new(), vec![profile.clone()], vec![ProfilePolicyId::parse("exact-v1").unwrap()], @@ -2789,7 +2814,7 @@ mod tests { vec![ResourceId::parse("mcp://reports").unwrap()], AudienceSet::new(vec![Audience::parse("mcp://reports").unwrap()]).unwrap(), ValidityWindow::new(Timestamp::new(0), Timestamp::new(100)).unwrap(), - None, + anchor_ceiling, 4, policy_id, StatusPolicy::ExpiryOnly, @@ -2802,7 +2827,7 @@ mod tests { let configuration = ImmutableRegistries::new(&methods, &suites) .unwrap() .configuration_id(); - let context = VerifierContext::new( + let context = TrustedContext::new( configuration, CompositionRequirement::exact(computed_plan), vec![anchor], @@ -2861,6 +2886,65 @@ mod tests { assert_eq!(action.authorized_branches(), &[ProofRef::new([1; 32])]); } + fn verify_budget_fixture( + anchor_ceiling: Option, + requested_budget: Option, + ) -> VerificationOutcome { + let fixture = target_fixture_with_budget(false, anchor_ceiling, requested_budget); + let method = RawKeyMethod::new().unwrap(); + let suite = Ed25519Suite::new().unwrap(); + let methods: [&dyn auths_ports::PrincipalMethod; 1] = [&method]; + let suites: [&dyn auths_ports::SignatureSuite; 1] = [&suite]; + let registries = ImmutableRegistries::new(&methods, &suites).unwrap(); + verify( + &fixture.bytes, + &fixture.canonical, + &fixture.context, + ®istries, + ) + } + + /// Regression: a bounded authority used to be satisfied by an action that + /// declared no budget at all, because the terminal coverage check was + /// gated on `(Some(ceiling), Some(requested))`. An unbounded request under + /// a bounded ceiling now denies. + #[test] + fn bounded_ceiling_denies_an_action_that_requests_no_budget() { + assert_eq!( + verify_budget_fixture(Some(numeric_ceiling(10_000)), None), + VerificationOutcome::Denied(DenialReason::BudgetCeilingExceeded) + ); + } + + #[test] + fn bounded_ceiling_authorizes_a_request_within_the_ceiling() { + let outcome = + verify_budget_fixture(Some(numeric_ceiling(10_000)), Some(numeric_ceiling(10_000))); + assert!( + matches!(outcome, VerificationOutcome::Authorized(_)), + "expected an in-ceiling request to authorize: {outcome:?}" + ); + } + + #[test] + fn bounded_ceiling_denies_a_request_over_the_ceiling() { + assert_eq!( + verify_budget_fixture(Some(numeric_ceiling(10_000)), Some(numeric_ceiling(10_001))), + VerificationOutcome::Denied(DenialReason::BudgetCeilingExceeded) + ); + } + + /// An unbounded authority is still the top scope: it does not require the + /// action to declare a budget. + #[test] + fn unbounded_authority_still_authorizes_an_action_without_a_budget() { + let outcome = verify_budget_fixture(None, None); + assert!( + matches!(outcome, VerificationOutcome::Authorized(_)), + "expected an unbounded authority to authorize: {outcome:?}" + ); + } + #[test] fn work_reservation_enforces_exact_boundary_and_overflow() { let mut meter = WorkMeter::new(10); diff --git a/core/testkit/auths-testkit/src/conformance.rs b/core/testkit/auths-testkit/src/conformance.rs index e3609858..f86e38ce 100644 --- a/core/testkit/auths-testkit/src/conformance.rs +++ b/core/testkit/auths-testkit/src/conformance.rs @@ -13,7 +13,7 @@ use auths_did_key::DidKeyMethod; use auths_hsm_attested::HsmAttestedMethod; use auths_model::{ CompositionRequirement, DenialReason, EvidenceId, EvidenceObject, EvidenceTypeId, MediaType, - ModelError, PrincipalStatusSnapshot, Requirement, Timestamp, VerifierContext, + ModelError, PrincipalStatusSnapshot, Requirement, Timestamp, TrustedContext, }; use auths_ports::{ ControlPurpose, PrincipalControlError, PrincipalControlInput, PrincipalMethod, SignatureSuite, @@ -351,12 +351,12 @@ fn work_limit_minus_one() -> CorpusFixture { } fn rebuild_context( - context: &VerifierContext, + context: &TrustedContext, composition: CompositionRequirement, anchors: Vec, principal_status: Option, -) -> Result { - VerifierContext::new( +) -> Result { + TrustedContext::new( context.configuration(), composition, anchors, diff --git a/core/testkit/auths-testkit/src/lib.rs b/core/testkit/auths-testkit/src/lib.rs index 26be1fb4..7bc07fec 100644 --- a/core/testkit/auths-testkit/src/lib.rs +++ b/core/testkit/auths-testkit/src/lib.rs @@ -30,8 +30,8 @@ use auths_model::{ PurposeId, RegistryManifestId, Requirement, ResourceId, ResourceMatcherId, SignatureBytes, SignatureDescriptor, SignatureEnvelope, SignatureSuiteId, SignedAction, SignedGrant, SignedGrantStatus, SignedPrincipalStatus, StatementRef, StatusMethodId, StatusPolicy, - StatusSnapshotId, Timestamp, TrustAnchor, TrustAnchorId, ValidityWindow, VerificationMethod, - VerifierConfigurationId, VerifierContext, VerifierLimits, + StatusSnapshotId, Timestamp, TrustAnchor, TrustAnchorId, TrustedContext, ValidityWindow, + VerificationMethod, VerifierConfigurationId, VerifierLimits, }; use auths_multikey::{Multikey, MultikeyType}; use auths_raw_key::{RAW_KEY_MEDIA_TYPE, RAW_KEY_V1, RawKeyDescriptor, RawKeyType}; @@ -754,7 +754,7 @@ fn anchor(identity: &Identity, depth: u16) -> TrustAnchor { anchor_with_status(identity, depth, StatusPolicy::ExpiryOnly) } -fn context(identities: &[Identity], anchors: Vec) -> VerifierContext { +fn context(identities: &[Identity], anchors: Vec) -> TrustedContext { context_with_assurance(identities, anchors, assurance_policy(identities)) } @@ -762,8 +762,8 @@ fn context_with_assurance( identities: &[Identity], anchors: Vec, assurance: AssurancePolicy, -) -> VerifierContext { - VerifierContext::new( +) -> TrustedContext { + TrustedContext::new( corpus_configuration_id(), CompositionRequirement::new(None, 1, 1, 1).expect("baseline composition"), anchors, @@ -896,7 +896,7 @@ fn fixture( name: &'static str, class: &'static str, bundle: &ProofBundle, - context: &VerifierContext, + context: &TrustedContext, canonical_action: CanonicalAction, expected: Expected, ) -> CorpusFixture { @@ -2282,7 +2282,7 @@ fn status_fixture(name: &'static str, variation: StatusVariation) -> CorpusFixtu } else { StatusPolicy::ExpiryOnly }; - let verifier_context = VerifierContext::new( + let verifier_context = TrustedContext::new( corpus_configuration_id(), CompositionRequirement::new(None, 1, 1, 1).expect("baseline composition"), vec![anchor_with_status(&identities[0], 1, anchor_policy)], @@ -2451,7 +2451,7 @@ fn principal_status_selection_fixture( let status = signed_principal_status(issuer, statement); let status_id = principal_status_id(status.statement()).expect("status ID"); let action_id = action_id(action.envelope()).expect("action ID"); - let context = VerifierContext::new( + let context = TrustedContext::new( corpus_configuration_id(), CompositionRequirement::new(None, 1, 1, 1).expect("baseline composition"), vec![anchor_with_status(&root, 1, required_status(METHOD))], @@ -3256,7 +3256,7 @@ fn replace_context( expected: Expected, ) -> CorpusFixture { let context = decode_context(&fixture); - let limited = VerifierContext::new( + let limited = TrustedContext::new( context.configuration(), context.composition(), context.trust_anchors().to_vec(), @@ -3763,7 +3763,7 @@ pub fn verifier_configuration_mismatch() -> CorpusFixture { fixture } -fn decode_context(fixture: &CorpusFixture) -> VerifierContext { +fn decode_context(fixture: &CorpusFixture) -> TrustedContext { auths_codec::decode_verifier_context(fixture.context_bytes()) .expect("repository-owned canonical context") } @@ -3795,13 +3795,13 @@ fn accepted_from( } fn context_replacement( - source: &VerifierContext, + source: &TrustedContext, anchors: Vec, accepted: AcceptedRegistries, resource_matcher: ResourceMatcherId, profile_policy: ProfilePolicyId, -) -> VerifierContext { - VerifierContext::new( +) -> TrustedContext { + TrustedContext::new( source.configuration(), source.composition(), anchors, @@ -3822,7 +3822,7 @@ fn context_replacement( fn registry_semantics_fixture( name: &'static str, - mutate: impl FnOnce(&VerifierContext) -> VerifierContext, + mutate: impl FnOnce(&TrustedContext) -> TrustedContext, expected: Expected, ) -> CorpusFixture { let mut fixture = raw_key_chain(); diff --git a/demos/github-issue/src/fixture.rs b/demos/github-issue/src/fixture.rs index 8bcbb873..8b23e3ca 100644 --- a/demos/github-issue/src/fixture.rs +++ b/demos/github-issue/src/fixture.rs @@ -16,7 +16,7 @@ use auths_model::{ PermissionSet, PrincipalId, PrincipalMethodId, PrincipalStatusSnapshot, ProfilePolicyId, ProofBundle, ProofRef, ResourceId, ResourceMatcherId, SignatureBytes, SignatureDescriptor, SignatureSuiteId, StatementRef, StatusPolicy, StatusSnapshotId, Timestamp, TrustAnchor, - TrustAnchorId, ValidityWindow, VerificationMethod, VerifierContext, VerifierLimits, + TrustAnchorId, TrustedContext, ValidityWindow, VerificationMethod, VerifierLimits, }; use auths_ports::{PrincipalMethod, SignatureSuite}; use auths_profile_api::ActionProfile as _; @@ -294,7 +294,7 @@ fn authorization_fixture( vec![ProfilePolicyId::parse(PROFILE_POLICY).map_err(|_| ProofError::Adapter)?], ) .map_err(|_| ProofError::Adapter)?; - let context = VerifierContext::new( + let context = TrustedContext::new( configuration, CompositionRequirement::exact(plan_identifier), vec![anchor], diff --git a/demos/kubernetes-rollout/src/fixture.rs b/demos/kubernetes-rollout/src/fixture.rs index 8c358607..e46de39b 100644 --- a/demos/kubernetes-rollout/src/fixture.rs +++ b/demos/kubernetes-rollout/src/fixture.rs @@ -12,7 +12,7 @@ use auths_model::{ PrincipalId, PrincipalMethodId, PrincipalStatusSnapshot, ProfilePolicyId, ProofBundle, ProofRef, ResourceId, ResourceMatcherId, SignatureBytes, SignatureDescriptor, SignatureSuiteId, StatementRef, StatusPolicy, StatusSnapshotId, Timestamp, TrustAnchor, TrustAnchorId, - ValidityWindow, VerificationMethod, VerifierContext, VerifierLimits, + TrustedContext, ValidityWindow, VerificationMethod, VerifierLimits, }; use auths_ports::{PrincipalMethod, SignatureSuite}; use auths_profile_api::ActionProfile as _; @@ -221,7 +221,7 @@ pub fn authorization_fixture( vec![ProfilePolicyId::parse(PROFILE_POLICY).unwrap()], ) .unwrap(); - let context = VerifierContext::new( + let context = TrustedContext::new( verifier_configuration(), CompositionRequirement::exact(plan_identifier), vec![anchor], diff --git a/demos/radicle-issue/src/fixture.rs b/demos/radicle-issue/src/fixture.rs index 26727c38..e97fb05e 100644 --- a/demos/radicle-issue/src/fixture.rs +++ b/demos/radicle-issue/src/fixture.rs @@ -11,7 +11,7 @@ use auths_model::{ PrincipalId, PrincipalMethodId, PrincipalStatusSnapshot, ProfilePolicyId, ProofBundle, ProofRef, ResourceId, ResourceMatcherId, SignatureBytes, SignatureDescriptor, SignatureSuiteId, StatementRef, StatusPolicy, StatusSnapshotId, Timestamp, TrustAnchor, TrustAnchorId, - ValidityWindow, VerificationMethod, VerifierContext, VerifierLimits, + TrustedContext, ValidityWindow, VerificationMethod, VerifierLimits, }; use auths_ports::{PrincipalMethod, SignatureSuite}; use auths_profile_api::ActionProfile as _; @@ -242,7 +242,7 @@ pub(crate) fn authorization_fixture_with_seeds( vec![ProfilePolicyId::parse(PROFILE_POLICY).unwrap()], ) .unwrap(); - let context = VerifierContext::new( + let context = TrustedContext::new( configuration, CompositionRequirement::exact(plan_identifier), vec![anchor], diff --git a/demos/rest-api-authorization/src/fixture.rs b/demos/rest-api-authorization/src/fixture.rs index 244b4747..b1ba5a31 100644 --- a/demos/rest-api-authorization/src/fixture.rs +++ b/demos/rest-api-authorization/src/fixture.rs @@ -13,7 +13,7 @@ use auths_model::{ PrincipalId, PrincipalMethodId, PrincipalStatusSnapshot, ProfilePolicyId, ProofBundle, ProofRef, ResourceId, ResourceMatcherId, SignatureBytes, SignatureDescriptor, SignatureSuiteId, StatementRef, StatusPolicy, StatusSnapshotId, Timestamp, TrustAnchor, TrustAnchorId, - ValidityWindow, VerificationMethod, VerifierContext, VerifierLimits, + TrustedContext, ValidityWindow, VerificationMethod, VerifierLimits, }; use auths_ports::{PrincipalMethod, SignatureSuite}; use auths_profile_api::ActionProfile; @@ -227,7 +227,7 @@ pub fn authorization_fixture( vec![ProfilePolicyId::parse(PROFILE_POLICY).unwrap()], ) .unwrap(); - let context = VerifierContext::new( + let context = TrustedContext::new( verifier_configuration(), CompositionRequirement::exact(plan_identifier), vec![anchor], diff --git a/demos/stripe-payment-common/src/fixture.rs b/demos/stripe-payment-common/src/fixture.rs index 3023dd88..979a5d3b 100644 --- a/demos/stripe-payment-common/src/fixture.rs +++ b/demos/stripe-payment-common/src/fixture.rs @@ -10,8 +10,8 @@ use auths_model::{ GrantStatusSnapshot, MediaType, ParticipantRole, PermissionSet, PrincipalId, PrincipalMethodId, PrincipalStatusSnapshot, ProfilePolicyId, ProofBundle, ProofRef, ResourceId, ResourceMatcherId, SignatureBytes, SignatureDescriptor, SignatureSuiteId, StatementRef, StatusPolicy, - StatusSnapshotId, Timestamp, TrustAnchor, TrustAnchorId, ValidityWindow, VerificationMethod, - VerifierContext, VerifierLimits, + StatusSnapshotId, Timestamp, TrustAnchor, TrustAnchorId, TrustedContext, ValidityWindow, + VerificationMethod, VerifierLimits, }; use auths_ports::{PrincipalMethod, SignatureSuite}; use auths_raw_key::{RAW_KEY_MEDIA_TYPE, RAW_KEY_V1, RawKeyDescriptor, RawKeyMethod, RawKeyType}; @@ -234,7 +234,7 @@ pub fn authorization_fixture( vec![ProfilePolicyId::parse(PROFILE_POLICY).expect("static profile policy")], ) .expect("static accepted registries"); - let context = VerifierContext::new( + let context = TrustedContext::new( verifier_configuration(), CompositionRequirement::exact(plan_identifier), vec![anchor], diff --git a/demos/stripe-refund/src/fixture.rs b/demos/stripe-refund/src/fixture.rs index 20091059..5f664f10 100644 --- a/demos/stripe-refund/src/fixture.rs +++ b/demos/stripe-refund/src/fixture.rs @@ -11,7 +11,7 @@ use auths_model::{ PrincipalId, PrincipalMethodId, PrincipalStatusSnapshot, ProfilePolicyId, ProofBundle, ProofRef, ResourceId, ResourceMatcherId, SignatureBytes, SignatureDescriptor, SignatureSuiteId, StatementRef, StatusPolicy, StatusSnapshotId, Timestamp, TrustAnchor, TrustAnchorId, - ValidityWindow, VerificationMethod, VerifierContext, VerifierLimits, + TrustedContext, ValidityWindow, VerificationMethod, VerifierLimits, }; use auths_ports::{PrincipalMethod, SignatureSuite}; use auths_profile_api::ActionProfile as _; @@ -218,7 +218,7 @@ pub fn authorization_fixture( vec![ProfilePolicyId::parse(PROFILE_POLICY).unwrap()], ) .unwrap(); - let context = VerifierContext::new( + let context = TrustedContext::new( configuration, CompositionRequirement::exact(plan_identifier), vec![anchor], diff --git a/demos/testkit/auths-apps-testkit/src/lib.rs b/demos/testkit/auths-apps-testkit/src/lib.rs index 7ddab67c..1ad35210 100644 --- a/demos/testkit/auths-apps-testkit/src/lib.rs +++ b/demos/testkit/auths-apps-testkit/src/lib.rs @@ -18,8 +18,8 @@ use auths_model::{ ParticipantRole, Permission, PermissionSet, PrincipalMethodId, PrincipalStatusSnapshot, ProfilePolicyId, ProofBundle, ProofRef, RegistryManifestId, ResourceMatcherId, SignatureBytes, SignatureDescriptor, SignatureSuiteId, StatementRef, StatusPolicy, StatusSnapshotId, Timestamp, - TrustAnchor, TrustAnchorId, ValidityWindow, VerificationMethod, VerifierConfigurationId, - VerifierContext, VerifierLimits, + TrustAnchor, TrustAnchorId, TrustedContext, ValidityWindow, VerificationMethod, + VerifierConfigurationId, VerifierLimits, }; use auths_profile_api::ActionProfile; use auths_profile_mcp::{McpProfile, McpToolCall}; @@ -127,13 +127,17 @@ impl McpToolExecutor for StaticReportExecutor { async fn execute( &self, action: ExecutableAction, - ) -> Result, String> { + ) -> Result, auths_runtime::ToolExecutionFailure> { let command = action.command(); if command.name() != "read_report" || command.arguments().get("name") != Some(&Value::String("q3".into())) || action.lease().challenge() != self.expected_challenge { - return Err("verified report command is outside the demo policy".into()); + // Rejected by local policy: the report was never read, so the + // definite non-effect claim is provable. + return Err(auths_runtime::ToolExecutionFailure::before_provider_entry( + "verified report command is outside the demo policy", + )); } self.executions.fetch_add(1, Ordering::SeqCst); Ok(br#"{"name":"q3","status":"approved"}"#.to_vec()) @@ -218,7 +222,7 @@ struct DemoFixture { body: Vec, canonical_action: Vec, proof: Vec, - context: VerifierContext, + context: TrustedContext, root_principal: String, } @@ -260,17 +264,18 @@ pub fn demo_fixture_bytes_for_challenge(nonce: [u8; 32]) -> DemoFixtureBytes { /// assertion fails. pub async fn run_memory_demo() -> DemoResult { let fixture = build_fixture(DEMO_CHALLENGE, None); - let (service, executor, receipts) = demo_service( - fixture.context, - ChannelBindingPolicy::RequireAuthenticatedPeer, - None, - ); + // The in-process reference channel authenticates no peer. It therefore + // runs under `ChannelBindingPolicy::None` and reports the truth. It used + // to claim `AuthenticatedOpaque { kind: "memory-demo" }` under + // `RequireAuthenticatedPeer`, which is exactly the forged transport + // assertion the runtime now refuses. The authenticated-transport path is + // covered by `run_iroh_demo` and + // `authenticated_transport_does_not_upgrade_bad_proof`. + let (service, executor, receipts) = + demo_service(fixture.context, ChannelBindingPolicy::None, None); let (mut client, mut server) = channel_pair( - PeerObservation::ServerAuthenticated, - PeerObservation::AuthenticatedOpaque { - kind: "memory-demo".into(), - identifier: vec![1], - }, + PeerObservation::Unauthenticated, + PeerObservation::Unauthenticated, ); let server_service = service.clone(); let server_task = tokio::spawn(async move { @@ -642,7 +647,7 @@ fn demo_call() -> McpToolCall { } fn demo_service( - context: VerifierContext, + context: TrustedContext, channel_policy: ChannelBindingPolicy, local_endpoint: Option<[u8; 32]>, ) -> ( @@ -654,7 +659,7 @@ fn demo_service( } fn demo_service_with_challenge( - context: VerifierContext, + context: TrustedContext, channel_policy: ChannelBindingPolicy, local_endpoint: Option<[u8; 32]>, challenge: ChallengeNonce, @@ -878,7 +883,7 @@ pub fn exact_action_fixture( vec![ProfilePolicyId::parse("exact-v1").unwrap()], ) .unwrap(); - let context = VerifierContext::new( + let context = TrustedContext::new( demo_configuration_id(), CompositionRequirement::exact(plan_id(proof.plan()).unwrap()), vec![anchor], @@ -1041,7 +1046,7 @@ fn build_fixture(challenge: ChallengeNonce, signed_permission: Option`, `DomainCommand`, + canonicalization, and verified-command decoding. Determine which symbols + belong in the advanced Rust reference tier; do not assume they must be + deleted merely because the bindings should be smaller. +- `product/profiles/auths-profile-domains/src/lib.rs:271-867` — generic HTTP, + Git, deployment, supply-chain, and edge action family. +- `product/profiles/auths-profile-domains/src/lib.rs:954-1002` — generic + reference canonicalizers. +- `product/sdk/auths-sdk/src/lib.rs:26-29` — Rust SDK re-exports generic domain + profile types. +- `product/integrations/auths-deployment/src/lib.rs:7-83` — production + integration consumes the generic deployment profile. +- `product/sdk/auths-profile-kit/src/lib.rs:1-120` — generic profile fixture and + mutation tooling; classify which pieces are neutral test mechanisms and which + encourage generic shipping semantics. +- `bindings/wasm/auths-proof-wasm/src/lib.rs:37` — imports generic Rust domain + profiles. +- `bindings/wasm/auths-proof-wasm/src/lib.rs:2802-2928` — exposes generic HTTP, + Git, deployment, supply-chain, and edge parsers to bindings. +- `bindings/typescript/src/profiles/application/index.ts:37-86` — generic + profile factory/runtime registration. +- `bindings/typescript/src/profiles/application/index.ts:342-430` — generic + `ApplicationProfile`. +- `bindings/typescript/src/profiles/application/index.ts:577-583` — public + `defineProfile` factory. +- `bindings/typescript/src/profiles/domains/index.ts:1-133` — generic domain + action, command, gateway, authority, receipt, and error families. +- `bindings/typescript/src/profiles/domains/index.ts:136-219` — runtime-generated + generic profiles. +- `bindings/python/python/auths/_application_profile.py:70-180` — generic + canonical action, definition, request, and application types. +- `bindings/python/python/auths/_application_profile.py:732-870` — generic + `ApplicationProfile` and `define_profile`. +- `bindings/python/src/http.rs:15-660` — native Python HTTP binding over the + generic Rust profile. +- `bindings/python/src/domains.rs:2-107` — native Python edge/domain parser + binding. + +### Qualified production client and duplicated profile registries + +- `product/runtime/auths-production-client/src/lib.rs:6-9` — canonical product + client version and byte limits. +- `product/runtime/auths-production-client/src/lib.rs:129-207` — Rust product + verbs and qualified profile enumeration. +- `bindings/typescript/src/profiles.ts:4-22` — separately hardcoded TypeScript + qualified profile list. +- `bindings/typescript/src/production-client.ts:364-379` — second hardcoded + TypeScript profile list and route mapping. +- `bindings/python/python/auths/profiles/__init__.py:24-45` — separately + hardcoded Python qualified profile list. +- `bindings/python/python/auths/_production_client.py:375-413` — second + hardcoded Python profile list and route mapping. +- `bindings/wasm/auths-proof-wasm/src/lib.rs:31-70` — WASM projection of the + Rust production client contract. + +### Error, telemetry, and support-bundle duplication + +- `bindings/typescript/src/product-errors.ts:1-18` — known error definitions + widened to arbitrary strings. +- `bindings/typescript/src/product-errors.ts:111-159` — `auths.support/2` + support bundle. +- `bindings/typescript/src/observability.ts:3-68` — `auths.telemetry/2` events. +- `bindings/typescript/src/observability.ts:70-110` — a second support bundle + named `createSupportBundle`, reporting `auths.support/1`. +- `bindings/python/python/auths/_product_errors.py:13` — error codes collapse to + `str`. +- `bindings/python/python/auths/_product_errors.py:166-199` — + `auths.support/2` support bundle. +- `bindings/python/python/auths/_observability.py:29-79` — Python telemetry + model. +- `bindings/python/python/auths/_observability.py:82-109` — incompatible + `auths.python-support-bundle/1` support bundle. +- `product/runtime/auths-production-client/src/lib.rs:11-127` — Rust-owned + operational event validation and projection. + +### Time, failure translation, and state transitions + +- `bindings/typescript/src/profiles/mcp/index.ts:589-623` — direct wall-clock + acquisition and broad failure replacement during MCP authorization. +- `bindings/typescript/src/profiles/application/index.ts:397-405` and + `bindings/typescript/src/profiles/application/index.ts:605` — broad exception + replacement and direct wall-clock acquisition. +- `bindings/python/python/auths/_application_profile.py:158-172` and + `bindings/python/python/auths/_application_profile.py:744-750` — implicit + clock and broad exception replacement. +- `bindings/python/python/auths/profiles/_mcp.py:1043-1069` — direct time use in + receipt preparation. +- `bindings/typescript/src/production-client.ts:269-301` — all transport + exceptions collapse to one indeterminate result, while malformed response + cases follow inconsistent throw/result paths. +- `bindings/python/python/auths/_production_client.py:272-303` — same broad + production transport collapse. +- `bindings/typescript/src/internal/development-store-node.ts:66-167` — + file-backed development execution and receipt state. +- `bindings/python/python/auths/integrations.py:163-290` — matching Python + file-backed development state. + +### Demo private dependencies and generated browser artifacts + +- `demos/cross-company-incident-response/agent-service/auths_incident_agent/incident.py:6-15` + — imports private approval, application-profile, bootstrap, receipt, and + workflow modules. +- `demos/cross-company-incident-response/agent-service/auths_incident_agent/execution.py:13-21` + — imports private error, application-profile, receipt, and runtime modules. +- `demos/cross-company-incident-response/agent-service/auths_incident_agent/sdk.py:9-11` + — imports private lifecycle, native, and runtime modules. +- `demos/cross-company-incident-response/agent-service/auths_incident_agent/domain_profile.py:8-16` + — constructs a generic private edge profile. +- `demos/cross-company-incident-response/control-room/src/app.ts:1-3` — current + public SDK imports used by the browser app. +- Every `.d.ts` file below each of: + `demos/cross-company-incident-response/control-room/public/vendor/`, + `public/vendor-v2/`, and `public/vendor-v3/`. +- `bindings/typescript/wasm/auths_proof_wasm.d.ts:1` — current binding + declaration to compare against the vendored artifact. + +### Large and dead source + +- `bindings/wasm/auths-proof-wasm/src/lib.rs` — approximately 5,484 lines. +- `core/crates/auths-model/src/lib.rs` — approximately 5,091 lines. +- `core/testkit/auths-testkit/src/lib.rs` — approximately 4,772 lines. +- `core/crates/auths-verifier/src/lib.rs` — approximately 3,466 lines. +- `core/crates/auths-codec/src/decode.rs` — approximately 2,160 lines. +- `bindings/python/python/auths/_workflow.py` — approximately 1,799 lines. +- `bindings/python/python/auths/profiles/_mcp.py` — approximately 1,671 lines. +- `bindings/typescript/src/profiles/application/index.ts` — approximately + 1,169 lines. +- `demos/stripe-subscription-create/src/receipts.rs:108-110` and + `demos/stripe-subscription-modify/src/receipts.rs:108-110` — explicit dead + canonicalization helpers. + +The inventory must also record every consumer discovered beyond this seed list. +The list above is a starting point, not permission to ignore additional +references. + +## Target architecture + +### One product API per language + +The supported public topology must be: + +```text +TypeScript + @auths-dev/sdk production client and shared result types + @auths-dev/sdk/profiles closed qualified profile selectors/types + @auths-dev/sdk/verify offline verification and receipt inspection + @auths-dev/sdk/identity standalone identity functionality + @auths-dev/sdk/integrations explicitly labelled development compositions + @auths-dev/sdk/framework narrow proven ports only + @auths-dev/sdk/testkit conformance helpers only + +Python + auths production client and shared result types + auths.profiles closed qualified profile selectors/types + auths.verify offline verification and receipt inspection + auths.identity standalone identity functionality + auths.integrations explicitly labelled development compositions + auths.framework narrow proven ports only + auths.testkit conformance helpers only +``` + +The root product API is the remote production client contract currently owned +by Rust `auths-production-client`. The local MCP composition remains available +only as `development.createAuths` / `development.create_auths` under the +integration entry point. It must not define a second root-level `Auths`, +`Authority`, `Receipt`, `ExecutionResult`, or `createAuths` vocabulary. + +There must be no overloaded constructor that guesses which product is wanted +by testing whether an object contains an `endpoint` property. + +### One source of production profile truth + +`product/runtime/auths-production-client/src/lib.rs::QualifiedProfile` owns the +closed production profile set, stable identifiers, and execution routes. + +TypeScript and Python must not maintain hand-written duplicate arrays or route +maps. Export the Rust registry through the existing WASM/native boundaries: + +```rust +pub struct QualifiedProfileDescriptor { + pub id: &'static str, + pub execute_path: &'static str, +} + +pub const fn qualified_profiles() -> &'static [QualifiedProfileDescriptor]; +``` + +Add bounded ABI functions that return the descriptor set in deterministic +order. Generate language type declarations from that output at package-build +time and compare the generated files byte-for-byte in CI. Runtime routing must +use the Rust-owned descriptor, not a second language-owned switch. + +### Profile-specific product verticals and a broader Rust reference tier + +Every shipping effect must be owned by its concrete Rust product integration: + +- OpenTofu semantics: `product/integrations/auths-opentofu/`; +- PostgreSQL semantics: `product/integrations/auths-postgresql/`; +- GitHub semantics: `product/integrations/auths-github/`; +- Kubernetes semantics: `product/integrations/auths-kubernetes/`; +- Radicle semantics: `product/integrations/auths-radicle/`; +- Stripe semantics: profile-specific modules under + `product/integrations/auths-stripe/`; +- records API semantics: `product/integrations/auths-records-api/`. + +Deterministic portions of `auths-profile-domains` may remain as an advanced Rust +reference API if they satisfy the reference-tier criteria above. They must not +become a generic product executor or acquire credentials, provider I/O, mutable +state, retry policy, reconciliation, or receipt meaning. + +Do not expose generic domain/profile machinery through TypeScript, Python, or +the default Rust product facade merely for symmetry. Do not replace it with +another generic action enum, operation tag, callback registry, or +optional-field carrier. + +The current edge incident profile has no qualified closed product vertical. For +this cutover, create one complete profile-specific package only if the demo is +intended to remain a product-shaped reference. Suggested package: + +```text +product/integrations/auths-edge-incident/ +``` + +It must own the exact edge action, policy, evaluator, evidence, verified +command, state transition, credential port, provider request, reconciliation, +receipts, stable codes, fixtures, mutation tests, and demo service boundary. +Do not merely move the generic `EdgeAction` into a differently named crate. + +If that complete vertical is outside the intended product, remove the custom +edge authorization path from the incident-response demo and rebase the demo on +an already qualified vertical. Do not retain the generic application-profile +framework solely to keep this demo working. + +### One operational contract + +Rust owns these final prelaunch schemas: + +```text +auths.telemetry/1 +auths.error/1 +auths.support/1 +``` + +Because the project is prelaunch, reset the final telemetry and support schema +numbers to `1`; do not retain `/2` readers or aliases. + +`auths.support/1` must contain: + +- SDK and runtime identity; +- ABI/contract version; +- semantic subject; +- qualified profiles; +- capabilities; +- a bounded, sorted list of sanitized `auths.error/1` values; and +- an optional bounded, sorted timeline of `auths.telemetry/1` events. + +Rust validates and deterministically projects the complete bundle. TypeScript +and Python provide idiomatic wrappers over that projection. Delete the separate +telemetry support-bundle builder, the product-error support-bundle builder, and +the Python-specific support-bundle schema after the single implementation is +in place. + +### Explicit time boundary + +Stateful product code receives time through an explicit clock port: + +```text +Clock.nowSeconds() -> unsigned integer seconds +Clock.nowMilliseconds() -> monotonic or wall value only where named explicitly +``` + +Requirements: + +- The offline core continues to receive evaluation time as explicit input. +- Production server composition owns the trusted wall clock. +- Development composition supplies a system clock by default and permits a + deterministic test clock. +- Profile canonicalization must not read time. +- Authorization, signing expiry, approval expiry, receipt observation, and + lifecycle transitions receive a captured timestamp explicitly. +- One operation captures time once per semantic stage; it must not perform + scattered wall-clock reads that can cross boundaries inconsistently. + +### Bounded error causes without secret leakage + +Do not preserve raw provider messages, bodies, credentials, proofs, keys, +signatures, or arbitrary exception strings. + +Replace broad `catch { ... }` and `except Exception: ...` translation with: + +1. typed validation errors for caller input; +2. typed adapter/provider errors with bounded cause categories; +3. typed native contract/ABI errors; +4. explicit cancellation and timeout handling; +5. a final sanitized `unknown` category for unclassified external failures; and +6. programmer/invariant errors that fail loudly in development and tests rather + than being mislabeled as an authorization denial. + +Serialized errors carry only registered family, code, stage, retry, effect, +entered-boundary flags, bounded remediation, references allowed by the +registry, and bounded cause categories. + +### Known and unknown stable codes + +TypeScript must expose: + +```ts +type KnownAuthsErrorCode = /* generated literal union */; +type UnknownAuthsErrorCode = string & { readonly __unknownAuthsCode: unique symbol }; +type AuthsErrorCode = KnownAuthsErrorCode | UnknownAuthsErrorCode; +``` + +Unknown codes may only be constructed by a parser after a registry lookup +fails. Application-authored arbitrary strings must not type-check as an +`AuthsErrorCode`. + +Python must expose a generated `KnownAuthsErrorCode` enum or `Literal` union and +an explicit `UnknownAuthsErrorCode` value type. Result objects use that union, +not plain `str`. + +Denial and indeterminacy result types in both bindings must use the same code +type. Add exhaustive tests for every generated known code and forward-compatible +tests for one syntactically valid unknown code. + +## Implementation plan + +## Phase 1: freeze behavioral evidence, not APIs + +Before deleting old code: + +1. Record the exact existing decisions, canonical action bytes, verified + commands, provider requests, state transitions, recovery classifications, + and receipt bytes for every path that will survive. +2. Add differential tests between old and target implementations where a + direct replacement is being made. +3. Keep those old implementations test-only only for the duration of the + bounded cutover branch. +4. Delete the oracle when the target implementation and canonical fixtures are + sufficient. Do not ship it behind a compatibility flag. + +Required scenarios include allowed action, widening denial, body mutation, +configuration mismatch, exact replay, reservation conflict, provider failure +before entry, ambiguous failure after entry, restart, resume, reconciliation, +receipt verification, and malformed/oversized input. + +## Phase 2: classify and separate the Rust reference profile model + +1. Review `auths-profile-domains`, `auths-profile-api`, and + `auths-profile-kit` symbol by symbol using the Phase 0 inventory. +2. Keep deterministic, bounded canonicalization, verified-command decoding, + profile contracts, fixtures, and conformance helpers available to advanced + Rust users when their contracts are coherent. +3. Remove generic Rust profile types from the default `auths-sdk` root facade. + Prefer a separately imported reference crate or an explicit advanced module + over root-level re-exports. +4. Keep retained reference crates independently importable and document their + audience, limits, errors, security properties, and non-goals. +5. Rewrite or delete `product/integrations/auths-deployment/`; its product + execution path must consume a concrete deployment profile owned by a real + domain vertical rather than a generic executor over + `DomainProfile`. +6. Remove generic domain parsing and canonicalization exports from the consumer + `auths-proof-wasm` package. If advanced cross-language reference access is + genuinely required, create a separately named reference-only artifact; do + not enlarge the consumer npm SDK. +7. Remove corresponding Python native generic profile bindings unless replaced + by a profile-specific ABI owned by a concrete vertical. +8. Retain `auths-profile-api::ActionProfile` as a public Rust reference contract + when it remains a narrow deterministic port without state, credentials, + execution, or receipt meaning. +9. Retain `auths-profile-kit` for Rust profile authors when it remains neutral + fixture and conformance tooling. It does not need a TypeScript or Python + equivalent. +10. Add tests proving retained reference profile APIs cannot invoke providers, + acquire credentials, read clocks, or mutate state. +11. Update architecture, compliance, workspace membership, generated snapshots, + and ownership entries atomically with any move or deletion. + +The cutover is complete when generic reference APIs are confined to the +advanced Rust tier and no TypeScript, Python, consumer WASM, default product +facade, or generic effect runtime depends on them. Rust uses of `DomainProfile` +or `DomainCommand` are permitted in explicitly classified reference crates, +conformance tooling, and tests. + +## Phase 3: consolidate TypeScript + +1. Make `bindings/typescript/src/product.ts` the sole root product + implementation. Replace its contents with the production-client + implementation or move the production implementation there. +2. Delete `bindings/typescript/src/production-client.ts` after the move. +3. Remove the local MCP `Authority`, `Receipt`, `Completed`, `Denied`, + `Indeterminate`, `ExecutionReference`, and `Auths` definitions from the root + product module. +4. Remove the overloaded `createAuths`. The root `createAuths` accepts only the + production options contract and returns one `Auths` product client. +5. Keep local MCP development creation only under + `bindings/typescript/src/integrations.ts::development`. +6. Rename local development-only types with an explicit `DevelopmentMcp...` + prefix where they must remain accessible from `integrations` or `testkit`. +7. Delete public or package-addressable legacy entry modules: + `internal-sdk.ts`, `workflow.ts`, `workflow-client.ts`, `profile-kit.ts`, and + `mcp.ts`. +8. Move any still-required workflow implementation behind + `src/internal/development/` and expose no public re-export. Then delete it + completely if the MCP development vertical can use the Rust-owned closed + profile boundary without the old orchestrator. +9. Delete `src/profiles/application/` and `src/profiles/domains/` after all + consumers move to profile-specific surfaces. +10. Make `src/profiles.ts` generated from the Rust qualified-profile registry; + do not hand-code IDs or route paths. +11. Update `src/index.ts` so it exports one product vocabulary, not parallel + local and `Production...` types. Once the production client is the only + product, drop the `Production` prefixes: + +```ts +createAuths +Auths +Authority +Receipt +RecoveryReference +Completed +Denied +Indeterminate +Recoverable +Rejected +ExecutionResult +VerificationResult +``` + +12. Keep development imports explicit: + +```ts +import { development } from "@auths-dev/sdk/integrations"; +import { mcp } from "@auths-dev/sdk/profiles"; +``` + +No deprecated export aliases are permitted. + +## Phase 4: consolidate Python + +1. Make `bindings/python/python/auths/_product.py` the sole root product + implementation by moving the production-client implementation into it. +2. Delete `_production_client.py` after the move. +3. Remove the local MCP aliases and local product result classes from + `_product.py`. +4. Root `auths.create_auths` accepts only the production client arguments and + returns one `Auths` type. +5. Keep MCP development creation only under + `auths.integrations.development.create_auths`. +6. Remove parallel `Production...` public names. Once there is one production + product, expose the unprefixed names matching TypeScript. +7. Delete `_application_profile.py` and all generic profile factories after + consumers move to closed verticals. +8. Reduce `_workflow.py` to private development mechanisms only, move those + mechanisms into an accurately named internal package, and delete the rest. + If the closed MCP development profile no longer needs it, delete it entirely. +9. Ensure `auths.profiles` is generated from or validated against the Rust + qualified-profile registry rather than maintaining independent profile IDs. +10. Update `auths.__all__`, lazy ownership maps, type stubs, wheel-content + checks, and installed-wheel tests atomically. +11. Add a negative installed-wheel test proving that removed private modules + cannot be imported by a consumer. + +No module alias or `sys.modules` compatibility mapping is permitted. + +## Phase 5: unify errors, telemetry, and support bundles + +1. Extend `auths-production-client` with Rust-owned bounded structures for the + final telemetry event and support bundle. +2. Add deterministic encode/project functions to the WASM and Python native + boundaries. +3. Generate the known error-code types from the Rust-owned registry. +4. Change result `code` fields in TypeScript and Python from arbitrary strings + to the known-or-unknown parsed code type. +5. Merge the useful fields of the two support bundle families into the final + `auths.support/1` contract. +6. Delete TypeScript `createSupportBundle` from both old modules. Add one + canonical function in the product error/operations module. +7. Delete Python `_observability.support_bundle` and + `_product_errors.create_support_bundle`; add one canonical public builder. +8. Add cross-language golden tests proving Rust, TypeScript, and Python emit + byte-equivalent canonical JSON for the same bounded input. +9. Add negative tests for sensitive attribute names, oversized collections, + invalid numbers, unknown fields, malformed references, retry/effect + mismatches, and illegal boundary flags. + +## Phase 6: make time and failure handling explicit + +1. Define a narrow clock port in the shared framework surface. +2. Thread an explicit captured timestamp into authoring, authorization, + approval, receipt, lifecycle, and recovery functions. +3. Replace direct `Date.now()` and `time.time()` inside semantic functions. +4. Keep system-clock creation at the outer production/development composition + boundary only. +5. Add boundary tests for exactly-at-expiry, one second before, one second + after, clock regression, and a long-running operation crossing expiry. +6. Replace broad transport and profile catches with typed mapping functions. +7. Make malformed successful responses a typed contract failure. Do not return + `core.malformed-input`, which incorrectly describes caller-controlled core + input when the actual problem is a malformed remote response. +8. Preserve only bounded cause categories in serialized output. +9. Add tests proving secrets and provider response bodies do not appear in + errors, telemetry, support bundles, or exception formatting. + +## Phase 7: harden development and production state boundaries + +The existing file stores remain development-only. Make that true in type names, +exports, diagnostics, and tests. + +1. Rename `FileMcpResources` / `_FileMcpResources` to include + `SingleProcessDevelopment`. +2. Do not expose the file store through `framework` or any production + composition. +3. Add advisory process locking or reject concurrent opens so two processes + cannot perform read-modify-replace transitions concurrently. +4. Capture the owner process/session in the development manifest and fail + closed on concurrent ownership. Because state is disposable, do not migrate + the old manifest. +5. Add crash tests around reservation, provider entry, receipt persistence, and + completion cleanup. +6. Production state ports must require compare-and-swap or transactional + transition methods with expected prior version/stage. +7. Add concurrent tests proving only one claimant can enter the provider for an + execution, completed state cannot regress, and exact replay returns the + recorded result without provider re-entry. + +Do not present filesystem replacement as a production durability mechanism. + +## Phase 8: repair demos + +### Cross-company incident response + +1. Remove all imports beginning with `auths._` from the agent service. +2. Consume the installed public Python package exactly as a customer would, or + call the public `auths-node` HTTP contract through `auths.create_auths`. +3. If the edge incident profile is retained, route it through the new complete + `auths-edge-incident` vertical and add it to the Rust-owned qualified profile + registry only after its evidence gates pass. +4. Replace the demo's direct generic application profile construction with + profile-specific request and result types. +5. Run the demo integration test against a built wheel, not the repository + source tree. + +### Browser vendor directory + +Perform a destructive prelaunch consolidation: + +```text +demos/cross-company-incident-response/control-room/public/vendor/ DELETE +demos/cross-company-incident-response/control-room/public/vendor-v2/ DELETE +demos/cross-company-incident-response/control-room/public/vendor-v3/ DELETE +demos/cross-company-incident-response/control-room/public/vendor-v1/ CREATE +``` + +`public/vendor-v1/` must contain exactly one current packed TypeScript SDK +browser artifact, including the matching WASM and declarations. It must be +generated from the same revision under test; do not manually copy selected +files from the three old directories. + +Implementation requirements: + +1. Add one deterministic vendor-generation command owned by the demo. +2. Generate into a temporary directory. +3. Verify package identity, ABI, semantic subject, and artifact digests. +4. Replace `public/vendor-v1/` atomically. +5. Reject unexpected files and source maps containing local absolute paths. +6. Add a check that regenerates the artifact and fails on any diff. +7. Update browser imports, server static paths, tests, and deployment packaging + to refer only to `/vendor-v1/`. +8. Add a repository check that fails if `public/vendor`, `public/vendor-v2`, or + `public/vendor-v3` reappears. + +The `v1` name identifies the one browser artifact contract, not a compatibility +window. There are no v2/v3 fallback loaders. + +### Stripe dead code and copied helpers + +1. Delete the unused `_canonical` functions in both subscription receipt + modules. +2. Search all Stripe demo `.rs` files for identical receipt, persistence, HTTP, + and test-harness helpers. +3. Classify each duplicate as profile semantic, family semantic, neutral demo + infrastructure, or accidental duplication. +4. Keep profile semantics in their vertical modules. +5. Extract only neutral demo/test infrastructure whose inputs, outputs, errors, + limits, and crash behavior are identical. +6. Do not create a generic Stripe operation dispatcher. + +## Phase 9: split oversized modules along semantic boundaries + +This phase follows behavioral consolidation so file moves do not conceal API or +semantic changes. + +Suggested Rust splits: + +```text +core/crates/auths-model/src/ + lib.rs re-exports only + identifiers.rs + authority.rs + plans.rs + actions.rs + evidence.rs + status.rs + registries.rs + context.rs + limits.rs + decisions.rs + portable_result.rs + +core/crates/auths-verifier/src/ + lib.rs public facade + decode.rs + resolve.rs + control.rs + authority.rs + plans.rs + action_binding.rs + work.rs + outcome.rs + +bindings/wasm/auths-proof-wasm/src/ + lib.rs wasm exports only + production_client.rs + verification.rs + authoring.rs + identity.rs + receipts.rs + errors.rs + conversion.rs +``` + +Split `auths-codec/src/decode.rs` by wire object or decoding stage while keeping +one bounded decoder context and one canonical error mapping. + +Split testkit fixtures by concern, with a small root registry that makes the +complete corpus discoverable. + +TypeScript and Python should mirror public concepts, not Rust file layout. Aim +for modules with one clear responsibility. A shipping implementation file over +roughly 1,500 lines requires an explicit review justification; generated tables +and test corpora are exempt. + +Requirements for every split: + +- no changed public symbol ownership unless specified elsewhere in this plan; +- no changed canonical bytes; +- no changed stable codes; +- no new dependency direction; +- no duplicate type definitions; and +- focused tests remain adjacent to the owning semantic module. + +## Required deletion list + +Delete these paths when their consumers have moved: + +```text +bindings/typescript/src/production-client.ts +bindings/typescript/src/internal-sdk.ts +bindings/typescript/src/workflow.ts +bindings/typescript/src/workflow-client.ts +bindings/typescript/src/profile-kit.ts +bindings/typescript/src/mcp.ts +bindings/typescript/src/profiles/application/ +bindings/typescript/src/profiles/domains/ +bindings/python/python/auths/_production_client.py +bindings/python/python/auths/_application_profile.py +demos/cross-company-incident-response/control-room/public/vendor/ +demos/cross-company-incident-response/control-room/public/vendor-v2/ +demos/cross-company-incident-response/control-room/public/vendor-v3/ +``` + +`product/profiles/auths-profile-domains/` is not an unconditional deletion +target. Keep it, rename it, split it, or delete it according to the Phase 0 +inventory and the Rust reference-tier rules. Its generic behavior must not be +re-exported through consumer bindings or used as a generic effect runtime. + +`bindings/typescript/src/workflow/`, +`bindings/python/python/auths/_workflow.py`, generic WASM parser exports, and the +Python native generic profile modules are also deletion targets. They may remain +temporarily only while the new closed MCP development implementation is being +completed. They must not remain in the finished cutover merely because moving +their last consumer is inconvenient. + +Delete generated declarations, wheel contents, API snapshots, tests, and +packaging entries belonging solely to removed surfaces. Regenerate them from +the final API. Do not hand-edit generated snapshots to resemble success. + +## Test and evidence requirements + +### Public API tests + +- Compile TypeScript customer programs using only each documented entry point. +- Build a wheel and run Python customer programs in an isolated environment. +- Assert removed TypeScript subpaths do not resolve. +- Assert removed Python private modules are absent or inaccessible to installed + consumers. +- Assert there is one unprefixed product vocabulary and no parallel + `Production...` vocabulary. +- Assert development MCP examples import through `integrations` and `profiles` + only. + +### Cross-language tests + +- Qualified profile IDs and routes match Rust exactly. +- Known error code sets match Rust exactly. +- Unknown error code parsing behaves consistently. +- Telemetry and support-bundle projections are byte-equivalent. +- Production request/response bytes are equivalent. +- Decisions, stages, codes, retry classes, effect states, and receipt + projections match. + +### Profile tests + +For every retained concrete profile: + +- canonicalization is deterministic and bounded; +- verified-command decoding accepts only verifier-minted actions; +- requested authority cannot widen parent authority; +- provider request is derived only from the verified command; +- required and executed configuration mismatch stops before credentials; +- credentials are acquired only after authorization and durable reservation; +- exact replay does not re-enter the provider; +- ambiguous provider outcomes remain recoverable/reconcilable; +- receipt claims match durable state and observed provider behavior; and +- mutations and boundary-plus-one inputs fail closed. + +### State tests + +- concurrent reservation has one winner; +- transitions use expected version/stage; +- completed state cannot regress; +- crash before provider entry is safely resumable or not applied; +- crash after provider entry becomes outcome-unknown until reconciliation; +- receipt persistence is idempotent for identical bytes and rejects conflicts; +- old disposable state is rejected rather than migrated. + +### Repository hygiene tests + +Add automated checks rejecting: + +- imports from `auths._` in demos; +- public exports from TypeScript `internal` or `workflow/internal` modules; +- `defineProfile`, `define_profile`, `DomainProfile`, or generic domain parser + symbols in shipping code; +- duplicate support-bundle schema strings; +- hand-written binding profile route maps; +- the removed vendor directories; +- more than one `public/vendor-v1/` artifact source; +- `#[allow(dead_code)]` in shipping or demo code without a narrow documented + invariant; and +- direct wall-clock reads in canonicalization, verification, or transition + functions. + +## Sequencing and pull-request boundaries + +Use bounded, reviewable changes, but never merge a state where a new public path +ships alongside an old compatibility path. + +Recommended sequence: + +1. Source inventory and behavioral evidence. +2. Rust-owned qualified-profile/error/telemetry/support registries and binding + projections. +3. Production API consolidation in TypeScript and Python. +4. Generic domain/profile removal and concrete vertical migrations. +5. Explicit clock and typed failure mapping. +6. Development state ownership/concurrency hardening. +7. Demo public-API migration and `vendor-v1` regeneration. +8. Dead-code removal and neutral demo-helper consolidation. +9. Large-module splits. +10. Final architecture, compliance, packaging, release, and CI closure. + +If a temporary branch requires both implementations for differential testing, +keep the old implementation inaccessible to packaging and delete it before the +cutover commit is declared complete. + +## Completion checklist + +The work is complete only when all of the following are true: + +- [ ] The source inventory exists and every seed reference has a disposition. +- [ ] Root TypeScript and Python expose one product client vocabulary. +- [ ] Development MCP composition exists only under the development integration + namespace. +- [ ] No public legacy workflow or internal SDK entry point remains. +- [ ] Generic domain factories are absent from TypeScript, Python, consumer + WASM, and default product facades. +- [ ] Any retained broad Rust profile APIs are explicitly classified as + reference/advanced, deterministic, bounded, effect-free, and documented. +- [ ] Every shipping effect is owned by a concrete profile vertical. +- [ ] Rust is the only source of qualified profile IDs and routes. +- [ ] Rust is the only source of known error, telemetry, and support-bundle + contracts. +- [ ] TypeScript and Python distinguish known from unknown stable codes. +- [ ] There is one `auths.support/1` builder and one `auths.telemetry/1` model. +- [ ] Semantic stages receive explicit captured time. +- [ ] Broad catches no longer relabel programmer or contract failures as + authorization outcomes. +- [ ] Development file state rejects concurrent ownership and is unmistakably + non-production. +- [ ] Production state transitions require atomic compare-and-swap or database + transactions. +- [ ] No demo imports `auths._...` modules. +- [ ] The incident-response demo uses an installed public artifact or public + HTTP contract. +- [ ] Only `public/vendor-v1/` exists, generated from the current packed SDK. +- [ ] Removed vendor generations cannot reappear unnoticed. +- [ ] Explicit dead receipt helpers are gone. +- [ ] Large source modules have been split along semantic boundaries or carry a + written review justification. +- [ ] Canonical fixtures and stable protocol results are unchanged unless an + independently reviewed protocol change explicitly authorized them. +- [ ] Architecture and compliance inventories match the final source tree. +- [ ] Installed package tests, cross-language tests, demo tests, and the + authoritative repository CI pass on the exact final revision. + +## Explicit non-goals + +- Supporting imports or state formats removed by this cutover. +- Preserving old demo URLs or vendor directory names. +- Maintaining both local embedded and remote production clients at the package + root. +- Providing a universal user-defined effect/execution callback framework in + product bindings. Deterministic Rust profile contracts and authoring tools are + allowed in the advanced reference tier. +- Turning demos into sources of production semantics. +- Moving clocks, stores, networks, credentials, or provider execution into the + offline core. +- Changing protocol V1 merely to simplify binding code. +- Claiming a security audit from conformance or refactoring work. + +## Handoff requirements + +The implementing agent must report: + +1. the completed source inventory; +2. every deleted public or private surface; +3. the final public API topology for Rust, TypeScript, and Python; +4. each profile vertical and its owning package; +5. the canonical owner of profile IDs, routes, stable codes, telemetry, and + support bundles; +6. proof that demos use installed public artifacts; +7. proof that `vendor-v1` was generated from the final SDK revision; +8. tests run and exact results; +9. any canonical byte, stable-code, or fixture changes, with explicit review + justification; and +10. any remaining item from this specification, which blocks declaring the + consolidation complete. diff --git a/docs/target-state/v1-api-contract.md b/docs/target-state/v1-api-contract.md new file mode 100644 index 00000000..86722844 --- /dev/null +++ b/docs/target-state/v1-api-contract.md @@ -0,0 +1,527 @@ +# Auths v1.0 Frozen API Contract + +## Status + +**Draft — Phase 1 of the v1.0 API hardening effort. Not yet ratified.** + +This document is the single source of truth that all implementation lanes work against. Rust, +TypeScript, Python, and Security agents build to *this file*, not to their own judgment. Where an +implementer believes this contract is wrong, they report it and stop — they do not diverge. + +Derived from 139 findings (48 blockers) produced by 15 read-only auditors across two workflow runs +(`wf_4ff587be-0a3`, `wf_f7878425-e97`). Evidence: `docs/target-state/v1-api-review-findings.md`. + +Authoritative over this document: `AGENTS.md`, `architecture.toml`, `compliance.toml`, `xtask`. +Where they conflict, they win and this file is corrected. + +## 1. Why this document exists + +Three implementation lanes will edit Rust, TypeScript, and Python in parallel. Without a frozen +contract they will produce three internally-consistent, mutually-incompatible APIs — a failure that +cannot be repaired in review, because by then each is self-consistent. + +The audit proved this is not hypothetical. It is the *current* state: + +- `EffectState` — the field that answers "did the real-world effect happen?" — has **five + incompatible value sets** across three languages. +- `RetryClass` names **three different closed sets**, and both bindings export two of them under the + same identifier. +- `recover` is a **sixth product operation that both bindings invented independently**, with no Rust + owner and no registry entry. + +In each case the projections agreed with each other and disagreed with the owner. That is the +signature of an ownership failure, and it is what this contract exists to end. + +## 2. The three goals and how each is measured + +| Goal | Measure | Today | v1.0 target | +|---|---|---|---| +| **Simple** | One canonical name per concept; one entry point per operation | 5 `EffectState` sets, 3 `RetryClass` sets, 2 complete SDKs at the TS root | 1 each | +| **Extensible** | Non-generated files touched outside a new vertical's own package | **28** (single commit) / **36** (union) | **≤ 3** | +| **Secure** | Effect state reaches the caller unflattened in every language | Unreachable in TS and Python public APIs; destroyed at the WASM boundary | Reachable and typed in all three | + +Breadth is not the enemy. A large Rust reference surface that is bounded, deterministic, and +documented is correct. A *second definition of meaning* is the enemy, at any size. + +## 3. Tier model + +| Tier | Audience | Rule | +|---|---|---| +| **T1 — Reference Rust** | Protocol implementers, auditors, advanced embedders | Broad, deterministic, effect-free. Breadth is a feature. Never re-exported from the product facade. | +| **T2 — Rust product facade** | Ordinary Rust developers | Curated safe path. Contains no generic semantic carriers. | +| **T3 — TypeScript / Python** | Application developers | Small, product-shaped. Projects T2. Defines nothing. | +| **T0 — Internal** | Nobody | `pub(crate)` or unpublished. **Default for anything not explicitly listed.** | + +Semantic parity across T3 languages is **required**. Identical symbol counts are **not**. + +`bindings/wasm/auths-proof-wasm` and `bindings/python/src` (pyo3) are **not tiers**. They are +transport layers that ship *inside* T3 artifacts. They may expose no symbol that T3 does not expose. + +## 4. The frozen vocabulary + +One concept, one name, one spelling per language. Rust `snake_case`/`PascalCase`, TypeScript +`camelCase`/`PascalCase`, Python `snake_case`/`PascalCase`. + +### 4.1 Safety-critical — the effect axis + +**`EffectState`** — answers *did the real-world effect happen?* + +``` +Rust (OWNER): auths_errors::EffectState { NotApplied, Possible, Applied } +Wire: "not-applied" | "possible" | "applied" +TypeScript: type EffectState = "not-applied" | "possible" | "applied" +Python: class EffectState(str, Enum): NOT_APPLIED / POSSIBLE / APPLIED +Field name: `effect` in all three. `effect_state` is DELETED. +``` + +**Exactly three members. No fourth.** The `"unknown"` value invented at +`bindings/typescript/src/product-errors.ts:8` and `bindings/python/python/auths/_product_errors.py:27` +is deleted, as is the entire alternate vocabulary at `workflow/errors.ts:48` (`none|possible|occurred`) +and `_errors.py:9` (`not-started|in-progress|completed|failed|outcome-unknown`). + +> **The fail-closed rule.** An unrecognized error code maps to `effect: "possible"`. Never to a +> fourth value, never to `not-applied`. If a distinct "could not classify" signal is genuinely +> required, Rust adds it first and the fixtures move with it. + +**`RetryClass`** — answers *may I retry?* + +``` +Rust (OWNER): auths_errors::RetryClass { Never, Safe, Conditional, Unknown } +Wire: "never" | "safe" | "conditional" | "unknown" +``` + +**`NextCall`** — answers *what should I call next?* (renamed from the second `RetryClass`) + +``` +Rust (OWNER): auths_production_client::NextCall { Never, Backoff, Resume, Reconcile } +Wire: "never" | "backoff" | "resume" | "reconcile" +``` + +These are different questions and must never share an identifier again. +`auths_lifecycle::ProviderRetryClass` stays **T1 and is not projected**. + +### 4.2 Operations + +**Exactly five product verbs.** + +``` +Rust (OWNER): ProductVerb { Create, Delegate, Execute, Resume, Verify } +TypeScript: type ProductVerb = "create" | "delegate" | "execute" | "resume" | "verify" +Python: class ProductVerb(str, Enum) +Wire field: `verb` (the `step` spelling is DELETED) +``` + +`ProductStep` is deleted in both bindings. `ErrorDefinition.operation` changes from +`&'static str` to `ProductVerb`, which forces two open questions to be answered (§11). + +**`recover` is deleted from both bindings.** It has no Rust owner, no registry entry, and its +implementation decides *what identity to recover under* inside the binding. Either Rust gains +`McpExecutionSession::recover` and `ProductVerb::Recover` in one change, or the operation does not +exist. + +### 4.3 Nouns + +| Concept | Rust (T2) | TypeScript | Python | Notes | +|---|---|---|---|---| +| Product authority | `Authority` | `Authority` | `Authority` | Product noun | +| Signed statement | `SignedGrant` | — | — | **T1 only.** `grant` never appears in a binding | +| Decision+execution pair | `Receipt` | `Receipt` | `Receipt` | One `Receipt`, one subpath | +| Single attested receipt | `AttestedReceipt` | `AttestedReceipt` | `AttestedReceipt` | | +| Receipt signer port | `ReceiptSigner` | `ReceiptSigner` | `ReceiptSigner` | Drop `Application*` prefix | +| Receipt attestor port | `ReceiptAttestor` | `ReceiptAttestor` | `ReceiptAttestor` | | +| Trusted context | `TrustedContext` | `TrustedContext` | `TrustedContext` | **Rename Rust `VerifierContext`** | +| Multi-step plan | `AuthorizationPlan` | `AuthorizationPlan` | `AuthorizationPlan` | Rename Python `ProofPlan*` | +| Threshold combinator | `threshold` | `threshold` | `threshold` | **Rename Rust `k_of_n`** | +| Profile-scoped plan | `ProfilePlan` | `ProfilePlan` | `ProfilePlan` | Rename Python `McpPlan` | +| Resume token | `ExecutionReference` | `ExecutionReference` | `ExecutionReference` | One wire format (§11) | +| Telemetry event | `AuthsEvent` | `AuthsEvent` | `AuthsEvent` | One field set | +| Telemetry port | `TelemetryPort` | `TelemetryPort` | `TelemetryPort` | | +| Metrics | `VerificationMetrics` | `VerificationMetrics` | `VerificationMetrics` | Delete `InspectionMetrics`, `AuthorizationMetrics` | +| Product stage | `Stage` | `Stage` | `Stage` | One Rust enum over the registry's 20 values | +| Kernel phase | `VerificationStage` | `VerificationStage` | `VerificationStage` | Separate, 5 members, already aligned | + +**Deleted outright:** `McpAttestedReceipt`, `LinkedAttestedReceipt`, `ApplicationReceiptSigner`, +`ApplicationReceiptAttestor`, `AuthorizationPlanSummary`, `TelemetryStage`, `DecisionTimeline`, +`AuthorizationRequest` (Python), `auths.python-support-bundle/1`, and every `SignedGrant*` name in +either binding. + +**Homonyms — banned.** No identifier may name two unrelated types: +- TypeScript exports two `Auths` (product facade + verifier engine) → rename the engine. +- Python ships two `AuthsError` → one is deleted. +- Two `development` values at `/integrations` and `/testkit` → testkit's becomes `fixtures`. +- Two `Receipt` aliases at root and `/verify` → one type, one export. + +### 4.4 The `Production*` prefix is deleted + +The TypeScript root publishes **two complete unrelated SDKs** — 19 of 41 root symbols are a +`Production*` mirror of the other 14, sharing zero methods and drawing `code` from disjoint spaces. + +The remote client moves to a new subpath **`@auths-dev/sdk/service`** / **`auths.service`**, added +to `bindings/public-topology-v1.json`. Types drop the prefix (`ServiceAuthority`, `ServiceReceipt`). +The local product facade keeps no import edge to the remote client. + +**Unify the two `code` spaces on the registry before splitting**, or the split merely relocates the +ambiguity. + +## 5. The result model + +This section is safety-critical. Every rule here has a failing-then-passing test as its acceptance +criterion. + +1. **The effect axis must reach the caller.** In every language, from every public entry point, a + caller can read `effect` on a failed operation. Today it is unreachable from every TypeScript + public surface and every public Python API. + +2. **No error may be flattened to a string.** `bindings/wasm/auths-proof-wasm/src/lib.rs:4927` + (`js_error` → `JsValue::from_str`) destroys code identity, effect state, and recommended action + for all 45 codes. It is replaced by a structured envelope. + +3. **Transport failure is `possible`, never `not-applied`.** Both production clients currently map + every transport failure and every non-2xx response to `retry: backoff` with registry codes whose + declared effect is `not-applied`. That tells a caller a possibly-applied PostgreSQL update is + safe to blindly retry. This is the single most dangerous defect found. + +4. **Bindings mint no error codes.** Python currently mints 25 codes that exist in no registry, on + the path reachable from public `execute()`. All codes originate in + `product/errors/v1/registry.json` (45 today) and are generated into both bindings. + +5. **Known vs unknown codes must be distinguishable**, so a newer Rust code neither crashes nor is + silently swallowed by an older binding. Unknown → `effect: "possible"`. + +6. **`recommendedAction` is reachable in all three languages.** It is currently Rust-only. + +7. **No binding-owned catch may relabel a programmer error or contract violation as an + authorization outcome.** + +## 5A. Launch blockers surfaced after this contract was first drafted + +Phase 0's final adjudicated pass produced four findings more severe than anything in §5. All four are +**verified in source by hand**. They are listed here in danger order and take precedence over the +vocabulary work in §4. + +### 5A.1 The reference production runtime is a second authorization system + +`product/runtime/auths-node` is what `demos/open-production-reference/Dockerfile:4,11` builds and +runs as its entrypoint — the deployment README instructs operators to run three of them behind TLS +with PostgreSQL and an HSM. It is **not** in the 42-crate publishable closure, so no release gate +covers it. + +Verified: + +- **It depends on no kernel crate.** Its `Cargo.toml` lists `auths-operations`, + `auths-operations-otel`, `auths-production-client`, `axum`, `ed25519-dalek`, `minicbor`, + `postgres`, `rustls`. There is no `auths-algebra-kernel`, no `auths-verifier`, no `auths-authority`. +- **`sandbox.rs:95-107` mints root authorities with no authentication of the requester.** `create()` + builds `Authority { parent: None, subject: digest(request.identity()), .. }` and signs it. `parent: + None` is a root. The only input is the caller's self-asserted identity. +- It hand-rolls narrowing over **4 dimensions** (`sandbox.rs:123-135`) where the kernel checks **11** + (`core/crates/auths-algebra-kernel/src/generated.rs:52-75`). +- **8 of the 10 error codes it puts on the wire are unregistered** (`profiles.rs:24-31`). + +**Disposition:** requires a human decision (§11.7). Either `auths-node` is rebuilt on the kernel, or +it is removed from the reference deployment and labeled non-production. It cannot ship as-is under +the product's own security claims. + +### 5A.2 The formal proof of root preservation is vacuous + +`core/crates/auths-authority/src/lib.rs:201` sets `root_preserved: true` as a **literal**. +`AuthorityStateView` carries no root to compare against, so there is nothing the check could compute. +The Kani harness at `core/crates/auths-algebra-kernel/src/lib.rs:43-74` therefore proves an identity +over arbitrary booleans while presenting it as a security invariant. + +Root preservation — that a delegated authority still descends from the same root — is one of the +system's central claims. It is currently unproven and unchecked. + +**Disposition:** in scope. `AuthorityStateView` gains the root identity; `root_preserved` computes a +real comparison; the Kani harness is re-checked against the non-vacuous version. + +### 5A.3 The signed receipt cannot express "unknown" + +`product/receipts/auths-receipts/src/lib.rs:299-304`: + +```rust +pub enum ExecutionOutcome { Succeeded, Failed } +``` + +Two variants. `exchange/crates/auths-proof-exchange-model/src/lib.rs:545-554` likewise has no +indeterminate member. So for a provider timeout, the reference runtime signs a durable receipt +asserting **Failed** for an effect that may have applied — and §5's error-model fixes cannot repair +it, because the evidence artifact itself has no way to say "possible". + +**Disposition:** adding a third variant changes signed bytes and is therefore a **protocol change**, +out of scope under §10 without separate review. Flagged, scoped, not auto-fixed. This is the highest +priority item for the review that follows this wave. + +### 5A.4 A signed "Authorized" receipt is written before the replay check runs + +`product/runtime/auths-runtime/src/lib.rs:816-828` writes the decision receipt; `:830` performs the +replay check. Consequences: audit records assert authorization for requests that are then refused, +and an attacker gets unbounded write amplification into the receipt sink. + +**Disposition:** in scope. Reorder so the receipt is written only after every check that can refuse. + +## 6. Public surface + +### 6.1 Rust + +42 crates are in the publishable closure and `xtask/src/public_naming.rs:394` already enforces +set-equality against `semantic-freeze.json` with zero drift. **This is the model — hold every other +language to it.** + +Two changes: + +- **`auths` and `auths-proof` are two crates.io coordinates for one byte-identical API.** Pick one, + delete the other. +- **Rust has crate-level gates only**, while TypeScript and Python have symbol-level gates. Rust + gains a symbol-level public-API snapshot, byte-compared in CI. + +`auths-sdk` (T2) exports **no** T1 generic machinery. Specifically, `product/sdk/auths-sdk/src/lib.rs:26` +is deleted: + +```rust +pub use auths_profile_domains::{DeploymentAction, DomainCommand, DomainProfile}; // DELETE +``` + +Every downstream break is intended and is fixed by giving the vertical its own owned types. + +### 6.2 TypeScript and Python + +The seven declared entry points in `bindings/public-topology-v1.json` **stand**, plus `service`: + +``` +product @auths-dev/sdk, /identity, /verify │ auths, auths.identity, auths.verify +service @auths-dev/sdk/service │ auths.service ← NEW +vertical @auths-dev/sdk/profiles │ auths.profiles +mechanism @auths-dev/sdk/integrations │ auths.integrations +extension @auths-dev/sdk/framework │ auths.framework +test @auths-dev/sdk/testkit │ auths.testkit +``` + +`layers` is already byte-enforced in both languages. **`frameworkContracts` and `qualifiedProfiles` +are read by nothing** — they gain gates (§10). + +**Framework contracts must be structurally identical, not merely name-identical.** `close?()` +optional in TypeScript versus `aclose()` required in Python currently passes an 11-of-11 name-parity +check. Parity checks compare shape. + +**Async parity is required.** Receipt-disclosure protector and store ports are async in TypeScript +and synchronous in Python, making a KMS-backed protector unimplementable in Python. + +### 6.3 WASM and pyo3 + +Neither may export a symbol its host tier does not export. + +- WASM: 228 public JS symbols; 38 undeclared by the ABI manifests; ≥22 are generic domain machinery + including parsers for five **unqualified** reference profiles. +- pyo3: 147 module attributes; 23 exported-and-undeclared; 18 generic reference-vertical symbols + (`HttpAction`, `EdgeAction`) exposed as typed callable Python symbols. +- **A Python caller can currently define an entire vertical** via `define_profile` + + `_native.application_action`, with canonicalization in a Python callback. Deleted. +- `__init__.pyi` omits 19 of 35 public symbols in a `py.typed` wheel. Regenerated and gated. + +## 7. The extension point + +**A new profile vertical touches ≤ 3 non-generated files outside its own package.** Today: 28. + +Permitted: workspace `Cargo.toml` members, workspace dependency entry, demo directory. + +Everything else becomes **derived from a single vertical-owned Rust descriptor**: +`bounded-domains.toml`, `compliance.toml [packages.*]`, `architecture.toml [layers]`, the +bounded-policy evaluator registry, fixtures, and both bindings' profile registries. + +Three findings must be fixed for this to work: + +1. `auths-lifecycle` **already contains the correct vertical-owned descriptor, with zero + implementors.** Adopt it rather than designing a new one. +2. `xtask/src/fixtures.rs` authors each vertical's canonical fixture corpus as Rust literals. + Verticals own their own corpora. +3. The qualified-profile registry is hand-maintained in three languages with three different failure + modes, and six mutually-disagreeing copies exist. One generated source. + +**Acceptance test:** build a throwaway vertical, count files touched outside its package, report the +number, delete the spike. The number is reported whether or not it is good. + +## 8. Stability promises + +"Advanced tier" is a documentation label. Users read it as "supported" unless told otherwise. + +| Tier | Version at launch | Promise | +|---|---|---| +| T2 product facade, T3 bindings | `1.0.0` | Full semver | +| T1 reference Rust | `0.x` | **No stability promise**, stated in each crate's docs | +| Internal | unpublished | None | + +Every publicly promised crate documents: **audience, limits, errors, security properties, +non-goals.** A crate without those five sections is not v1.0-eligible. + +Version coherence across Rust, npm, and wheel is a launch gate. The wheel currently advertises three +operating systems and ships one Linux wheel with no sdist. + +## 9. Enforcement + +A declared contract with no gate drifts silently. `semantic-freeze` is **already red on `main`** — +`auths.product.lifecycle` changed under frozen identity v8 without a version assignment — which +proves the point. + +Gates that must exist before v1.0: + +1. **Symbol-level ownership gate.** No `product/integrations/**` may name `DomainProfile`, + `DomainCommand`, or any `auths-profile-domains` type, transitively or via re-export. + Crate-level checks miss this: `auths-deployment`'s `Cargo.toml` is clean while its source is + coupled. +2. **`binding_semantics` must scan what it claims to.** It scans only + `bindings/typescript/src` and `bindings/python/python` — blind to `bindings/wasm/` (5,586 lines) + and `bindings/python/src/` (6,779 lines). It is also a token grep, not semantic analysis. +3. **`frameworkContracts` and `qualifiedProfiles` gates**, comparing shape not names. +4. **Rust symbol-level public-API snapshot**, byte-compared. +5. **Effect-axis reachability test** per language: assert a caller can read `effect` from every + public failure path. +6. **Testkit isolation proof.** That a testkit-minted verifier result cannot reach `Auths.execute` + is currently asserted only in a doc comment. + +**Regenerating `semantic-freeze.json` to make a build pass is forbidden.** Drift means a semantic +identity needs a new version — a deliberate decision, not a mechanical regeneration. + +## 10. Out of scope + +Protocol bytes, canonical CBOR, canonical fixtures, and stable decision codes do not change. They +are test oracles. A refactor that requires changing one is a protocol change and needs separate +review. + +Rust reference breadth is not reduced for symmetry. The Go independent implementation +(`bindings/independent/go`, 5,019 lines, zero third-party dependencies, running in CI) is a genuine +strength and is **kept** — only its `compliance.toml:1473` role claim is corrected, since it +currently claims both `independent-semantic-implementation` and `language-binding`. + +## 10Z. Execution policy: never block — resolve empirically + +**Ratified 2026-08-15. This supersedes every "stop and report" instruction anywhere in this document +or in any wave brief.** + +An implementer who hits an open question does **not** stop, does **not** defer to a human, and does +**not** guess. They *determine the answer* and let the determination become a permanent artifact. + +### The three routes + +| Route | Use it for | How | +|---|---|---| +| **TDD — unit** | "Does this code actually do X?" | Write the test asserting the intended behavior. Run it. **The result is the answer.** Red → the finding is real and you now own its regression test. Green → the finding was a false positive; record that and move on. | +| **TDD — e2e / differential** | "Do two implementations agree?" "Does meaning survive a boundary?" | Drive both sides with identical inputs and assert identical outputs. | +| **Formal — Kani** | Bounded exhaustive questions over small state | Add `#[kani::proof]`; run `cargo xtask formal`. 8 harnesses already exist in `auths-model`, `auths-algebra-kernel`, `auths-lifecycle/src/kernel.rs`. | +| **Formal — Lean** | Invariants that must hold for *all* inputs | `formal/Auths/` — `Attenuation.lean`, `Authority.lean`, `Composition.lean`, `Theorems.lean`, plus `Lifecycle/`, `Product/`, `Rich/`. Run `cargo xtask formal`. | + +### Worked mappings for the currently-open questions + +- **"Is the budget ceiling inert when the action requests no budget?"** → unit test: ceiling present, + action requests nothing, assert **deny**. Run it. Whatever happens is the finding, settled. +- **"Can `PeerObservation` be forged?"** → test from *outside* the owning crate that attempts to + construct the authenticated variant. If it compiles, it is forgeable. Compilation is the proof. +- **"Does `root_preserved` actually hold?"** → Lean. `formal/Auths/Attenuation.lean` and + `Authority.lean` are where this theorem belongs. A theorem that is true because the hypothesis is a + literal is not a theorem — make it quantify over a real root and re-check. +- **"Does `auths-node` agree with the kernel?"** → **differential test, and this is the rebuild's + acceptance criterion.** Feed identical authority/action/context triples to `auths-node`'s narrowing + and to `auths-algebra-kernel`, and assert identical decisions across all 11 dimensions. Every + disagreement is a bug in `auths-node` by definition, because the kernel is the owner. +- **"Does the effect axis survive each boundary?"** → e2e. Force each of the 9 `effect: possible` + codes through Rust → WASM → TypeScript and Rust → pyo3 → Python, and assert `effect` arrives intact + at the public API. A boundary that cannot pass this does not ship. + +### The one thing still forbidden + +**Never weaken a test, gate, assertion, or type to make something pass.** That is not empirical +resolution — it is fabricating the answer, and it is the exact failure mode this whole effort exists +to correct. If a test fails because behavior *legitimately* changed, update it **and say so +explicitly in the wave report**, naming the behavior change that justifies it. + +Corollary: `root_preserved: true` is what a weakened check looks like after the fact. Do not create +the next one. + +### Uncertainty is a work item, not a blocker + +If you cannot decide something, you have not yet written the test that decides it. Write it. +An unresolved question at the end of a wave is only acceptable if the report states **what test or +theorem would settle it** and why it could not be written. + +## 10A. Decisions ratified by the maintainer + +Recorded 2026-08-15. These are settled and implementers follow them without re-asking. + +| # | Decision | Ruling | +|---|---|---| +| §11.7 | `auths-node` | **Rebuild on the verified kernel.** It uses `auths-verifier` / `auths-algebra-kernel` for narrowing and authority minting. The hand-rolled 4-dimension check and unauthenticated root minting at `sandbox.rs:95-107` are deleted. | +| §11.8 | Protocol changes | **Authorized before 1.0, both.** `ExecutionOutcome` gains an indeterminate variant so a receipt can record "possible". `ExecutionReference` unifies on one wire format. Canonical fixtures are regenerated **with explicit written justification per fixture**. | +| — | Commit authority | **Commit to `dev-cleanup` at each verified checkpoint. Never push.** | +| — | Deletion authority | **Delete zero-consumer crates** (`auths-deployment`, `auths-cache`, and any other confirmed dead), atomically with `architecture.toml`, `compliance.toml`, and workspace membership. | +| — | `semantic-freeze` | **Investigate first.** Diff what changed in `auths-lifecycle` under PR #109. If the change is deliberate, assign `auths.product.lifecycle` v9, bump `FREEZE_VERSION`, regenerate, and record the evidence. If it looks accidental, stop and report. | + +### `auths.product.lifecycle` — investigation result: DELIBERATE, assign v9 + +Frozen at identity version **8**, classification `FrozenMeaning`, covering the subjects +`reservation-state`, `claim-state`, `execution-state`, `reconciliation-state`, `lifecycle-codes` +(`xtask/src/semantic_freeze.rs:457-476`). + +PR #109 made two changes inside that frozen meaning: + +1. **New durable state field** — `model.rs` gained + `recovery_reference_digest: RecoveryReferenceDigest`, documented as an "opaque recovery-reference + commitment created before durable decision state." That is `execution-state` and + `reconciliation-state`. +2. **Three new sealed failure variants** — `sealed.rs` gained `PoolExhausted`, `Timeout`, and + `SchemaMismatch`. That is `lifecycle-codes`. + +Both are coherent additions belonging to the open-production epic — recovery commitments, pool +exhaustion, statement timeouts, and schema compatibility are exactly that epic's concerns. This is +**deliberate product work that skipped the version bump**, not an accidental semantic drift. + +**Ruling: assign `auths.product.lifecycle` v9 and bump `FREEZE_VERSION` 110 → 111.** + +**Sequencing:** the bump happens at the END of the implementation waves, not now. The freeze digests +`product/runtime/auths-lifecycle/src` and five other paths that the waves are actively editing; +regenerating now would only have to be redone. Until then the gate stays red **by design**, and every +gate report names it as the one known-red baseline gate. + +**The fixture exception is narrow.** §10 still forbids changing fixtures to make a refactor pass. It +is now permitted *only* where a ratified protocol change (`ExecutionOutcome`, `ExecutionReference`) +requires it, and every such regeneration carries a written justification naming the decision above. + +### Defaults taken without escalation + +Reversible; each is recorded with rationale in the wave reports. + +- **`sign`** — treated as a stage of `create`/`delegate`, not a sixth verb. `ProductVerb` stays five. +- **crates.io coordinate** — `auths` survives; `auths-proof` is dropped. +- **Budget in TypeScript** — projected into TS rather than removed from Python. Removing a caller's + ability to state a budget ceiling is the worse failure. +- **TS identity tiers** — collapsed to Python's single tier. +- **Go role claim** — `compliance.toml:1473` corrected to `independent-semantic-implementation` only; + the `language-binding` claim is removed. Go stays. + +## 11. Open decisions — remaining + +These change the contract's content and cannot be defaulted by an implementer. + +1. **`auths.product.lifecycle` v9.** Assigning it asserts the PR #109 lifecycle semantic change was + intended. Blocks a green `semantic-freeze`. +2. **`sign` — sixth verb or a stage?** The registry declares `operation: "sign"` but has no + `delegate`. Typing `ErrorDefinition.operation` as `ProductVerb` forces the answer. +3. **`auths` vs `auths-proof`** — which crates.io coordinate survives. +4. **`ExecutionReference` wire format** — two incompatible formats exist and the profile-neutral one + is MCP-namespaced. Pick one. +5. **Budget in TypeScript** — Python exposes `BudgetCeiling`/`NoBudget`/`InheritBudget`/`BudgetSummary`; + TypeScript has none. Either project them, or remove them from Python so the bindings agree. + Related: a grant's budget ceiling is currently **inert whenever the action requests no budget** — + which every money-moving Stripe profile and the shipped MCP profile mandate. +6. **Identity tiers in TypeScript** — TS ships descriptor-tier *and* packet-tier; Python ships one. + Collapse TS, or name the descriptor tier explicitly and add it to Python. +7. **`auths-node` (§5A.1) — the launch-blocking decision.** The reference production runtime is a + second authorization system that never touches the verified kernel and mints root authorities + from an unauthenticated caller identity. Three options, all of which need your call: + (a) rebuild it on `auths-verifier` / `auths-algebra-kernel`, deleting the hand-rolled narrowing; + (b) remove it from `demos/open-production-reference` and label it a non-production sandbox; + (c) keep it, and publicly scope the security claims to exclude the reference deployment. + Option (c) is not recommended — the deployment README is what operators will follow. +8. **`ExecutionOutcome` third variant (§5A.3).** Adding `Indeterminate` changes signed receipt bytes. + That is a protocol change requiring review, but without it no honest receipt can be written for a + provider timeout. Recommended: accept the protocol change before 1.0, since after 1.0 it becomes + permanently harder. diff --git a/docs/target-state/v1-api-review-findings-bindings.md b/docs/target-state/v1-api-review-findings-bindings.md new file mode 100644 index 00000000..6f14bd75 --- /dev/null +++ b/docs/target-state/v1-api-review-findings-bindings.md @@ -0,0 +1,194 @@ +{ + "summary": "Phase 0b: deep audit of every bindings/ surface missed by the first pass - WASM, Go, independent TS verifier, sqlite adapter, recipes, declared topology, and actual published packaging", + "agentCount": 8, + "logs": [ + "7/7 auditors returned; 117 findings across bindings/" + ], + "result": { + "bindings": "I've verified the load-bearing claims against source. Several needed correction. Here is the consolidated document.\n\n---\n\n# Phase 0b — bindings/ Surface Findings\n\n## Executive summary\n\nThe npm and PyPI packages' *declared* public APIs are clean and genuinely gated, but almost everything beneath them defines Auths semantics locally rather than projecting Rust — and the one gate built to catch exactly that (`architecture.toml:270-295` `binding_semantics`) is a token grep that does not scan `bindings/wasm/` or `bindings/python/src/` at all, so every finding below is structurally invisible to it. The single largest defect is that `product/errors/v1/registry.json` has **45** stable codes (not 20 — the brief's premise is wrong, corrected below) whose `effect` axis distinguishes `not-applied` from `possible`, and **zero** of them survive the WASM boundary (`bindings/wasm/auths-proof-wasm/src/lib.rs:4927-4929` flattens every error to a bare JS string) while TypeScript and Python *independently invented the same fiction* — mapping a timed-out `execute` to `core.runtime-unavailable` / `retry:\"backoff\"`, a code the registry defines as `effect:not-applied`, telling the caller a possibly-applied PostgreSQL update is safe to blindly retry. The generic `auths-profile-domains` reference machinery is wired into three separate consumer artifacts (22 WASM exports, 18 pyo3 exports, one 397-line orphan TS module compiled into `dist/`), is required by a hard load-time guard at `bindings/typescript/src/verifier/wasm.ts:57-66`, and is the *only* receipt inspector reachable from the public `auths.verify.inspect_receipt()` — which therefore supports five unqualified profiles and zero of the four qualified ones. `bindings/public-topology-v1.json`'s `layers` field is byte-enforced in both languages; its other two fields, `frameworkContracts` and `qualifiedProfiles`, are read by **nothing**, and behind that gap sit six mutually-disagreeing copies of the qualified-profile set and two structurally incompatible framework contracts (`close?()` optional in TypeScript, `aclose()` required in Python) that pass an 11-of-11 name-parity check. The Go tree is a genuine strength — a from-scratch 5,019-line stdlib-only oracle that really runs in CI — but `compliance.toml:1473` claims the role `language-binding` for it, which under stated policy is the blocker, and its `api.go` exports an SDK-shaped surface that is provably dead. The canonical Python recipe 03 does not run (reproduced: `ImportError: cannot import name 'verify_receipt'`), 62 commits after the symbol was pruned, and Python recipe 01's advertised tamper-rejection proof is vacuous because the development suite never binds the message. Packaging ships one Linux wheel that advertises three operating systems with no sdist, a `.pyi` stub omitting 19 of 35 public symbols, and a workspace-wide `panic = \"abort\"` that turns any pyo3 panic into SIGABRT. Rust's own release order is exactly coherent and mechanically frozen at 42 crates — it is the model everything non-Rust should be held to.\n\n---\n\n## Shipped surface inventory\n\n| # | Artifact | Language | What it is | Measured size | Exported symbols | In release plan? | Public surface enforced? |\n|---|---|---|---|---|---|---|---|\n| 1 | `@auths-dev/sdk` @ 1.0.0-rc.1 (npm tarball) | TypeScript | **product** | 70 `.ts` / 14,433 lines src → `dist` 136 files; +4 wasm files; 10 docs | **193** (`.` 41, `/verify` 38, `/profiles` 37, `/identity` 33, `/testkit` 29, `/framework` 11, `/integrations` 4) — verified | **Yes** — `release/public-naming.toml` `npm-sdk` surface | **Yes, strong.** `tools/public-api.mjs:14-21` byte-compares `exports` keys against topology; declaration sha256 digest (`:33-43`); `test/package/package.test.js:9-42` freezes `exports`/`files`/`bin` |\n| 2 | `auths` global bin (inside #1) | Node | **product CLI** | `dist/doctor-cli.js`; usage `auths doctor` | 1 subcommand | Inherits #1 | **Partial** — value-frozen at `package.test.js:27` only; `bin` is absent from `bindings/public-topology-v1.json` and unreadable by `public-api.mjs` |\n| 3 | `auths` @ 1.0.0rc1 (PyPI wheel) | Python | **product** | 33 `.py` / 12,690 lines under `python/auths/` + one abi3 `_native` | **159** across 7 modules (`auths` 35, `auths.verify` 36, `auths.testkit` 33, `auths.profiles` 20, `auths.identity` 19, `auths.framework` 11, `auths.integrations` 5) — verified | **Yes** — `python-sdk` surface | **Mostly.** `tools/check_public_api.py:11-16` imports the declared modules and snapshots runtime `__all__`; `check_wheel.py` asserts file inventory. **`__init__.pyi` is checked by neither** (16 re-exports vs 35 runtime — see B-14) |\n| 4 | `auths._native` (crate `auths-proof-python`, cdylib) | Rust → Python | **reference machinery inside a product wheel** | 13 `.rs` / 6,779 lines | **147** module attrs (101 `#[pyfunction]` + 45 `#[pyclass]` + 1 exception) — verified | Ships **inside** #3; crate itself `publish = false` (`bindings/python/Cargo.toml:7`) | **Weak, one-directional.** `tools/check_contract.py:27-32` asserts 124 declared names exist; never asserts the converse → **23 exported-and-undeclared** |\n| 5 | `auths-proof-wasm` (`wasm/auths_proof_wasm_bg.wasm`, 2,311,312 B) | Rust → WASM | **reference machinery inside a product tarball** | 5,586 lines, **one file, no modules**; 245 `#[wasm_bindgen` attributes | **228 public JS symbols** = 75 functions + 21 classes + 132 class members (verified by parsing `auths_proof_wasm.d.ts`) | Ships **inside** #1 (`release/release-subjects.toml:50,71`); crate `publish = false` | **Weak.** Two hand-written manifests declare 58 of the 96 top-level names → **38 undeclared, 0 missing** (verified). `release/evolution-policy-v1.json:47` pins only `authoring-abi-v1.json` |\n| 6 | `auths.dev/independent-verifier` (`bindings/independent/go`) | Go | **conformance ORACLE** | 8 files / **5,019 lines**; `go.mod` has **zero** third-party deps | 16 exported (`Decision`, `Result`, `Engine`, `Verify`, `NewEngine`, `AuditCorpus`, …) | **No** — absent from `public-naming.toml`, `release-subjects.toml`, `public-topology-v1.json` | **Yes, and it runs.** `go vet ./...` + `go test -race ./...` + three-way digest, all in `xtask/src/checks.rs:159-177` |\n| 7 | `bindings/independent/typescript` | TypeScript | **conformance ORACLE** | 2 files / **2,355 lines** (`semantic-verifier.ts` 2,159) | **1** (`semanticAudit`) | **No** | **Digest only.** Invisible to `compliance.toml` — `xtask/src/compliance.rs:460` skips it because `package.json` is `\"private\": true` |\n| 8 | `auths-sqlite` @ 1.0.0rc1 | Python | **reference ADAPTER** (only real impl of `atomic-reservation-store`) | 3 files, 1 class | 1 (`SQLiteAtomicReservationStore`) | **No** — absent from `public-naming.toml`; structurally barred by `xtask/src/release.rs:550-556` (exactly one `.whl`) | CI-tested (`.github/workflows/python-sdk.yml:52-64`) but **never built into a wheel** |\n| 9 | `@auths-dev/runtime-json-store` @ **0.1.0** | TypeScript | **stale ADAPTER** | 2 files, 1 class | 1 (`DurableJsonExecutionStateStore`) | **No** | **Nothing.** No `\"private\": true` → publishable. License `Apache-2.0` only, contradicting `MIT OR Apache-2.0` everywhere else |\n| 10 | `bindings/recipes` | TS + Python | **worked examples / demo** | 10 files / 609 lines (TS 250, PY 359) + 2 Node tools | n/a | **No** | `.github/workflows/sdk-recipes.yml` runner; **1 of 10 currently fails** (B-16) |\n| 11 | 42 crates.io crates @ 1.0.0-rc.1 | Rust | **product + reference** | workspace | — | **Yes** — tiers 0-8, verified 11/11/7/2/3/3/1/2/2 = 42 | **Yes, exemplary.** `xtask/src/public_naming.rs:394-411` set-equality against `semantic-freeze.json` `rustPublishableClosure` (42, verified). Zero drift both directions |\n| 12 | `bindings/typescript/auths-dev-sdk-1.0.0-rc.1.tgz` | — | **stale committed artifact** | 302,505 B, git-tracked | — | Travels inside `source-archive` release subject | Blocked from the npm tarball by `package.test.js:86`; not removed from the repo |\n\n---\n\n## The three questions\n\n### (a) Go: independent conformance oracle, or second source of semantic truth?\n\n**Verdict: it is a genuine oracle and a real strength. It executes in CI. The only blocker is the label, not the code.**\n\nEvidence it is an oracle, not a binding:\n- It re-derives the entire V1 waist from scratch with **zero third-party dependencies** — `bindings/independent/go/auths/semantic.go:19-20` says so explicitly, and `semantic.go:1108-1145` re-derives `domainHash`/`signingPreimage` byte-for-byte.\n- It never reads the expected verdict before deciding: `verifier.go:139-144` carries the comment \"keeps expected data out of the verifier itself\"; disagreement aborts at `verifier.go:71-80`.\n- The action body stays opaque bytes (`semantic.go:462-469`) — **no profile or domain machinery leaks in**, which is more than the WASM and pyo3 layers can say.\n- `architecture.toml:276` `exempt_paths = [\"bindings/independent\"]` — the binding-semantics scanner deliberately excludes it *because it is not a binding*.\n- It appears in **no** release artifact: verified absent from `release/public-naming.toml`, `release/release-subjects.toml`, and `bindings/public-topology-v1.json`.\n\n**It genuinely runs in CI — verified.** `xtask/src/checks.rs:159-177` `bindings_conformance()` runs `go vet ./...`, `go test -race ./...`, then `cross_language_corpus()`, which at `xtask/src/fixtures.rs:23-85` requires `go_semantic == typescript_semantic == rust_semantic` on a normalized 102-fixture projection. `bindings_conformance()` is reached from `ci_compliance()` (`checks.rs:73,82`) and from `bindings_check()` (`checks.rs:156`), and `.github/ci/phase-ownership.toml:135-138` maps the `bindings/` prefix to `[\"authoritative\", \"compliance\", \"secrets\"]`.\n\nThe blocker is one line of metadata: `compliance.toml:1473` claims role `language-binding` — the same role the real TypeScript SDK claims at `compliance.toml:1440` — for a tree whose own header says it \"intentionally does not link to Rust.\" Drop that claim; keep `independent-semantic-implementation`.\n\nThree caveats that shrink what the oracle *proves*:\n- **It is one oracle, ported, not two.** Go and TypeScript share ~29 identically-named functions in the same order, the same `0x33` fixture hardcode (`verifier.go:262` / `semantic-verifier.ts:1297`), and the same status-quantification shortcut. `docs/TRACEABILITY.md:5-6` defines \"independent\" in a way these two do not jointly satisfy.\n- **Three-way divergence already exists outside the corpus** — verified: `core/crates/auths-codec/src/hash.rs:105` uses `u16::try_from(...).map_err(|_| CodecError::LimitExceeded)?`; `bindings/independent/go/auths/semantic.go:1135` silently truncates with `uint16(len(profile.id))`; `bindings/independent/typescript/semantic-verifier.ts:671` `header.writeUInt16BE(profileBytes.length, 4)` throws. Same bytes, three different stable codes.\n- **The SDK costume must go.** `api.go:89-97` exports `Verify(proof, action, context)` documented as \"the portable three-input boundary\"; it delegates to `(&Engine{}).Verify`, and `verifier.go:264-269` does `hex.DecodeString(\"\")` → length 0 ≠ 32 → **`denied(\"verifier-configuration-mismatch\")` unconditionally**. `NewEngine` is broken on its own documented input: `adapters.go:87-93` declares five fields with `DisallowUnknownFields`, while `core/fixtures/v1/manifest.json` `adapter_context` has **six** keys including `did_keri` (verified). No test exercises either — `api_test.go:35` bypasses both with `&Engine{adapters: corpus.AdapterContext}` (I read the full file).\n\n*Nuance between auditors:* GO-01 cites `TestVerifyReturnsNativeAuthorizedResult` as the `language-binding` evidence while GO-02 says no test calls `Verify`. Both are right: the test calls the *method* `engine.Verify`, never the package-level *function* or `NewEngine`. Not a contradiction.\n\n**Recommendation: advertise the oracle loudly in `docs/TRACEABILITY.md` (it is currently absent from the differential-oracle column), fix the `compliance.toml` role, and delete `Verify`/`Engine`/`NewEngine`/`Explanation` from `api.go`, exporting only `AuditCorpus`.**\n\n---\n\n### (b) WASM: how many exports belong in a consumer package?\n\n**First, a correction to the brief.** \"262\" is the count of occurrences of the *string* `wasm_bindgen` in `bindings/wasm/auths-proof-wasm/src/lib.rs`, which includes 16 `serde_wasm_bindgen` calls and one prelude import. The real numbers, all verified:\n\n| Measure | Count |\n|---|---|\n| `#[wasm_bindgen` attributes in `src/lib.rs` | **245** |\n| Top-level exported names in `auths_proof_wasm.d.ts` | **96** (75 functions + 21 classes) |\n| Total public JS symbols including class members | **228** (75 + 21 + 132 members) |\n\n**Belong in a consumer package: 183 of 228. Reference machinery that must be removed: 45 of 228.**\n\n**Remove — 45 symbols (verified present in the `.d.ts`):**\n\n*Generic domain reference surface — 22 symbols, named for deletion by the consolidation spec at `PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:183,185,530`:*\n`parseHttpActionV1`, `parseGitActionV1`, `parseDeploymentActionV1`, `parseSupplyChainActionV1`, `parseEdgeActionV1`, `parseCanonicalHttpActionV1`, `parseCanonicalGitActionV1`, `parseCanonicalDeploymentActionV1`, `parseCanonicalSupplyChainActionV1`, `parseCanonicalEdgeActionV1` (10 functions), plus class `DomainActionFieldsV1` + its 11 members. Backed by `src/lib.rs:37-41` importing `reference_canonicalize_{http,git,deployment,supply_chain,edge}` by name.\n\n*Caller-owned profile canonicalization — 7 symbols:* `prepareProfileActionV1`, `canonicalizeProfilePlanMemberV1`, class `ProfileActionPreparationV1` + 4 members.\n\n*Issuer-side / generic policy authoring — 12 symbols:* `compileTrustedContextV1`, class `TrustedContextCompilationV1` + 2 members; `encodePrincipalStatusStatementV1`, `encodeGrantStatusStatementV1`, `parsePrincipalStatusSnapshotV1`, `parseGrantStatusSnapshotV1`, class `StatusSnapshotV1` + 3 members.\n\n*Dual paths and dev scaffolding — 4 symbols:* `planChildGrantV1` (duplicate of `planChildGrantFieldsV1`, and the only SDK-unreferenced authoring export), `developmentEd25519PublicKeyV1` (the sole reason `ed25519-dalek` is a direct dependency), `commitCanonicalV1` (arbitrary caller-chosen domain separation), `verifyBatchV1` (second verification vocabulary with WASM-local limits at `src/lib.rs:164-165`).\n\nDeleting the 12 policy-authoring symbols also removes the *only* reason the 26-name `LimitKind` vocabulary at `src/lib.rs:785-815` exists.\n\n**Reachability is the inverse of the declared topology, verified:** `Cargo.toml` depends on `auths-profile-mcp` (reaching `auths.mcp/1`) and on `auths-profile-domains` (reaching five *unqualified* domains), and has **zero** dependency on `auths-github`, `auths-opentofu`, or `auths-postgresql` — 3 of the 4 `qualifiedProfiles` are unreachable. `SUPPORTED_PROFILES.md` documents *principal methods*, not action profiles, and would remain accurate after deleting every profile export.\n\n**The cut is coordinated, not local.** `bindings/typescript/src/verifier/wasm.ts` has **56** `typeof loaded.X !== \"function\"` load-time assertions (verified), ten of which are the domain parsers at `:57-66`, plus `prepareProfileActionV1` and `canonicalizeProfilePlanMemberV1`. Remove the Rust exports first and every `@auths-dev/sdk` import throws. Sequence: shrink the guard → delete `src/profiles/domains/index.ts` (**0 importers**, verified) → delete the Rust exports.\n\n**ABI drift, verified exactly:** union of `authoring-abi-v1.json` (46) and `identity-abi-v1.json` (12) = 58 declared, zero overlap. Against the 96 top-level exports: **0 declared-but-missing, 38 exported-and-undeclared** — including `verifyV1` itself, `verifyBatchV1`, `configurationV1`, all 6 production-client exports, all 9 receipt exports, and 18 result classes.\n\n**Coverage:** `tests/node-smoke.cjs` (60 lines) exercises `verifyV1` and `configurationV1` — **2 of 228 ≈ 0.9%**. `examples/generate-node-vectors.rs` (636 lines) calls only the two plain `pub fn` helpers and reaches **zero** `#[wasm_bindgen]` symbols.\n\n---\n\n### (c) `bindings/public-topology-v1.json` — the exact delta, per language\n\n**Layer names: zero drift. Both gates verified.**\n\n| Layer | TypeScript declared | Actual `exports` keys | Python declared | Actual importable |\n|---|---|---|---|---|\n| product | `@auths-dev/sdk`, `/identity`, `/verify` | `.`, `./identity`, `./verify` ✅ | `auths`, `auths.identity`, `auths.verify` | ✅ |\n| vertical | `/profiles` | `./profiles` ✅ | `auths.profiles` | ✅ |\n| mechanism | `/integrations` | `./integrations` ✅ | `auths.integrations` | ✅ |\n| extension | `/framework` | `./framework` ✅ | `auths.framework` | ✅ |\n| test | `/testkit` | `./testkit` ✅ | `auths.testkit` | ✅ |\n\n`bindings/typescript/tools/public-api.mjs:19` does an **order-sensitive `JSON.stringify` comparison** of `Object.keys(packageJson.exports)` against the flattened topology and throws on drift. `bindings/python/tools/check_public_api.py:11-16` imports exactly the declared list and requires an explicit string `__all__`. Both verified by reading the source.\n\n**Below the layer names, the two SDKs are not the same API — measured from the two committed snapshots:**\n\n| Layer | TS symbols | PY symbols | Shared (case/underscore-normalized) | TS-only | PY-only |\n|---|---|---|---|---|---|\n| product `.` / `auths` | 41 | 35 | **35** | 6 | 0 |\n| `/identity` | 33 | 19 | **4** | 29 | 15 |\n| `/verify` | 38 | 36 | **26** | 12 | 10 |\n| `/profiles` | 37 | 20 | **14** | 23 | 6 |\n| `/integrations` | 4 | 5 | **2** | 2 | 3 |\n| `/framework` | 11 | 11 | **11** | 0 | 0 |\n| `/testkit` | 29 | 33 | **17** | 12 | 16 |\n\nThe identity layer shares exactly four names: `DecodedIdentity`, `IdentityPrincipal`, `ResolutionEvidence`, `ValidatedIdentity`. TypeScript models Adapter/Registry and splits every concept into a `*Descriptor` twin; Python models Method/Registry/Resolver. Neither is visibly a projection of a Rust-owned vocabulary.\n\n**Framework is the trap: 11/11 exact name parity, and the contracts are incompatible.** Verified:\n```ts\n// bindings/typescript/src/internal/mechanisms.ts:7-11\nreserve(record): Promise<\"acquired\"|\"exact-replay\"|\"conflict\">;\nreopen?(): AtomicReservationStore | Promise;\nclose?(): void | Promise;\n```\n```python\n# bindings/python/python/auths/_mechanisms.py:14-21\nasync def reserve(self, record) -> Literal[\"acquired\",\"exact-replay\",\"conflict\"]: ...\nasync def aclose(self) -> None: ...\nasync def reopen(self) -> AtomicReservationStore: ...\n```\nDifferent disposer name, different optionality. A valid TypeScript store is not a valid Python one, and name parity is 100%.\n\n**Undeclared shipped entry points:** the npm `bin` `auths` (`package.json:6-8`, verified), Python `auths/__main__.py` (asserted into every wheel by `check_wheel.py:11`), and the 96-symbol `wasm/auths_proof_wasm.d.ts` carried by `package.json` `files` — none appear in the topology, and `public-api.mjs` walks only the `exports` map.\n\n**Does ANY gate enforce the file? Only its `layers` field.** Verified: repo-wide, exactly four files reference `public-topology` outside the artifact itself and its stale copy — `bindings/python/tools/check_public_api.py`, `bindings/typescript/tools/public-api.mjs`, `bindings/typescript/sdk-capability.json`, `.github/workflows/python-sdk.yml`. `xtask/src/main.rs` dispatches ~50 subcommands and **none is `public-topology`**; `ci_authoritative()` (`checks.rs:11-43`) never touches the file. Grepping `frameworkContracts|qualifiedProfiles` across the repo returns **only** the artifact, the untracked `release/docs-bundle/public-topology.json` (which says `\"qualifiedProfiles\": [\"auths.mcp/1\"]` — one, not four), the assurance-manifest schema, and `release/assurance/open-production-candidate-1/manifest.json` (which says **three**, omitting `auths.mcp/1`, matching `xtask/src/assurance.rs:15-19` `REQUIRED_PROFILES` which **hard-fails** on any other set at `assurance.rs:612-619`). So the assurance gate actively rejects the topology contract's own four-item set.\n\n---\n\n## Semantic ownership violations — the blocker class\n\nEvery place a binding **defines** rather than **projects**. Effect-freedom is satisfied at most of these; that is the point.\n\n| # | Where meaning is authored | Binding | Rust owner | Proof |\n|---|---|---|---|---|\n| 1 | The verdict itself: integer → `\"authorized\"` | TypeScript | **none exported** | `bindings/typescript/src/verifier/decoder.ts:149-153` — a local `[\"authorized\",\"denied\",\"indeterminate\"]` array indexed by a hand-decoded CBOR integer; feeds `verifier/result.ts:256-263` which **mints `VerifiedAction`**. No `projectVerificationResultV1` exists on the WASM ABI |\n| 2 | What a valid **root authority** is | WASM + Python | **none** — `grep validate_root_authority` across `core/` and `product/` returns 0 | `bindings/wasm/.../lib.rs:1595-1627` (4 string comparisons) vs `bindings/python/src/workflow.rs:370-400` (typed, single collapsed message). WASM has no `bind_delegated_authority` at all |\n| 3 | What `raw-key-baseline` **requires** | WASM vs Python | **none** in production Rust | `lib.rs:3179-3196` builds **2** requirements; `bindings/python/python/auths/_bootstrap.py:107-115` builds **4** for the same policy id, adding `offline-verifiable` for root and actor. Verified by reading both. Same name, materially weaker meaning, chosen by which SDK you used |\n| 4 | ~150 lines of root-of-trust policy | WASM | none | `lib.rs:3103-3255`: `ActionConstraint::AnyBody`, `StatusPolicy::ExpiryOnly` twice (no revocation ever consulted), suite list pinned to `ed25519-v1` only, placeholder snapshot ids `[0x44;32]`/`[0x55;32]`, `ChannelBindingId::parse(\"none-v1\")` |\n| 5 | What \"canonical\" means for an arbitrary profile | WASM | the vertical should | `lib.rs:4262-4296` → `4465-4497`: caller supplies both the profile-id string and the bytes; `body.to_vec()` accepted verbatim. Contrast `lib.rs:4367-4404`, which runs `McpProfile.canonicalize` first, and `lib.rs:2791`, where even the *domain* path asserts `canonical.body() == body` |\n| 6 | 67 inbound string→enum vocabularies | WASM (+ Python twins) | **partly none** | `lib.rs:785-815` maps 26 `LimitKind` strings — `\"bundle-bytes\"`, `\"plan-branching\"` etc. exist **only** here and in generated demo `.d.ts`; `auths-model` has no `parse`/`as_str` for `LimitKind`. 15 more tables at `225-232, 234-241, 651-659, 661-667, 3443-3448, 3485-3490, 3742-3752, 3863-3866, 3962-3972, 4075-4080, 4749-4834`. `participant_role` duplicated at `bindings/python/src/authoring.rs:1433` |\n| 7 | The decision oracle strings | WASM + Python + vector generator | enums exist, **no `as_str()`** | `lib.rs:4158,4177,4179` spell `\"summary\"/\"full\"/\"opaque\"`, `\"authorized\"/\"denied\"/\"indeterminate\"`, `\"succeeded\"/\"failed\"` inline. `product/receipts/auths-receipts/src/lib.rs:186-193,299-304` define the variants without accessors. A **fourth** copy lives in `examples/generate-node-vectors.rs:569-575` — the conformance-vector generator |\n| 8 | Receipt-error classification | WASM + Python | **exists but private** | `lib.rs:4205-4221` and `bindings/python/src/receipts.rs:477-493` are byte-for-byte duplicates of `product/receipts/auths-receipts/src/disclosure.rs:611-624`, which is `fn`, not `pub fn`. Five further codes exist only in bindings. Both copies merge `InvalidSignature` with `SigningUnavailable` — a fail-closed crypto rejection collapsed into a transient custody condition |\n| 9 | `auths.portable-receipt/1` — a wire format | TypeScript + Python | **none** | Verified repo-wide: the schema string appears at `bindings/typescript/src/internal/receipt-attestation.ts:138,154` and `bindings/python/python/auths/_receipts.py:244,264` and **nowhere in any `.rs`, CDDL, or fixture**. Two bindings, one versioned protocol, zero cores |\n| 10 | Production endpoint routing | TypeScript + Python | **exists, not exposed** | `product/runtime/auths-production-client/src/lib.rs:200-206,330-338` are exhaustive `const fn match`. `bindings/typescript/src/production-client.ts:372-380` is five `if`s and a **silent fallback** to the GitHub endpoint; `bindings/python/python/auths/_production_client.py:400-413` hardcodes all seven paths. The WASM ABI exposes no path function |\n| 11 | The closed qualified-profile set | 6 copies | `QualifiedProfile` exists; the spec-mandated **`qualified_profiles()` / `QualifiedProfileDescriptor` do not** (verified: grep returns 0 across all `.rs`) | (1) topology: **4**; (2) `auths-production-client/src/lib.rs:167-172`: **3**, no mcp; (3) `xtask/src/assurance.rs:15-19`: **3**, hard-fails otherwise; (4) `bindings/typescript/src/profiles.ts:4-7`; (5) `bindings/python/python/auths/profiles/__init__.py:24-45`; (6) `xtask/src/checks.rs:232-234` inside an npm smoke string literal. `PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:340-352` names TypeScript and Python by name as forbidden |\n| 12 | The extension contract shape | TypeScript vs Python | **none** — no Rust trait for either `frameworkContract` | `internal/mechanisms.ts:7-11` vs `_mechanisms.py:14-21` (above). Load-bearing: `testkit/conformance.ts:272` `await store.close?.()` silently skips a store with no disposer; `_conformance.py:640` `getattr(value, \"aclose\", None)` |\n| 13 | Sealed types minted from caller bytes | TypeScript | should be native | `src/product.ts:91-93,110-119` — `ExecutionReference.decode` rebuilds a sealed value from consumer bytes via a TS-authored regex; the *same* format is re-declared with different bounds at `profiles/mcp/index.ts:1041` (`length > 256` vs exactly 134 — verified). `internal/receipt-attestation.ts:144-159` `decodeLinkedReceipt` reconstructs a `Receipt` from arbitrary JSON with **no cryptographic check** |\n| 14 | Recovery-reference validation, weaker than Rust | TypeScript | exists | `production-client.ts:445-450` accepts `/^[A-Za-z0-9_-]{43}$/`. `auths-production-client/src/lib.rs:219-230` additionally requires canonical base64url decode into 32 bytes **and rejects the all-zero value** (verified). 43 `A`s pass TS, fail Rust |\n| 15 | Generic profile machinery in the pyo3 ABI | Python native | reference crate | `bindings/python/src/domains.rs:2` and `http.rs:15` import from `auths-profile-domains`, registered unconditionally at `lib.rs:38,40` (verified). **18 symbols with zero consumers** in the shipped `auths` package — the Python-layer wrappers were already deleted, and `tools/check_wheel.py:54-55` asserts they stay deleted |\n| 16 | Receipt inspection reachable only for unqualified profiles | Python | wrong owner | `bindings/python/src/receipts.rs:5,409` wires `DomainReceiptInspector` into public `auths.verify.inspect_receipt()`. `product/profiles/auths-profile-domains/src/lib.rs:873-913` handles `auths.{http,git,deploy,supply-chain,edge}/1` and returns `UnsupportedProfile` for everything else — **zero of the four qualified profiles** |\n| 17 | Lifecycle state adjudication | Python | **exists in Rust, bypassed** | `bindings/python/src/runtime.rs:12-57` exports the 15-gate `TransitionGates` kernel; only the two trivial outcome mappers are ever called. `_application_profile.py:905-919` invents a 5-value rejection vocabulary that matches neither `KernelCode` nor `ReplayCode`, defaulting unknown values to `effect_state=\"not-started\"` |\n| 18 | Retry class restated as a literal | TypeScript | invariant exists | `production-client.ts:396,413,420,246` hardcode `retry` per branch, overriding `projection.retry`. `auths-production-client/src/lib.rs:480-511` `valid_shape` enforces the kind↔retry pairing; TS accepts a violating projection and launders it |\n| 19 | `ErrorEnvelope::parse` transcribed twice | Python + TypeScript | exists, unreachable | `_product_errors.py:207-277` and `product-errors.ts:161-205` re-derive `auths-errors/src/lib.rs:168-219,293-333` statement-by-statement and **both omit** `lib.rs:329-331` (`NotApplied` + receipt reference ⇒ `InvalidReference`). Latent today — I verified **all 45** registry codes have `allowsReceiptReference: false` |\n| 20 | Explanation prose, five copies | Go + TS + Python + 2× Rust | diffuse | `bindings/independent/go/auths/api.go:72-82`, `bindings/typescript/src/verifier/explanation.ts:5-10`, `bindings/python/python/auths/verify.py:169-173`, `core/crates/auths-proof/src/lib.rs:227`, `product/sdk/auths-sdk/src/lib.rs:378,386` |\n\n---\n\n## Error model degradation\n\nThe `effect` axis is the product's premise. **Correction to the brief: `product/errors/v1/registry.json` has 45 definitions, not 20** — verified by parsing: outcome pairs are `{never,not-applied}×26`, `{safe,not-applied}×4`, `{conditional,not-applied}×5`, `{unknown,possible}×9`, `{conditional,applied}×1`. The nine `possible` codes are `core.outcome-unknown`, `core.observation-pending`, `core.observation-inconclusive`, `mcp.invalid-handler-output`, `mcp.handler-failed`, `mcp.handler-timeout`, `mcp.reconciliation-pending`, `plan.member-interrupted`, `plan.reconciliation-pending` — all with `allowsExecutionReference: true` and `recommendedAction: resume-and-reconcile`. Ten codes total allow an execution reference. **Zero** allow a receipt reference.\n\n| # | Losing code path | What is lost | Direction |\n|---|---|---|---|\n| **E-1** | `bindings/wasm/auths-proof-wasm/src/lib.rs:4927-4929` — every fallible export funnels through `js_error` into `JsValue::from_str` (a JS *string*, not an `Error`, so `catch (e) { e.message }` is `undefined`) | **All 45 codes, all retry classes, all effect states.** The 9 `possible` codes become byte-identical to the 26 `never/not-applied` ones. `grep -c 'core\\.\\|mcp\\.\\|plan\\.' src/lib.rs` = 0 | Fail-open |\n| **E-2** | `bindings/typescript/src/production-client.ts:276-286` and `bindings/python/python/auths/_production_client.py:274-284` — any transport throw, **including the 15s `AbortController` timeout**, on any step including `execute`, returns `code:\"core.runtime-unavailable\", retry:\"backoff\"` | The registry defines that code as `retry:safe, effect:not-applied, recommendedAction:retry-execution`. The correct code is `core.outcome-unknown` (`effect:possible`). Two bindings independently invented the same fiction; **no Rust owner exists** (`grep core.runtime-unavailable` in `auths-production-client` and `auths-node` = 0) | **Fail-open — highest consequence.** A retried `POST /v1/profiles/postgresql/bounded-update/execute` may apply the update twice |\n| **E-3** | Same two files, next branch (`production-client.ts:287-297` / `_production_client.py:286-300`) — any non-2xx or content-type mismatch → `core.malformed-input`, `retry:\"backoff\"` | Registry says `retry:never, effect:not-applied`. A 500 emitted *after* a successful write is reported as definitively not applied | Fail-open |\n| **E-4** | `bindings/typescript/src/product.ts:70-78,385-387` — `Denied`/`Indeterminate` are `{kind, code}` only, and the projection does `Object.freeze({kind, code})` | `retry`, `effect`, `recommendedAction`, `executionReference` all dropped at the last hop. `EffectState` is not even exported from `.` | Silent |\n| **E-5** | `bindings/python/python/auths/_product.py:69-78` and `_production_client.py:96-117` — `Denied(kind, code)`, `ProductionIndeterminate(kind, step, code, retry)` | **No `effect` field exists on either Python product result surface at all** | Silent |\n| **E-6** | Four incompatible `EffectState` vocabularies in TypeScript | `product-errors.ts:8` widens the sealed 3-value Rust enum with a 4th (`\"unknown\"`); `workflow/errors.ts:47-48` invents `\"none\"|\"possible\"|\"occurred\"` (publicly re-exported via `workflow-client.ts:31`); `profiles/mcp/index.ts:163-166` and `profiles/domains/index.ts:78-83` both **drop `possible`** while `src/generated/mcp-profile.ts:24-28` lists it as mandatory (verified in the generated file) | Consumers have no `possible` case to `switch` on |\n| **E-7** | Three incompatible `RetryClass` in Python — `_errors.py:8` (4 values), `_product_errors.py:20-24` (enum), `_production_client.py:31` (`never/backoff/resume/reconcile`, and this is what `auths.RetryClass` binds to) | Root cause is upstream: Rust itself carries two disjoint enums, `auths_errors::RetryPolicy` and `auths_production_client::RetryClass` (no effect field) | Duplicate vocabulary |\n| **E-8** | Two unrelated `AuthsError` classes in one wheel — `_errors.py:30-49` vs `_product_errors.py:110-140`, no shared base | The former's effect vocabulary (`not-started/in-progress/completed/failed/outcome-unknown`) **cannot express `possible`**, and it is the ancestor of `ProviderOperationError`, which is *publicly exported* via `auths.framework`. `_application_profile.py:598-601` branches on `effect_state == \"not-started\"`; `:905-919` hardcodes it for every store rejection | Fail-open at the extension boundary |\n| **E-9** | `bindings/python/python/auths/verify.py:167-173` — `retryable = kind == \"indeterminate\"` | A boolean derived from the coarse verdict, discarding the per-code answer. `core.observation-inconclusive` (`effect:possible`, `resume-and-reconcile`) is advertised as retryable | Fail-open |\n| **E-10** | `_product_errors.py:280-306` `_unknown_details` returns `execution_reference=None`; `product-errors.ts` `parseUnknownDetails` omits it | A newer Rust emitting an unregistered `effect:possible` code **with a valid execution reference** is handed to an older SDK, which reports `effect=unknown`, says `contact-support`, and **discards the reconciliation handle** | Forward-compat loss |\n| **E-11** | `bindings/python/src/receipts.rs:477-500` plus literals at `:330,348,355,367,382,385` | An 11-value code namespace (`receipt-malformed`, `receipt-suite-unavailable`, `inspection-mode-unsupported`, …) registered in **none** of the 45 definitions. Surfaces publicly as `InvalidReceiptInspection.code: str` via `auths.verify`. *Credit: the function returns bytes, not `PyResult`, so it genuinely cannot raise — the structure is fail-closed, only the vocabulary is unowned* | No lookup possible |\n| **E-12** | `bindings/python/python/auths/_product_errors.py:13` — `AuthsErrorCode = str` | `PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:483-486` requires a generated `KnownAuthsErrorCode` union plus explicit `UnknownAuthsErrorCode`. Two of three code-carrying surfaces (`_production_client._required_code:494-498`, `verify.py:66-96,124-163`) skip the registry lookup entirely. Direct enum construction at `_product_errors.py:223,224,230,261` raises a bare `ValueError` on a newer Rust retry class | Known-vs-unknown distinction absent from the type system |\n| **E-13** | `docs/product/recipes/06_PRODUCTION_FAILURES.md:24,40-45` branches on `result.retry === \"reconcile\"` | The field **does not exist** on `Indeterminate` in either language (`product.ts:75-78`, `_product.py:76-78`). `undefined` falls through to `retryWithBackoff(result.code)`. The one document about not repeating ambiguous effects teaches repeating them, and it is the only recipe never executed | Fail-open, in documentation |\n| **E-14** | Recipes never touch the registry — grep across all 10 files for `AuthsError\\|AuthsErrorCode\\|RecommendedAction\\|RetryClass` returns 5 hits, all `effect:` string literals in MCP handler outcomes | `Denied.code` is never read or printed anywhere; no recipe produces an `Indeterminate`; `expected` values in `manifest.json` are 5 successes and 0 denials as the primary outcome. A developer copying these learns `if (result.kind !== \"completed\") throw` | Teaching loss |\n| **E-15** | `Cargo.toml:281-284` `[profile.release] panic = \"abort\"` with `crate-type = [\"cdylib\"]` | pyo3's `catch_unwind` cannot catch an aborting panic → SIGABRT, not `PanicException`. No `try/except`, no `finally`, no error envelope, no correlation id; any in-flight reservation is orphaned with effect permanently unknown | **Not fail-closed — fail-absent** |\n\n---\n\n## Findings\n\nRanked: blockers, then major, then minor; within a tier, root causes before symptoms. `Src` maps to the originating auditor ids.\n\n### Blockers\n\n| ID | Title | Evidence | Goal | Lane | Disposition | Src |\n|---|---|---|---|---|---|---|\n| **B-01** | Generic `auths-profile-domains` machinery is wired into **three** consumer artifacts and load-bearing in a fourth | `bindings/wasm/auths-proof-wasm/src/lib.rs:37-41` imports `reference_canonicalize_*` by name → 22 exports at `:2650-2932`; `bindings/python/src/domains.rs:2` + `http.rs:15` registered at `lib.rs:38,40` → 18 pyo3 symbols with **0** consumers (`check_wheel.py:54-55` proves the Python-layer removal already happened); `bindings/typescript/src/profiles/domains/index.ts` 397 lines, **0 importers** (verified), yet compiled into `dist/profiles/domains/index.{js,d.ts}` (9,457 + 5,545 B, verified on disk) and shipped by `package.json:40`; `bindings/typescript/src/verifier/wasm.ts:57-66` **hard-throws** without all ten parsers. Spec: `PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:183,185,530,533` | simple | cross | **delete**, sequenced: guard → TS module → pyo3 modules → Rust exports → `Cargo.toml` deps → ABI manifests | WASM-01, PYN-01, TSN-07, WASM-14, TOPO-09, PKG-05 |\n| **B-02** | Every WASM error collapses to a bare JS string — 45 codes, retry class, and effect state all lost | `src/lib.rs:4927-4929`; `grep -c 'core\\.\\|mcp\\.\\|plan\\.' src/lib.rs` = 0. `verifyV1` already does this right by returning canonical protocol bytes (`:5003-5011`) | secure | wasm | **redesign** — structured `{code, retry, effect, executionReference?}`; delete `js_error` | WASM-06 |\n| **B-03** | Both bindings synthesize `core.runtime-unavailable` / `retry:\"backoff\"` for a possibly-applied `execute` | `production-client.ts:276-286`, `_production_client.py:274-284` (both read in full). Registry: `retry:safe, effect:not-applied`. No Rust producer exists | secure | cross | **move-to-rust** — `ProductionResponse::transport_failure(verb)` | TSN-02, PYN-02 |\n| **B-04** | The trust bootstrap has no Rust owner and already diverges between SDKs | `lib.rs:3179-3196` (**2** requirements) vs `_bootstrap.py:107-115` (**4**) for the same `raw-key-baseline` id — both read; `lib.rs:3103-3255` encodes ~150 lines of root-of-trust policy; `lib.rs:1595-1627` vs `bindings/python/src/workflow.rs:370-400` implement root-authority validation twice with no Rust `validate_root_authority` anywhere | secure | cross | **move-to-rust** — one `bootstrap_raw_key_authority` + `validate_root_authority`/`bind_delegated_authority` in `auths-author` | WASM-02, WASM-03, WASM-09 |\n| **B-05** | TypeScript decides the verdict: a local array maps a hand-decoded CBOR integer to `\"authorized\"` | `verifier/decoder.ts:149-153`; the whole 165-line file is a hand-rolled CBOR reader (`:116` `reader.map() !== 16`, `:147` `abiVersion !== 2n`). Result mints `VerifiedAction` at `verifier/result.ts:256-263`. No native projection export exists | secure | typescript | **move-to-rust** — `projectVerificationResultV1`; delete `decoder.ts` | TSN-01 |\n| **B-06** | `auths.portable-receipt/1` is a versioned wire format owned by two bindings and zero cores | Verified repo-wide: `receipt-attestation.ts:138,154`, `_receipts.py:244,264`, and nowhere in Rust/CDDL/fixtures. Published as `decodeReceipt`/`encodeReceipt`/`type Receipt` | secure | cross | **move-to-rust** into `auths-receipts` + canonical fixture | TSN-04 |\n| **B-07** | Sealed types are reconstructible from caller bytes with no cryptographic check | `product.ts:91-93,110-119` (`ExecutionReference.decode`, exported as a value); duplicate format with different bounds at `profiles/mcp/index.ts:1041`; `receipt-attestation.ts:144-159` returns an unverified `Receipt`. `test/contract/nonforgeability.ts` (105 lines) exercises neither | secure | typescript | **fix** — native parse; `decodeReceipt` verify-or-throw | TSN-05 |\n| **B-08** | `prepareProfileActionV1` lets the caller declare the profile id **and** supply the canonical bytes | `lib.rs:4262-4296` → `4465-4497`: `body.to_vec()` accepted verbatim. Contrast `:4367-4404` (MCP canonicalizes) and `:2791` (the domain path at least asserts canonicality) | secure | wasm | **delete** (7 symbols) | WASM-04 |\n| **B-09** | 67 inbound + 3 outbound + 2 receipt vocabularies authored in bindings, several with no Rust definition at all | `lib.rs:785-815` — 26 `LimitKind` names existing nowhere in Rust (verified); 15 more tables; `lib.rs:4158,4177,4179` re-invent the decision oracle, as does the **vector generator** at `examples/generate-node-vectors.rs:569-575`; `lib.rs:4205-4221` ≡ `bindings/python/src/receipts.rs:477-493` ≡ private `disclosure.rs:611-624` | secure | cross | **move-to-rust** — `parse()`/`as_str()` in `auths-model`/`auths-receipts`; make `from_receipt` `pub` | WASM-05, WASM-07, WASM-08 |\n| **B-10** | `qualifiedProfiles` exists as **six** disagreeing lists; the spec-mandated Rust registry does not exist | Verified: `grep QualifiedProfileDescriptor\\|fn qualified_profiles` across all `.rs` = **0**. Topology **4**; `auths-production-client/src/lib.rs:167-172` **3**; `xtask/src/assurance.rs:15-19` **3** + hard-fail at `:612-619`; `profiles.ts:4-7`; `profiles/__init__.py:24-45`; `checks.rs:232-234`. Spec `:340-352` names both languages. Consequence: `production-client.ts:372-380` falls back to the GitHub endpoint for an unknown profile | multiple | cross | **fix-before-v1** — build `qualified_profiles()`, generate both unions, derive `REQUIRED_PROFILES` and the smoke string | TOPO-02, PKG-10, PYN-10, TSN-06 |\n| **B-11** | `frameworkContracts` have no Rust owner and the two bindings are structurally incompatible | `internal/mechanisms.ts:7-11` vs `_mechanisms.py:14-21` (both read); `conformance.ts:272` vs `_conformance.py:640` make the divergence load-bearing. No Rust trait for either contract exists. 11/11 name parity hides it | extensible | cross | **fix-before-v1** — one Rust-owned trait + descriptor; project both | TOPO-01 |\n| **B-12** | Effect and retry vocabularies fragment into 4 (TS) and 3 (PY) incompatible sets; the product facade drops the field | `product-errors.ts:8` widens the sealed enum; `workflow/errors.ts:47-48` invents `none/occurred`; `mcp/index.ts:163-166` and `domains/index.ts:78-83` drop `possible`; `product.ts:70-78,385-387` and `_product.py:69-78` carry no effect at all; `_errors.py:8`/`_product_errors.py:20-24`/`_production_client.py:31`. Root cause is Rust: two disjoint retry enums | secure | cross | **fix-before-v1** — one Rust pair, projected once | TSN-03, TSN-09, PYN-04, PYN-05, TSN-15 |\n| **B-13** | Public `inspect_receipt()` supports five unqualified profiles and zero qualified ones | `bindings/python/src/receipts.rs:5,409` injects `DomainReceiptInspector`; `auths-profile-domains/src/lib.rs:873-913` `_ => UnsupportedProfile`. The only test asserts the **EdgeProfile** labels (`tests/test_receipt_inspection.py:56-63`) against fixture `profile: auths.edge` | secure | python | **fix** — compose from qualified verticals; add an `auths.mcp/1` disclosure fixture (do not mutate the existing oracle) | PYN-06 |\n| **B-14** | Shipped `.pyi` omits 19 of 35 public symbols — `from auths import create_auths` is a hard type error for every consumer | Verified by reading both: `__init__.pyi` re-exports **16**; runtime `__all__` is **35** (`create_auths`, `ProductionAuths`, `RetryClass`, 15 `Production*`, `ProductStep` absent). `py.typed` ships, so checkers use the stub. `check_public_api.py` reads runtime state; `check_wheel.py:8-40` asserts mere presence — **nothing validates the stub** | simple | python | **delete the stub** (`__init__.py:8-48` already has a full `TYPE_CHECKING` block) | PKG-02 |\n| **B-15** | `panic = \"abort\"` defeats pyo3's `catch_unwind` — any native panic kills the interpreter | `Cargo.toml:281-284` + `bindings/python/Cargo.toml:10-12` `crate-type = [\"cdylib\"]` (both verified). Reachable sites: `application.rs:385`, `http.rs:366`, `workflow.rs:482`, and every `borrow`/`borrow_mut` across `application.rs`, `mcp.rs`, `http.rs`, `authoring.rs` — plus the whole transitive dep graph | secure | python | **fix** — `panic = \"unwind\"` for the cdylib profile; convert the two `expect(\"non-empty\")` to `ok_or_else` | PYN-03 |\n| **B-16** | The canonical Python recipe 03 does not run — **reproduced** | `bindings/recipes/python/03_execute_exact_action.py:6` `from auths import verify_receipt` → `ImportError: cannot import name 'verify_receipt' from 'auths'` (I ran it). Removed by `22b7334`; **62 commits** since (`git rev-list --count`). Published verbatim at `docs/product/recipes/03_EXECUTE_ONE_ACTION.md:60,84`. Correct home is `auths.verify`, which is where the TS twin imports from and where recipe 05 correctly imports from — **two Python recipes disagree in one directory** | simple | python | **fix** + resolve why `.github/workflows/sdk-recipes.yml` (whose path filters do cover this file) never blocked it | REC-01, REC-09 |\n| **B-17** | Python recipe 01's advertised tamper proof is vacuous | `bindings/python/python/auths/testkit.py:258-265` — `if not material or not preimage or signature != self._signature` — the preimage is checked for **non-emptiness only**; the message is never bound. `recipes/python/01:38-40` changes **both** message and signature. The TS twin does it correctly (`01-authenticate-identity.ts:29-38` reuses the signature). `tools/generate-docs.mjs:27` asserts \"CI fails if the adversarial result changes\" | secure | security | **rewrite** against `auths.identity`'s real `Ed25519SignatureSuite`; change only the message | REC-02 |\n| **B-18** | Recipe 06 is unexecuted fiction teaching an unsafe retry on a possibly-applied effect | Listed at `docs/product/recipes/README.md:8`; absent from `bindings/recipes/manifest.json` (verified: 5 recipes) and from `generate-docs.mjs`. Uses the `ProductionAuths` call shape mid-series; branches on `result.retry`, a field neither `Indeterminate` type has | secure | cross | **delete or make executable** | REC-03 |\n| **B-19** | `compliance.toml` claims role `language-binding` for the Go oracle | `compliance.toml:1473`, the same role the real TS SDK claims at `:1440`, for a tree that states at `semantic.go:19-20` that it does not link to Rust — and that `architecture.toml:276` exempts from the binding-semantics scan precisely because it is not a binding | simple | packaging | **fix the claim** — keep only `independent-semantic-implementation` | GO-01 |\n| **B-20** | Release admits exactly one Linux wheel while the wheel asserts Windows + macOS + Linux, with no sdist | `xtask/src/release.rs:550-556` hard-fails unless exactly one `.whl`; only producer is `release-builder.yml:27 runs-on: ubuntu-latest`. `check_wheel.py:130-140` **requires** the three OS classifiers (verified in both files); `pyproject.toml:19-21` supplies them. The real three-OS matrix (`python-sdk.yml:108-113`) uploads to a PR artifact with `retention-days: 1`. No `sdist` step exists | secure | python | **fix** — multi-wheel set-equality, or narrow the classifiers. Do not weaken `check_wheel.py` | PKG-01 |\n| **B-21** | The one gate designed to catch all of the above cannot see any of it, and holds a false permanent allowance | `architecture.toml:270-295` (read in full): `scanned_paths` are **only** `bindings/typescript/src` and `bindings/python/python` — it never scans `bindings/wasm/**` or `bindings/python/src` (pyo3). Its `forbidden_patterns` are crypto primitives + `JSON.stringify` + `DataView`; verified that `decoder.ts` and `production-client.ts` contain **none** of them. `atob`/`btoa` (3 sites) are uncovered. `architecture.toml:339-343` grants `receipt-attestation.ts` a **`status = \"permanent\"`** `JSON.stringify` allowance reasoning it \"is not a protocol codec\" — contradicted by B-06 | secure | cross | **redesign** — positive rule: every schema/version literal and wire regex in binding source must appear in a Rust-owned generated file. Downgrade the receipt allowance to `temporary` | TSN-08 |\n| **B-22** | The two most security-relevant fields of the v1.0 API contract are enforced by nothing | Verified: `public-api.mjs:14-16` and `check_public_api.py:11-15` read only `layers`; no `public-topology` subcommand exists in `xtask/src/main.rs`; `ci_authoritative()` never touches the file. Repo-wide grep for `frameworkContracts\\|qualifiedProfiles` finds only the artifact, its stale copy, and the assurance schema | secure | cross | **fix-before-v1** — add `cargo xtask public-topology` to `ci_authoritative()`, modelled on `xtask/src/mechanism_conformance.rs:22-53` | TOPO-03 |\n| **B-23** | Two reference adapters exist, are documented, and are structurally excluded from every release | `bindings/python-adapters/sqlite/pyproject.toml:6-7` `auths-sqlite` 1.0.0rc1 — the **only** implementation in any language of `atomic-reservation-store`, registered as an official `referenceAdapter` at `bindings/python/adapter-contracts.json:41-47`, never built into a wheel. `bindings/typescript/adapters/durable-json/package.json` `@auths-dev/runtime-json-store` **0.1.0**, `license: \"Apache-2.0\"` only, **no `\"private\": true`**, advertised at `bindings/typescript/docs/production-integration.md:25-28` (which *ships* in the tarball while `adapters/` does not), implementing a port name `ExecutionStatePort` that returns 0 hits in `src/` | extensible | packaging | **sqlite: add to release plan. durable-json: delete** (stale vocabulary, prelaunch no-shims) | PKG-03, PKG-04 |\n| **B-24** | `sdk-vocabulary`'s collision detector prints; its only hard check is dead code | `xtask/src/sdk_vocabulary.rs:56-71` computes collisions and emits them as report JSON at `:111`, returning `Ok(())` at `:120` (read in full). The one `Err` branch at `:97-102` requires `enforcement == \"final\"`; `docs/product/sdk-glossary.json` is `\"prototype\"` (verified). Its `equivalentNames` has exactly 5 entries, of which 4 name TypeScript symbols absent from the public API. Live collisions include **`Receipt` as four distinct public types** and `Denied`/`Indeterminate` colliding between the product and verify tiers | simple | cross | **fix-before-v1** — `Err` on non-empty collisions; extend the projection to class methods; flip to `final` | TOPO-06, TOPO-07, REC-04 |\n\n### Major\n\n| ID | Title | Evidence | Goal | Lane | Disposition | Src |\n|---|---|---|---|---|---|---|\n| **B-25** | 38 of 96 top-level WASM exports — including `verifyV1` — are outside both frozen ABI manifests | Verified by set-difference: declared union 58 (46 + 12, no overlap), **0 missing, 38 undeclared**, incl. `verifyV1`, `verifyBatchV1`, `configurationV1`, all 6 production-client, all 9 receipt exports, 18 result classes. `release/evolution-policy-v1.json:47` pins only `authoring-abi-v1.json` | extensible | packaging | **generate** both manifests from source at build time | WASM-11 |\n| **B-26** | pyo3 ABI drift check is one-directional: 23 of 147 exports are undeclared | `tools/check_contract.py:27-32` asserts declared ⊆ actual, never the converse. Manifest 124; actual **147** (verified: 101 `#[pyfunction]` + 45 `#[pyclass]` + 1 exception). Undeclared includes the two generic domain functions and `DomainActionProjection` — i.e. B-01 is invisible to the only gate that inspects this surface | simple | python | **fix** — set equality | PYN-07 |\n| **B-27** | WASM ABI surface is asserted three ways, none derived from Rust | **56** hand-written `typeof loaded.X` checks in `verifier/wasm.ts` (verified count); `sdk-runtime-contract.json` `requiredWasmExports` = **7**, checked by a **regex text match** against the `.d.ts` (`tools/runtime-contract.mjs:19-21`); the generated `.d.ts` has 96. Nothing reconciles any pair. `evaluateRuntimeContract` (`runtime-contract.ts:36-49`) has no caller outside its own unit test | simple | wasm | **generate** `src/generated/wasm-abi.ts`; delete the 56 checks and the regex | TSN-16 |\n| **B-28** | WASM ABI coverage is 2 of 228 symbols (0.9%) | `tests/node-smoke.cjs` = 60 lines, 2 exports. `examples/generate-node-vectors.rs` = 636 lines calling only the two non-`wasm_bindgen` `pub fn`. The 10 `#[cfg(test)]` tests call `*_native` helpers, not the JS boundary. The 46 authoring exports, all 12 identity exports, the 15-member MCP session machine, and all 9 receipt exports have **zero** end-to-end coverage | secure | wasm | **fix** — round-trip assertion per surviving export | WASM-12 |\n| **B-29** | Runtime client is tested on the success path only | `test/integration/production-client.test.js` = 83 lines / 3 tests. The Rust fixture `product/fixtures/v1/production-client/contract-v1.json` carries 6 responses + 7 adversarial vectors, but the test routes them through the **native** decoder, never the TS client; the client test pins `completed` for every call and asserts 2 of 7 routes. Untested: every failure projection, the transport synthesis (B-03), `productionRecoveryReference`, forgery rejection | secure | typescript | **fix** — parameterize over all fixture responses × verbs × profiles | TSN-12 |\n| **B-30** | TypeScript's recovery-reference validation is strictly weaker than Rust's and accepts the zero reference | `production-client.ts:445-450` vs `auths-production-client/src/lib.rs:219-230` (both read). Also `decodeBase64Url:474-479` never rejects `length % 4 === 1` | secure | typescript | **fix** — native `parseRecoveryReferenceV1` | TSN-11 |\n| **B-31** | Retry class hardcoded per branch, overriding the native projection and a Rust invariant | `production-client.ts:396,413,420,246`; `parseProjection:382-392` validates the token but never against `kind`. `auths-production-client/src/lib.rs:480-511` `valid_shape` enforces the pairing | secure | typescript | **fix** — project through; cross-check kind↔retry | TSN-10 |\n| **B-32** | The identity layer is two independently designed vocabularies: **4** shared of 33 + 19 | Verified by normalized diff: shared = `DecodedIdentity`, `IdentityPrincipal`, `ResolutionEvidence`, `ValidatedIdentity`. Adapter/Registry (TS, plus a `*Descriptor` twin for every concept) vs Method/Registry/Resolver (PY) | simple | cross | **fix-before-v1** — name the Rust concepts once; delete one scheme | TOPO-04 |\n| **B-33** | The vertical layer ships a full execution surface in TS and a development fixture in Python — 14 shared of 37 + 20 | `profiles.ts:2` `export * from \"./profiles/mcp/index.js\"` leaks 23 TS-only symbols incl. `executeMcpClosed`, `resumeMcpClosed`, `McpGateway`, `McpReceiptSink`. Python exports `DevelopmentMcpProvider` at the **vertical** layer, where TS keeps development composition under integrations/testkit | simple | cross | **fix-before-v1** — explicit export list; move the dev provider | TOPO-05 |\n| **B-34** | P-256 receipts can never verify — the suite is hard-coded regardless of the caller's argument | `lib.rs:3936-3973` checks `descriptor.suite() != suite` at `:3953` then unconditionally builds `Ed25519Suite::new()` at `:3960`; `:4112-4115` does it twice more. `core/adapters/auths-raw-key/src/lib.rs:29-34` defines `P256`; `SUPPORTED_PROFILES.md` advertises P-256/SHA-256. No P-256 identity derivation export exists | secure | wasm | **fix** — resolve via `ImmutableRegistries` as `verifyV1` does (`lib.rs:4947-4954`), or narrow the docs | WASM-10 |\n| **B-35** | Generic trusted-context compilation and issuer-side status authoring are reference surface in a consumer package | `lib.rs:890-931` `compileTrustedContextV1` takes caller-chosen registries, matchers, policies and an arbitrary `LimitKind` list; `:249-308,431-454` author and canonicalize status-authority statements — an **issuer** operation. Narrow consumer forms already exist alongside (`:3252`, `:4651`) | simple | wasm | **delete** (12 symbols; removes the `LimitKind` vocabulary as a side effect) | WASM-15 |\n| **B-36** | GIL held through full chain verification in every `authorize_*` entry point; no async story exists | `mcp.rs:780`, `http.rs:496`, `application.rs:492` call `verify_sealed` with the GIL held; only `result.rs:127,152` and `identity.rs:240` use `py.detach`. No `pyo3-asyncio`/`future_into_py` anywhere, while the Python layer is thoroughly `async` | secure | python | **fix** — `py.detach` the verification tail; state the contract in `sdk-runtime-contract.json` | PYN-12 |\n| **B-37** | Both oracles quantify status controls over the **selected** statement; Rust quantifies over **every** matching statement | `core/crates/auths-verifier/src/lib.rs:2388-2395` iterates all matching statements; `verifier.go:987-999` and `semantic-verifier.ts:1715-1720` check only the winner. Rust is stricter, so this is an **oracle blind spot**: a future Rust relaxation would go undetected by a green three-way gate | secure | cross | **fix both oracles**, or add a two-statement corpus fixture | GO-07 |\n| **B-38** | Nothing enforces oracle independence, and no negative control proves they would catch a wrong Rust answer | `bindings/independent/README.md:3-4` states the rule as prose; `architecture.toml:271-276` explicitly exempts the tree from the only scanner. `semanticAudit` reads `ExpectedDecision`/`ExpectedCode` from the same manifest whose digest is compared. Honest today (`verifier.go:139-144`), but a careless edit makes the gate a tautology and CI stays green | secure | cross | **fix** — mutation/negative-control gate | GO-08 |\n| **B-39** | Go silently truncates the profile-id length in the signing preimage where Rust checks and TypeScript throws | `hash.rs:105` `u16::try_from(...).map_err(|_| LimitExceeded)?` vs `semantic.go:1135` `uint16(len(profile.id))` vs `semantic-verifier.ts:671` `writeUInt16BE` (throws `ERR_OUT_OF_RANGE`) — all three read. Three different stable codes on the same bytes; `semantic.go:504-525` bounds the version but not the id text | secure | go | **fix** Go to a checked conversion + add a corpus fixture | GO-06 |\n| **B-40** | The Go SDK costume: an exported, documented, untested, provably dead public API | `api.go:89-97` `Verify` → `hex.DecodeString(\"\")` → always `denied` (`verifier.go:264-269`); `NewEngine` fails on the corpus's own six-key `adapter_context` under `DisallowUnknownFields` (`adapters.go:87-93` declares five — both verified); `api_test.go` (read in full) exercises neither; `verifier.go:262` bakes in the fixture constant `[]byte{0x33}×32`; `api.go:72-82` hand-copies the explanation vocabulary and collapses retryability to `Decision == Indeterminate` | secure | go | **delete** `Verify`, `Engine`, `NewEngine`, `(*Engine).Verify`, `Explanation`; export only `AuditCorpus` | GO-02, GO-03, GO-04, GO-11, GO-13 |\n| **B-41** | Async polarity is inverted per concept between the two SDKs | Identity: TS sync (`01:14,23`), Python awaited (`01:32,34`). Verify: TS two-step `await loadVerifier()` (`02:11-12`), Python direct (`02:14`). Builders: TS `await mcp.plan(...)` (`05:49`), Python sync (`05:57`); TS `await approval.planOnce(...)` (`05:96`), Python sync (`05:30`). The `load*` prefix is a WASM-init workaround occupying 4 of 11 TS recipe imports | simple | cross | **fix** — one polarity per concept; hide init | REC-05 |\n| **B-42** | `ExecutionReference` uses `encode`/`decode` in TS and `to_bytes`/`from_bytes` in Python, while receipts use `encode`/`decode` in both | `05-cross-organization-plan.ts:57,83` vs `05_cross_organization_plan.py:76,109`; receipts at `:13,58,87` / `:17,79,116,128`. Python is internally inconsistent inside one 160-line reference file | simple | cross | **fix** — one verb pair, direct cutover | REC-07 |\n| **B-43** | Tool-handler cancellation exists in TypeScript and not in Python; `reconcile` has different arity in each | `profiles/mcp/index.ts:188-192` `(input, context, signal: AbortSignal)` vs `profiles/_mcp.py:451-457` `(service, tool, arguments, context)`. `reconcile`: TS `(executionId, service, signal)` (`:204-208`) but the recipe declares **zero** params (`05:76`); Python declares two (`05:91`) | secure | cross | **fix** — same signature both sides | REC-11 |\n| **B-44** | `createAuths` is one TS name with two return shapes discriminated by `\"endpoint\" in configuration`; Python splits it; no recipe uses either | `product.ts:353-361` — one overload returns `Promise`, the other a synchronous `ProductionAuths`. Python's `create_auths` is production-only (`_production_client.py:306-313`). The glossary declares `{create: createAuths / Auths}`, matching neither. All 5 recipes bypass both via `development.create_auths` | simple | cross | **fix** — split or unify; correct the glossary | REC-12 |\n| **B-45** | Recipes never exercise the error registry, the indeterminate outcome, or a denial as a primary outcome | grep across all 10 files for the error vocabulary returns 5 hits, all `effect:` literals. `Denied.code` never read (`typescript/02:13`, `03:21,28`, `04:23`, `python/02:16`, `03:29,36`, `04:40-44`). `manifest.json` (verified) has 5 success headlines, 0 denials. `python/03:42` hardcodes `\"outcome\": \"completed\"` where the TS twin reports the observed `completed.kind` | secure | cross | **fix** — add a denial-headline recipe and a real indeterminate + reconcile path | REC-08, REC-17 |\n| **B-46** | `sdk-experience` reports missing journey evidence instead of failing on it, and hardcodes availability | `xtask/src/sdk_experience.rs:320` `\"availability\": {\"typescript\": true, \"python\": true}` is a literal; `:322-323` emit `executable`/`missingEvidence` as data with `Ok(...)`. It runs in `ci_authoritative` (`checks.rs:17`) and `release_preflight` (`:53`). The baseline drift check immediately above (`:125-130`) *does* fail hard | secure | rust | **fix** — `Err` on non-empty `missing`; derive or delete `availability` | TOPO-08 |\n| **B-47** | The npm package has no license, no `publishConfig.access`, and no repository metadata | Verified by parsing `package.json`: `license`, `repository`, `homepage`, `bugs`, `publishConfig`, `sideEffects` all **absent**; no LICENSE file in `files`. Scoped package ⇒ npm defaults to restricted. Workspace is `MIT OR Apache-2.0` (`Cargo.toml:123`, `pyproject.toml:9`), the durable-json adapter says `Apache-2.0` only. No provenance linkage despite the release generating SLSA attestations (`release-builder.yml:155-158`) | secure | packaging | **fix-before-v1** | PKG-07, PKG-08 |\n| **B-48** | `auths-sqlite` hard-pins `auths==1.0.0rc1` while the TS adapter uses a range | `bindings/python-adapters/sqlite/pyproject.toml:10` (verified) vs `adapters/durable-json/package.json:8-10` `\">=1.0.0-rc.1 <2\"`. The sqlite package cannot follow its base across the 1.0 boundary it exists to demonstrate; it also declares no `readme` and no OS classifiers | extensible | python | **fix** — one convention (`auths>=1.0,<2`) | PKG-09 |\n| **B-49** | Two retry vocabularies collide under one exported name at the package root | `index.ts:48` exports `type RetryClass` = `never/backoff/resume/reconcile`; `AuthsError.retry` (`product-errors.ts:76`) refers to a **different, unexported** `RetryClass` = `never/safe/conditional/unknown`. `api/public-api.txt` shows a single row, so the gate sees one symbol. A third notion (`retryable: boolean`) at `verifier/explanation.ts:3-11` | simple | cross | **fix in Rust first**, then project once | TSN-15 |\n\n### Minor\n\n| ID | Title | Evidence | Goal | Lane | Disposition | Src |\n|---|---|---|---|---|---|---|\n| **B-50** | Two vocabularies for one delegation-planning operation, one unused | `lib.rs:1815-1832` `planChildGrantV1` and `:1834-1873` `planChildGrantFieldsV1` both funnel to `plan_child_grant`. Only three names in the `.d.ts` are never referenced from `bindings/typescript/src`, and `planChildGrantV1` is one. Both are frozen in `authoring-abi-v1.json` | simple | wasm | **delete** `planChildGrantV1` | WASM-16 |\n| **B-51** | A development-only seed→public-key derivation ships in the production consumer package | `lib.rs:3020-3029`; sole reason `ed25519-dalek` is a direct dep; undeclared in both ABI manifests (confirmed in my 38-item list) | secure | security | **delete** → `auths-testkit` | WASM-17 |\n| **B-52** | Caller-chosen domain separation, and a second verification vocabulary with binding-local limits | `lib.rs:1881-1886` passes any caller string to `auths_codec::domain_commitment` (every other site pins its domain, e.g. `:3575`); `:5029-5057` + constants at `:164-165` | secure | wasm | **delete** both | WASM-18 |\n| **B-53** | `SUPPORTED_PROFILES.md` documents principal methods, not action profiles, and names zero qualified profiles | Confirmed by reading; would remain accurate after deleting every profile export | simple | wasm | **rewrite** against the topology | WASM-13 |\n| **B-54** | Four orphan TypeScript modules keep the duplicate vocabulary alive; five dead workflow barrels | Verified with exact-string grep: `profile-kit.ts`, `mcp.ts`, `diagnostics.ts`, `internal-sdk.ts` have **0** importers; `workflow/{approvals,attached-agent,client,custody,types}.ts` have **0** each. `sdk-glossary.json` `sameMeaningCandidates` names exactly `loadAuths`/`AuthsClient`/`AttachedAgent`, which `internal-sdk.ts` keeps alive. Python already deleted its counterparts and asserts it (`check_wheel.py:44-58`); TypeScript has **no** `REMOVED_PUBLIC_FILES` equivalent | simple | typescript | **delete**; add the negative packaging assertion | TOPO-10, TSN-14 |\n| **B-55** | Journey matrix `imports` names three non-existent modules; the mechanism layer has zero journeys | `profile` (singular), `profiles/mcp`, `receipts` resolve to nothing in `package.json:9-32`. `integrations` — the entry point 3 of 5 TS and 3 of 5 Python recipes actually import — appears in **no** journey. `sdk_experience.rs:305` passes the tokens through untouched | simple | cross | **fix** — constrain the vocabulary; validate in the new gate | TOPO-11 |\n| **B-56** | No golden-path recipe in either language imports the framework or testkit layer | The 10 maintained recipes cover 5 of 7 entry points; `framework`/`testkit` appear in none. This is *how* B-11's contract divergence survived: no recipe exercises both sides | extensible | cross | **fix** — one `AtomicReservationStore` + certifier recipe per language | TOPO-12 |\n| **B-57** | `experience-evidence.json` is an ungated orphan whose numbers cannot be runner output | Only two references repo-wide (itself and `README.md:14`). Not among `sdk_experience.rs:145-156` authoritative inputs. `run.mjs:57` emits `{language, id, elapsedMs}` and **12** rows including the two cross-language verifications (verified by reading `run.mjs`); the file has 10 rows with `{language, recipe, elapsedMs}` and 2-char ids. `maintainedRecipes` paths are counted but never existence-checked | secure | packaging | **delete**; add path existence checks | REC-10 |\n| **B-58** | Recipes package does not declare the SDK it imports | `bindings/recipes/typescript/package.json` declares only `@types/node` + `typescript`; `@auths-dev/sdk` is absent from `package-lock.json`. CI compensates out-of-band (`sdk-recipes.yml:70-73`). Local `tsc` passes against a stale installed copy. No Python equivalent declaration exists. **Structurally the same failure mode that let B-16 persist** | extensible | packaging | **fix** — declare it; add `docs/product/recipes/**` to the path filters | REC-15 |\n| **B-59** | TypeScript recipes use bare `catch {}` to conclude \"correctly rejected\" | `01-authenticate-identity.ts:36`, `02-verify-authority.ts:17-21`. Python narrows (`01:41` `except ValueError`, `02:21`). Any throw — including a `TypeError` from the buffer juggling two lines above — counts as a passing security assertion | secure | typescript | **fix** — catch the specific type | REC-14 |\n| **B-60** | Recipe 01 is a codec round-trip self-test in both languages; recipe 05 is a systems integration test | `01-authenticate-identity.ts:14,24` encode and immediately decode inside one expression; `:16-17` is pure WebCrypto buffer ceremony. Python's twin requires four positional magic strings in `VerificationRelationship`. Recipe 05 is 126/160 lines with three process modes. *Recipes 02, 03, 04 are genuinely good* — `mcp.allowTools([\"publish_report\"])` next to `callTool({name:\"delete_report\"})` → `denied` reads well without prior knowledge | simple | cross | **rewrite 01** to cross a real boundary | REC-16 |\n| **B-61** | Resource lifecycle: Python has `async with`, TypeScript hand-writes six `try/finally` blocks | `03:20-22`, `04:19-21`, `05:52-56,103-107` vs `03:15,30-32`, `04:9,27-29`, `05:48,59-61,82,88-90`. `Auths` (`product.ts:133-141`) has no `Symbol.asyncDispose`, so `await using` is unavailable | simple | typescript | **fix** — implement `Symbol.asyncDispose` | REC-13 |\n| **B-62** | A stale 302KB npm tarball is committed to git | Verified: `bindings/typescript/auths-dev-sdk-1.0.0-rc.1.tgz`, 302,505 B, git-tracked, embedded manifest declares one export and no `bin`. Blocked from the npm tarball by `package.test.js:86`, but travels inside the `source-archive` release subject. `.gitignore` does not exclude `*.tgz` | simple | packaging | **delete** + gitignore | PKG-12 |\n| **B-63** | Internal engineering retrospectives ship inside the npm tarball | `package.json:41` ships all of `docs` (56K), incl. `external-consumer-scorecard.md` (\"**Consumer:** `auths-agent-demo`\", \"## Before AP-SPEC-036\"). `package.test.js:22` freezes the directory in. The Python wheel ships **zero** docs (`check_wheel.py:115-121` rejects anything outside `auths/` and `*.dist-info/`) | simple | typescript | **fix** — narrow `files` | PKG-13 |\n| **B-64** | `.npmignore` is dead configuration beside `files` | 2 lines, both already excluded by the allowlist and independently asserted at `package.test.js:29`. Prelaunch forbids dual paths | simple | typescript | **delete** | PKG-14 |\n| **B-65** | `public-naming.toml` names `auths-stores` as the replacement for two published crates; it is `publish = false` | `auths-index` (656 downloads) has it as its **only** replacement; `auths-storage` (732) lists it too. Absent from every tier and from the 42-name closure (verified). `public_naming.rs:394-411` never cross-checks that replacements resolve | extensible | packaging | **fix** + extend `validate_release_order` | PKG-15 |\n| **B-66** | Framework contracts cite conformance evidence from a profile id that does not exist | `product/conformance/v1/mechanism-profile-conformance.json:7-13,15-22` justify `publish-framework` with `[\"auths.mcp/1\", \"auths.records/1\"]`. `auths.records/1` resolves only to the manifest and its three generated projections; the records integration defines `auths.demo.records.create/1`, `auths.records-api/1`, etc. — none of them that id. The two-independent-verticals rule is the stated basis for promotion | secure | rust | **fix** — point at a real second vertical or reclassify to candidate | TOPO-15 |\n| **B-67** | The npm install smoke test hardcodes the seven entry points and the three profile ids | `xtask/src/checks.rs:216-243` is a Rust string literal. It *is* a real check (`:240-242` asserts `'Verifier' in auths` is false), but it is an eighth hand-maintained topology copy. Contrast `public-api.mjs:14-21`, which reads the contract | simple | rust | **fix** — generate from the contract | TOPO-16 |\n| **B-68** | Both documented ways to run the oracles point at a non-existent directory | `bindings/independent/README.md:20-25,33-38` say `cd implementations/go`; the path is `bindings/independent/go` (as `fixtures.rs:25-28` uses). `README.md:9` cites `auths-proof/fixtures/v1/manifest.json`; the manifest is at `core/fixtures/v1/manifest.json`. Every command in the only human entry point to a 7,374-line assurance artifact is broken | simple | cross | **fix** three paths | GO-09 |\n| **B-69** | The 2,355-line TypeScript oracle is invisible to the compliance inventory | `compliance.rs:439-452` discovers any `go.mod`; `:460` skips `\"private\": true` packages, and `bindings/independent/package.json` is exactly `{\"private\": true, \"type\": \"module\"}`. One of two identical artifacts is inventoried | simple | typescript | **fix** — declare both or neither | GO-10 |\n| **B-70** | The semantic corpus digest is pinned as a bare literal in the Go test | `api_test.go:61-68` `const expected = \"102:114c05...\"` (read). The authoritative comparison at `fixtures.rs:74-82` is **derived** from the live Rust verifier | simple | go | **fix** — assert against Rust or drop | GO-14 |\n| **B-71** | `ambiguous-terminal-grant` is a Rust code with no Go counterpart | `auths-model/src/lib.rs:4366,4415`. Safe today: `fixtures.rs:2013-2039` declares it reserved and **fails** if it gains a vector. Go implements the other two reserved codes | secure | cross | **accept** — note it as a tripwire for the un-reserving change | GO-15 |\n| **B-72** | Four dead functions in the Go oracle, one encoding unchecked protocol vocabulary | `verifier.go:832` `stringSliceEqual`, `semantic.go:1355` `purposeName`, `:1366` `hasPrefix`, `:1234` `decodeHex`. `purposeName` reads as a check that was written then dropped; `go vet` does not flag unused functions | simple | go | **fix** — delete; confirm whether Rust binds status purpose to signing purpose | GO-12 |\n| **B-73** | Unknown stable codes discard the `executionReference` needed to reconcile a possible effect | `_product_errors.py:302` `execution_reference=None`; `parseUnknownDetails` omits it. Ten registry codes allow one (verified) | secure | cross | **fix** both bindings together | PYN-14 |\n| **B-74** | Wall-clock time is read in Python at 27 sites and passed into Rust as signed material | `int(time.time())` across `_bootstrap.py:82`, `_application_profile.py` (7 sites), `profiles/_mcp.py` (7), `_workflow.py` (3), etc.; consumed as plain `u64` with no monotonicity or bound checks. `testkit.FixedClock` exists, and `_workflow.py:638` already uses a `Callable[[], int]` port — the abstraction was intended and not applied consistently | secure | python | **fix** — one injectable clock port; bound-check the native entry points | PYN-16 |\n| **B-75** | The Rust lifecycle kernel is exported to Python and bypassed by Python's own gateway | `runtime.rs:12-99` exports six functions; only the two trivial mappers are called (`_application_profile.py:889`, `_mcp.py:1602`). `_application_profile.py:905-919` invents a 5-value vocabulary matching neither `KernelCode` (16 labels) nor `ReplayCode` (3), defaulting unknowns to `effect_state=\"not-started\"`. Two store protocols coexist (`AtomicReservationStore` public, `ApplicationExecutionStore` private and undeclared) | multiple | python | **fix** — route through the kernel or delete the exports; reconcile the two protocols | PYN-13 |\n| **B-76** | Browser conformance test imports deep `dist/` paths the exports map does not expose | `test/package/packed-browser.mjs:34,47-50`. `docs/support-matrix.md` states \"no deep imports\". The Node equivalents use public subpaths (`packed-consumer.test.js:9-15`). Also unstated: `internal/development.ts:37,42,56,96` requires WebCrypto **Ed25519** | secure | packaging | **fix** — use public subpaths; document the Ed25519 requirement | TSN-17 |\n| **B-77** | A stale generated copy of the topology contract disagrees with the source | `release/docs-bundle/public-topology.json:31` `[\"auths.mcp/1\"]` vs the source's four. The bundle carries `manifest.json:3-4` `contractDigest` + `sourceCommit` and a pinned sha256, presenting as attested. The tree is **untracked** and no generator exists (verified: `git status` shows `?? release/docs-bundle/`). Same tree holds `runtime-facts.json` declaring 7 endpoints owned by `product/runtime/auths-runtime/src/docs.rs` — **a file that does not exist** — none of whose paths match the real router at `auths-node/src/api.rs:115-137` | secure | packaging | **delete or generate + byte-compare** | TOPO-14, TSN-14 |\n| **B-78** | `bindings/typescript/adapters/durable-json` is a second publishable npm package outside every gate | `package.json` has no `\"private\": true` (verified). Version 0.1.0 vs the SDK's 1.0.0-rc.1; absent from the topology, `public-api.txt`, and every xtask input | simple | packaging | **delete** (see B-23) or mark private + declare | TSN-18 |\n| **B-79** | Two shipped entry points sit outside the topology contract entirely | npm `bin.auths` (`package.json:6-8`) and `auths/__main__.py` (asserted into every wheel by `check_wheel.py:11`). `public-api.mjs:22-25` walks only `exports`; `check_public_api.py:12-16` imports only declared modules — **an undeclared public Python module is structurally undetectable**. `auths` also collides with the crates.io lineage (`core/crates/auths/Cargo.toml`, retired-but-undeletable `auths-cli`) | simple | packaging | **accept-with-note** — add a `cli` layer; close the Python detection gap | TOPO-13, PKG-06 |\n| **B-80** | `_parse_details` transcribes `ErrorEnvelope::parse` and omits one invariant | `_product_errors.py:207-277` and `product-errors.ts:161-205` vs `auths-errors/src/lib.rs:168-219,293-333`; both omit `:329-331`. **Latent** — verified that all 45 codes have `allowsReceiptReference: false` — and will fire the day the first `true` is registered | secure | cross | **move-to-rust** — native `parse_error_envelope_v1` | PYN-08 |\n| **B-81** | The MCP profile's Rust-owned contract is imported but only partially consumed | **Corrected** — see contradictions. `MCP_PROFILE` **is** imported (`profiles/mcp/index.ts:23`) and used at `:449,506,515,516` for `toolCount` and durations. It is *not* used for `toolNameBytes` (hardcoded `128` at `:1108`), the service bound (`64` at `:1096` — a value absent from the generated contract entirely), `inputBytes`/`outputBytes`, or the `handlerEffects`/`errorCodes` unions — which is why `McpGatewayError` drops `possible` (B-12) with the correct list sitting in the same file | simple | typescript | **fix** — derive every bound and union from `MCP_PROFILE` | TSN-13 (corrected) |\n\n---\n\n## Extensibility observations\n\n**What does it cost to add a new qualified vertical today?** Minimum edit sites, all verified: `auths-production-client` (`QualifiedProfile::parse`, `as_str`, `execute_path`), `auths-node`'s router, `xtask/src/assurance.rs:15-19`, `xtask/src/checks.rs:232-234`, `bindings/typescript/src/profiles.ts` (union + factory), `bindings/typescript/src/production-client.ts` (`isProductionProfile` + `endpointPath`), `bindings/python/python/auths/profiles/__init__.py` (union + factory), `bindings/python/python/auths/_production_client.py` (`_PROFILE_IDS` + `_endpoint_path`), `bindings/python/python/auths/_native.pyi`, `bindings/public-topology-v1.json`, `release/assurance/.../manifest.json`. **Eleven-plus sites, of which the compiler links exactly one pair.** The TypeScript `endpointPath` chain ends in a silent fallback, so forgetting it routes the new vertical's effects to the GitHub issue endpoint. Rust's exhaustive `const fn match` would be a compile error. That asymmetry is the extensibility defect.\n\n**The framework contract is the extension layer's whole point and it has no owner.** No Rust trait exists for `atomic-reservation-store` or `signer-custody`; the strings appear in Rust only as identifiers in the conformance manifest. Consequences chain: the two bindings authored incompatible shapes (B-11); no recipe in either language exercises the layer (B-56), so nothing surfaced the mismatch; and the promotion to `publish-framework` rests on a second vertical `auths.records/1` that does not exist (B-66).\n\n**`auths-sqlite` is the single most valuable extensibility artifact in the repo and it cannot be installed.** `SQLiteAtomicReservationStore` (`bindings/python-adapters/sqlite/python/auths_sqlite/__init__.py:13`) is the only reference implementation in any language, it demonstrates that the contract is satisfiable without importing Auths lifecycle or authorization semantics, and it is the template an integrator copies for their own database. It is CI-tested (editable install, pytest, `mypy --strict`, pyright) and never built into a wheel; `release.rs:550-556` structurally forbids a second `.whl`. Shipping `auths.framework.AtomicReservationStore` and `auths.testkit.certify_atomic_store` with no obtainable implementation makes the extension layer unexercisable by real consumers. Its `auths==1.0.0rc1` hard pin also means it cannot follow its base across the 1.0 boundary it exists to demonstrate.\n\n**The healthy counter-example is right next door.** `cargo xtask mechanism-conformance` generates the Rust-owned conformance catalog into `bindings/typescript/src/generated/mechanism-conformance.ts` and `bindings/python/python/auths/_mechanism_conformance.py` and byte-compares them, inside `ci_authoritative`. Same for `error-registry` and `mcp-session-contract`. All three generated files exist and are verified; two of the three are actually consumed. **That is exactly the shape `public-topology`, `qualified_profiles`, and the framework contracts need and do not have.**\n\n---\n\n## What is unenforced\n\nDeclared contracts with no gate — these drift silently, and every one has already drifted:\n\n| Contract | Declared at | Enforced by | Observed drift |\n|---|---|---|---|\n| `frameworkContracts` | `public-topology-v1.json:30` | **nothing** | Two incompatible shapes (B-11) |\n| `qualifiedProfiles` | `public-topology-v1.json:31` | **nothing** (the assurance gate enforces a *different*, 3-item set and would reject this one) | Six lists, ≥2 disagreeing (B-10) |\n| npm `bin` | `package.json:6-8` | value-frozen only | Undeclared entry point (B-79) |\n| The `wasm/` payload inside the npm tarball | `package.json:46-49` | `public-api.mjs` walks only `exports` | 96 undeclared symbols visible in a shipped `.d.ts` (B-01) |\n| pyo3 native ABI | `native-abi-v2.json` | `check_contract.py` — **one-directional** | 23 undeclared, incl. the generic domain surface (B-26) |\n| WASM ABI | two hand-written manifests | manual | 38 undeclared incl. `verifyV1`; `evolution-policy-v1.json:47` pins only one file (B-25) |\n| Required WASM exports | `sdk-runtime-contract.json` (7) + `wasm.ts` (56 checks) | a **regex text match** against the `.d.ts` | Three disagreeing lists, none derived from Rust (B-27) |\n| Cross-language name parity | `sdk-glossary.json` | `sdk_vocabulary.rs:97-102`, dead behind `enforcement:\"prototype\"` | 4 of 5 declared operations absent from the TS public API (B-24) |\n| Same-name-different-meaning | computed by `sdk_vocabulary.rs:56-71` | **printed, never `Err`** | `Receipt` is four distinct public types (B-24) |\n| Journey evidence + recipe paths | `customer-journey-matrix-v1.json` | `sdk_experience.rs:296-330` — reports, `Ok(...)` | `availability` is a literal; `maintainedRecipes` never existence-checked (B-46, B-57) |\n| Journey `imports` vocabulary | same file | passed through untouched | 3 of 9 tokens name nothing (B-55) |\n| Oracle independence | `bindings/independent/README.md:3-4`, prose | **nothing** — `architecture.toml:276` explicitly exempts the tree | Two ports of one design; no negative control (B-38) |\n| Binding semantic ownership | `architecture.toml:270-295` | token grep over 2 of 5 binding source trees | Misses the CBOR decoder, both WASM/pyo3 trees, `atob`/`btoa`, and holds a false permanent allowance (B-21) |\n| `.pyi` type projection | shipped beside `py.typed` | **nothing** | 19 of 35 symbols missing (B-14) |\n| TS file deletions | — | **nothing** (Python has `REMOVED_PUBLIC_FILES`) | 9 orphan modules survive (B-54) |\n| `predecessor_crates.replacement` targets | `public-naming.toml` | not validated | `auths-stores` is `publish = false` (B-65) |\n| `release/docs-bundle/` | carries `contractDigest` + `sourceCommit` + sha256 | **nothing** — untracked, no generator exists | Contradicts the topology on `qualifiedProfiles`; `runtime-facts.json` cites a non-existent owner file and 7 wrong endpoints (B-77) |\n\nThe Rust release order is the sole counter-example: `public_naming.rs:394-411` does set-equality against `semantic-freeze.json`, `:368-386` validates tier shape and uniqueness, `:414-421` pins the final tier. Verified: **42 = 42, zero drift both directions.**\n\n---\n\n## Contradictions between auditors, and corrections I made\n\n1. **The brief itself is wrong about the registry size.** It states `product/errors/v1/registry.json` \"has 20 stable codes.\" It has **45** (verified by parsing). The WASM and TypeScript auditors both said 45 and are correct. The brief's *substantive* point — that losing the `effect` distinction is a security finding — is exactly right and is strengthened, not weakened, by the correction: 9 codes carry `effect: possible`, and 10 carry `allowsExecutionReference: true`.\n\n2. **\"262 WASM exports\" is a miscount, and the auditors disagreed about the replacement.** 262 is the count of *occurrences of the string* `wasm_bindgen`; there are **245** `#[wasm_bindgen` attributes, **96** top-level exported names, and **228** total public JS symbols. I verified all four numbers. The WASM auditor's 228 figure is correct and I reproduced its decomposition (75 + 21 + 132) exactly.\n\n3. **TSN-13 is materially wrong and I corrected it.** It claims \"repo grep for `MCP_PROFILE` across `bindings/typescript/src` returns exactly one hit: its own declaration.\" False — it is imported at `profiles/mcp/index.ts:23` and used at `:449, :506, :515, :516`. The finding survives in reduced form (B-81): the generated contract is consumed for `toolCount` and durations but not for `toolNameBytes`, byte limits, or the `handlerEffects`/`errorCodes` unions. **I believe my grep**: I ran it directly and read the call sites.\n\n4. **TOPO-10's importer counts for `authority.ts` are wrong.** It claims 3 importers; the exact-string grep returns **1** (`src/workflow/internal/orchestrator.ts`). The four genuine orphans (`profile-kit.ts`, `mcp.ts`, `diagnostics.ts`, `internal-sdk.ts`) are confirmed at 0 each, as are TSN-14's five workflow barrels. My first grep was itself wrong (unescaped `.` in the pattern) and I re-ran it with `grep -F`; the corrected numbers are above.\n\n5. **GO-01 and GO-02 appear to contradict each other and do not.** GO-01 cites `TestVerifyReturnsNativeAuthorizedResult` as the evidence backing the `language-binding` claim; GO-02 says no test calls the package-level `Verify`. I read `api_test.go` in full: the test calls the *method* `engine.Verify` on a directly-constructed `&Engine{adapters: corpus.AdapterContext}`. Both statements are true. Both findings stand.\n\n6. **The WASM auditor calls the package \"a REFERENCE package wearing a CONSUMER package's clothes\" while the PKG auditor calls the npm SDK's typed API clean.** Not a contradiction — different layers. Both verified: `bindings/typescript/api/public-api.txt` exposes 193 reviewed symbols, and the 2.31 MB WASM binary carrying 228 unreviewed symbols ships beneath it inside the same tarball, with a 66 KB `.d.ts` documenting all 96 top-level names.\n\n7. **TOPO-09/PKG-05 say `src/profiles/domains/index.ts` is 397 lines; TSN-07 says 398.** Immaterial; both agree on 0 importers, which I verified, and I confirmed the compiled output exists on disk at `dist/profiles/domains/index.js` (9,457 B) and `index.d.ts` (5,545 B).\n\n8. **REC-01's claim that the recipes gate \"has not blocked 62 commits\" is one I could only half-verify.** I confirmed the ImportError reproduces, confirmed 62 commits since `22b7334`, and confirmed `.github/workflows/sdk-recipes.yml` path filters *do* cover `bindings/recipes/**` and `bindings/python/**`, and that `tools/run.mjs:19` executes this exact file as a subprocess and throws on non-zero exit. I cannot see GitHub check status from here, so I state the mechanical facts and flag the inference — the workflow either is not a required check, or has been red and merged past. That question needs answering independently of the one-line import fix.\n\n---\n\n## Coverage statement\n\n**Read directly and verified in this pass** (every claim above marked \"verified\" was checked against source in this session):\n\n- `bindings/public-topology-v1.json` in full; both gates that read it (`bindings/typescript/tools/public-api.mjs:1-50`, `bindings/python/tools/check_public_api.py:1-35`) in full; repo-wide grep for every consumer of the file and of its two inert fields.\n- `bindings/wasm/auths-proof-wasm/src/lib.rs` at 15 cited ranges; full parse of `bindings/typescript/wasm/auths_proof_wasm.d.ts` (228-symbol decomposition, per-class member counts); both ABI manifests parsed and diffed against the generated surface.\n- `product/errors/v1/registry.json` fully parsed — 45 definitions, outcome-pair histogram, the `possible` list, both reference-allowance lists.\n- `bindings/independent/go/auths/api_test.go` in full; `api.go:85-115`, `verifier.go:258-272`, `adapters.go:85-95`, `semantic.go:1128-1142`; `core/fixtures/v1/manifest.json` `adapter_context` keys; `bindings/independent/typescript/semantic-verifier.ts:665-675`.\n- `xtask/src/checks.rs:1-60,73-90,155-182`; `xtask/src/fixtures.rs:20-85`; `xtask/src/sdk_vocabulary.rs:50-130`; `xtask/src/assurance.rs:10-25,605-625`; `xtask/src/release.rs:545-560`; `xtask/src/main.rs:81-161`; `xtask/src/public_naming.rs` tier arithmetic reproduced.\n- `architecture.toml:265-350` in full; `compliance.toml:1450-1478`; `.github/ci/phase-ownership.toml` bindings rule; `.github/workflows/sdk-recipes.yml` in full.\n- `bindings/typescript/src/{verifier/decoder.ts:143-165, verifier/wasm.ts:50-70, production-client.ts:265-300,443-482, internal/mechanisms.ts:1-15, generated/mcp-profile.ts (full), profiles/mcp/index.ts:1035-1112}`; `bindings/typescript/package.json` fully parsed.\n- `bindings/python/python/auths/{__init__.pyi (full), _bootstrap.py:100-120, _production_client.py:268-300, testkit.py:250-270, _mechanisms.py:10-25}`; runtime `auths.__all__` enumerated in a live interpreter; `bindings/python/src/{lib.rs:30-50, domains.rs:1-20}`; `bindings/python/tools/check_wheel.py:42-60,125-145`; `bindings/python/pyproject.toml`.\n- `bindings/recipes/{manifest.json, tools/run.mjs}` in full; `python/03_execute_exact_action.py:1-12` and `python/01_authenticate_identity.py:30-45`; **recipe 03 executed and its ImportError reproduced**.\n- `product/runtime/auths-production-client/src/lib.rs:160-235`; `core/crates/auths-codec/src/hash.rs:100-112`; `Cargo.toml:278-290`; `release/public-naming.toml` release-order tiers parsed; `release/semantic-freeze.json` closure counted.\n- `bindings/python-adapters/sqlite/pyproject.toml`; `bindings/typescript/adapters/durable-json/package.json`; `bindings/typescript/sdk-runtime-contract.json`; `git ls-files` for the committed tarball.\n- Line/file counts measured for every row of the inventory table.\n\n**Accepted on auditor citation without independent re-reading** (cited precisely; spot-checked where cheap, but not exhaustively re-derived):\n\n- The full enumeration of the 67 `\"literal\" =>` arms in `lib.rs` — I verified the `limit_kind` table at `:785-815` and the absence of `bundle-bytes`/`plan-branching` elsewhere in Rust, and spot-checked three other tables, but did not count all 67 arms.\n- The 165-line/2,159-line internal structure of the two oracle files beyond the ~40 lines I read; the claimed ~29 identically-named-function correspondence (I confirmed the shared `0x33` hardcode and the shared status shortcut directly).\n- The pyo3 per-file function/class breakdown and the 1,035-line `_native.pyi` inventory (I verified the aggregate 101/45 counts and the `domains.rs`/`http.rs` registration).\n- Detailed line numbers inside `product/receipts/auths-receipts/src/disclosure.rs`, `core/crates/auths-verifier/src/lib.rs`, `auths-lifecycle` kernel, and `auths-profile-domains/src/lib.rs` beyond the ranges I opened.\n- The 12 timing rows / experience-evidence schema mismatch (I read `run.mjs` and confirmed it emits `{language, id, elapsedMs}` and 12 rows; I did not open `experience-evidence.json`).\n- `mypy` reproduction of PKG-02 — I verified the `.pyi`/`__all__` delta arithmetic directly (16 vs 35) but did not re-run the type checker.\n\n**Not covered by this sweep at all:** `demos/` (including the untracked `demos/open-production-reference/`), `product/stores/auths-stores/tests/postgres_tls/` (untracked), the Rust core and product crates except where a binding cited them, the fuzz corpora, and the actual CI run history on GitHub (unavailable from this environment — see contradiction 8).", + "auditors": 7, + "findings": 117 + }, + "workflowProgress": [ + { + "type": "workflow_phase", + "index": 1, + "title": "Sweep" + }, + { + "type": "workflow_phase", + "index": 2, + "title": "Reconcile" + }, + { + "type": "workflow_agent", + "index": 1, + "label": "sweep:wasm", + "phaseIndex": 1, + "phaseTitle": "Sweep", + "agentId": "a4b519f8c28481261", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786758763724, + "queuedAt": 1786758762094, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "bindings/wasm/auths-proof-wasm", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No mutating git commands.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.\nDe…", + "lastProgressAt": 1786759453386, + "tokens": 205048, + "toolCalls": 71, + "durationMs": 689662, + "resultPreview": "{\"area\":\"bindings/wasm/auths-proof-wasm\",\"summary\":\"This is a REFERENCE package wearing a CONSUMER package's clothes, and it ships inside the npm SDK tarball (release/release-subjects.toml:50,71). One 5,586-line file emits 228 public JS symbols across 15 unrelated concerns — verification, identity, grant authoring, delegation planning, status issuance, trusted-context compilation, receipts, MCP se…" + }, + { + "type": "workflow_agent", + "index": 2, + "label": "sweep:go", + "phaseIndex": 1, + "phaseTitle": "Sweep", + "agentId": "afd4f454746fd229b", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786758763715, + "queuedAt": 1786758762094, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "bindings/independent — the Go conformance oracle and the se…", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No mutating git commands.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.\nDe…", + "lastProgressAt": 1786759499989, + "tokens": 204211, + "toolCalls": 76, + "durationMs": 736274, + "resultPreview": "{\"area\":\"bindings/independent — the Go conformance oracle and the second (independent) TypeScript verifier\",\"summary\":\"`bindings/independent/go` is NOT a Go SDK and NOT a second source of truth — it is a conformance ORACLE, and it genuinely runs. It re-implements the entire V1 semantic waist from scratch in pure Go stdlib (canonical CBOR decode, `AUTHS-ID`/`AUTHS` domain separation, ed25519 + P-25…" + }, + { + "type": "workflow_agent", + "index": 3, + "label": "sweep:topology", + "phaseIndex": 1, + "phaseTitle": "Sweep", + "agentId": "a385bbd66f6e2938a", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786758763834, + "queuedAt": 1786758762094, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "bindings topology: declared public API contract (bindings/p…", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No mutating git commands.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.\nDe…", + "lastProgressAt": 1786759477123, + "tokens": 149052, + "toolCalls": 79, + "durationMs": 713289, + "resultPreview": "{\"area\":\"bindings topology: declared public API contract (bindings/public-topology-v1.json) vs actual TypeScript/Python exports, framework contracts, qualified profiles, journey matrix, and xtask enforcement\",\"summary\":\"The layer map in `bindings/public-topology-v1.json` is honest and enforced: all 7 TypeScript specifiers and all 7 Python modules exist, and `bindings/typescript/tools/public-api.mj…" + }, + { + "type": "workflow_agent", + "index": 4, + "label": "sweep:recipes", + "phaseIndex": 1, + "phaseTitle": "Sweep", + "agentId": "abb2c199284ffc978", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786758763772, + "queuedAt": 1786758762094, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "bindings/recipes — the canonical TypeScript + Python worked…", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No mutating git commands.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.\nDe…", + "lastProgressAt": 1786759505970, + "tokens": 134917, + "toolCalls": 77, + "durationMs": 742198, + "resultPreview": "{\"area\":\"bindings/recipes — the canonical TypeScript + Python worked examples, their runner, doc generator, evidence file, and CI gate\",\"summary\":\"The recipes are real executable code run against packed artifacts in CI, not vapor — but they are not a matched pair, and one of the ten is currently broken in the checked-in tree. Python recipe 03 fails at import (`from auths import verify_receipt`; th…" + }, + { + "type": "workflow_agent", + "index": 5, + "label": "sweep:packaging", + "phaseIndex": 1, + "phaseTitle": "Sweep", + "agentId": "a9e0770702f48fbcf", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786758763968, + "queuedAt": 1786758762094, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "packaging — the actually-published surface (npm tarball, Py…", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No mutating git commands.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.\nDe…", + "lastProgressAt": 1786759357214, + "tokens": 130970, + "toolCalls": 57, + "durationMs": 593246, + "resultPreview": "{\"area\":\"packaging — the actually-published surface (npm tarball, Python wheel, crates.io release order) versus the declared release plan\",\"summary\":\"The published surfaces are: (a) one npm tarball `@auths-dev/sdk@1.0.0-rc.1` with exactly 7 module specifiers, 1 global bin, 193 exported symbols, 136 `dist/` files, 4 wasm files and 10 markdown docs; (b) one Python wheel `auths 1.0.0rc1` with 7 impor…" + }, + { + "type": "workflow_agent", + "index": 6, + "label": "sweep:python-native", + "phaseIndex": 1, + "phaseTitle": "Sweep", + "agentId": "aa6d607fc8abd6184", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786758764324, + "queuedAt": 1786758762094, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "bindings/python native layer (pyo3 crate `auths-proof-pytho…", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No mutating git commands.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.\nDe…", + "lastProgressAt": 1786759551013, + "tokens": 192885, + "toolCalls": 92, + "durationMs": 786688, + "resultPreview": "{\"area\":\"bindings/python native layer (pyo3 crate `auths-proof-python` → `auths._native`) and its relationship to `bindings/python/python/auths/`\",\"summary\":\"The pyo3 crate is memory-safe and disciplined at the mechanical level — `#![forbid(unsafe_code)]` at `bindings/python/src/lib.rs:3`, zero `unsafe` blocks, zero raw pointers, every `expect()` guarded by a prior emptiness check, and genuinely c…" + }, + { + "type": "workflow_agent", + "index": 7, + "label": "sweep:ts-native", + "phaseIndex": 1, + "phaseTitle": "Sweep", + "agentId": "aafd32917152d19f2", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786758764595, + "queuedAt": 1786758762094, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "bindings/typescript — native/generated boundary, runtime cl…", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No mutating git commands.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.\nDe…", + "lastProgressAt": 1786759521595, + "tokens": 207565, + "toolCalls": 111, + "durationMs": 757000, + "resultPreview": "{\"area\":\"bindings/typescript — native/generated boundary, runtime client, workflow subtree, tools/api gates, test suite, runtime matrix\",\"summary\":\"The hand-written product modules were audited previously; this sweep covers everything else, and the everything-else is where the semantic ownership actually leaks. Three generated files exist and all three are byte-verified against Rust by `cargo xtas…" + }, + { + "type": "workflow_agent", + "index": 8, + "label": "reconcile", + "phaseIndex": 2, + "phaseTitle": "Reconcile", + "agentId": "a484c728d878501a2", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786759555468, + "queuedAt": 1786759552613, + "attempt": 1, + "lastToolName": "Bash", + "lastToolSummary": "echo \"=== sqlite adapter ===\"; cat bindings/python-adapters…", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No mutating git commands.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.\nDe…", + "lastProgressAt": 1786760316518, + "tokens": 190988, + "toolCalls": 52, + "durationMs": 761050, + "resultPreview": "I've verified the load-bearing claims against source. Several needed correction. Here is the consolidated document.\n\n---\n\n# Phase 0b — bindings/ Surface Findings\n\n## Executive summary\n\nThe npm and PyPI packages' *declared* public APIs are clean and genuinely gated, but almost everything beneath them defines Auths semantics locally rather than projecting Rust — and the one gate built to catch exact…" + } + ], + "totalTokens": 1415636, + "totalToolCalls": 615 +} \ No newline at end of file diff --git a/docs/target-state/v1-api-review-findings-phase0-final.md b/docs/target-state/v1-api-review-findings-phase0-final.md new file mode 100644 index 00000000..7a7f162f --- /dev/null +++ b/docs/target-state/v1-api-review-findings-phase0-final.md @@ -0,0 +1,304 @@ +{ + "summary": "Phase 0 of the v1.0 API hardening brief: holistic read-only audit of Rust/TS/Python surfaces, cross-language divergence, error model, security invariants, and extension cost", + "agentCount": 13, + "logs": [ + "8/8 auditors returned; 139 raw findings" + ], + "result": { + "findings": "# Phase 0 — v1.0 API Review Findings\n\n**Repository:** `/Users/bordumb/workspace/repositories/auths-proof-base/auths-proof`\n**Branch:** `codex/spec-0040-docs` · **Working tree at review time:** commit `d0e4a53`\n**Policy basis:** `AGENTS.md` (prelaunch, zero external users, direct cutover, no compat shims), `docs/target-state/PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md`, `docs/target-state/PROFILE_AND_DOMAIN_ABSTRACTION_BOUNDARY_PLAN.md`.\n\nThis is the adjudicated Phase 0 document. Every claim carries `file:line`. Claims re-verified against the working tree in this session are marked **VERIFIED**; claims carried on a reporting auditor's citation are listed explicitly in the Coverage statement. Three adversarial critiques were integrated; where a critique was wrong, the original claim is kept and the disagreement is stated.\n\n---\n\n## Executive summary\n\n1. **Semantic ownership leaks through facade re-exports, not through dependency graphs**: `product/sdk/auths-sdk/src/lib.rs:26` re-exports `DomainProfile`/`DomainCommand`/`DeploymentAction` — generic machinery whose sealing trait `DomainMeaning` is private (`product/profiles/auths-profile-domains/src/lib.rs:19`) — behind a doc comment calling it \"closed,\" and a shipping vertical (`product/integrations/auths-deployment`) is built on it through a manifest that looks clean, so no crate-level gate can see it.\n2. **The safety spine is not one vocabulary but five**: Rust owns `EffectState` with exactly three variants (`product/errors/auths-errors/src/lib.rs:38-43`), while TypeScript and Python both widen it with `unknown` and independently ship a workflow effect axis (`bindings/typescript/src/workflow/errors.ts:48` = `none|possible|occurred`; `bindings/python/python/auths/_errors.py:9-11` = a *task-lifecycle* axis) — and the root of both packages exports the *wrong* `RetryClass` (`bindings/typescript/src/index.ts:48`, `bindings/python/python/auths/__init__.py:122`, both pointing at the four-variant wire enum, not the registry's).\n3. **The signed evidence artifact cannot express \"unknown\"**: `ExecutionOutcome` has exactly two variants, `Succeeded` and `Failed` (`product/receipts/auths-receipts/src/lib.rs:299-304`), and `ExchangeOutcome` has no indeterminate member (`exchange/crates/auths-proof-exchange-model/src/lib.rs:545-554`), so the reference runtime signs a durable receipt saying **Failed** for a provider timeout and returns a **refusal** for an effect that definitely applied (`product/runtime/auths-runtime/src/lib.rs:898-921`, `:930-941`, `:952-961`).\n4. **A signed \"Authorized\" decision receipt is written before the replay check runs** (`product/runtime/auths-runtime/src/lib.rs:816-828` precedes `:830`), producing audit records that assert authorization for requests that are then refused, plus unbounded attacker-driven write amplification into the receipt sink.\n5. **The only shipping `NodeRuntime` is an 800-line parallel authorization system** (`product/runtime/auths-node/src/sandbox.rs:276`) that depends on no kernel crate, hand-rolls narrowing over 4 dimensions where the kernel checks 11 (`core/crates/auths-algebra-kernel/src/generated.rs:52-75`), and mints root authorities with no authentication of the requester (`sandbox.rs:95-107`) — and eight of the ten error codes it puts on the wire are unregistered (`product/runtime/auths-node/src/profiles.rs:24-31`).\n6. **Two enforcement mechanisms that would have caught most of this do not exist**: there is no Rust symbol-level gate or re-export allowlist (the freeze operates on file digests and crate names, `xtask/src/semantic_freeze.rs:8-63`), and there is no check that TypeScript's 193 public symbols and Python's 159 name the same concepts.\n7. **`grant` vs `authority` is the deepest naming split** — Rust's reference tier says `grant` (`core/crates/auths-author/src/lib.rs:350,496,537`), the product surface and every route say `authority` (`product/runtime/auths-node/src/api.rs:119-120`), and the bindings use **both nouns in one identifier** (`bindings/typescript/src/workflow/contracts.ts:806` `WorkflowSignedGrantAuthority`).\n8. **Adding a vertical is a cross-cutting edit, not a package**: the profile roster is hand-maintained in at least thirteen places across three languages and five release artifacts, five of which currently disagree about which profiles are qualified, and `auths.mcp/1` — the reference vertical — is not expressible on the production wire at all (`product/runtime/auths-production-client/src/lib.rs:181-188`, `product/runtime/auths-node/src/api.rs:121-133`).\n9. **Two crates ship in the workspace with zero consumers and live compliance claims** — `auths-deployment` (`compliance.toml:234`) and `auths-cache` (`compliance.toml:143-144`) — and the latter defines a *second* canonicalization of \"what action is this\" (`product/cache/auths-cache/src/lib.rs:47`) whose doc comment openly says it differs from the proof's.\n10. **The one formal proof about root preservation is vacuous and structurally unfalsifiable**: `root_preserved: true` is a literal (`core/crates/auths-authority/src/lib.rs:201`), and `AuthorityStateView` (`:108-122`) carries no root to compare against, so the Kani harness (`core/crates/auths-algebra-kernel/src/lib.rs:43-74`) proves an identity over arbitrary booleans while calling it a security invariant.\n\n---\n\n## Current numbers\n\n**Rust** — all **VERIFIED** this session.\n\n| Quantity | Value | Source |\n|---|---|---|\n| Workspace members | 114 | `Cargo.toml` |\n| Publishable crates (frozen closure) | **42** | `xtask/src/semantic_freeze.rs:20-63` |\n| Declared public roots | **10** | `xtask/src/semantic_freeze.rs:8-19` |\n| Freeze version | 110 | `xtask/src/semantic_freeze.rs:7` |\n| Crates in the closure with **zero** workspace consumers | 2 (`auths-deployment`, `auths-cache`; `auths-identity-authority` is a third, a declared *root*) | exhaustive grep |\n| `AttenuationChecks` dimensions | 11 | `core/crates/auths-algebra-kernel/src/generated.rs:52-75` |\n| Dimensions the shipping node checks | 4 | `product/runtime/auths-node/src/sandbox.rs:123-135` |\n\n**Error registry** — **VERIFIED** by parsing `product/errors/v1/registry.json`.\n\n| Quantity | Value |\n|---|---|\n| Registered codes | **45** (`custody` 18, `core` 14, `mcp` 8, `plan` 5) |\n| Distinct `stages` strings | **20** |\n| Distinct `operation` values | **5** — `create, execute, resume, sign, verify` (**has `sign`, lacks `delegate`**) |\n| `EffectState` variants in Rust | **3** — `not-applied, possible, applied` (`product/errors/auths-errors/src/lib.rs:38-43`) |\n| `RetryClass` variants in Rust (registry) | **4** — `never, safe, conditional, unknown` (`:29-36`) |\n| Codes where `allowsDecisionReference` is true | **0 of 45** |\n| Codes where `allowsReceiptReference` is true | **0 of 45** |\n| Unregistered codes emitted by shipping code | ~62 binding-side (carried) + **8 server-side** (VERIFIED) → registry target ≈ **115** |\n\n> **The brief's \"20 stable error codes\" is wrong; it is 45.** Any plan sized against 20 under-scopes by more than half.\n\n**Bindings** — **VERIFIED** by recount.\n\n| Surface | TypeScript | Python |\n|---|---|---|\n| Root | **41** | **35** |\n| `identity` | 33 | 19 |\n| `verify` | 38 | 36 |\n| `profiles` | 37 | 20 |\n| `testkit` | 29 | 33 |\n| `framework` | 11 | 11 |\n| `integrations` | **4** | **5** |\n| **Total public symbols** | **193** | **159** |\n\nShared names at root after case conversion: **34**. TypeScript-only at root: **7** — `approval` (value), `ApprovalPolicy`, `AuthsConfiguration`, `ProductionAuthsOptions`, `DoctorMode`, `DoctorOptions`, `DoctorState`. Python-only at root: **1** — `Approval` (class). Shared names in `./identity` vs `auths.identity`: **4 of 33/19** (`DecodedIdentity`, `ResolutionEvidence`, `ValidatedIdentity`, `IdentityPrincipal`).\n\n**FFI (neither is covered by any parity gate)** — **VERIFIED**.\n\n| Surface | Size |\n|---|---|\n| `bindings/wasm/auths-proof-wasm/src/lib.rs` | 5,586 lines, 262 `wasm_bindgen` attribute occurrences |\n| `bindings/python/src/` | 13 modules, 6,779 lines |\n| `bindings/python/python/auths/_native.pyi` | **147** declared top-level symbols |\n\nBoth ABIs are semantic-frozen artifacts and therefore public surface by the project's own definition, and **neither appears in the 193/159 counts above or in any cross-language check**.\n\n**Extensibility** — see the dedicated section. Headline: **28 files** (carried from git archaeology) is an *undercount*; the verified floor is **≥ 34**, and thirteen of those are hand-maintained rosters.\n\n---\n\n## Root causes\n\nFive upstream problems generate almost every finding below.\n\n**R1 · There is no symbol-level gate on the Rust surface, and leaks travel through re-exports.**\n`xtask/src/semantic_freeze.rs` freezes file digests and a crate-name closure; `architecture.toml` gates the dependency graph. Neither can see that `product/sdk/auths-sdk/src/lib.rs:26` publishes `DomainProfile` — a type generic over a *private* trait — into the product facade, nor that `product/integrations/auths-deployment/src/lib.rs:7,65,74,83` consumes generic domain machinery through that facade while its `Cargo.toml` shows no dependency on `auths-profile-domains`. Every \"the label says closed, the type is generic\" finding is a consequence.\n\n**R2 · Vocabularies are restated rather than projected.**\nThe registry (`product/errors/v1/registry.json`) is the only artifact with a generator (`xtask/src/error_registry.rs`). Everything else — retry classes, effect states, families, stages, profile rosters, ABI numbers, SDK version strings, telemetry stage names, narrowing bounds — is typed by hand in each language. Where a vocabulary is restated *n* times, it has diverged *n−1* times. The `k_of_n`/`threshold` split is the diagnostic case: **both projections agreed with each other and overrode the owner** (`core/crates/auths-author/src/lib.rs:673` vs `bindings/typescript/src/plans.ts`, `bindings/python/python/auths/profiles/_mcp.py`).\n\n**R3 · The product waist was designed twice.**\n`Auths` (local, packaged-engine, MCP) and `ProductionAuths` (remote, byte-oriented, three verticals) are two complete SDKs exported from one namespace, with `createAuths` dispatching between them by **sniffing for an `endpoint` property**. Their vocabularies are disjoint: local says `execute(action)`, remote says `execute(authority, bytes)`; local outcomes are `Completed/Denied/…`, remote outcomes are `ProductionCompleted/ProductionDenied/…`; local retry is registry `RetryClass`, remote retry is the wire `RetryClass` (`bindings/typescript/src/production-client.ts:12`). 19 of TypeScript's 41 root symbols are the `Production*` mirror.\n\n**R4 · \"Unknown\" has no representation below the error envelope.**\nThe registry's `effect: unknown` — the whole point of the design — cannot be carried by `ExecutionOutcome` (2 variants), `ExchangeOutcome` (2 variants, no indeterminate), or the executor port (`Result<_, String>`). The runtime therefore *must* lie, and it does, in three places. Conversely, the bindings' unregistered-code path *requires* an `unknown` effect that Rust does not own. The vocabulary is simultaneously too narrow where evidence is produced and too wide where errors are parsed.\n\n**R5 · A vertical is a distributed edit, not a package.**\n`ActionProfile` (`product/profiles/auths-profile-api/src/lib.rs`, 117 lines, 3 methods) carries no identity. Every fact about a vertical — fixtures, algebra id, provider contract, route, endpoint path, doctor visibility, release qualification, config allowlist membership — lives outside it, in TOML, JSON, Rust literals inside `xtask`, and hand-typed lists in two bindings. The descriptor that would fix this **already exists and has zero implementors** (`product/runtime/auths-lifecycle/src/registry.rs:13-32`).\n\n---\n\n## Findings\n\nRanked. `Goal` is one of **simple / extensible / secure**. `Lane` is where the work happens. Detailed evidence and dispositions follow the index.\n\n### Index\n\n| ID | Title | Goal | Severity | Lane |\n|---|---|---|---|---|\n| **B1** | No Rust symbol gate or re-export allowlist; 42 published crates have crate-level gates only | simple | Blocker | rust |\n| **B2** | `auths-sdk` re-exports generic domain machinery and labels it \"closed\" | simple+secure | Blocker | rust |\n| **B3** | `DomainReceiptInspector` is the sole `ReceiptProfileInspector` and owns receipt meaning for five verticals | extensible | Blocker | rust |\n| **B4** | Both binding crates expose a caller-asserted `CanonicalAction` constructor | secure | Blocker | bindings |\n| **B5** | WASM and PyO3 export generic domain parsers/canonicalizers to consumer SDKs | secure+ext | Blocker | bindings |\n| **B6** | `optional_budget_covers` returns `true` when the action requests no budget | secure | Blocker | rust |\n| **B7** | `DeploymentAction` mints an unregistered budget algebra | secure | Blocker | rust |\n| **B8** | `PeerObservation` is a public enum with public variants; authenticated peers are forgeable | secure | Blocker | rust |\n| **B9** | `AuthsError` — the only registry-bound projection — is exported by both bindings and constructed by neither | secure | Blocker | cross |\n| **B10** | `RetryClass`/`EffectState`/`ErrorFamily`: three concepts, eleven vocabularies, wrong one at both roots | simple+secure | Blocker | cross |\n| **B11** | Both production clients relabel transport failures as `not-applied` + `backoff` | secure | Blocker | cross |\n| **B12** | `ProviderOperationError` hardcodes non-effect; a possibly-applied provider call is signed \"failed\" | secure | Blocker | python |\n| **B13** | `createAuths` sniffs for `endpoint`; two SDKs in one namespace | simple | Blocker | cross |\n| **B14** | Qualified-profile roster hand-maintained in three languages with three failure modes | extensible | Blocker | cross |\n| **B15** | `ActionProfile` carries no identity; ~81 facts declared out of band | extensible | Blocker | rust |\n| **B16** | `auths` and `auths-proof` are two crates.io coordinates for one byte-identical surface | simple | Blocker | rust |\n| **B17** | Python ships two unrelated classes named `AuthsError` | simple+secure | Blocker | python |\n| **B18** | *(merged into B10)* | — | — | — |\n| **B19** | `recover` — a sixth product operation invented identically by both bindings, with no Rust owner | simple | Blocker | cross |\n| **B20** | Operation identifier has three vocabularies; the type says `step`, the wire says `verb` | simple | Blocker | cross |\n| **B21** | Resume token: two names, two wire formats; the \"neutral\" one hardcodes `/^mcp1\\./` | secure | Blocker | cross |\n| **B22** | `auths-deployment` is dead, publishable, and cites compliance evidence that cannot reach it | simple | Blocker | rust |\n| **B23** | Published npm tarball ships `dist/workflow.js`, star-exporting capability-granting hooks | secure | Blocker | typescript |\n| **B24** | TypeScript exports two unrelated types named `Auths` | simple | Blocker | typescript |\n| **B25** | `verify`: class in TS, free functions in Python, two names in Rust; **Python has no `VerifiedAction`** | simple+secure | Blocker | cross |\n| **B26** | **NEW** — The signed execution receipt cannot record \"unknown\"; three shipping sites are forced into a wrong claim | secure | Blocker | rust |\n| **B27** | **NEW** — The only shipping `NodeRuntime` is a parallel authorization system with a 4-dimension attenuation check | secure+simple | Blocker | rust |\n| **B28** | **NEW** — The per-action single-use guard is dead for every budget-free action, i.e. for all of MCP | secure | Blocker | rust |\n| **B29** | **NEW** — A signed \"Authorized\" decision receipt is written before the replay check | secure | Blocker | rust |\n| **M1** | Lifecycle acknowledgement is not semantically bound to the submitted intent | secure | Major | rust |\n| **M2** | Definite non-effect is a caller assertion, not evidence | secure | Major | rust |\n| **M3** | Bindings invent `effect: unknown` and hardcode `entered.provider = false`, discarding wire values | secure | Major | cross |\n| **M4** | The executor port cannot express an ambiguous outcome | secure | Major | rust |\n| **M5** | `BudgetAlgebraId` `numeric-ceiling-v1` means money in one profile and a count in five | secure | Major | rust |\n| **M6** | ~62 binding + 8 server codes exist in no registry; nothing checks registry membership | secure | Major | cross |\n| **M7** | Default trusted context ships empty status snapshot and `none-v1` channel binding | secure | Major | rust |\n| **M8** | `auths-runtime` carries the most generic name in the workspace and is MCP-specific | simple | Major | rust |\n| **M9** | Three product-tier clocks substitute `0` for unreadable system time | secure | Major | rust |\n| **M10** | TypeScript duck-types a tool's own result as a handler outcome via an `effect` property | secure | Major | typescript |\n| **M11** | `Indeterminate`/reconcile is the one outcome forbidden from carrying a recovery reference | secure | Major | cross |\n| **M12** | `\"ed25519-v1\"` has **five** independent literals plus five inline occurrences | simple | Major | rust |\n| **M13** | `PUBLIC_RUST_ROOTS` contradicts the tier model: a dead bridge in, every protocol crate out, three transport roots with no published implementation | simple | Major | rust |\n| **M14** | Four hardcoded vertical lists plus embedded JavaScript inside `xtask` Rust source | extensible | Major | rust |\n| **M15** | `ExecutionResult` at the TS root names two unexported members and is returned by no method | simple | Major | cross |\n| **M16** | `auths.integrations.production` is unreachable public API that raises `AttributeError` | simple | Major | python |\n| **M17** | `auths-config` publishes a TOML vocabulary no shipping binary reads, with an allowlist excluding every vertical | simple+ext | Major | rust |\n| **M18** | Disclosure ports are async in TS and sync in Python; a KMS-backed protector is unimplementable | simple | Major | cross |\n| **M19** | Production timeout is milliseconds in TS and seconds in Python, with different bounds | simple | Major | cross |\n| **M20** | Plan noun has three names; Rust's `k_of_n` is `threshold` in both projections | simple | Major | cross |\n| **M21** | The receipt spine is structurally identical and named differently in all four types | simple | Major | cross |\n| **M22** | `AuthsEvent` differs per binding; Python hardcodes the correlation id | simple+secure | Major | cross |\n| **M23** | \"Stage\" names **six** vocabularies, not four | simple | Major | cross |\n| **M24** | Approval, delegation-constraint, trusted-context, budget, identity, testkit vocabularies each diverge | simple+ext | Major | cross |\n| **M25** | `grant` vs `authority`: one object, two nouns, both in one identifier | simple | Major | cross |\n| **M26** | `execute` overloads differently; **TypeScript** cannot supply a request context Python and Rust can | simple+secure | Major | cross |\n| **M27** | `doctor` reports one profile in **both** bindings when four ship; Python additionally hardcodes three fields | simple | Major | cross |\n| **M28** | Root export lists diverge: 7 TS-only, 1 Python-only | simple | Major | cross |\n| **M29** | **NEW** — `ExchangeOutcome` is a fourth outcome vocabulary with no indeterminate member | simple+secure | Major | rust |\n| **M30** | **NEW** — Five mutually inconsistent qualified-profile rosters, zero code consumers, zero cross-checks | extensible | Major | release |\n| **M31** | **NEW** — `auths.mcp/1` is not expressible on the production wire | extensible | Major | rust |\n| **M32** | **NEW** — `auths-cache` is a second dead crate with live compliance claims and a competing action fingerprint | simple | Major | rust |\n| **M33** | **NEW** — The `./framework` custody contract diverges three ways, including a sync/async and a retry inversion | extensible+secure | Major | cross |\n| **M34** | **NEW** — Verifier options, input shape, and artifact cache exist only in TypeScript | simple+secure | Major | cross |\n| **M35** | **NEW** — `CauseCategory`, `EnteredBoundaries`, and `AuthsError` construction have no contract row; Python's constructor is public | secure | Major | cross |\n| **M36** | **NEW** — Lifecycle/close is an untabled operation; caller-misuse raises different exception classes | simple | Major | cross |\n| **M37** | **NEW** — `./integrations` diverges wholesale, including a Python-only async operation | simple | Major | cross |\n| **M38** | **NEW** — Telemetry attribute policy: Rust allowlist, Python denylist, TypeScript neither | secure | Major | cross |\n| **N1** | `auths-algebra-kernel` publishes two entry points its own Kani proof asserts are identical | simple | Minor | rust |\n| **N2** | `root_preserved` is a hardcoded `true`, and is structurally unfalsifiable | secure | Minor | rust |\n| **N3** | Three custody codes whose explanations admit no proof are registered `not-applied` | secure | Minor | rust |\n| **N4** | The generated error reference prints Rust `Debug` identifiers instead of wire tokens | simple | Minor | rust |\n| **N5** | A published tier-0 crate freezes a predecessor-named schema id outside the naming inventory | simple | Minor | rust |\n| **N6** | Published docs instruct callers to import two subpaths that do not exist | simple | Minor | typescript |\n| **N7** | `DecisionInspection` is anonymous nested objects in TS and five named dataclasses in Python | simple | Minor | cross |\n| **N8** | The MCP profile surface — the shape every vertical copies — diverges six ways | simple+ext | Minor | cross |\n| **N9** | **NEW** — `decisionReference` and `receiptReference` are dead in all three languages | simple | Minor | cross |\n| **N10** | **NEW** — `Explanation.retryable: boolean` is a fourth retry vocabulary with duplicated English prose | simple | Minor | cross |\n| **N11** | **NEW** — `verifyReceipt` is async in TS and sync in Python — the reverse of M18 | simple | Minor | cross |\n| **N12** | **NEW** — Error-envelope serialization differs: TS omits absent keys, Rust and Python emit nulls | simple | Minor | cross |\n| **N13** | **NEW** — The two error-schema parsers accept different inputs | secure | Minor | cross |\n\n---\n\n### Detail — new and corrected findings\n\nFindings whose text is unchanged from the prior synthesis are summarized in the index and in the divergence matrix. The entries below are the ones that are **new**, or whose evidence or disposition **changed** under the critiques.\n\n---\n\n#### B26 · The signed execution receipt cannot record \"unknown\"\n**Goal:** secure · **Lane:** rust · **VERIFIED**\n\n**Evidence.** `product/receipts/auths-receipts/src/lib.rs:299-304`:\n\n```rust\npub enum ExecutionOutcome {\n /// Command completed successfully.\n Succeeded,\n /// Authorized command failed during execution.\n Failed,\n}\n```\n\nTwo variants, closed, and this is the `outcome` field of `ExecutionReceipt` (`:308-315`) — the canonical signed durable record. The registry's `effect: unknown` has **no representation in the artifact the product exists to produce**.\n\nThree shipping sites in the reference runtime are forced into a false claim:\n\n- `product/runtime/auths-runtime/src/lib.rs:897-921` — the executor returns `Err(message)`; the runtime calls `record_execution(..., ReceiptExecutionOutcome::Failed, None)` and then returns `RefusalKind::ApplicationPolicy`. A provider timeout, where the charge may well have gone through, is **signed into the durable receipt as `Failed`**.\n- `:930-941` — the provider **succeeded**, `result` is in hand, the effect definitely happened; `record_execution` fails; the runtime returns `Self::refusal(RefusalKind::ApplicationPolicy, None, \"receipt store unavailable\", …)`. It reports **refused** for an applied effect. That is not imprecision — it is the inverse of the truth on the effect axis, and it is the opposite of fail-closed.\n- `:944-961` — completed effect, oversized result → `ExchangeOutcome::refused(..., \"tool result exceeds exchange limit\")`. Applied, reported refused.\n\n**Disposition.** Add a third variant `ExecutionOutcome::Unknown` carrying the reconciliation handle, and give the executor port a three-valued return (M4). `record_execution` failure after a successful provider call must produce an *indeterminate* response, never a refusal. ⚠ This changes receipt protocol bytes — **sequence behind the fixture audit**, alongside N3.\n\n**Rationale.** The document's entire safety spine is the claim that \"we do not know\" is expressible. It is expressible in the error envelope and nowhere else. The one durable artifact a reconciliation process would consult cannot carry it.\n\n---\n\n#### B27 · The only shipping `NodeRuntime` is a parallel authorization system\n**Goal:** secure + simple · **Lane:** rust · **VERIFIED**\n\n**Evidence.** `product/runtime/auths-node/` is 2,970 lines. `product/runtime/auths-node/src/sandbox.rs:276` (`impl NodeRuntime for SandboxRuntime`) is the only non-test implementation. `product/runtime/auths-node/src/main.rs:33-37` **refuses to boot** unless `config.sandbox_providers()` is true — `\"production ports must be assembled with the auths-node library\"`. There is no assembled production runtime anywhere in the tree.\n\n`SandboxRuntime` reimplements the product from scratch: its own `Scope`/`Authority` (`sandbox.rs:28-41`), raw `ed25519_dalek` signing (`:13`), its own domain-separated envelopes (`:22-25`), its own recovery references and receipts. It depends on **no** kernel crate — no `auths-verifier`, `auths-model`, `auths-authority`, or `auths-profile-api`.\n\nIts `delegate` (`sandbox.rs:110-138`) hand-rolls narrowing over **four** dimensions:\n\n```rust\nif parent.scope.remaining_depth == 0\n || scope.remaining_depth >= parent.scope.remaining_depth\n || scope.expires_at > parent.scope.expires_at\n || scope.max_uses > parent.scope.max_uses\n || !scope.action_digests.iter().all(|digest| parent.scope.action_digests.contains(digest))\n```\n\nThe kernel checks **eleven** (`core/crates/auths-algebra-kernel/src/generated.rs:52-75`). Audiences, budget, status policy, assurance floor, critical extensions, profile attenuation, and root preservation are never checked. `verify_authority` (`:248-251`) validates one signature envelope and does **not** walk a chain. `create` (`sandbox.rs:95-107`) mints a root authority for `digest(request.identity())` with caller-supplied scope and **no authentication of the requester**.\n\n**Disposition.** Name it what it is at the type level and gate it: rename `SandboxRuntime` → `FixtureNodeRuntime`, put it behind a non-default `sandbox` feature, and make `publish = false` explicit. Assemble a real `NodeRuntime` on `auths-kernel-runtime`/`auths-verifier` before any §6 remote-path disposition is called final. If that is out of 1.0 scope, then say so in `release/assurance/*` and stop describing the remote waist as a product surface.\n\n**Rationale.** The whole remote/production half of the frozen contract (§6.1's wire row, B10/B11/C2's `RetryClass`↔`NextCall` split, both `production-client` modules) is written against a counterparty that implements four of eleven narrowing dimensions and authenticates nobody.\n\n---\n\n#### B28 · The per-action single-use guard is dead for every budget-free action\n**Goal:** secure · **Lane:** rust · **VERIFIED**\n\n**Evidence.** All three `BudgetLedger` implementations short-circuit before touching the single-use set:\n\n- `product/runtime/auths-runtime/src/lib.rs:209-217` — `NoBudgetLedger` has no set at all.\n- `product/stores/auths-stores/src/lib.rs:76-78` — `fn claim(...) { let Some(requested) = requested else { return BudgetClaim::Claimed; };`\n- `product/stores/auths-stores/src/lib.rs:243-245` — identical.\n\n`claimed: BTreeSet` (`product/stores/auths-stores/src/lib.rs:41`) is the only per-action single-use guard in the store layer, checked at `:82` and surfaced by `is_claimed` (`:68`). It is **never populated for a budget-free action**. Call site: `product/runtime/auths-runtime/src/lib.rs:862-866`, passing `verified.canonical_action().requested_budget()`.\n\nCombined with the verified fact that `auths-profile-mcp` canonicalizes with `None` and *rejects* any action carrying `requested_budget().is_some()`: **for the reference vertical the budget ledger is a total no-op.** `is_claimed` always returns false and the persistent file is never written. Replay of the *same* proof is still blocked by the challenge ledger (`product/stores/auths-stores/src/lib.rs:178`), but two distinct challenges naming the same `ActionId` both execute.\n\n**Disposition.** Move the `claimed.insert(action)` check *above* the budget short-circuit in all three implementations so single-use is enforced independently of whether a ceiling was requested; or delete `claimed` and state plainly that single-use is a challenge property only. Do not ship a set that looks like a guard and guards nothing.\n\n**Rationale.** This is the ledger half of the decision the D6 open question asks a human to make, and it was not on the table: whichever way MCP budgets go, the guard is unreachable today.\n\n---\n\n#### B29 · A signed \"Authorized\" decision receipt is written before the replay check\n**Goal:** secure · **Lane:** rust · **VERIFIED**\n\n**Evidence.** `product/runtime/auths-runtime/src/lib.rs:816-828` calls `record_decision(..., &VerificationOutcome::Authorized(Box::new(verified.clone())), now)` and returns a refusal only if the receipt store is unavailable. Only afterwards, at `:830`, does `match self.replay.claim(challenge.challenge(), now)` run, refusing `ConsumedChallenge` at `:838-844`. The budget claim follows at `:862`.\n\n**Consequences.** (a) Every replayed proof produces a signed, durably stored decision receipt asserting **Authorized** for a request that is then refused — an audit record that says the opposite of what happened. (b) An attacker with one captured proof can drive unbounded receipt-sink writes with no rate limit in front of them, because the replay check is downstream of the write.\n\n**Disposition.** Reorder: claim the challenge and the budget, then record the decision — or record the decision with the refusal attached so the receipt is truthful. The reordering is preferable; it also removes the write-amplification surface.\n\n---\n\n#### M29 · `ExchangeOutcome` is a fourth outcome vocabulary with no indeterminate member\n**Goal:** simple + secure · **Lane:** rust · **VERIFIED**\n\n`exchange/crates/auths-proof-exchange-model/src/lib.rs:545-554` defines `ExchangeOutcome { Completed{result}, Refused{kind, verdict, message} }` with `RefusalKind` (`:533-542`, 8 variants). `auths-proof-exchange-model` is in `PUBLIC_RUST_CLOSURE` (`xtask/src/semantic_freeze.rs:50`), so this ships.\n\nThe prior §6.3 \"outcome set\" row named only `ClientOutcomeKind` (6 variants) and counted \"12 TS + 13 PY names for one Rust enum.\" There are **two** Rust enums, and the second one — the one the reference runtime actually returns — has no `Recoverable`/`Indeterminate` member. That absence is the *mechanical cause* of B26's three mis-claims: the runtime has nothing else to return.\n\nSame file: `ExchangeMetrics` (`:596-604`, two fields) is a **fourth** metrics record; M26 counted three and attributed them all to Python.\n\n**Disposition.** Add `ExchangeOutcome::Indeterminate { reference, verdict, message }` and require it wherever an effect may have applied. Add a §6.3 row. Fold `ExchangeMetrics` into the one `VerificationMetrics` shape or name it distinctly (`ExchangeTiming`).\n\n---\n\n#### M30 · Five qualified-profile rosters, mutually inconsistent, with zero code consumers and zero cross-checks\n**Goal:** extensible · **Lane:** release · **VERIFIED**\n\n| Source | Roster |\n|---|---|\n| `bindings/public-topology-v1.json:31-36` | 4 — `github`, **`auths.mcp/1`**, `opentofu`, `postgresql` |\n| `release/assurance/open-production-candidate-1/manifest.json:12-16` | 3 — **omits `auths.mcp/1`** |\n| `bindings/typescript/sdk-runtime-contract.json` `profiles` | 1 — `{\"auths.mcp\": 1}` |\n| `bindings/python/python/auths/_doctor.py:42` | 1 — `(\"mcp/1\",)` |\n| `release/docs-bundle/public-topology.json:31` | 1 — `[\"auths.mcp/1\"]`, under the **same** `\"schema\": \"auths.public-topology/1\"` as the bindings file, different content |\n\nA tree-wide grep for `qualifiedProfiles` hits only those files plus `product/spec/v1/assurance-manifest.schema.json` (a `required` key). **No code reads any of them.** Note also two id formats in play: `auths.mcp/1` vs `mcp/1`.\n\n**Disposition.** One generated roster, Rust-owned, derived from the vertical descriptors of B15/G8. Add a `cargo xtask profile-roster` check to §6.5 that fails when any of the five artifacts disagrees with it. This is the artifact that answers \"which verticals are qualified,\" and it currently has five answers and no gate.\n\n---\n\n#### M31 · `auths.mcp/1` is not expressible on the production wire\n**Goal:** extensible · **Lane:** rust · **VERIFIED**\n\n`QualifiedProfile` is closed over exactly three ids (`product/runtime/auths-production-client/src/lib.rs:167-172`), and `parse` returns `ProductionClientError::UnknownProfile` for anything else (`:181-188`). `product/runtime/auths-node/src/api.rs:121-133` exposes exactly three `execute` routes, none of them MCP. The topology file lists `auths.mcp/1` as qualified.\n\nM27 observes that Python's doctor reports one profile when the wheel supports four. The mirror-image fact is that the **server supports three and the reference vertical is not one of them** — and every §6.4 disposition that says \"project X into both bindings\" for the remote path is unreachable for MCP by construction.\n\n**Disposition.** Decide explicitly: either `auths.mcp/1` is local-only (say so in the topology file and remove it from `qualifiedProfiles`), or it gains a `QualifiedProfile` variant, a route, and a handler. Do not leave the two answers coexisting.\n\n---\n\n#### M32 · `auths-cache` is a second dead crate with live compliance claims and a competing action fingerprint\n**Goal:** simple · **Lane:** rust · **VERIFIED**\n\nExhaustive grep: `auths-cache` is referenced only by `Cargo.toml:82,182`, `architecture.toml:107`, `compliance.toml:127-144`, `Cargo.lock:349`, and the generated dependency graph. `grep '-> \"auths-cache\"' architecture/dependency-graph.dot` returns **nothing** — zero consumers, exactly the `auths-deployment` pattern in C1.\n\nTwo aggravating facts:\n- `compliance.toml:143-144` registers two assurance claims (`core-api-consumer`, `verification-cache`) against a crate nothing can reach — the same defect C1 used to justify deleting `auths-deployment`.\n- `product/cache/auths-cache/src/lib.rs:47` `canonical_action_fingerprint` is an **independent second canonicalization of a `CanonicalAction`**, and its own doc comment (`:43-45`) says it \"is intentionally distinct from the body digest recorded by the proof.\" A second definition of \"what action is this,\" outside any profile, in the crate that answers \"have I already verified this.\" That is precisely the brief's semantic-ownership invariant, and it is the only instance of it outside the `auths-profile-domains` axis.\n\nIt is `publish = false` (`product/cache/auths-cache/Cargo.toml:7`), so it is a Major rather than a Blocker.\n\n**Disposition.** Delete the crate and its two compliance claims in Wave 1 alongside `auths-deployment`.\n\n---\n\n#### M33 · The `./framework` custody contract diverges three ways\n**Goal:** extensible + secure · **Lane:** cross · **VERIFIED**\n\n`./framework` is the published extension point — 11 symbols in each language, the only surface a third party implements. It diverges on three axes and none had a contract row.\n\n- **Disposal.** `Signer.dispose?(): Promise` is **optional** in TypeScript (`bindings/typescript/src/workflow/contracts.ts:70`); `Signer.aclose()` is **required** in Python (`bindings/python/python/auths/_workflow.py:143`) and actively enforced — `_validate_signer` raises `TypeError` unless `aclose` is callable (`_workflow.py:1470-1477`). A conformant TypeScript signer has no Python analogue.\n- **Cancellation.** `SigningRequest.signal?: AbortSignal` (`contracts.ts:48`) has no Python counterpart (`_workflow.py:109-117`). An HSM- or KMS-backed signature cannot be cancelled from Python. N8 flags exactly this for `McpExecutionResources` and missed it on the signer port, where it matters more.\n- **Retry/effect inversion.** `ProviderFailureKind` has the same 5 members in both (`bindings/typescript/src/workflow/errors.ts:2-7`, `_errors.py:12-14`) and maps to **contradictory** verdicts. Python (`_errors.py:82-107`): `retry = \"safe\" if kind in (\"unavailable\", \"timeout\") else \"never\"`, and `effect_state` is the literal `\"not-started\"` for **every** kind. TypeScript classifies `timeout` and `cancelled` as `conditional`/`possible`. **Python tells a caller that a timed-out signing operation is safe to retry and definitely did not happen.** Also `causeChain` (TS) vs `cause_codes` (PY), and TS's constructor takes an `ErrorContext` second parameter Python lacks.\n- **Principal type.** `PrincipalDescriptor.principal` is `readonly principal: string` in TypeScript (`contracts.ts:28`); in Python it is a nominal `Principal` read via `.value` — visible at `bindings/python/python/auths/_product.py:198` (`resources.agent.identity.principal.principal.value`) and declared at `_native.pyi:135,140`. `Principal` has **no public Python name** (`identity.py`'s `__all__` does not export it), so an implementer of the published contract must handle an unnamed type.\n\n**Disposition.** Required `dispose`/`aclose` in both. Add a cancellation token to Python's `SigningRequest`. Delete `_errors.py`'s independent classification entirely and derive provider failures from registry codes (B10). Make `PrincipalDescriptor.principal` the same type in both — either `str` in both, or export `Principal` publicly from Python and add the equivalent to TypeScript.\n\n---\n\n#### M34 · Verifier options, input shape, and artifact cache exist only in TypeScript\n**Goal:** simple + secure · **Lane:** cross · **VERIFIED**\n\n- `VerificationOptions{correlationId, telemetry}` and `VerificationBatchOptions{signal, chunkSize, correlationId, telemetry}` (`bindings/typescript/src/verifier/result.ts:103-113`) are TypeScript-only. `verify.verify()` / `verify_many()` (`bindings/python/python/auths/verify.py:105,114`) accept **no options at all** — no cancellation, no telemetry port, no correlation id.\n- `correlationId` is on TypeScript's `CommonResult` (`verifier/result.ts:56`) and therefore on all three verification results; **no Python verification result has it** (`verify.py:66-98`). M22 treats correlation id as a telemetry problem; the join key is missing from the decision itself in one projection.\n- `VerificationInput` is a named record in TypeScript (`verifier/result.ts:97-101`) and `Tuple[bytes, bytes, bytes]` in Python (`verify.py:102`), consumed positionally (`verify.py:105-109`). Three same-typed byte strings passed positionally is an argument-order bug that type-checks.\n- `ImmutableArtifactCache` (`bindings/typescript/src/verifier/cache.ts:12,18`), published at `./verify`, has a caller-writable `put(commitment, bytes)` in the verification path and no Python counterpart, no owner, and no row.\n- Variant naming: TS `AuthorizedResult`/`DeniedResult`/`IndeterminateResult` (`verifier/result.ts:71,76,80`); Python `Authorized`/`Denied`/`Indeterminate` (`verify.py:66,78,90`) — and Python's `Denied`/`Indeterminate` **collide with the package root's own** `Denied`/`Indeterminate` (`_product.py:70,76`), which have two fields against verify's eight. TypeScript deliberately avoids this with the `*Result` suffix.\n\n**Disposition.** Named `VerificationInput` record in both. `VerificationOptions`/`VerificationBatchOptions` in both. `correlationId` on every verification result in both. Adopt TypeScript's `*Result` suffix in Python to break the root collision. Adjudicate `ImmutableArtifactCache`: either project it (and specify who may write to it) or delete it — a caller-writable cache in the verification path needs a stated threat model.\n\n---\n\n#### M35 · `CauseCategory`, `EnteredBoundaries`, and `AuthsError` construction have no contract row\n**Goal:** secure · **Lane:** cross · **VERIFIED**\n\n- `CauseCategory` is Rust-owned with 8 kebab-case members (`product/errors/auths-errors/src/lib.rs:61-70`) and projected identically (`bindings/typescript/src/product-errors.ts:10-18`, `_product_errors.py:45-53`), but the **classifiers** diverge: `causeCategoryFrom` (`product-errors.ts:98-109`) can never return `invalid-response` or `corrupt-state`; `cause_category_from` (`_product_errors.py:154-163`) can never return `conflict` or `limit-exceeded`. The same failure produces different causes.\n- `EnteredBoundaries` (`auths-errors:101-107`, `product-errors.ts:20-26`, `_product_errors.py:56-62`) is the **witness both parsers require** before accepting `effect = possible` (`product-errors.ts:196-199`, `_product_errors.py:247-254`). It gates the single most safety-relevant assertion in the model and has no row and no owner statement.\n- **`AuthsError` construction is sealed in TypeScript and open in Python.** TS: `private constructor`, only `static parse` (`product-errors.ts:64,70`). Python: `def __init__(self, details)` is public (`_product_errors.py:111`), so a caller can mint an `AuthsError` with any `effect`/`retry` pair, bypassing every invariant at `_product_errors.py:245-254`. An error-wrapping helper can downgrade `possible` → `not-applied` in Python and cannot in TypeScript.\n- **Projection form is unspecified.** Python projects these vocabularies as `str, Enum` classes (`_product_errors.py:20,27,34,45`); TypeScript as string-literal unions. `error.retry` is an enum member in one and a string in the other (`format_auths_error` needs `.value`, `_product_errors.py:145-146`), while Python's *other* error module types the same-named `RetryClass`/`EffectState` as `Literal[...]` (`_errors.py:8-11`).\n\n**Disposition.** Add rows for `CauseCategory` and `EnteredBoundaries` with Rust as owner. Make the two `causeCategoryFrom` classifiers a generated projection so they cannot disagree. **Seal Python's `AuthsError.__init__`** — private constructor, `parse` only, matching TypeScript. Fix the projection form in the contract: enums in Python, literal unions in TypeScript, one rule stated once.\n\n---\n\n#### M36 · Lifecycle/close is an untabled operation; caller-misuse raises different exception classes\n**Goal:** simple · **Lane:** cross · **VERIFIED**\n\n`Auths.close()` + `[Symbol.asyncDispose]` (`bindings/typescript/src/product.ts:161-162,316,324`) vs `aclose()` + `__aenter__`/`__aexit__` (`bindings/python/python/auths/_product.py:335,344,348`) — a public product operation absent from §6.1's verb list entirely.\n\nThe exception classes diverge for identical conditions: use-after-close is `TypeError` in TypeScript (`product.ts:329`) and `RuntimeError` in Python (`_product.py:358`); delegation expiry out of bounds is `TypeError` (`product.ts:289`) vs `ValueError` (`_product.py:298`). **None of these is an `AuthsError` in either language** — which is the failure users hit first, and the one the registry says nothing about.\n\n**Disposition.** Add `close` to §6.1. Decide once which class carries caller-input misuse — the honest answer for a system whose thesis is \"every failure is a registry code\" is that these should be registry-backed too (M6 registers them), with `TypeError` reserved for genuine protocol-type violations in both languages.\n\n---\n\n#### M37 · `./integrations` diverges wholesale, including a Python-only async operation\n**Goal:** simple · **Lane:** cross · **VERIFIED**\n\nTypeScript's `./integrations` exports 4 symbols: `development`, `production`, `DevelopmentAuthsOptions`, `RecoverableDevelopmentAuthsOptions`. Python's exports 5: `development`, `production`, `FrameworkAdapter`, `IdentityTransport`, `exchange_identity` (`bindings/python/python/auths/integrations.py:666-672`). Shared: **2**.\n\n`exchange_identity(transport, packet, *, maximum_bytes=131072, timeout=10.0) -> bytes` (`integrations.py:64-70`) is an async identity-exchange operation over a byte transport that exists **only in Python** and appears in no §6.1 verb row.\n\nPreconditions differ too: `create_recoverable_auths` requires an absolute `Path` and creates it, where TypeScript accepts any non-empty string; and Python's factories return a synchronous `_PendingAuths` awaitable/context-manager where TypeScript returns `Promise`.\n\n**Disposition.** Adjudicate `exchange_identity`: promote to both (with a §6.1 row) or delete. Align the two option records. Make the factory return shape identical.\n\n---\n\n#### M38 · Telemetry attribute policy: Rust allowlist, Python denylist, TypeScript neither\n**Goal:** secure · **Lane:** cross · **VERIFIED**\n\nRust owns a 14-name **allowlist**: `ALLOWED_EVENT_ATTRIBUTES` (`product/runtime/auths-production-client/src/lib.rs:11-26`). Python adds a separate 10-substring **denylist**, `SENSITIVE_ATTRIBUTE_PARTS` (`bindings/python/python/auths/_observability.py:15-26`: `proof, signature, private, credential, secret, token, payload, cbor, public_key, idempotency_key`). TypeScript has neither.\n\nValue types diverge: TS `string | number | boolean` accepts floats (`observability.ts:16`); Python rejects non-`(str,int,bool)` and bounds ints to i64 (`_observability.py:129-134`). Container diverges: `Record` vs an ordered `Tuple[Tuple[str, …], …]` (`_observability.py:36`).\n\n**Disposition.** One Rust-owned allowlist, projected. Delete the Python denylist — a denylist behind an allowlist is either redundant or evidence the allowlist is wrong. Fix the value type to match Rust's serializer in both.\n\n---\n\n#### N9 · `decisionReference` and `receiptReference` are dead in all three languages\n**Goal:** simple · **Lane:** cross · **VERIFIED**\n\n`allowsDecisionReference` and `allowsReceiptReference` are true for **0 of 45** registry codes (verified by parsing `product/errors/v1/registry.json`), yet all three implementations declare, parse, and validate them: `product/errors/auths-errors/src/lib.rs:139-145`, `bindings/typescript/src/product-errors.ts:41-42,185-190`, `bindings/python/python/auths/_product_errors.py:78-79,234-244`.\n\n**Disposition.** Delete both reference kinds and their validation, or register the codes that need them. Two named-but-unreachable reference kinds beside the one real `executionReference` is exactly the \"one way to do each thing\" violation the SIMPLE goal targets.\n\n---\n\n#### N10 · `Explanation.retryable: boolean` is a fourth retry vocabulary\n**Goal:** simple · **Lane:** cross · **VERIFIED**\n\n`bindings/typescript/src/verifier/result.ts:19-23` and `bindings/python/python/auths/verify.py:48-51` both carry `retryable: bool`, computed in Python as `kind == \"indeterminate\"` (`verify.py:174`) with hardcoded English prose duplicated per language (`verify.py:168-173`). §6.2 enumerates `RetryClass`, `NextCall`, and `ProviderRetryClass` and omits this one.\n\n**Disposition.** Delete `retryable` and carry the registry `RetryClass`. Move the explanation prose into the Rust registry so it is authored once.\n\n---\n\n#### N11 · `verifyReceipt` is async in TypeScript and sync in Python — the reverse of M18\n**Goal:** simple · **Lane:** cross · **VERIFIED**\n\n`bindings/typescript/src/internal/receipt-attestation.ts:163` is `async` (published at `./verify`, used by `product.ts:364`); `bindings/python/python/auths/_receipts.py:184` is sync (published as `verify.verify_receipt`). By §6.4's own async rule — user-implemented ports async, pure projections sync — **TypeScript is the deviant here**, where M18 records only ports that are wrongly sync in Python.\n\n**Disposition.** Make `verifyReceipt` sync in TypeScript. The rule has to cut both ways or it is not a rule.\n\n---\n\n#### N12 · Error-envelope serialization differs between the owner and one projection\n**Goal:** simple · **Lane:** cross · **VERIFIED**\n\nTypeScript omits absent reference keys (`product-errors.ts:220-224` spreads only when defined). Python always emits them as `null` (`_product_errors.py:102-104`). Rust's `ErrorEnvelope` declares `Option` with no `skip_serializing_if` (`product/errors/auths-errors/src/lib.rs:129-145`), so **the owner emits nulls too** — TypeScript is the deviant. These envelopes are embedded verbatim in the support bundle (`product-errors.ts:146`, `_product_errors.py:182`), so the byte-equality goal in M22 is unreachable until this is fixed, and no §6 row covers it.\n\n**Disposition.** Present-with-null in all three. Add a §6.2 row for envelope serialization.\n\n---\n\n#### N13 · The two error-schema parsers accept different inputs\n**Goal:** secure · **Lane:** cross · **VERIFIED**\n\nTypeScript's token parser requires an alphanumeric first character and measures its 128-byte bound in UTF-16 units (`product-errors.ts:49,271`); Python's `TOKEN_CHARS` frozenset imposes no first-character rule and measures UTF-8 bytes (`_product_errors.py:17,334-341`). `\".foo\"` is a valid code in Python and rejected in TypeScript. Relatedly, `error_reference_url` escapes in TypeScript (`:95`) and *raises* in Python (`:151`).\n\n**Disposition.** One generated parser spec from the Rust envelope definition; conformance corpus (§6.5 item 3) must include the rejection cases, not only the acceptance cases.\n\n---\n\n### Detail — corrections to previously reported findings\n\n#### M23 — corrected: \"Stage\" names **six** vocabularies, and the disposition was unimplementable\n\nThe prior disposition (\"one Rust-owned `Stage` covering the registry's 20 values; delete `TelemetryStage`\") does not hold. **VERIFIED** by enumeration:\n\n- Registry stages (20): `cancellation, central-validation, configuration, handler, handler-result, internal, key-lifecycle, lifecycle, lifecycle-store, parse, plan-commitment, plan-member, provider, provider-response, provider-result, receipt, reconciliation, reference, reservation, runtime`.\n- `TelemetryStage` (8, `bindings/typescript/src/observability.ts:5-13`): `acquisition, construction, approval, signing, verification, reservation, execution, receipt`.\n- **Intersection = `{reservation, receipt}`.** Two of twenty, two of eight.\n\nThey are different axes: a registry stage names *where a failure was raised*; a telemetry stage names *which lifecycle phase is being observed*. Merging yields a ~26-member enum in which most members are invalid in whichever context they appear.\n\nTwo further vocabularies the prior count missed:\n\n- **`SdkEventStage`, 12 members**, `product/runtime/auths-production-client/src/lib.rs:44-56`: `acquisition, construction, approval, signing, verification, reservation, execution, receipt, open, authority, cleanup, telemetry`. This is the Rust-side owner of the telemetry axis. It is **crate-private** (no `pub`, VERIFIED), and TypeScript's projection of it **drops `open`, `authority`, `cleanup`, `telemetry`** — a projection narrowing its own owner, which the prior table never recorded.\n- `McpExecutionCheckpointStage`, 6 members, aligned across bindings (`profiles/mcp/index.ts:230-236`, `profiles/_mcp.py:588-595`).\n\n**Revised disposition.** Preserve **three named axes**, do not merge:\n1. `Stage` — Rust-owned closed enum over the registry's 20 values; `ErrorDefinition.stages: &'static [Stage]`; `AuthsError.stage` typed as `Stage` in both bindings (it is `string` today: `product-errors.ts:35`, `_product_errors.py:70`).\n2. `TelemetryStage` — **make `SdkEventStage` public and rename it `TelemetryStage`** (12 members); project all 12 into both bindings; delete Python's open `stage: str` (`_observability.py:33`).\n3. `VerificationStage` — 5 kernel phases, already aligned, stays as is.\n\n`OperationalStage` (13, `product/operations/auths-operations/src/lib.rs:204-218`) and `PublicWorkflowStage` (9, `:519-530`) stay Rust reference-tier and are **not** projected. `McpExecutionCheckpointStage` is profile-owned and stays.\n\n#### M24 — corrected: the TypeScript budget claim was false as stated\n\nThe previous text said *\"TypeScript has none of them… A TypeScript caller cannot state a budget ceiling on a delegation,\"* and §6.4 froze that. **That is wrong.** `bindings/typescript/src/workflow/contracts.ts:272-275` (VERIFIED):\n\n```ts\nexport type DelegatedBudget =\n | Readonly<{ kind: \"inherit\" }>\n | Readonly<{ kind: \"none\" }>\n | Readonly<{ kind: \"ceiling\"; algebra: string; value: bigint }>;\n```\n\nwired into `DelegatedAuthorityRequest.budget` at `:290`. That is field-for-field `BudgetCeiling{algebra, value}` from `_workflow.py:418-425`. This is a **naming** gap, not a capability gap, and freezing the capability claim would have misdirected the lane — worse, M24's own fallback clause (\"if budget is deliberately not yet product surface, remove it from Python's public names too\") risked *deleting a working narrowing dimension*.\n\nThe related count was also wrong in both directions. A case-insensitive grep across `bindings/typescript/src` for all ten Python constraint names (`InheritAction, InheritBudget, NoBudget, BudgetCeiling, ExpiryOnly, InheritStatus, SnapshotRequired, AnyBody, ExactBody, AllowedBodies`) returns **zero** — so **0 of 10**, not 6 of 10, have a nominal TypeScript name. Simultaneously `contracts.ts:266-284` supplies a structural counterpart for **all ten**. The finding is real (nominal vs anonymous, and therefore not greppable or diffable by name) but it is a naming finding only.\n\n`BudgetSummary` is the one genuine TypeScript absence (grep: 0 hits). **Revised disposition:** TypeScript gains ten exported nominal types mirroring `_workflow.py:403-425` camelCased, replacing the anonymous unions; and gains `BudgetSummary`. Nothing about budget capability changes.\n\n#### M26 — corrected: the rationale was inverted; **TypeScript** is the deficient projection\n\nThe previous text said Rust's `RequestContext` is *\"constructed by the verifier host, not the caller.\"* **False.** `product/sdk/auths-sdk/src/lib.rs:47-57` (VERIFIED):\n\n```rust\npub fn new(\n audience: &str,\n challenge: [u8; 32],\n evaluation_time: u64,\n) -> Result {\n```\n\nA public constructor on the product SDK taking the replay challenge and evaluation time **from the caller**, plus the audience — which Python's `AuthorizationRequest` (`bindings/python/python/auths/profiles/_mcp.py:72-87`) does not even expose. Rust gives the caller *more* control, not less. Deleting Python's parameter would move Python **away** from the Rust-owned shape.\n\nThe correct reading is the mirror image: **TypeScript is the binding missing the projection.** `bindings/typescript/src/profiles/mcp/index.ts:606-607` mints `crypto.getRandomValues(new Uint8Array(32))` and `Date.now()` internally, and `executeMcpClosed(agent, action, resources)` (`:661-665`) has no request parameter, so a TypeScript caller cannot supply a challenge or an evaluation time at all — which makes deterministic replay testing and clock injection impossible in that language.\n\nOne nuance the finding overstated: `AuthorizationRequest` is in `_mcp.py.__all__` (`:1624`) but **not** in `auths/profiles/__init__.py.__all__`, so it is reachable only through the private `auths.profiles._mcp`. It was presented as public surface; it is not.\n\n**Revised disposition.** Keep the type. Name it `RequestContext` in all three, matching Rust; add `audience` to the binding projections; add the parameter to `executeMcpClosed` in TypeScript; export it publicly from `auths.profiles` in Python. Everything else in M26 stands and is verified: `execute_mcp_closed`'s fourth parameter (`_mcp.py:1043-1048`), `resume_mcp_closed(reference, resources)` dropping the agent (`_mcp.py:1170-1173`) where TypeScript uses it to obtain the bound engine (`mcp/index.ts:813`), and the triplicated metrics record.\n\n#### M27 — corrected: **both** doctors report one profile; the prescribed fix does not fix it\n\nThe previous text blamed Python for reporting one profile when the wheel supports four, and prescribed deriving from a generated runtime-contract module \"exactly as `bindings/typescript/src/runtime-contract.ts` does.\" **VERIFIED:** `bindings/typescript/sdk-runtime-contract.json` declares `\"profiles\": { \"auths.mcp\": 1 }`; `runtime-contract.ts:6-8` mirrors it; `doctor.ts:43-44` derives `profiles` from it. **TypeScript's doctor also prints `Profiles mcp/1`** — while `bindings/typescript/src/profiles.ts:13-23` ships `opentofuSavedPlanApply`, `postgresqlBoundedUpdate`, `githubIssueAddress`.\n\nAnd `sdk-runtime-contract.json` is hand-maintained: `bindings/typescript/tools/runtime-contract.mjs` only asserts that the TypeScript constant equals the JSON. Nothing generates either from Rust. Copying that pattern into Python would reproduce the defect with more ceremony.\n\n**Revised disposition.** Generate the runtime contract in **both** languages from the Rust vertical descriptors (B15/G8) and the ABI constants, so `sdkVersion`, `profiles`, and the expected ABI numbers are derived facts. Python's hardcoded `sdk_version=\"1.0.0rc1\"` (`_doctor.py:34`), `profiles=(\"mcp/1\",)` (`:42`), and `compatible = abi == 2` (`:32`) all go away, and so does the TypeScript JSON's hand-maintained profile map. Everything else in M27 stands: one `DoctorReport` shape, `DoctorOptions` in Python, `DoctorMode`/`DoctorState` exported from both roots, sync in both, `Portable ABI` vs `Native ABI` reconciled, `warnings`/`warning` pluralization reconciled, and byte-compared `render_doctor` output in CI.\n\n#### M28 — corrected count: **7** TypeScript-only root symbols, not four\n\n**VERIFIED** by set difference of `bindings/typescript/api/public-api.txt` (root rows) against `bindings/python/python/auths/__init__.py.__all__`, after case conversion:\n\nTypeScript-only: `approval` (value), `ApprovalPolicy`, `AuthsConfiguration`, `ProductionAuthsOptions`, `DoctorMode`, `DoctorOptions`, `DoctorState`.\nPython-only: `Approval` (class).\nShared: 34. (41 − 7 = 34; 35 − 1 = 34.)\n\n`ApprovalPolicy` was missing from the prior finding entirely — in the one finding whose deliverable is exact list parity.\n\n#### M20 — corrected scope: the `threshold` rename touches at least six sites, one of them public\n\n**VERIFIED.** There are two `k_of_n` functions, not one: `core/crates/auths-author/src/lib.rs:673` (`PlanBuilder::k_of_n`, the only one previously named) and `core/crates/auths-model/src/lib.rs:1768` (`AuthorizationPlan::k_of_n`). Plus the **public** variant `AuthorizationPlanRef::KOfN` (`auths-model:1723`), matched at `core/crates/auths-codec/src/encode.rs:672` and `core/crates/auths-composition/src/lib.rs:177`, and reconstructed at `core/crates/auths-codec/src/decode.rs:481`.\n\n`AuthorizationPlanRef` is public and inside the semantic-freeze closure, so this is a frozen-surface rename. Mitigating fact, since the project's own rule protects protocol bytes: `AuthorizationPlanNode` (`auths-model:1703`) is private and carries no serde derive — canonical CBOR is encoded by hand in `auths-codec` — so **the rename does not move a byte.** State that explicitly in the change so the fixture audit does not block it.\n\n#### M22 / support bundles — corrected: there are **four** producers and `auths.support/2` already exists in both languages\n\n**VERIFIED:**\n\n| Producer | Schema | Field name | Sync? |\n|---|---|---|---|\n| `bindings/typescript/src/observability.ts:79,103` | `auths.support/1` | `schemaVersion` | async |\n| `bindings/typescript/src/product-errors.ts:124,150` | `auths.support/2` | `schema` | sync |\n| `bindings/python/python/auths/_observability.py:95` | **`auths.python-support-bundle/1`** | `schema` | sync |\n| `bindings/python/python/auths/_product_errors.py:186` | `auths.support/2` | `schema` | sync |\n\nSo TypeScript ships **two** support-bundle types with the *same three identifiers* (`SupportBundleInput`, `AuthsSupportBundle`, `createSupportBundle`) in one package, and Python already has a conforming `auths.support/2` implementation. The finding is **duplicate producers**, not a missing one, and M22's disposition (\"Python's `support_bundle` returns a record under `auths.support/1`\") contradicted §6.4's own `auths.support/2` row.\n\n**Revised disposition.** Keep exactly one producer per language: `product-errors.ts:134` / `_product_errors.py:166`, schema `auths.support/2`, field name `schema`. Delete `observability.ts:70-110` and `_observability.py:82-109` and the `auths.python-support-bundle/1` identifier. Byte-compare the two outputs in CI. The rest of M22 stands and is verified — `_observability.py:46` `\"correlationId\": \"python-sdk\"`, and the Python `AuthsEvent` (`:29-36`) lacking `schemaVersion`, `correlationId`, and `durationMs` while using `observed_at` for `timestamp`.\n\n#### N2 — corrected: the proposed fix is not implementable, and the dimension is structurally unfalsifiable\n\nThe prior disposition said \"carry the parent root into `AuthorityStateView`; `root_preserved: principal_id_equal(view.root, parent.root)`.\" **VERIFIED as impossible:** `AuthorityStateView` (`core/crates/auths-authority/src/lib.rs:106-122`) **is** the parent view, and neither it nor `GrantAuthorityView` carries a root. `root` exists only as a private field on `EffectiveAuthority` (`:24`). There is no second root to compare, so `root_preserved` is not merely uncomputed — it is **unfalsifiable as the type is shaped**. The literal is at `:201` (not `:200-201`).\n\n**Revised disposition.** Two honest options: (a) delete the dimension from `AttenuationChecks`, the trait docs, and the Kani harness, so the formal claim matches what is checked; or (b) add a declared root to the grant statement and compare it — **a protocol change with fixture impact**, which the prior text did not flag. Option (a) is correct for 1.0. The property does hold today by construction (`delegate` at `:359-380` mutates eleven fields and never touches `self.root`) — that fact belongs in a comment, not in a proof.\n\n**Related, and previously unreported:** `evaluate_grant_view` is `#[doc(hidden)] pub` (`core/crates/auths-authority/src/lib.rs:192-195`) and **every field of `AuthorityStateView` is `pub`** (`:109-121`). Any crate depending on `auths-authority` can synthesize an arbitrary parent authority and evaluate a grant against it, bypassing `EffectiveAuthority`'s private fields entirely. `#[doc(hidden)]` is a documentation flag, not a seal, and it is semver-visible. This is a real unsealed path adjacent to D4 and belongs in the Security section.\n\n#### N6 — corrected citation, and two more files\n\n**VERIFIED:** `bindings/typescript/docs/adoption-layers.md` **does not exist** (that directory holds ten other files). The real path is `docs/adoption-layers.md`, and the line numbers are correct: `:17` documents `@auths-dev/sdk/authority`, `:18` documents `@auths-dev/sdk/approvals`, `:19` says \"or the compatibility root.\"\n\nTwo further files carry the same dead specifiers: `docs/plans/TYPESCRIPT_RUST_SURFACE_PARITY_PLAN.md:59` and `docs/scratch/09_LAYERED_PUBLIC_SDK_SURFACES.md:33-34`. The seven real subpaths are confirmed from `bindings/typescript/package.json` `exports`. Disposition otherwise unchanged, and the docs test must cover all three directories.\n\n#### D2 / S6 — corrected: the fork on that line pair is `EffectState`, not `RetryClass`\n\n**VERIFIED.** `bindings/typescript/src/workflow/errors.ts:47` is `RetryClass = \"never\" | \"safe\" | \"conditional\" | \"unknown\"` — **member-identical** to the registry, so a redundant *declaration*, not a fork. The fork is `:48`: `EffectState = \"none\" | \"possible\" | \"occurred\"`. The only genuine `RetryClass` fork is `production-client.ts:12` (`\"never\" | \"backoff\" | \"resume\" | \"reconcile\"`). B10's remedy (delete both shadow modules) is unaffected; only the adjudication text changes.\n\n`bindings/typescript/src/product-errors.ts:7` remains **correct and registry-derived** (`export type RetryClass = Definition[\"outcomes\"][number][\"retry\"];`, no widening). The widening is on lines **6** and **8**.\n\n---\n\n### Contradictions between auditors, adjudicated\n\n**C1 — `DeploymentAction`: move it, or delete it? → Delete both.**\nOne auditor said *move* `DeploymentAction` and its canonicalizer into `product/integrations/auths-deployment`, \"its only shipping consumer\"; another said *delete* `auths-deployment` outright. I verified the decisive fact: a workspace-wide grep for `auths-deployment`/`auths_deployment` returns only `Cargo.toml:58,190`, `architecture.toml:119`, `compliance.toml:219,222,234`, `Cargo.lock:455`, `product/README.md:52`, the generated dependency graph, and its own manifest. **No crate, demo, test, binding, or example depends on it**, and it has no `#[cfg(test)]` module. The \"only shipping consumer\" premise is false: it is not shipping. Moving code into a dead crate to satisfy a boundary rule produces a correctly-shaped dead crate. Deletion is correct and is the cheapest unblock for **B2**.\n\n**C2 — `RetryClass`: unify onto the registry, or split by question? → Split by question, but derive the wire one.**\n`{never, safe, conditional, unknown}` answers \"am I permitted to retry\"; `{never, backoff, resume, reconcile}` answers \"which call do I make next.\" Collapsing them loses information the client needs — `resume` and `reconcile` name different endpoints. But the wire vocabulary must be *derived*, not independent: `product/runtime/auths-production-client/Cargo.toml` has **no `auths-errors` dependency**, and none of `backoff`/`resume`/`reconcile` appears in the 45 registry codes. **Disposition: rename the wire enum to `NextCall`, add the `auths-errors` dependency, and require every response to carry both the registry `RetryClass` and `NextCall`, with a validated mapping** — `NextCall::Backoff` may not accompany `RetryClass::Never`, and so on. That closes **B11**, because the fabricated `retry: \"backoff\"` would then have to name a registry-consistent pair.\n\n**C3 — `auths-config`: unpublish, or fix the allowlist? → Both, in that order.**\nUnpublishing removes the semver obligation; deleting `known_profile` removes the extensibility tax. `auths-operations` still consumes `BoundConfiguration` (`product/operations/auths-operations/src/lib.rs:134`) regardless of publish status, so the allowlist would keep rejecting real profiles inside the workspace. **VERIFIED:** `known_profile` (`product/config/auths-config/src/lib.rs:425-436`) allows exactly `auths.mcp`, `auths.http`, `auths.git`, `auths.deploy`, `auths.supply-chain`, `auths.edge` at version 1 — **none of the three shipping production profiles.**\n\n**C4 — NEW: B15 proposes inventing a descriptor that already exists.**\n`product/runtime/auths-lifecycle/src/registry.rs:13-32` defines `LifecycleRegistrationV1` with exactly the fields B15 proposes to invent as `VerticalDescriptorV1`: `domain_id`, `reservation_algebra_id`, `provider_contract_id`, `retry_class`, `reservation_fixture`, `lifecycle_fixture`, `formal_namespace`, `kani_harness`, `store_conformance`, `production_migrated` — plus a validator (`:52-63`). `auths-lifecycle` is in `PUBLIC_RUST_CLOSURE` (`xtask/src/semantic_freeze.rs:41`), so it already ships as public API. It has zero implementors outside its own `#[cfg(test)]` module.\n**Adjudication: extend `LifecycleRegistrationV1`, do not create a second descriptor.** Add `profile_id`/`profile_version`, `route`, and `qualified` (the fields B15 needs and it lacks), rename it `VerticalDescriptorV1` in a single clean break, and add `const DESCRIPTOR: VerticalDescriptorV1` to `ActionProfile`. Building a fresh type alongside a frozen, already-public, structurally-identical one would create two descriptor vocabularies — the exact defect SIMPLE targets.\n\n**D1 — the brief is wrong on the error-code count.** 45, not 20. **VERIFIED.** Any plan sized against 20 under-scopes by more than half; the post-M6 target is ≈115.\n\n**D3 — the ed25519 sprawl is worse than reported.** Five independent literals — `core/adapters/auths-signature-core/src/lib.rs:11`, `auths-raw-key-core/src/lib.rs:22`, `auths-multikey/src/lib.rs:18`, `auths-hsm-attested/src/lib.rs:38`, `auths-spiffe-x509/src/lib.rs:34` — plus five inline occurrences (`auths-author:1177`, `auths-identity:1091,1109`, `auths-identity-raw-key:75`, `auths-did-key:296`). Scope **M12** accordingly.\n\n**D4 — the sealed `VerifiedAction` boundary holds; the unsealed paths are upstream of it.** `VerifiedAction` has exactly one construction site reachable only from `verify_authority_measured`, with no `Default`/`From`/serde/`#[doc(hidden)]` escape, and TypeScript gates minting behind a module-private symbol plus a `WeakSet` of packaged engines. **Do not re-litigate the seal.** The unsealed paths are *what it seals*: **B4** lets a caller author the `CanonicalAction`, **B21** lets a caller mint an `ExecutionReference`, and — newly found — `evaluate_grant_view` + all-`pub` `AuthorityStateView` let a dependent crate evaluate a grant against a fabricated parent. Fix those three; leave the `VerifiedAction` mechanism alone.\n\n**D5 — `auths-profile-api` is clean and must stay small.** 117 lines, two dependencies, three deterministic methods, one closed six-variant error, no state/credentials/clocks/execution/receipt meaning. That is a correct minimal contract and must not grow behaviour. Adding `const DESCRIPTOR` is not behaviour — it is the compiler-visible declaration of facts currently living in TOML. **Add the descriptor; add nothing else.**\n\n**D6 — unresolved, flagged for a human.** The B6 fix (`(Some, None) => false` in `optional_budget_covers`, `core/crates/auths-model/src/lib.rs:927-937`) and the MCP profile's structural refusal to carry a requested budget are in direct conflict: under the fix, any grant with a ceiling denies every MCP action. **B28 adds a third face to the same decision**: the single-use ledger is a no-op for exactly the budget-free case. All three must be decided together. Do not let the implementation phase pick silently.\n\n---\n\n## Cross-language divergence\n\nThe frozen v1.0 contract. Each row names **one concept, one canonical spelling per language, one owner**. Rows marked ⚠ require a Rust-side change first. Rows marked **†** were corrected by this pass.\n\n### Verbs and operations\n\n| Concept | Rust (owner) | TypeScript | Python | Today | Finding |\n|---|---|---|---|---|---|\n| operation identifier | `ProductVerb` ⚠ becomes the type of `ErrorDefinition.operation` | `ProductVerb` | `ProductVerb` | 3 sets: `ProductVerb` (5, has `delegate`, lacks `sign`), registry `operation` (5, untyped, has `sign`, lacks `delegate`), `ProductStep` (`production-client.ts:11`) | B20 |\n| wire field name | `verb` | `verb` | `verb` | type says `step`, wire says `verb` (`production-client.ts:158`) | B20 |\n| `create` | `ProductVerb::Create` | `create(request): Promise` | `async create(request) -> AuthorityResult` | **aligned** | M28 |\n| `delegate` | `plan_child_grant` owns the narrowing policy ⚠ | `delegate(input: {authority, subject, attenuation?})` | `delegate(*, authority, subject, attenuation=None)` | 3 signatures; the 1..86_400 expiry bound written twice (`product.ts:287-288`, `_product.py:286,293-297`) | M25 |\n| `execute` | `begin` / `begin_plan_member` | `execute(action)` + `executePlan(plan)` | `execute(action)` + `execute_plan(plan)` | TS overloads into 2 result types; Python one 6-member union with runtime mutual-exclusion | M26 |\n| `resume` | `McpExecutionSession::resume` | `resume({reference, provider})` | `resume(*, reference, provider)` | `resume_mcp_closed` **drops the `agent`** (`_mcp.py:1170-1173`) | M26 |\n| `recover` | ⚠ fold into `resume` or give it a Rust owner | (from Rust) | (from Rust) | invented independently by both bindings | B19 |\n| `verify` | `Verifier::verify` | `Verifier.verify(...)` sync | `Verifier.verify(...)` sync | TS class + async batch; Python free functions, no options | B25, M34 |\n| **`close` †** | — | `close()` + `Symbol.asyncDispose` | `aclose()` + `__aexit__` | **untabled**; use-after-close is `TypeError` vs `RuntimeError` | M36 |\n| **`exchange_identity` †** | ⚠ adjudicate | — | `exchange_identity(...)` | **Python-only async operation** (`integrations.py:64`) | M37 |\n| request context **†** | **`RequestContext{audience, challenge, evaluation_time}`** (`auths-sdk:35-57`) | ⚠ **new** — TS has no way to supply one | `RequestContext` (rename from `AuthorizationRequest`, add `audience`, export publicly) | TS mints challenge/time internally (`mcp/index.ts:606-607`) | M26 |\n\n### Error and effect vocabulary — the safety spine\n\n| Concept | Rust (owner) | TypeScript | Python | Today | Finding |\n|---|---|---|---|---|---|\n| **`EffectState` †** | **⚠ must gain a fourth value or the envelope a discriminant** — see note below | same set | same set | 5 sets. Rust: 3. Both bindings widen with `unknown` **and require it** for unregistered codes. TS workflow: `{none,possible,occurred}`. PY workflow: a **task-lifecycle** axis `{not-started,in-progress,completed,failed,outcome-unknown}` | B10, M3, A1 |\n| field name for it | `effect` | `effect` | `effect` (delete `effect_state`) | PY `_errors.py:24` says `effect_state` | B10 |\n| **`RetryClass`** | `{never, safe, conditional, unknown}` | same | same | **root of both packages exports the wrong one** (`index.ts:48`, `__init__.py:122`) | B10 |\n| **`NextCall`** ⚠ rename from `auths_production_client::RetryClass` | `{never, backoff, resume, reconcile}` | `NextCall` | `NextCall` | both named `RetryClass`; `auths-production-client` has **no `auths-errors` dependency** | B10, C2 |\n| `ProviderRetryClass` | reference-tier only | not projected | not projected | correct today | B10 |\n| **`ErrorFamily`** | 7: `{configuration, input, runtime, profile, provider, state, internal}` | same 7 | same 7, typed | 3 sets; both bindings add `unknown`; TS workflow set (`workflow/errors.ts:39-45`) overlaps by 2; PY emits `\"workflow\"`, which Rust's `deny_unknown_fields` rejects | B10 |\n| **`Stage` †** | ⚠ new closed enum over the registry's 20 | `Stage` | `Stage` | `AuthsError.stage` is `string` in both | M23 |\n| **`TelemetryStage` †** | ⚠ **make `SdkEventStage` public**, 12 members (`production-client.rs:44-56`) | `TelemetryStage` (12) | `TelemetryStage` (12) | TS has 8 — drops `open, authority, cleanup, telemetry`; PY has open `stage: str` | M23 |\n| `VerificationStage` | 5 kernel phases | same 5 | same 5 | **aligned — keep separate** | M23 |\n| `McpExecutionCheckpointStage` | profile-owned, 6 | same 6 | same 6 | **aligned** | N8 |\n| **`CauseCategory` †** | 8 kebab-case (`auths-errors:61-70`) | same 8 | same 8 | **no row before**; the two classifiers can each never emit 2 of the 8 | M35 |\n| **`EnteredBoundaries` †** | 5 booleans (`auths-errors:101-107`) | same | same | **no row before**; gates the `possible` invariant | M35 |\n| `RecommendedAction` | 7 | projected **and reachable** | projected **and reachable** | root-exported in TS as a type no value ever has | B9 |\n| error base class | — | `AuthsError` only | `AuthsError` only | PY has **two unrelated classes named `AuthsError`**; TS has two unrelated hierarchies | B17 |\n| **`AuthsError` construction †** | — | `private constructor` + `static parse` | ⚠ **seal `__init__`** | Python's constructor is public; invariants bypassable | M35 |\n| **envelope serialization †** | `Option` → `null` (no `skip_serializing_if`) | ⚠ emit `null` | `null` (already correct) | **TS omits absent keys** — byte-equality unreachable | N12 |\n| `AuthsErrorCode` | 45 codes ⚠ + the ~70 unregistered | `KnownAuthsErrorCode` union + branded `UnknownAuthsErrorCode` | `Literal[...]` + explicit unknown type | TS: every string type-checks; PY: `AuthsErrorCode = str` | M6 |\n| **token/parser rules †** | one generated spec | — | — | different first-char rules and different length units | N13 |\n| `decisionReference` / `receiptReference` | ⚠ **delete** | delete | delete | true for **0 of 45** codes, implemented in all three | N9 |\n| registered code count | **45** → ≈115 | same | same | brief said 20 | D1 |\n\n> **† Note on the `EffectState` row — this is the one place the previous contract was self-contradictory.** The prior table froze `EffectState` as closed-three-and-no-`unknown` while §6.5 simultaneously required a conformance corpus covering the unknown-code path. But that path is *defined* by the deleted value: `bindings/typescript/src/product-errors.ts:239-252` and `bindings/python/python/auths/_product_errors.py:279-306` both return `family:\"unknown\", retry:\"unknown\", effect:\"unknown\"` for an unregistered code, and there is no legal substitute — `possible` is unconstructible for an unknown code because both parsers require `retry==unknown && recommendedAction==resume-and-reconcile && executionReference!=None && entered.provider` (`product-errors.ts:196-199`, `_product_errors.py:247-254`), and `not-applied`/`applied` would be lies. **Adjudication: add `EffectState::Unknown` and `ErrorFamily::Unknown` to Rust.** They are already load-bearing in two projections and in the wire contract's forward-compatibility story; making Rust own them is the smaller change and the only one that preserves the required path. The alternative — an `unregistered: true` discriminant on the envelope — is acceptable but touches the wire schema.\n\n### Result and outcome nouns\n\n| Concept | Rust | TypeScript | Python | Today | Finding |\n|---|---|---|---|---|---|\n| product facade | `auths_sdk` types | **`Auths`** (product only) | **`Auths`** | TS `Auths` names **both** the facade and the verifier engine | B24 |\n| verifier engine | `Verifier` | `Verifier` | `Verifier` (new class) | TS class is literally named `Auths`; Python has no class | B24, B25 |\n| outcome set | `ClientOutcomeKind{Completed, Denied, Indeterminate, Recoverable, Verified, Rejected}` | same 6, no `Production` prefix | same 6 | 12 TS + 13 PY names for one enum, all root-exported | B13 |\n| **exchange outcome †** | `ExchangeOutcome` ⚠ **add `Indeterminate`** (`exchange-model:545-554`) | not projected | not projected | **second Rust outcome enum, no row before**; no indeterminate member | M29 |\n| **receipt execution outcome †** | `ExecutionOutcome` ⚠ **add `Unknown`** (`auths-receipts:299-304`) | — | — | 2 variants; forces 3 false claims | B26 |\n| result envelope | `VerificationResult` ⚠ (from `VerifyResult`/`VerificationOutcome`) | `VerificationResult` | `VerificationResult` | Rust has two names, bindings a third | B25 |\n| result variants **†** | — | `AuthorizedResult`/`DeniedResult`/`IndeterminateResult` | **same `*Result` suffix** ⚠ | Python's bare names collide with `auths.Denied`/`auths.Indeterminate` | M34 |\n| sealed action | `VerifiedAction` | `VerifiedAction` | **`VerifiedAction`** ⚠ new | **absent from Python** | B25 |\n| **`correlationId` on results †** | — | on `CommonResult` | ⚠ **add** | TS has it on every result; Python on none | M34 |\n| **`VerificationInput` †** | — | named record | ⚠ **named record** | Python is a positional 3-tuple of same-typed bytes | M34 |\n| **verify options †** | — | `VerificationOptions`, `VerificationBatchOptions` | ⚠ **add both** | Python `verify()` takes no options at all | M34 |\n| execution result union | — | `ExecutionResult`, `PlanExecutionResult` | same two | TS root `ExecutionResult` names 2 unexported members and is returned by no method | M15 |\n| resume token | **`RecoveryReference`**, 32-byte base64url | `RecoveryReference` | `RecoveryReference` | 2 names, 2 formats; the \"neutral\" one hardcodes `/^mcp1\\./` in **both** bindings | B21 |\n| token methods | — | `.decode(bytes)` / `.encode()` — verifying, MAC-bound | same | TS `decode` mints from any hex; `create` is public; PY uses `from_bytes`/`to_bytes` | B21 |\n| receipt (pair) | — | **`Receipt`** | **`Receipt`** | TS aliases **two different types** to `Receipt` at two entry points (`product.ts:47`, `verify.ts:26`) | M21 |\n| receipt (single) | — | `AttestedReceipt` | `AttestedReceipt` | `AttestedApplicationReceipt` vs `AttestedReceipt` | M21 |\n| receipt signer / attestor | — | `ReceiptSigner` / `ReceiptAttestor` | same | `Application*` prefixes in TS only | M21 |\n| **`verifyReceipt` †** | — | ⚠ **sync** | sync | TS is async — reverse of the M18 direction | N11 |\n| metrics | — | `VerificationMetrics` | `VerificationMetrics` | Python defines the 7-field record **3 times**; `ExchangeMetrics` is a 4th in Rust | M26, M29 |\n| **`Explanation.retryable` †** | ⚠ **delete** | delete | delete | fourth retry vocabulary, prose duplicated per language | N10 |\n\n### Configuration, plans, and constraints\n\n| Concept | Rust | TypeScript | Python | Today | Finding |\n|---|---|---|---|---|---|\n| root constructor | — | `openLocalAuths` (`/integrations`), `connectAuthsService` (`/service`) | `open_local_auths`, `connect_auths_service` | TS `createAuths` **sniffs for `endpoint`**; PY `create_auths` is production-only and collides with `development.create_auths` | B13 |\n| options record | — | `AuthsOptions` | `AuthsOptions` | TS `ProductionAuthsOptions`; **Python has none** | M28 |\n| **`ProductionTransport` †** | ⚠ owner for url/timeout types | `url: URL`, `timeoutMs: number` | ⚠ `url: str` → align, `timeout_ms` | `timeoutMs` vs `timeout_seconds: float`; the one port a production deployment must implement, untabled | M19 |\n| trusted context | **`TrustedContext`** ⚠ (from `VerifierContext`) | `TrustedContext` + `TrustedContextSource` | same | Rust says `VerifierContext`, PY native says `TrustedContext`, TS has 5 source types and **no name for the context** | M24 |\n| plan | `AuthorizationPlan` / `AuthorizationPlanBuilder` | same | same ⚠ (from `ProofPlan*`) | 3 names | M20 |\n| threshold combinator **†** | **`threshold`** ⚠ — **6 rename sites**, incl. public `AuthorizationPlanRef::KOfN` | `threshold` | `threshold` | both projections agreed and overrode the owner; **no protocol bytes move** | M20 |\n| profile plan | `ProfilePlan` | `ProfilePlan` | `ProfilePlan` ⚠ (from `McpPlan`) | generic in TS, profile-specific in PY | M20, N8 |\n| narrowing constraints **†** | typed enums | ⚠ 10 exported **nominal** types | 10 nominal classes (today's shape) | TS uses anonymous `{kind}` unions; **0 of 10 have a nominal TS name, but all 10 are expressible** | M24 |\n| budget types **†** | `BudgetCeiling`, `BudgetAlgebraId`, `BudgetLedger` | `DelegatedBudget` exists ✔ — **rename to nominal**; ⚠ **add `BudgetSummary`** | same (today's shape) | TS *capability* is present (`contracts.ts:272-275,290`); only the names and `BudgetSummary` are missing | M24 |\n| grant vs authority | `grant` = reference-tier protocol term only | `Authority*` everywhere | `Authority*` everywhere | `WorkflowSignedGrantAuthority` uses **both nouns in one identifier** | M25 |\n| `ApprovalMode` | ⚠ new Rust enum, 7 | 7 modes | 7 modes | PY has 6 — **`headless` missing** (`_workflow.py:68-70`) though its own validator accepts it (`:1488-1497`) | M24 |\n| approval facade | — | `approval.*` → `ApprovalConfiguration` | `Approval.*` → `ApprovalConfiguration` | TS returns policy only + async; PY returns policy+provider + sync | M24 |\n| **`Signer` port †** | — | `dispose()` **required** | `aclose()` required | TS `dispose?()` optional; Python enforces `aclose` | M33 |\n| **`SigningRequest` †** | — | `signal?: AbortSignal` | ⚠ **add cancellation** | Python cannot cancel an in-flight HSM signature | M33 |\n| **`PrincipalDescriptor.principal` †** | `PrincipalId` | one type | **same one type** | `string` in TS; an unnamed nominal `Principal` in Python | M33 |\n| **`ProviderFailureKind` verdicts †** | ⚠ derive from registry | from registry | from registry | TS `timeout→conditional/possible`; PY `timeout→safe` and `effect=\"not-started\"` for **every** kind | M33, B12 |\n| identity **†** | — | one tier | one tier | TS ships **two** tiers (33 symbols); PY ships one (19); **exactly 4 names shared** | M24 |\n| telemetry event | `auths.telemetry/2` | `AuthsEvent` (9 fields) | same 9 | PY drops 3 and **hardcodes `correlationId: \"python-sdk\"`** | M22 |\n| **telemetry attributes †** | 14-name allowlist (`production-client:11-26`) | project the allowlist | project the allowlist | PY adds a 10-substring denylist; TS has neither | M38 |\n| **support bundle †** | — | `auths.support/2`, field `schema`, sync | same | **4 producers**; TS ships `/1` and `/2` under the *same three identifiers* | M22 |\n| doctor **†** | ⚠ generated runtime contract | `DoctorReport` (9 fields), sync | same, sync | **both** report 1 profile of 4; PY additionally hardcodes version, ABI, profile list | M27 |\n| conformance verb | — | `certify*` | `certify_*` | PY has both `check_*` and `certify_*`; port lists differ | M24 |\n| disclosure ports | — | async | **async** ⚠ | PY sync — a KMS-backed protector is unimplementable | M18 |\n| async rule | — | user-implemented ports async; pure projections sync | same | violated in **both** directions (M18 one way, N11 the other) | M18, N11 |\n| **profile id vocabulary †** | `QualifiedProfile` ⚠ generated from descriptors | generated | generated | **6 live vocabularies** (3/3/6/4/1/open-string) + 5 disagreeing rosters | M30, M31, B14 |\n| **HTTP route table †** | `execute_path` (`production-client:200-206`) | generated | generated | hand-duplicated 3×; TS falls open to the GitHub route (`production-client.ts:379`) | B14 |\n\n### Enforcement mechanism for the contract\n\nThe table is only as good as the check that holds it. **Five** are required; none exists today.\n\n1. **`cargo xtask rust-surface`** — per-crate symbol snapshot, symbol budget, and a **re-export allowlist**. Crate-level gating cannot see B2 (**B1**).\n2. **Root-list diff** — a CI test diffing TypeScript's root export list against Python's `auths.__all__` after case conversion, extended to all seven subpaths (**M28**).\n3. **Vocabulary conformance corpus** — shared fixtures asserting that, for each of the 45 (soon ≈115) registry codes, all three languages agree on `family`, `retry`, `effect`, `recommendedAction`, `stage`, and `entered` — **including the unregistered-code path and the rejection cases** (**M3, N13**).\n4. **NEW · ABI parity check** — the three existing gates all sit *above* the FFI. `bindings/wasm/auths-proof-wasm/src/lib.rs` (5,586 lines, 262 `wasm_bindgen` attributes) and `bindings/python/src/` (13 modules; 147 `_native.pyi` symbols) are both semantic-frozen and therefore public, and neither appears in the 193/159 counts or any parity check. **Two projections can diverge below the module surface and all three checks above pass.** (**G10**)\n5. **NEW · Profile-roster gate** — one generated roster; fail the build when any of `bindings/public-topology-v1.json`, `release/assurance/*/manifest.json`, `bindings/typescript/sdk-runtime-contract.json`, `bindings/python/python/auths/_doctor.py`, or `release/docs-bundle/public-topology.json` disagrees with it (**M30**).\n\n---\n\n## Security findings\n\nEach with the concrete attack or concrete false claim it produces. Ordered by severity.\n\n**S-1 · `optional_budget_covers` returns `true` when the child requests no budget (B6).**\n`core/crates/auths-model/src/lib.rs:927-937`, arm `(_, None) => true`; called at `core/crates/auths-authority/src/lib.rs:304`; the verifier's own gate at `core/crates/auths-verifier/src/lib.rs:2543` is `if let (Some, Some)`.\n*Attack:* an agent holding a grant with `BudgetCeiling{numeric-ceiling-v1, 100}` delegates to a child with **no budget dimension at all**; the delegation is accepted as a narrowing, and the child now acts without any ceiling. Delegation widened.\n*Gated on D6.*\n\n**S-2 · Post-execution failures are reported as refusals, and the receipt says `Failed` (B26, M4, M29).**\n`product/runtime/auths-runtime/src/lib.rs:930-941` and `:944-961`.\n*False claim:* an MCP tool call that charged a card succeeds; the receipt store hiccups; the caller receives `RefusalKind::ApplicationPolicy` and no reference. The caller's only safe behaviour is to retry, double-charging. There is no vocabulary in which the runtime could have said \"applied, evidence unavailable.\"\n\n**S-3 · A signed `Authorized` decision receipt precedes the replay check (B29).**\n`product/runtime/auths-runtime/src/lib.rs:816-828` before `:830`.\n*Attack:* capture one valid proof; replay it *n* times. Each replay is refused with `ConsumedChallenge` — **after** writing a signed, durable, attacker-triggered receipt asserting Authorized. Unbounded write amplification into the receipt sink plus a corrupt audit trail, with the replay defence sitting downstream of the cost.\n\n**S-4 · The shipping node authenticates nobody and checks 4 of 11 narrowing dimensions (B27).**\n`product/runtime/auths-node/src/sandbox.rs:95-107` (`create`), `:110-138` (`delegate`), `:248-251` (`verify_authority`).\n*Attack:* POST to `/v1/authority/create` with any identity bytes; receive a signed root authority for `digest(identity)` with caller-chosen scope. Then delegate freely across audience, budget, status, assurance, extension, and profile dimensions — none is checked. `verify_authority` validates one envelope signature and never walks the chain.\n\n**S-5 · The per-action single-use guard is unreachable for MCP (B28).**\n`product/stores/auths-stores/src/lib.rs:76-78`, `:243-245`; `product/runtime/auths-runtime/src/lib.rs:209-217`.\n*Attack:* obtain two distinct challenges for the same `ActionId`; both execute. `claimed` is never populated for a budget-free action, and MCP actions are always budget-free.\n\n**S-6 · `PeerObservation` is a public enum with public variants (B8).**\n`exchange/crates/auths-proof-exchange-model/src/lib.rs:448-479`.\n*Attack:* a downstream crate constructs `PeerObservation::Authenticated{…}` directly and satisfies `RequireAuthenticatedPeer` without any handshake.\n\n**S-7 · `evaluate_grant_view` + all-`pub` `AuthorityStateView` bypass `EffectiveAuthority` (NEW, from S4).**\n`core/crates/auths-authority/src/lib.rs:192-195` (`#[doc(hidden)] pub fn`), `:106-122` (every field `pub`).\n*Attack:* any crate depending on `auths-authority` synthesizes an `AuthorityStateView` with arbitrary permissions, validity, and depth and evaluates a grant against it, bypassing the private fields of `EffectiveAuthority` entirely. `#[doc(hidden)]` is a documentation flag, not a seal, and it is semver-visible.\n*Disposition:* make both `pub(crate)`, or seal `AuthorityStateView` behind a constructor that only `EffectiveAuthority` can call.\n\n**S-8 · Both bindings relabel transport failures as `not-applied`/`backoff` (B11, M3).**\n*False claim:* a request that reached the server and timed out on the response is reported as definitely-not-applied and safe to retry. Under the `retry-safe ⇒ not-applied` invariant that both parsers enforce (`product-errors.ts:191-193`, `_product_errors.py:245-246`), this is the exact fail-open the invariant exists to prevent — asserted by the code that is supposed to uphold it.\n\n**S-9 · `ProviderOperationError` hardcodes non-effect (B12, M33).**\n`bindings/python/python/auths/_errors.py:82-107` — `effect_state` is the literal `\"not-started\"` for all five kinds, and `retry` is `\"safe\"` for `timeout`.\n*False claim:* a signing operation that timed out against an HSM is reported as definitely-did-not-happen and safe to retry. TypeScript, for the same kind, says `conditional`/`possible`. Two languages, one port, opposite safety verdicts.\n\n**S-10 · Python's `AuthsError.__init__` is public (M35).**\n`bindings/python/python/auths/_product_errors.py:111`.\n*Attack:* an application-level error-wrapping helper constructs `AuthsError` with `effect=NOT_APPLIED` from a wire envelope that said `POSSIBLE`, bypassing every invariant at `:245-254`. TypeScript makes this impossible (`product-errors.ts:64`).\n\n**S-11 · Caller-asserted `CanonicalAction` in both binding crates (B4).** A caller supplies capability, resource, and budget *alongside* the body instead of deriving them from it, so the thing `VerifiedAction` later seals was never canonicalized by a profile.\n\n**S-12 · Caller-mintable `ExecutionReference` (B21).** TypeScript's `decode` accepts any hex and `create` is public, so the resume token — the handle that says \"this effect is mine to reconcile\" — is forgeable.\n\n**S-13 · Three product-tier clocks fail open to zero (M9).** `product/runtime/auths-runtime/src/lib.rs:48-63` — `Clock::now` returns `u64` and uses `map_or(0, …)`. An unreadable system clock disables challenge expiry and durable-lifecycle expiry. Five integration clocks correctly fail closed; the product tier does not.\n\n**S-14 · Default trusted context ships no revocation and `none-v1` channel binding (M7).** `product/sdk/auths-sdk/src/lib.rs:125-135,224,227`. A deployment that accepts the defaults never consults revocation and binds to no channel.\n\n**S-15 · `numeric-ceiling-v1` means money in one profile and an operation count in five (M5).** Attenuation and the stateful ledger key on the algebra string alone, so a ceiling of 100 \"operations\" satisfies a parent ceiling of 100 \"cents.\"\n\n**S-16 · Eight of ten server codes are unregistered (M6/G4).** `product/runtime/auths-node/src/profiles.rs:24-31` and `sandbox.rs:170,290`. Verified against `product/errors/v1/registry.json`: `core.malformed-input` ✔, `core.runtime-unavailable` ✔; **absent**: `authority.denied`, `authority.indeterminate`, `profile.disabled`, `workflow.unknown`, `receipt.unknown`, `receipt.disclosure-denied`, `provider.outcome-unknown`, `verification.rejected`. The worst is `provider.outcome-unknown` (`sandbox.rs:170`, paired with `RetryClass::Resume`) — emitted on the one genuinely effect-unknown path, and a near-miss on the real registry code `core.outcome-unknown`. A client that string-matches the registry gets the unknown-code path, which hardcodes `entered.provider = false` (M3) and discards the recovery handle.\n\n**S-17 · `root_preserved` is a hardcoded `true` and unfalsifiable (N2).** No attack today — `delegate` (`core/crates/auths-authority/src/lib.rs:359-380`) never touches `self.root`. But the contract, the trait doc, the harness, and the eleven-way conjunction all present root preservation as *verified*. If `delegate` is later extended, the conjunction keeps returning true and the proof keeps passing. A security invariant whose witness is `true` must be labelled a comment or made real.\n\n**S-18 · Testkit/product separation is asserted only in a doc comment (M24).** `bindings/typescript/src/verifier/result.ts:118-121` says caller-supplied engines \"belong on `@auths-dev/sdk/testkit`, whose results are never effect-capable.\" `./testkit` is a first-class published subpath exporting 29 symbols including `createDiagnosticVerifier` and an unconditionally-approving `DevelopmentApprovalProvider` (`src/testkit/index.ts:61-79`). **There is no test proving a testkit-minted verifier result cannot reach `Auths.execute`.**\n\n---\n\n## Extensibility measurement\n\n**The honest number: adding one vertical touches ≥ 34 files outside its own package.**\n\nThe previously reported figure was **28**, derived from git archaeology on `ad21fb2` and seven follow-up commits. That measurement is carried, not re-derived — but it is demonstrably an **undercount**, because it did not include `product/runtime/auths-node` or the release artifacts. Six additional touch points were verified this session.\n\n### Verified hand-maintained touch points\n\n| # | Site | What must be edited | Verified |\n|---|---|---|---|\n| 1 | `product/runtime/auths-production-client/src/lib.rs:167-206` | `QualifiedProfile` variant + `parse` arm + `as_str` arm + `execute_path` arm | ✔ |\n| 2 | `product/runtime/auths-node/src/api.rs:121-133` | one route | ✔ **new** |\n| 3 | `product/runtime/auths-node/src/api.rs:201-245` | one hand-written handler fn per vertical (`execute_opentofu`, `execute_postgresql`, `execute_github`) | ✔ **new** |\n| 4 | `product/config/auths-config/src/lib.rs:425-436` | `known_profile` allowlist — which today excludes **all three** production verticals | ✔ |\n| 5 | `bindings/typescript/src/production-client.ts:364-370` | `isProductionProfile` id list | ✔ |\n| 6 | `bindings/typescript/src/production-client.ts:372-380` | `endpointPath` route table — **falls open to the GitHub path** for anything unlisted | ✔ |\n| 7 | `bindings/typescript/src/profiles.ts:4-23` | `ProductionProfileId` union + one constructor | ✔ |\n| 8 | `bindings/python/python/auths/_production_client.py:375` | `_PROFILE_IDS` tuple | ✔ |\n| 9 | `bindings/python/python/auths/_production_client.py:409-413` | endpoint dict **keyed by positional index into that tuple** | ✔ |\n| 10 | `bindings/python/python/auths/profiles/__init__.py:24-45` | `ProductionProfileId` union + one constructor | ✔ |\n| 11 | `bindings/public-topology-v1.json:31-36` | `qualifiedProfiles` | ✔ |\n| 12 | `bindings/typescript/sdk-runtime-contract.json` | `profiles` map (feeds `doctor`) | ✔ **new** |\n| 13 | `bindings/python/python/auths/_doctor.py:42` | `profiles` tuple (feeds `doctor`) | ✔ |\n| 14 | `release/assurance/open-production-candidate-1/manifest.json:12-16` | `qualifiedProfiles` | ✔ **new** |\n| 15 | `release/docs-bundle/public-topology.json:31` | `qualifiedProfiles` (generated, disagrees) | ✔ **new** |\n| 16 | `Cargo.toml`, `architecture.toml`, `compliance.toml` | member, layer, claims | ✔ |\n| 17 | `xtask/src/fixtures.rs` | canonical fixture corpus + evaluator registry, as Rust literals | carried |\n| 18 | `bindings/wasm/auths-proof-wasm/src/lib.rs`, `bindings/python/src/` | FFI exports | ✔ (structure) |\n\n**Thirteen of these are rosters or route tables**, and **five of them currently disagree with each other** (M30). Two of them fail *open* rather than closed on an unlisted profile: `endpointPath` (`production-client.ts:379` — unknown vertical routes to GitHub's executor) and `_endpoint_path` (`_production_client.py:409-413` — positional index, so reordering the tuple silently rebinds routes).\n\n### The three drivers\n\n**D-i · `ActionProfile` carries no identity (B15/C4).** The trait (`product/profiles/auths-profile-api/src/lib.rs`) has three deterministic methods and no `DESCRIPTOR`. Every fact a vertical needs to publish therefore has to be restated somewhere else, by hand, once per artifact. The fix already exists and is unused: `LifecycleRegistrationV1` (`product/runtime/auths-lifecycle/src/registry.rs:13-32`) declares ten of the needed facts and has **zero implementors**.\n\n**D-ii · Nothing is generated except the error registry.** `xtask/src/error_registry.rs` and `xtask/src/mcp_session_contract.rs` prove the pattern works — `_error_registry.py` and `_mcp_profile.py` are generated and frozen. Everything else on the list above is typed by hand.\n\n**D-iii · The inverse tax is worse than the forward tax.** One change to `ActionProfile` required editing **17** vertical `profile.rs` files in a single commit (carried). A three-method trait with no descriptor pushes cost in both directions: adding a vertical costs 34 files, and changing the contract costs 17.\n\n### Target\n\nZero files outside the vertical's own package. `ActionProfile::DESCRIPTOR` supplies the facts; `cargo xtask` generates items 1, 2, 3, 5–15 from the descriptor set; items 16–18 become derived manifests. This is B15 + C4 + M14 + M30 as one wave.\n\n---\n\n## Open questions\n\n**Q1 · MCP budgets — three-way, blocks B6, B28, and M7.**\nThe B6 fix (`(Some, None) => false`) denies every MCP action under any grant carrying a ceiling, because `auths-profile-mcp` canonicalizes with `None` and rejects any action whose `requested_budget().is_some()`. Simultaneously, B28 shows the single-use ledger is a no-op for exactly the budget-free case. Three consistent answers exist: (a) `auths.mcp/1` gains a real budget dimension; (b) MCP grants are forbidden from carrying a ceiling, enforced at authoring time; (c) budget stops being an attenuation dimension and becomes a runtime policy. **A human must pick before any of the three findings is implemented.** *Blocks:* B6, B28, M7, and the `budget` rows of §\"Configuration\".\n\n**Q2 · Is there a production `NodeRuntime` in 1.0 scope?**\n`main.rs:33-37` refuses to boot without sandbox providers, and the only `impl NodeRuntime` is the sandbox. Either a kernel-backed runtime is built, or the remote waist is not a 1.0 product surface and `release/assurance/*` must say so. **Every §6 remote-path disposition depends on the answer.** *Blocks:* B27, and the finalization of B10/B11/B20/B21/C2.\n\n**Q3 · Is `auths.mcp/1` a production profile or a local-only one?**\nThe topology says qualified; `QualifiedProfile` and the routes say no; the assurance manifest omits it; both doctors report only it. Four artifacts, three answers. *Blocks:* M30, M31, M27.\n\n**Q4 · Does `EffectState` gain `Unknown`, or does the envelope gain a discriminant?**\nThe unregistered-code path requires one of the two. Adding the Rust variant is the smaller change and makes both existing projections correct; the discriminant is cleaner but touches the wire schema. *Blocks:* the §6.2 `EffectState` and `ErrorFamily` rows, B10, M3, M6.\n\n**Q5 · Is `./testkit` shipped in the published artifacts at 1.0?**\nIt exports `createDiagnosticVerifier` and an unconditionally-approving `DevelopmentApprovalProvider`, and the only thing separating it from `Auths.execute` is a doc comment. If it ships, S-18's test is mandatory; if not, remove it from the topology's seven subpaths. *Blocks:* M24's testkit clause, §6.5 item 2's scope.\n\n**Q6 · Reference-tier vs product-tier boundary for grant authoring.**\nPython ships a full grant-authoring module privately (`bindings/python/python/auths/_authority.py:12-24,177-217`) with no TypeScript counterpart and no public entry point in any of the seven topology modules. Either it is reference-tier (delete it) or product surface (TypeScript gains it identically). The seven-module topology contains no \"authoring\" entry, which argues for deletion — but that is a product decision. *Blocks:* M25's final clause.\n\n**Q7 · Do the four `publish = false` identity adapters ship?**\n`auths-webauthn`, `auths-did-web`, `auths-hsm-attested`, `auths-spiffe-x509` — 3,414 lines of identity resolution, all `publish = false`, appearing in `auths-verifier` only as `[dev-dependencies]` (`core/crates/auths-verifier/Cargo.toml:32-41`). Four identity methods ship in no published artifact. This looks deliberate but is stated nowhere, and a reader would assume otherwise from the adapter directory. *Blocks:* nothing, but it must be documented before 1.0.\n\n**Not determinable from source, listed for completeness:** whether the ~62 unregistered binding codes are all genuinely distinct failures or partially duplicates (only a fixture pass will tell); and whether `AuthorizationPlanRef::KOfN`'s rename is genuinely byte-neutral in every codec path (I verified `AuthorizationPlanNode` is private with no serde derive and that `auths-codec` encodes manually — a codec round-trip test should confirm).\n\n---\n\n## Proposed lane sequencing\n\nDependency structure matters more than tier order; several blockers are one-line deletions once their prerequisite lands.\n\n**Wave 0 — gates first.** B1 (Rust symbol gate + re-export allowlist); the `PUBLIC_RUST_ROOTS` rewrite from M13 (the gate is meaningless while the roots point at a dead bridge and three transport crates with no published implementation); **and — corrected — the `PUBLIC_RUST_CLOSURE` surgery in the same wave**, because Wave 1 deletes `auths-profile-domains` (`xtask/src/semantic_freeze.rs:50`), `auths-config` (`:31`), `auths-deployment`, and `auths-cache`, and `FREEZE_VERSION: 110` plus the affected entries must move with them. Land §6.5's five checks as *failing* tests allowlisted down to today's state, so every subsequent wave shrinks the allowlist.\n\n**Wave 1 — deletions that unblock other work.** B22 (delete `auths-deployment`) → **B2 becomes a two-line deletion**. Then B3, B5, B4, B7, B16, M32 (delete `auths-cache`), M12's dead-crate deletion, N9 (delete both dead reference kinds). All of Root R1 disappears here.\n\n**Wave 2 — the error contract.** Sequential. **Q4 must be answered first.** B10 (freeze the three vocabularies; delete `_errors.py` and `workflow/errors.ts`) → B17 → B9 → B11, B12, M33's retry inversion → M3 → M6 (register ≈70 orphan codes, **including the eight server codes**) → M35 (seal Python's constructor; generated cause classifier) → N12, N13, M5, M11, N3, N4.\n\n**Wave 3 — security invariants.** Parallel with Wave 2. B8, B26 + M4 + M29 (the outcome-vocabulary trio, one change), B29 (reorder), S-7 (seal `evaluate_grant_view`), M1, M2, M9, M10, N2. **B6, B28, and M7 are gated on Q1.** **B27 is gated on Q2.**\n\n**Wave 4 — the product waist and the cross-language contract.** Largest wave; after Wave 2. B13 → B24, B25, B19, B20, B21 → M15, M16, M18, M19, M21–M28, M34, M36, M37, M38, N6–N8, N10, N11. Everything types against the matrix; starting before Wave 2 means renaming twice.\n\n**Wave 5 — extensibility.** Parallel with Wave 4. **C4 first** (extend `LifecycleRegistrationV1` rather than inventing `VerticalDescriptorV1`) → B15 (`ActionProfile::DESCRIPTOR`; generate the registries; move fixture generators into verticals) → B14 + M30 + M31 (generate all thirteen rosters; **land the `never`-exhaustiveness hotfix and the nominal Python dict immediately, ahead of everything else in this wave**) → M17, M14, M27's generated runtime contract, B23, M8, M13, N1, N5.\n\n**Ordering rule:** deletions before renames, renames before additions. Roughly 40% of the blocker list is deletion; doing it first shrinks everything after it.\n\n---\n\n## Coverage statement\n\n### Read in full or at the public boundary this session\n\n`product/errors/auths-errors/src/lib.rs` and `product/errors/v1/registry.json` (parsed exhaustively); `product/sdk/auths-sdk/src/lib.rs`; `product/receipts/auths-receipts/src/lib.rs` (`ExecutionOutcome`/`ExecutionReceipt`); `product/runtime/auths-runtime/src/lib.rs` (execution path 805-965, budget ledger 200-217); `product/runtime/auths-production-client/src/lib.rs` (1-60, 127-210, 410-450); `product/runtime/auths-node/` — **all 2,970 lines across 9 files**, previously unread; `product/runtime/auths-lifecycle/src/registry.rs`; `product/stores/auths-stores/src/lib.rs` (ledger and challenge paths); `product/cache/auths-cache/src/lib.rs`; `product/config/auths-config/src/lib.rs:420-445`; `exchange/crates/auths-proof-exchange-model/src/lib.rs:525-605`; `core/crates/auths-authority/src/lib.rs:18-210, 300-400`; `core/crates/auths-algebra-kernel/src/generated.rs:1-15`; `core/crates/auths-model/src/lib.rs:1700-1840` (plan nodes); `xtask/src/semantic_freeze.rs:1-70`; `docs/reference/error-codes.md`; `docs/adoption-layers.md`.\n\nTypeScript: `product-errors.ts`, `observability.ts`, `doctor.ts`, `runtime-contract.ts`, `production-client.ts`, `profiles.ts`, `verifier/result.ts`, `verifier/cache.ts`, `workflow/errors.ts`, `workflow/contracts.ts` (framework and constraint sections), `api/public-api.txt` (full, recounted).\n\nPython: `_product_errors.py`, `_errors.py`, `_observability.py`, `_doctor.py`, `_workflow.py` (contracts, validators, approval), `_product.py` (facade), `verify.py`, `identity.py`, `integrations.py`, `profiles/__init__.py`, `_native.pyi` (symbol count).\n\nRelease/config: `Cargo.toml`, `architecture.toml`, `compliance.toml`, `architecture/dependency-graph.dot`, `bindings/public-topology-v1.json`, `bindings/typescript/sdk-runtime-contract.json`, `release/assurance/open-production-candidate-1/manifest.json`, `release/docs-bundle/public-topology.json`, `release/public-naming.toml` (partial).\n\n### Verified by exhaustive grep or counting\n\n114 workspace members; 42 publishable crates; 45 registry codes with family/stage/operation breakdown; TypeScript 193/41 and Python 159/35 with per-subpath splits and exact set differences; zero consumers for `auths-deployment`, `auths-cache`, and `auths-identity-authority`; zero nominal TypeScript names for all ten Python delegation constraints; zero `BudgetSummary` in TypeScript; five independent `\"ed25519-v1\"` literals plus five inline; all `k_of_n`/`KOfN` sites; all `qualifiedProfiles` occurrences; all `@auths-dev/sdk/authority|approvals` occurrences; `allowsDecisionReference`/`allowsReceiptReference` true for 0 of 45.\n\n### Read but not found to contain a v1.0 blocker\n\n`product/policy/auths-bounded-policy` (1,918 lines), `product/receipts/.../disclosure.rs` (741), `core/adapters/*` (6,281), `exchange/.../codec.rs` + `framing.rs` (1,067) — sampled at their public boundaries. One scoping note recorded above as **Q7**.\n\n### NOT independently verified — carried on the reporting auditor\n\nThese size work rather than justify it; none is load-bearing for a *disposition*. **Re-run each at the start of its lane.**\n\n- The git archaeology behind the 28-file extensibility count (`ad21fb2` + seven follow-ups) and the 17-file inverse-tax count. (My verified floor of 34 does not depend on it.)\n- The mypy transcript proving `__init__.pyi` drift (19 of 35 symbols invisible).\n- The runtime introspection producing the 362 private / 147 shadow-surface counts.\n- The AST scan producing the 25 locally-minted Python workflow codes, and the ~62-code total for unregistered binding codes.\n- The `xtask/src/fixtures.rs` line-range attributions.\n- Specific line numbers inside `bindings/typescript/src/workflow/` and `bindings/python/python/auths/_workflow.py` beyond the ranges I read (contracts 25-75, 260-300, 800-810; `_workflow.py` 64-150, 400-460, 1465-1500).\n- The 5,586-line WASM module's per-export semantics (I counted attributes and read the ten domain-parser export names; I did not read every export).\n- `bindings/python/src/` per-module `#[pyfunction]` semantics (I counted modules and `_native.pyi` symbols).\n\n### Explicitly NOT read\n\n`core/testkit/auths-testkit` (beyond grep hits), the Lean/Aeneas formal sources behind `auths-algebra-kernel/src/generated.rs`, `demos/`, and the untracked working-tree additions (`demos/open-production-reference/`, `product/stores/auths-stores/tests/postgres_tls/`, `scripts/`). `release/docs-bundle/` is untracked and was read only for the roster comparison in M30 — treat its contents as generated output, not source.", + "rawCount": 139, + "auditors": 8 + }, + "workflowProgress": [ + { + "type": "workflow_phase", + "index": 1, + "title": "Read" + }, + { + "type": "workflow_phase", + "index": 2, + "title": "Synthesize" + }, + { + "type": "workflow_phase", + "index": 3, + "title": "Critique" + }, + { + "type": "workflow_agent", + "index": 1, + "label": "read:rust-surface", + "phaseIndex": 1, + "phaseTitle": "Read", + "agentId": "a5704c15ce98bb6e9", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786757912924, + "queuedAt": 1786757910963, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "rust-surface", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No git commands that mutate.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.…", + "lastProgressAt": 1786758438380, + "tokens": 145991, + "toolCalls": 37, + "durationMs": 525456, + "resultPreview": "{\"area\":\"rust-surface\",\"summary\":\"The workspace has 114 Cargo members (core 30, product 34, demos 31, exchange 15, bindings 2, xtask 2). Exactly 42 are publishable today, and that number is hard-frozen in `xtask/src/semantic_freeze.rs:20` (`PUBLIC_RUST_CLOSURE: [&str; 42]`) and mirrored in `release/semantic-freeze.json` — so the promise is deliberate, not accidental. By group: core 23 published (1…" + }, + { + "type": "workflow_agent", + "index": 2, + "label": "read:rust-ownership", + "phaseIndex": 1, + "phaseTitle": "Read", + "agentId": "aff47947ed4a0e45a", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786757912874, + "queuedAt": 1786757910963, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "rust-ownership", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No git commands that mutate.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.…", + "lastProgressAt": 1786758567598, + "tokens": 189691, + "toolCalls": 64, + "durationMs": 654722, + "resultPreview": "{\"area\":\"rust-ownership\",\"summary\":\"The vertical pattern in this repo is actually good: 13 of 14 crates under `product/integrations/` define their own `ActionProfile`, their own action type, their own capability/resource derivation, and their own receipts (e.g. `auths-github/src/profile.rs:31-93`, `auths-opentofu/src/profile.rs:34-91`, `auths-stripe/src/*/profile.rs`). `auths-profile-api` is confi…" + }, + { + "type": "workflow_agent", + "index": 3, + "label": "read:typescript", + "phaseIndex": 1, + "phaseTitle": "Read", + "agentId": "a0b38926d76088419", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786757912981, + "queuedAt": 1786757910963, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "typescript", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No git commands that mutate.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.…", + "lastProgressAt": 1786758622636, + "tokens": 182964, + "toolCalls": 57, + "durationMs": 709655, + "resultPreview": "{\"area\":\"typescript\",\"summary\":\"`bindings/typescript` ships two complete, unrelated SDKs behind one root entry point: 19 of the 41 root symbols in `api/public-api.txt` are a `Production*` mirror of the other 14, and `createAuths` picks between them by sniffing for an `endpoint` property. The binding is a faithful projection where it calls WASM (identity.ts, delegation narrowing, plan/receipt autho…" + }, + { + "type": "workflow_agent", + "index": 4, + "label": "read:python", + "phaseIndex": 1, + "phaseTitle": "Read", + "agentId": "aec09d96612d33101", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786757913062, + "queuedAt": 1786757910963, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "python", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No git commands that mutate.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.…", + "lastProgressAt": 1786758599538, + "tokens": 157431, + "toolCalls": 67, + "durationMs": 686475, + "resultPreview": "{\"area\":\"python\",\"summary\":\"The Python binding ships 159 symbols across 7 topology-declared public modules (`bindings/python/api/public-api.txt`), but the wheel also carries 362 `__all__`-declared symbols in 24 underscore-prefixed modules plus 147 symbols in `auths._native` — a ~3.2x shadow surface with no symbol-level guard, because `tools/check_public_api.py:9-16` only walks `bindings/public-top…" + }, + { + "type": "workflow_agent", + "index": 5, + "label": "read:vocabulary-matrix", + "phaseIndex": 1, + "phaseTitle": "Read", + "agentId": "a379bac22cb44bab8", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786757913097, + "queuedAt": 1786757910963, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "vocabulary-matrix (cross-language: Rust reference / TypeScr…", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No git commands that mutate.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.…", + "lastProgressAt": 1786759009361, + "tokens": 251360, + "toolCalls": 104, + "durationMs": 1096263, + "resultPreview": "{\"area\":\"vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection)\",\"summary\":\"The five core operations are the only part of the vocabulary that is genuinely aligned, and even there the alignment is nominal: Rust owns them as `ProductVerb` (product/runtime/auths-production-client/src/lib.rs:130), both bindings re-spell them as `ProductStep`, and the error regis…" + }, + { + "type": "workflow_agent", + "index": 6, + "label": "read:error-model", + "phaseIndex": 1, + "phaseTitle": "Read", + "agentId": "a5c9e44e4c38900db", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786757913129, + "queuedAt": 1786757910963, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "error-model", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No git commands that mutate.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.…", + "lastProgressAt": 1786758605031, + "tokens": 200637, + "toolCalls": 79, + "durationMs": 691901, + "resultPreview": "{\"area\":\"error-model\",\"summary\":\"The Rust registry is the strongest artifact in this area: `product/errors/auths-errors/src/lib.rs` defines 45 stable codes (14 `core.`, 8 `mcp.`, 5 `plan.`, 18 `custody.` — not 20 as stated in the brief) across `ErrorFamily`/`RetryClass{never,safe,conditional,unknown}`/`EffectState{not-applied,possible,applied}`/`RecommendedAction`, and `validate_registry`/`validat…" + }, + { + "type": "workflow_agent", + "index": 7, + "label": "read:security", + "phaseIndex": 1, + "phaseTitle": "Read", + "agentId": "ae8aff4d9d02ed5dc", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786757912939, + "queuedAt": 1786757910963, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "security", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No git commands that mutate.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.…", + "lastProgressAt": 1786758863285, + "tokens": 273112, + "toolCalls": 111, + "durationMs": 950346, + "resultPreview": "{\"area\":\"security\",\"summary\":\"The sealed verified-action boundary holds. I could not find any public path in Rust, TypeScript, or Python that reaches a provider effect without passing verification: `VerifiedAction` has exactly one construction site (auths-verifier/src/lib.rs:1653) reachable only from `verify_authority_measured`, with no Default/From/serde/`#[doc(hidden)]` escape; TS gates minting …" + }, + { + "type": "workflow_agent", + "index": 8, + "label": "read:extensibility", + "phaseIndex": 1, + "phaseTitle": "Read", + "agentId": "a41e1a79675b92ef5", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786757913204, + "queuedAt": 1786757910964, + "attempt": 1, + "lastToolName": "StructuredOutput", + "lastToolSummary": "extensibility — the file-touch cost of adding a new profile…", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No git commands that mutate.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.…", + "lastProgressAt": 1786758483019, + "tokens": 161629, + "toolCalls": 87, + "durationMs": 569815, + "resultPreview": "{\"area\":\"extensibility — the file-touch cost of adding a new profile vertical\",\"summary\":\"The v1.0 goal is that a new vertical touches zero files outside its own package. The measured reality is 28. I took `auths-records-api` (the most recent full profile vertical, introduced 2026-07-30 in ad21fb2) and counted: 40 files in the introduction commit, 12 inside `product/integrations/auths-records-api/…" + }, + { + "type": "workflow_agent", + "index": 9, + "label": "synthesize", + "phaseIndex": 2, + "phaseTitle": "Synthesize", + "agentId": "abf131b3ecf846822", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786759014157, + "queuedAt": 1786759010957, + "attempt": 1, + "lastToolName": "Bash", + "lastToolSummary": "cd /Users/bordumb/workspace/repositories/auths-proof-base/a…", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No git commands that mutate.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.…", + "lastProgressAt": 1786760072782, + "tokens": 275189, + "toolCalls": 17, + "durationMs": 1058625, + "resultPreview": "an` (`src/plans.ts:37`), Python `McpPlan` (`profiles/_mcp.py:237`) — **generic in TypeScript, profile-specific in Python**, so the file-touch cost of a new vertical differs between the two bindings for the same feature.\n\n**Disposition**\nCanonical name `AuthorizationPlan` / `AuthorizationPlanBuilder` / `AuthorizationPlanKind` in all three; rename Python's `ProofPlan*`. Canonical combinator …" + }, + { + "type": "workflow_agent", + "index": 10, + "label": "critic:coverage", + "phaseIndex": 3, + "phaseTitle": "Critique", + "agentId": "af7207c1b76836d5c", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786760076625, + "queuedAt": 1786760074639, + "attempt": 1, + "lastToolName": "Bash", + "lastToolSummary": "grep -n \"record_decision\\|self.replay.claim\\|self.budgets\" …", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No git commands that mutate.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.…", + "lastProgressAt": 1786760480804, + "tokens": 125009, + "toolCalls": 41, + "durationMs": 404178, + "resultPreview": "COMPLETENESS REVIEW — coverage gaps. Every item below was read in source this session and is absent from the document.\n\n---\n\n## G1 · The signed execution receipt cannot record \"unknown.\" The document's entire safety spine has no projection into the artifact the product exists to produce.\n\n`product/receipts/auths-receipts/src/lib.rs:299-304` — `pub enum ExecutionOutcome { Succeeded, Failed }`. Two …" + }, + { + "type": "workflow_agent", + "index": 11, + "label": "critic:correctness", + "phaseIndex": 3, + "phaseTitle": "Critique", + "agentId": "a9d99fcdd59a63d41", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786760076957, + "queuedAt": 1786760074639, + "attempt": 1, + "lastToolName": "Bash", + "lastToolSummary": "cd /Users/bordumb/workspace/repositories/auths-proof-base/a…", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No git commands that mutate.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.…", + "lastProgressAt": 1786760507425, + "tokens": 115243, + "toolCalls": 41, + "durationMs": 430468, + "resultPreview": "## Verified-correct (do not re-spend budget here)\n\n`product/errors/v1/registry.json`: **45** definitions, split `custody` 18 / `core` 14 / `mcp` 8 / `plan` 5, **20** distinct stage strings, operations `{sign, execute, resume, create, verify}` (has `sign`, lacks `delegate`) — D1 and §6.1 confirmed. Rust `EffectState` is exactly 3 variants (`auths-errors/src/lib.rs:38-43`), `RetryClass` 4 (`:29-36`)…" + }, + { + "type": "workflow_agent", + "index": 12, + "label": "critic:divergence", + "phaseIndex": 3, + "phaseTitle": "Critique", + "agentId": "ab4434cf28ef64d02", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786760076753, + "queuedAt": 1786760074639, + "attempt": 1, + "lastToolName": "Bash", + "lastToolSummary": "grep -n \"async def aclose\\|async def __aenter__\\|async def …", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No git commands that mutate.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.…", + "lastProgressAt": 1786760743197, + "tokens": 182779, + "toolCalls": 53, + "durationMs": 666443, + "resultPreview": "## A. Errors in the table (things §6 states that are wrong or unimplementable)\n\n**A1 · The `EffectState` row makes the unknown-code path it also mandates impossible.** §6.2 freezes `EffectState` as \"closed, three variants, no `unknown`\" and orders both bindings to drop `unknown`; §6.5 item 3 simultaneously requires a conformance corpus covering \"the unknown-code path\". But the unregistered-code br…" + }, + { + "type": "workflow_agent", + "index": 13, + "label": "final-doc", + "phaseIndex": 3, + "phaseTitle": "Critique", + "agentId": "a3a3363b83008cdf6", + "model": "claude-opus-5", + "state": "done", + "startedAt": 1786760746803, + "queuedAt": 1786760744622, + "attempt": 1, + "lastToolName": "Bash", + "lastToolSummary": "cd bindings/typescript/src && sed -n '1,30p' production-cli…", + "promptPreview": "You are a READ-ONLY auditor. You MUST NOT edit, create, or delete any file. No git commands that mutate.\n\nREPO: /Users/bordumb/workspace/repositories/auths-proof-base/auths-proof\n\nPRODUCT CONTEXT:\nAuths is a proof-carrying authorization system. An actor (human or AI agent) gets precise, portable,\ncryptographically bounded permission to do ONE EXACT THING, and the system leaves verifiable evidence.…", + "lastProgressAt": 1786761503671, + "tokens": 147759, + "toolCalls": 39, + "durationMs": 756868, + "resultPreview": "# Phase 0 — v1.0 API Review Findings\n\n**Repository:** `/Users/bordumb/workspace/repositories/auths-proof-base/auths-proof`\n**Branch:** `codex/spec-0040-docs` · **Working tree at review time:** commit `d0e4a53`\n**Policy basis:** `AGENTS.md` (prelaunch, zero external users, direct cutover, no compat shims), `docs/target-state/PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md`, `docs/target-state/PROFILE_AND_…" + } + ], + "totalTokens": 2408794, + "totalToolCalls": 797 +} \ No newline at end of file diff --git a/docs/target-state/v1-api-review-findings.md b/docs/target-state/v1-api-review-findings.md new file mode 100644 index 00000000..593bd41c --- /dev/null +++ b/docs/target-state/v1-api-review-findings.md @@ -0,0 +1,1477 @@ +# Phase 0 + 0b — Consolidated v1.0 API Review Findings +Generated from 15 read-only auditors across two workflow runs (wf_4ff587be-0a3, wf_f7878425-e97). +**Totals:** 139 findings — blocker: 48, major: 72, minor: 19 + +--- + +## BLOCKERS + +### `no-rust-symbol-gate` — 42 published Rust crates have crate-level gates only; TypeScript and Python have symbol-level gates and Rust does not +- **Lane:** cross · **Goal:** simple · **Area:** rust-surface +- **Evidence:** xtask/src/semantic_freeze.rs:20 `const PUBLIC_RUST_CLOSURE: [&str; 42]` freezes the crate list; xtask/src/semantic_freeze.rs:768-782 is the only per-package check and it validates `license`, `description`, `repository`, `homepage` — no symbols. By contrast xtask/src/sdk_experience.rs:4 `const TYPESCRIPT_API_PATH: &str = "bindings/typescript/api/public-api.txt"` and xtask/src/sdk_experience.rs:8 `const PYTHON_API_PATH: &str = "bindings/python/api/public-api.txt"` pin checked-in symbol lists, and xtask/src/sdk_experience.rs:37-39 budgets `typescript_public_symbols` / `python_public_symbols`. `find . -name '*public-api*'` returns bindings/typescript/api/public-api.txt, bindings/python/api/public-api.txt, release/docs-bundle/typescript-public-api.txt, release/docs-bundle/python-public-api.txt — and no Rust equivalent anywhere. +- **Disposition:** Add `/api/public-api.txt` snapshots for every crate in PUBLIC_RUST_CLOSURE, generated by `cargo public-api` (or an rustdoc-JSON walker) and diff-gated in a new `cargo xtask rust-surface`. Wire it into the same job that runs sdk-experience. Enforce a per-crate symbol budget and, critically, a re-export allowlist so a `pub use` of a symbol from a non-allowed owner crate fails the gate. +- **Rationale:** The brief's stated invariant is that leaks travel through facade re-exports and enforcement must be at the SYMBOL level, not the crate level. Today the only crate-level gate would pass unchanged if auths-sdk added `pub use auths_profile_domains::*;` tomorrow. The tier that OWNS all semantics is the only tier with no symbol contract, so every other finding here is currently undetectable by CI. + +### `dual-coordinate-auths-auths-proof` — `auths` and `auths-proof` are two published crates.io coordinates for one byte-identical API surface +- **Lane:** cross · **Goal:** simple · **Area:** rust-surface +- **Evidence:** core/crates/auths/src/lib.rs:30 is the entire body of the crate: `pub use auths_proof::*;` — zero symbols of its own. core/crates/auths/Cargo.toml:10 describes it as "Supported embedded core facade for Auths" and :9 claims `documentation = "https://docs.rs/auths"`. Reverse-dependency scan over all 114 members: `auths` has zero consumers other than the root workspace manifest. Both are in the publishable closure (xtask/src/semantic_freeze.rs:21 `auths`, :50 `auths-proof`) and both are in release/public-naming.toml release_order (tier 8 and tier 4 respectively). +- **Disposition:** Pick one coordinate and delete the other in the same change. The correct direction per release/public-naming.toml:66-72 (`rust-core` target = `auths`) is: move the contents of core/crates/auths-proof/src/lib.rs (Engine, Verdict, Explanation, VerificationResult) into core/crates/auths, delete core/crates/auths-proof, and drop `auths-proof` from PUBLIC_RUST_CLOSURE and every release_order tier. Do not keep an alias crate. +- **Rationale:** AGENTS.md forbids dual paths and deprecation aliases prelaunch. A glob re-export crate with zero symbols and zero consumers is exactly a compat shim, shipped before there is anything to be compatible with. It also breaks 'one way to do each thing' at the very first import line a new Rust user writes, and it doubles the semver surface for free — a breaking change in auths-proof is silently a breaking change in auths with no signal. + +### `peer-observation-fail-open` — `PeerObservation` is a public enum with public variants; any downstream caller can fabricate an authenticated peer and satisfy RequireAuthenticatedPeer +- **Lane:** cross · **Goal:** secure · **Area:** rust-surface +- **Evidence:** exchange/crates/auths-proof-exchange-model/src/lib.rs:448-468 defines `pub enum PeerObservation` with public variants including `AuthenticatedOpaque { kind: String, identifier: Vec }` (line 462). exchange/crates/auths-proof-exchange-model/src/lib.rs:472-479 `pub const fn is_authenticated(&self) -> bool` returns true for everything except `Unauthenticated | TcpEndpoint(_) | FileEnvelope{..}`. That predicate is the sole gate at product/runtime/auths-runtime/src/lib.rs:1047: `ChannelBindingPolicy::RequireAuthenticatedPeer => peer.is_authenticated()`, and at exchange/adapters/auths-proof-exchange-https/src/lib.rs:59 `if !peer.is_authenticated()`. Test code already exercises the forgery path: exchange/testkit/auths-proof-exchange-testkit/src/lib.rs:114 and demos/testkit/auths-apps-testkit/src/lib.rs:270 both construct `PeerObservation::AuthenticatedOpaque { .. }` directly. The sibling type in the same published family does it correctly — exchange/crates/auths-byte-channel/src/lib.rs:74 `pub fn transport_authenticated(bytes: Vec) -> Result` is a validating smart constructor over private state. +- **Disposition:** Make the variants unconstructible from outside: wrap the enum in a `pub struct PeerObservation(Observation)` with a private inner enum, and expose only adapter-specific smart constructors (`iroh_endpoint`, `mutual_tls`, `unix_peer_credentials`, …) plus `unauthenticated()`. Remove `AuthenticatedOpaque`'s free-form `kind: String` entirely, or gate it behind a sealed adapter trait so only in-tree transports can mint it. Keep `is_authenticated` but make it total over the sealed set. +- **Rationale:** 'Fails closed' is a claimed invariant of the system and this path fails open by construction. A published tier-0 crate hands every downstream user a one-line way to assert transport authentication that never happened, and the runtime's channel-binding policy trusts it without a second check. The bug is invisible to any dependency-graph or effect-freedom audit: the type is pure data, effect-free, no I/O — it is a pure semantic-ownership failure, which is exactly the class the brief warns is most likely to be missed. + +### `retry-vocabulary-fork` — Two published crates define `RetryClass` with disjoint variant sets, and the wire client does not depend on the error registry that owns retry meaning +- **Lane:** cross · **Goal:** secure · **Area:** rust-surface +- **Evidence:** product/errors/auths-errors/src/lib.rs:31-37 `pub enum RetryClass { Never, Safe, Conditional, Unknown }`. product/runtime/auths-production-client/src/lib.rs:417-422 `pub enum RetryClass { Never, Backoff, Resume, Reconcile }`. Only `Never` is shared. Both are serde `rename_all = "kebab-case"` and both are published in release_order tier 0 (release/public-naming.toml:231). product/runtime/auths-production-client/Cargo.toml:11-15 lists only base64ct, minicbor, serde, serde_json — it has NO dependency on auths-errors. Ground truth from the registry: `grep -o '"retry"[^,}]*' product/errors/v1/registry.json` yields only never(26), conditional(6), safe(4), unknown(9) — none of backoff/resume/reconcile appear anywhere in the 20 stable codes. +- **Disposition:** Delete `RetryClass` from auths-production-client. Add `auths-errors` to product/runtime/auths-production-client/Cargo.toml and have `ProductionResponse.retry` (product/runtime/auths-production-client/src/lib.rs:449) hold `auths_errors::RetryClass`. If backoff/resume/reconcile are genuinely needed operator actions, they belong in `auths_errors::RecommendedAction` (which already has RetryExecution, ResumeAndReconcile at product/errors/auths-errors/src/lib.rs:53-54), not in a second retry enum. +- **Rationale:** The error registry is the spine of safe API design: a code's `outcomes[{retry, effect}]` tells a caller whether re-issuing can duplicate a real-world effect. The production wire contract independently redefines that vocabulary with terms the registry has never heard of, so nothing forces the bytes on the wire to agree with the registry a caller reasons from. A client told `retry: "resume"` has no registry entry to map that to an effect state, and there is no compile-time or test-time link between the two enums. Two published tier-0 `RetryClass` types is also the plainest possible violation of one-vocabulary. + +### `vertical-add-touches-28-files` — Adding a vertical touches 28 files outside its own package; the v1.0 target is zero +- **Lane:** cross · **Goal:** extensible · **Area:** extensibility — the file-touch cost of adding a new profile vertical +- **Evidence:** Measured on the most recent full profile vertical. `git show --name-only ad21fb2` ("feat: implement transport-neutral records API demo", 2026-07-30) = 40 files: 12 under product/integrations/auths-records-api/, 28 outside. The 28 outside break down as: .github/workflows/records-api-demo.yml; Cargo.lock; Cargo.toml; architecture.toml; architecture/dependency-graph.dot; architecture/dependency-graph.json; compliance.toml; product/fixtures/v1/records-api/{configuration,create-action,manifest,policy,read-action}.json (5); demos/rest-api-authorization/** (16). Union across all eight records-api commits (ad21fb2, 582d31f, efb528e, 12622cd, 703cfff, 6636d8e, a8a6348, d51e5ae) = 36 distinct files outside the package, adding bounded-domains.toml, release/semantic-freeze.json, release/open-production-candidate.{json,toml}, xtask/Cargo.toml, xtask/src/{fixtures,bounded_domains,architecture,compliance,semantic_freeze,checks,main}.rs, bindings/typescript/api/public-api.txt, bindings/typescript/src/generated/error-registry.ts, bindings/python/python/auths/_error_registry.py, bindings/python/sdk-runtime-contract.json. The demo directory is not optional scope: xtask/src/bounded_domains.rs:250 `require_repository_directory(&domain.demo_path)?` makes a demo package a CI-enforced precondition of marking a domain implemented. +- **Disposition:** Set the target explicitly at <=3 non-generated files outside the package (workspace Cargo.toml members, workspace Cargo.toml dependency entry, and a demo directory) and drive every other item below to a generated or discovered artifact. Concretely: make `bounded-domains.toml`, `compliance.toml [packages.*]`, the `architecture.toml [layers]` entry, and the `product/fixtures/v1/bounded-policy/registry.toml` evaluator blocks all DERIVED from a single vertical-owned Rust descriptor rather than hand-authored. +- **Rationale:** Every one of these files is a place a contributor can forget, and several fail silently rather than loudly. The cost is not merely tedium: the same semantic identity is written in four places with no mechanism forcing agreement, so the registries can describe a vertical that the code does not implement. That is the exact drift the boundary plan's 'Machine-enforced inventories' section (docs/target-state/PROFILE_AND_DOMAIN_ABSTRACTION_BOUNDARY_PLAN.md:451-494) exists to prevent. + +### `lifecycle-registration-descriptor-is-dead` — The correct vertical-owned descriptor already exists in auths-lifecycle and has zero implementors +- **Lane:** rust · **Goal:** extensible · **Area:** extensibility — the file-touch cost of adding a new profile vertical +- **Evidence:** product/runtime/auths-lifecycle/src/registry.rs:11-33 defines `LifecycleRegistrationV1` with domain_id, reservation_algebra_id, provider_contract_id, retry_class, reservation_fixture, lifecycle_fixture, formal_namespace, kani_harness, store_conformance, production_migrated — and its doc comment at :8-10 says exactly the right thing: "Every value is declarative. Provider functions, credentials, requests, evidence interpretation, and callbacks are intentionally absent." It is re-exported at product/runtime/auths-lifecycle/src/lib.rs:42. A repo-wide grep for `LifecycleRegistrationV1` and `validate_lifecycle_registry` across all *.rs/*.toml/*.json (excluding target/) returns hits ONLY in registry.rs itself (:12,:52,:53,:89,:90,:110) and the lib.rs re-export. No vertical constructs one. Meanwhile every vertical DOES declare the same facts as free consts in its own source: product/integrations/auths-records-api/src/lifecycle.rs:34-50 declares POLICY_TYPE_ID, IMPLEMENTATION_ID, CANONICALIZATION_ID, CONFIGURATION_SEMANTIC_ID, EVIDENCE_SCHEMA_ID, EVIDENCE_SOURCE_ID, STATE_SCHEMA_ID, CREATE_INTENT_SCHEMA_ID, READ_INTENT_SCHEMA_ID, CREATE_OBLIGATION_SCHEMA_ID, READ_OBLIGATION_SCHEMA_ID, RESERVATION_ALGEBRA_ID, CREATE_PROVIDER_CONTRACT_ID, READ_PROVIDER_CONTRACT_ID, DOMAIN_ID. +- **Disposition:** Widen `LifecycleRegistrationV1` into the single `VerticalDescriptorV1` that carries everything the registries currently restate (profile ids, evaluator symbol, action/policy/evidence/state/result/intent/obligation/receipt schema ids, provider contract ids, fixture dir, spec path, demo path, layer, migration status). Require every vertical to export `pub const DESCRIPTOR: VerticalDescriptorV1`. Have xtask collect descriptors via one inventory crate that depends on all verticals, and GENERATE bounded-domains.toml, the compliance.toml package block, the bounded-policy registry evaluator rows, and the binding profile tables from it. Delete the hand-authored duplicates. +- **Rationale:** The repository already agreed on the right shape and then did not adopt it. Because nothing links the vertical's own consts to the strings restated in xtask/src/fixtures.rs and bounded-domains.toml, the two can diverge silently — I grepped for IMPLEMENTATION_ID / RESERVATION_ALGEBRA_ID / CANONICALIZATION_ID / POLICY_TYPE_ID inside xtask/ and product/policy/ and got zero hits, meaning no code compares them. Adopting the descriptor collapses roughly 20 of the 22 hand-edited files into one file inside the vertical. + +### `xtask-fixtures-owns-vertical-corpora` — xtask/src/fixtures.rs authors each vertical's canonical fixture corpus and evaluator registry as Rust literals +- **Lane:** rust · **Goal:** extensible · **Area:** extensibility — the file-touch cost of adding a new profile vertical +- **Evidence:** xtask/src/fixtures.rs is 2121 lines and roughly half of it is per-vertical content. Seven generator functions live there: github_product_fixtures at :890, radicle at :1020, stripe at :1099, kubernetes at :1221, records_api at :1285, opentofu at :1466, postgresql at :1552 — about 790 lines total, averaging ~113 lines each. records_api_product_fixtures at :1285-1291 does `use auths_records_api::{BoundedRecordApiPolicyV1, CREATE_OPERATION, CreateEvaluation, CreateRecordV1, CustomerRecordV1, READ_OPERATION, ReadEvaluation, ReadField, ReadRecordV1, RecordIdentifier, canonical::canonical_json, demo_configuration, evaluate_create, evaluate_read}` and then hand-builds the policy at :1295-1320. Separately, `const REGISTRY: &str` at :554 spans to ~:870 and is a TOML document embedded in Rust holding a 28-field `[[evaluators]]` block per profile — the records-api create block is :594-622 and the read block :624-652. `product_fixtures()` at :87-106 must gain a new `expected.extend(_product_fixtures()?)` line. The generated outputs are written at :118 and named at :885-886 (`bounded-policy/manifest.json`, `bounded-policy/registry.toml`). xtask/Cargo.toml lists every vertical as a build dependency (auths-github, auths-kubernetes, auths-opentofu, auths-postgresql, auths-radicle, auths-records-api, auths-stripe). +- **Disposition:** Move each vertical's fixture corpus generator into the vertical package behind a `fixture-support` feature (the pattern auths-github, auths-radicle and auths-stripe already use in xtask/Cargo.toml) exposing one `pub fn fixture_corpus() -> BTreeMap>`. Replace the `const REGISTRY` TOML literal with serialization of the collected `VerticalDescriptorV1` values. xtask then iterates an inventory instead of naming verticals. +- **Rationale:** Calling product/fixtures/v1/** 'generated' is technically true and materially false: the generator is hand-written Rust that must be edited per vertical, so regeneration saves nothing. Worse, this places the authoritative canonical corpus for a vertical's security semantics in a build tool rather than in the package the boundary plan says owns them (docs/target-state/PROFILE_AND_DOMAIN_ABSTRACTION_BOUNDARY_PLAN.md:104-122 lists 'its fixtures, mutation corpus, live contract, and demo' among what every profile owns). Fixture bytes are test oracles and must not change — this proposal moves where they are authored, not what they contain. + +### `qualified-profile-triplicated-across-three-languages` — The production profile registry is hand-maintained three times in three languages with three different failure modes +- **Lane:** cross · **Goal:** extensible · **Area:** extensibility — the file-touch cost of adding a new profile vertical +- **Evidence:** Rust: product/runtime/auths-production-client/src/lib.rs:168-172 `pub enum QualifiedProfile { OpenTofuSavedPlanApply, PostgreSqlBoundedUpdate, GitHubIssueAddress }`, with three exhaustive matches — `parse` at :181-188, `as_str` at :191-197, `execute_path` at :200-206. TypeScript: bindings/typescript/src/profiles.ts:4-7 declares the `ProductionProfileId` union and :13-23 the three factories; bindings/typescript/src/production-client.ts:364-370 re-lists the same three ids in `isProductionProfile`, and :372-380 re-lists the execute paths in `endpointPath`. Python: bindings/python/python/auths/profiles/__init__.py:24-28 the Literal union, :36-45 the factories, :65-67 the __all__ entries; bindings/python/python/auths/_production_client.py:375-379 `_PROFILE_IDS` and :400-413 `_endpoint_path`. That is FIVE independent hand-maintained lists of the same three strings plus TWO independent path tables. The Rust value is already reachable from both bindings — bindings/wasm/auths-proof-wasm/src/lib.rs:97 calls `QualifiedProfile::parse(&input.profile)` inside `encodeProductionRequestV1`, and bindings/python/python/auths/_production_client.py:257 calls `encode_production_request_v1`. Both bindings therefore validate the profile themselves (production-client.ts:188, _production_client.py:169) and then hand it to Rust which validates it again. `execute_path` is never exposed across either boundary — grep for `execute_path` outside target/ returns only product/runtime/auths-production-client/src/lib.rs:200, :334 and its own tests at :1105,:1109,:1113. +- **Disposition:** Export one Rust-owned descriptor across both boundaries: `productionProfileDescriptorsV1() -> [{ id, executePath }]` from bindings/wasm/auths-proof-wasm/src/lib.rs (the file already has the precedent — `encodeIdentityDescriptorV1` at :1016) and the equivalent from bindings/python/src. Then delete bindings/typescript/src/production-client.ts:364-380 and bindings/python/python/auths/_production_client.py:375-413 and drive both from the descriptor. profiles.ts and profiles/__init__.py keep only ergonomic factory helpers generated from the same source, so adding a vertical edits zero binding files. +- **Rationale:** This is the single biggest extensibility item available: adding one production profile today requires edits in Rust, TypeScript, and Python by three different people who cannot break each other's build. It also violates the ownership rule directly — TypeScript and Python are supposed to be projections of Rust-owned meaning, and here they independently define which profiles exist and what URL each one posts to. The Rust value is already on the other side of the FFI; the bindings simply do not read it. + +### `ts-endpoint-path-falls-open-to-github` — TypeScript endpointPath falls through to the GitHub execute path for any unlisted profile +- **Lane:** typescript · **Goal:** secure · **Area:** extensibility — the file-touch cost of adding a new profile vertical +- **Evidence:** bindings/typescript/src/production-client.ts:372-380: +``` +function endpointPath(step: ProductStep, profile: ProductionProfile["id"]): string { + if (step === "create") return "/v1/authority/create"; + if (step === "delegate") return "/v1/authority/delegate"; + if (step === "resume") return "/v1/workflows/resume"; + if (step === "verify") return "/v1/authority/verify"; + if (profile === "auths.opentofu.saved-plan-apply/1") return "/v1/profiles/opentofu/saved-plan-apply/execute"; + if (profile === "auths.postgresql.bounded-update/1") return "/v1/profiles/postgresql/bounded-update/execute"; + return "/v1/profiles/github/issue-address/execute"; +} +``` +There is no exhaustiveness check (no `never` assertion, no default throw). The gate that would catch this is a SEPARATE hand-maintained list 8 lines above, `isProductionProfile` at :364-370, called at :188. Compare the two peers: Rust product/runtime/auths-production-client/src/lib.rs:200-206 is an exhaustive `match` and fails at compile time; Python bindings/python/python/auths/_production_client.py:409-413 is a dict lookup that raises KeyError. Three languages, three failure modes for the same omission — compile error, KeyError, and silent misrouting. +- **Disposition:** Replace the fall-through with a table lookup over the same descriptor that backs the validation gate, and end the function with an exhaustiveness assertion (`const _never: never = profile; throw new TypeError(...)`). Once both come from one Rust-owned descriptor per the previous finding, the divergence is structurally impossible. +- **Rationale:** Adding a fourth profile to `isProductionProfile` but not to `endpointPath` silently POSTs one vertical's execute request to another vertical's endpoint. That is not a routing bug; it is authorization for one exact thing being submitted as authorization for a different exact thing, which is the precise failure the product exists to prevent. It fails open, and the two lists that must agree are 8 lines apart in the same file with nothing enforcing agreement. + +### `deploy-budget-algebra-registered-nowhere` — DeploymentAction mints an unregistered budget algebra, so the only domain profile in the product facade is unauthorizable with a ceiling and unbounded without one +- **Lane:** rust · **Goal:** secure · **Area:** rust-ownership +- **Evidence:** `product/profiles/auths-profile-domains/src/lib.rs:664-670` is the ONLY occurrence of the string `deploy-blast-radius-v1` in the repository (verified by grep across *.rs/*.toml/*.json/*.md): +```rust +fn budget(&self) -> Result, ProfileContractError> { + Ok(Some(BudgetCeiling::new( + BudgetAlgebraId::parse("deploy-blast-radius-v1") + .map_err(|_| ProfileContractError::MeaningMismatch)?, + self.blast_radius, + ))) +} +``` +`core/crates/auths-registries/src/lib.rs:30` defines the only budget algebra id (`pub const NUMERIC_CEILING_V1: &str = "numeric-ceiling-v1";`) and `:486-489` installs it as the sole `budget:` adapter in the V1 registry. `product/sdk/auths-sdk/src/lib.rs:225-228` hardcodes the accepted list to that one id: +```rust +vec![BudgetAlgebraId::parse( + auths_registries::NUMERIC_CEILING_V1, +)?], +``` +`core/crates/auths-registries/src/lib.rs:130-147`: `attenuates` returns `RegistryOperationError::InvalidInput` when `child.algebra() != &self.id`, and `covers` delegates to it. `core/crates/auths-verifier/src/lib.rs:2542-2556` calls `algebra.covers(ceiling, requested)` using the GRANT's algebra; `:2585-2587` maps `InvalidInput` to `Denied(LocalPolicyDenied)`. `:2541` gates the entire check on `if let (Some(ceiling), Some(requested))`. Meanwhile `product/integrations/auths-deployment/src/lib.rs:130-137` claims real state off that number: +```rust +.claim(action_id, authorized.command().action().blast_radius()) +``` +No test anywhere exercises a `DeploymentAction` through the kernel; the `auths.deploy` fixtures in `core/crates/auths-author/src/lib.rs:1156,1187,1215` use the capability `deploy/release`, which `DeploymentAction::permission()` (`auths-profile-domains/src/lib.rs:637-645`) can never produce (its operations are `activate|deploy|rollback`). +- **Disposition:** Delete `DeploymentAction::budget()` (auths-profile-domains/src/lib.rs:664-670) together with `DeploymentAction` itself when the crate is re-scoped. If a deployment vertical is rebuilt, it must derive its ceiling under `numeric-ceiling-v1` exactly as every real vertical does (auths-github/src/profile.rs:16, auths-kubernetes/src/profile.rs:17, auths-opentofu/src/profile.rs:18, auths-postgresql/src/profile.rs:20 all use `const ...BUDGET_ALGEBRA: &str = "numeric-ceiling-v1";`). Separately, add a test asserting every `ProfileContractError`-clean `CanonicalAction` produced by any shipping profile carries a budget algebra present in `auths_registries::TARGET_V1_REGISTRY_MANIFEST` — this class of bug is invisible today. +- **Rationale:** A profile that emits an algebra the protocol does not know has moved the definition of 'bounded' out of the kernel. Either the action is rejected for a reason unrelated to authority (LocalPolicyDenied, which reads as a policy decision, not a configuration error), or — when no grant in the chain declares a ceiling — the kernel skips the check entirely at auths-verifier:2541 and `auths-deployment` proceeds to claim blast radius from a store against a number no grant ever bounded. The blast-radius ceiling is the whole safety story for a deploy action, and it is currently enforced only by the integration's own bookkeeping. + +### `domain-receipt-inspector-owns-five-verticals` — DomainReceiptInspector is the sole ReceiptProfileInspector implementation and centralizes receipt meaning for five profiles in the reference crate, wired into both shipping bindings +- **Lane:** rust · **Goal:** secure · **Area:** rust-ownership +- **Evidence:** `product/profiles/auths-profile-domains/src/lib.rs:871-914` defines `pub struct DomainReceiptInspector;` and multiplexes receipt projection across five profile ids: +```rust +match (profile.id().as_str(), profile.version()) { + ("auths.http", 1) => receipt_projection(command, result, &HttpProfile::default(), reference_canonicalize_http), + ("auths.git", 1) => ... + ("auths.deploy", 1) => ... + ("auths.supply-chain", 1) => ... + ("auths.edge", 1) => ... + _ => Err(ReceiptInspectionError::UnsupportedProfile), +} +``` +A repo-wide grep for `ReceiptProfileInspector` returns exactly five sites: the trait at `product/receipts/auths-receipts/src/disclosure.rs:124-135`, its re-export at `product/receipts/auths-receipts/src/lib.rs:9`, the import and impl at `auths-profile-domains/src/lib.rs:11,873`, and the two consumers — `bindings/python/src/receipts.rs:410` and `bindings/wasm/auths-proof-wasm/src/lib.rs:4138`, both identical: +```rust +(mode != ReceiptViewMode::Opaque) + .then_some(&DomainReceiptInspector as &dyn auths_receipts::ReceiptProfileInspector), +``` +There is no other implementation. Every shipping vertical instead defines its own receipt types outside this port (`product/integrations/auths-github/src/receipts.rs:19-40` `GitHubDecisionReceipt`, plus `auths-opentofu/src/receipts.rs`, `auths-postgresql/src/receipts.rs`, `auths-radicle/src/receipts.rs`, `auths-kubernetes/src/receipts.rs`, `auths-stripe/src/receipts.rs`, `auths-records-api/src/receipts.rs`). Consequence: passing any of the seven qualified production profiles to the disclosure path hits `_ => Err(UnsupportedProfile)` at `auths-profile-domains/src/lib.rs:911`. `bounded-domains.toml:32` declares `receipt_payloads_remain_domain_owned = true`, and `docs/target-state/PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:374-376` states the retained reference tier must not 'acquire credentials, provider I/O, mutable state, retry policy, reconciliation, or receipt meaning'. +- **Disposition:** Delete `DomainReceiptInspector` (auths-profile-domains/src/lib.rs:869-943, including the private `receipt_projection` helper). Make `ReceiptProfileInspector` an inversion point the vertical supplies: each integration implements it for its own profile ref and passes it in, exactly as `auths-enforcement::Enforcement

` already takes the profile by value. Update `bindings/python/src/receipts.rs:410` and `bindings/wasm/auths-proof-wasm/src/lib.rs:4138` to take the inspector from the caller rather than naming a concrete generic one. +- **Rationale:** This is the archetype the brief names: `DomainReceiptInspector` is fully effect-free — no I/O, no clock, no credentials, no interior mutability — and it still owns the meaning of what a receipt says happened for five separate domains. A test proving 'the reference crate performs no I/O' passes cleanly while the disclosure vocabulary for every vertical lives in one shared match arm. It also silently guarantees that no qualified production profile can ever be disclosed, because the multiplexer only knows the five reference ids. + +### `caller-asserted-canonical-action-in-bindings` — Both Rust binding crates expose a generic CanonicalAction constructor where capability, resource, and budget are asserted alongside the body instead of derived from it +- **Lane:** rust · **Goal:** secure · **Area:** rust-ownership +- **Evidence:** `bindings/python/src/application.rs:316-355`: +```rust +#[pyfunction] +fn application_action( + profile_id: &str, profile_version: u16, media_type: &str, body: &[u8], + capability: &str, resource: &str, budget: Option<(String, u64)>, + resource_namespace: &str, audience: &str, +) -> PyResult { + let canonical = CanonicalAction::new( + ProfileRef::new(ProfileId::parse(profile_id)?, profile_version)?, + MediaType::parse(media_type)?, + body.to_vec(), + Permission::new(CapabilityId::parse(capability)?, ResourceId::parse(resource)?), + budget.map(|(algebra, value)| BudgetCeiling::new(BudgetAlgebraId::parse(&algebra)?, value)).transpose()?, + )?; +``` +No `ActionProfile`, no canonicalization, no re-derivation. `bindings/wasm/auths-proof-wasm/src/lib.rs:4464-4496` is the same function (`canonical_profile_action_native`), reached from two exported entry points: `#[wasm_bindgen(js_name = prepareProfileActionV1)]` at `:4261-4262` and `#[wasm_bindgen(js_name = canonicalizeProfilePlanMemberV1)]` at `:2205-2206`. The doc comment at `:4250-4256` states the intent plainly: 'This boundary constructs protocol objects only.' Contrast the MCP path in the same file, `:4415-4429`, which does run a Rust-owned profile: +```rust +let call = McpToolCall::new(service, name, mcp_arguments_from_js(arguments)?)?; +let profile = McpProfile; +let canonical = profile.canonicalize(&call.canonical_bytes()?)?; +``` +The guarantees being skipped are concrete and already implemented for real profiles: `auths-profile-domains/src/lib.rs:123-131` rejects non-canonical bytes (`NonCanonical`) and enforces `action.permission()? != *canonical.permission()` → `MeaningMismatch`, plus the same for budget. `auths-profile-api/src/lib.rs:100-101` documents `MeaningMismatch` as 'Derived permission/resource/budget does not match verified data' — on this path nothing derives anything. On the verify side, `PyApplicationCommand` (`bindings/python/src/application.rs:111-178`) only projects the `CanonicalAction` back out; there is no `decode_verified` re-check equivalent to `auths-sdk/src/lib.rs:314`. The Python wheel ABI freezes these: `bindings/python/native-abi-v2.json:41-46` lists `ApplicationAction`/`ApplicationCommand`/`ApplicationGatewayCall`, and `:177` claims 'application actions are parsed into native canonical types before authoring' — they are constructed, not parsed. +- **Disposition:** Delete `application_action` and the `PyApplicationAction`/`PyApplicationCommand`/`PyApplicationPlanCommand` family from `bindings/python/src/application.rs` (whole file, 826 lines), and delete `canonical_profile_action_native`, `prepare_profile_action_native`, `canonicalize_profile_plan_member_native`, `prepareProfileActionV1`, and `canonicalizeProfilePlanMemberV1` from `bindings/wasm/auths-proof-wasm/src/lib.rs`. Keep only profile-specific native entry points that canonicalize through a Rust-owned `ActionProfile`, on the `canonicalize_mcp_plan_member_native` model at wasm:4415-4429. Remove the corresponding symbols and the two false `workflowInvariants` strings from `bindings/python/native-abi-v2.json`. +- **Rationale:** This is the sharpest inversion of the ownership rule in the codebase: the binding surface is strictly MORE permissive than the Rust surface it is supposed to project. `auths_sdk::Verifier::verify` cannot be called without a profile; these entry points let the caller name any profile id and attach any capability/resource/budget to any body bytes. The binding between what the bytes say and what was authorized — the single thing this product sells — is supplied by the caller, in the untrusted language, with no Rust-side check on either the authoring or the decoding leg. + +### `auths-deployment-is-dead-with-unbacked-claims` — auths-deployment, the only Rust shipping consumer of the generic domain machinery, has zero consumers and cites compliance evidence that never touches it +- **Lane:** rust · **Goal:** simple · **Area:** rust-ownership +- **Evidence:** A workspace-wide grep for `auths-deployment` / `auths_deployment` across all *.rs and *.toml (excluding target/) returns only: `Cargo.toml:58` (workspace member), `Cargo.toml:190` (path dependency declaration), `architecture.toml:119`, `compliance.toml:219,222,234`, and its own manifest. No crate, demo, test, binding, or example depends on it. A grep for `DeploymentService|DeploymentOutcome|NoopDeploymentAuditSink|DeploymentBudgetStore` returns only definitions inside `product/integrations/auths-deployment/src/lib.rs` itself; the file is 178 lines with no `#[cfg(test)]` module. Its manifest declares `publish = false` and exactly two dependencies (`auths-enforcement`, `auths-sdk`) — nothing that reveals the coupling. `compliance.toml:234-236` claims: +```toml +[packages.auths-deployment.claims] +runtime-enforcement-boundary = ["demos/testkit/auths-apps-testkit/src/lib.rs#authenticated_transport_does_not_upgrade_bad_proof"] +stateful-replay-budget-component = ["demos/testkit/auths-apps-testkit/src/lib.rs#concurrent_duplicate_executes_exactly_once"] +``` +Those functions exist (`demos/testkit/auths-apps-testkit/src/lib.rs:525,578`) but `auths-apps-testkit` does not depend on `auths-deployment` at all, so neither test can exercise `DeploymentService`. The coupling itself is at `product/integrations/auths-deployment/src/lib.rs:6-9,65,74,83`: +```rust +use auths_sdk::{ DomainCommand, DomainProfile, Explanation, RequestContext, Verifier, ... }; +enforcement: Enforcement>, +E: CommandExecutor>, +Enforcement::new(verifier, DomainProfile::default()), +``` +Every other integration imports only concrete facade types — a grep of `use auths_sdk::` across all of `product/integrations/*/src/` yields exactly `Authorized`, `Explanation`, `RequestContext`, `SdkError`, `Verifier`, `VerifyResult`, `VerifiedAction`, and `auths-deployment` is the sole multiline outlier. +- **Disposition:** Delete `product/integrations/auths-deployment/` outright. Remove `Cargo.toml:58` and `Cargo.toml:190`, `architecture.toml:119`, and `compliance.toml:219-236`. Do not rewrite it: the spec at PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:526-529 offers 'rewrite or delete', and with zero consumers and no tests there is nothing to preserve. Deleting it removes the last Rust shipping dependency on `DomainProfile`/`DomainCommand` and unblocks their removal from the facade at auths-sdk/src/lib.rs:26-27. +- **Rationale:** The one crate cited as proof that a shipping vertical is built on generic machinery is not shipping at all — it is unreachable code carrying compliance claims that assert evidence from a crate that cannot import it. That is worse than the coupling itself: it means the compliance ledger will report a green claim for a package with no executed code path, and any future audit that trusts `compliance.toml` inherits a false positive. Deleting it is also the cheapest possible unblock for the facade cleanup. + +### `config-hardcodes-reference-profiles-and-excludes-every-vertical` — auths-config rejects every shipping vertical profile and holds two disjoint closed profile registries whose intersection is empty +- **Lane:** rust · **Goal:** extensible · **Area:** rust-ownership +- **Evidence:** `product/config/auths-config/src/lib.rs:425-441`: +```rust +fn known_profile(profile: &ProfileRef) -> bool { + profile.version() == 1 + && matches!( + profile.id().as_str(), + "auths.mcp" | "auths.http" | "auths.git" | "auths.deploy" | "auths.supply-chain" | "auths.edge" + ) +} +``` +Enforced at `:57-70`: +```rust +let parsed = profile.profile_ref()?; +if !known_profile(&parsed) || !profiles.insert(parsed) { + return Err(ConfigError::UnsupportedProtocolOrProfile); +} +``` +None of the seven qualified bounded-domain profiles can be named in a validated configuration: `auths.github.issue-address/1`, `auths.opentofu.saved-plan-apply/1`, `auths.postgresql.bounded-update/1`, `auths.radicle...`, `auths.kubernetes...`, `auths.stripe...`, `auths.records-api...` (see `bounded-domains.toml:47` and `product/integrations/auths-github/src/types.rs:18`). The SAME published crate carries a second, disjoint registry at `product/config/auths-config/src/production.rs:64-68`: +```rust +pub enum ProductionProfileId { + OpentofuSavedPlanApplyV1, + PostgresqlBoundedUpdateV1, + GithubIssueAddressV1, +} +``` +and a third copy of that same list exists at `product/runtime/auths-production-client/src/lib.rs:167-206` (`QualifiedProfile` with the identical three ids and their `/execute` paths). A fourth lives in `compliance.toml:156,1044,1390`. `auths-config` is published (release/public-naming.toml:257, tier 5) and its `BoundConfiguration` gates `product/operations/auths-operations/src/lib.rs:12,782`. +- **Disposition:** Delete `known_profile` (auths-config/src/lib.rs:425-441) and the `!known_profile(&parsed)` arm at `:67`. Configuration must validate that a profile ref is well-formed and unique, not that it is one of six names hardcoded in a shared crate. Collapse `ProductionProfileId` (production.rs:64-68) and `QualifiedProfile` (auths-production-client/src/lib.rs:167-206) into one type owned by one crate; the two are byte-identical in meaning and drift independently today. +- **Rationale:** Adding a new profile vertical currently requires editing a published shared crate that the vertical has no relationship with — that is the file-touch cost the EXTENSIBLE goal is measuring, and it is nonzero for a crate the vertical does not even depend on. Worse, the allowlist enumerates exactly the six reference-tier profiles and excludes all seven shipping ones, so the product configuration compiler encodes the reference tier as the product. Three copies of the qualified-profile list in three crates is the duplicate-vocabulary failure the SIMPLE goal forbids. + +### `python-http-vertical-owned-by-reference-crate` — A shipping HTTP execution vertical lives in the Python binding but its authorized meaning and its gateway request shape are owned by auths-profile-domains +- **Lane:** rust · **Goal:** secure · **Area:** rust-ownership +- **Evidence:** `bindings/python/src/http.rs:15` imports the generic family directly: +```rust +use auths_profile_domains::{HttpAction, HttpCommand, HttpProfile}; +``` +The file (719 lines) builds a full product surface on it: `#[pyclass(name = "HttpAction")]` at `:59-60`, `PyHttpCall` at `:26-33`, `http_call` at `:313-335`, `review_http_call` at `:337-352`, `commit_http_plan` at `:354`, `authorize_http` decoding at `:497-503` (`HttpProfile::default().decode_verified(action)`), and `canonical_http` at `:630-635`. The effect shape itself is derived from the generic type at `:660-679`: +```rust +fn gateway_request(action: &HttpAction) -> PyHttpGatewayRequest { + PyHttpGatewayRequest { method: action.method().to_owned(), scheme: ..., authority: ..., path: ..., query: ..., headers: ..., content_type: ..., body_digest: ... } +} +``` +The ownership is frozen into the wheel ABI contract at `bindings/python/native-abi-v2.json:176`: +```json +"HTTP permission mapping and command decoding are owned by auths-profile-domains" +``` +with the exported symbols at `:35-40` (`HttpCall`, `HttpAction`, `NativeHttpPlan`, `HttpCommand`, `HttpPlanCommand`, `HttpGatewayRequest`) and operations at `:90-97`. The parallel edge surface is at `bindings/python/src/domains.rs:2,54-98` (`canonicalize_edge_action_v1`, `parse_canonical_edge_action_v1`, `PyDomainActionProjection`) importing `EdgeAction, EdgeProfile, reference_canonicalize_edge`. Compare the MCP entry (`native-abi-v2.json:173`): 'MCP permission mapping and command decoding are owned by auths-profile-mcp' — a dedicated single-profile crate, which is the correct shape. +- **Disposition:** Either (a) create `product/profiles/auths-profile-http/` owning `HttpAction`, its validation, its permission derivation, its review display, and its gateway request shape, and have the Python binding depend on that alone — deleting `HttpAction`/`HttpProfile`/`HttpCommand` from `auths-profile-domains`; or (b) delete `bindings/python/src/http.rs` and `bindings/python/src/domains.rs` per PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:534-536 ('Remove corresponding Python native generic profile bindings unless replaced by a profile-specific ABI owned by a concrete vertical'). Do not leave the ABI string at native-abi-v2.json:176 pointing at a five-profile shared crate. +- **Rationale:** HTTP is the archetype the brief calls out. `HttpAction` is effect-free, but it is simultaneously the canonicalization input for the Python HTTP vertical, one of five arms in `DomainReceiptInspector`, and one of five parsers exported through WASM. Any change to `HttpAction::validate` or `permission()` at auths-profile-domains:357-401 silently changes what 'authorized' means for the Python gateway, the WASM parser, and receipt disclosure at once, with no vertical owning the decision. The ABI file states the leak as if it were a design property. + +### `wasm-exports-five-generic-domain-parsers-to-the-consumer-sdk` — The consumer npm SDK exports parsers and canonicalizers for all five reference domain profiles +- **Lane:** rust · **Goal:** simple · **Area:** rust-ownership +- **Evidence:** `bindings/wasm/auths-proof-wasm/src/lib.rs:37-41`: +```rust +use auths_profile_domains::{ + DeploymentProfile, DomainReceiptInspector, EdgeProfile, GitProfile, HttpProfile, + SupplyChainProfile, reference_canonicalize_deployment, reference_canonicalize_edge, + reference_canonicalize_git, reference_canonicalize_http, reference_canonicalize_supply_chain, +}; +``` +Ten `#[wasm_bindgen]` exports follow at `:2802-2929`: `parseHttpActionV1` (2802), `parseGitActionV1` (2817), `parseDeploymentActionV1` (2832), `parseSupplyChainActionV1` (2847), `parseEdgeActionV1` (2862), `parseCanonicalHttpActionV1` (2877), `parseCanonicalGitActionV1` (2887), `parseCanonicalDeploymentActionV1` (2897), `parseCanonicalSupplyChainActionV1` (2911), `parseCanonicalEdgeActionV1` (2927). The generic reference canonicalizers they wrap are `auths-profile-domains/src/lib.rs:951-1003`. `docs/target-state/PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:530-533` requires: 'Remove generic domain parsing and canonicalization exports from the consumer `auths-proof-wasm` package. If advanced cross-language reference access is genuinely required, create a separately named reference-only artifact; do not enlarge the consumer npm SDK.' Vendored build output confirms these reach browsers: `demos/cross-company-incident-response/control-room/public/vendor-v3/dist/verifier/wasm.js:44,49` gates on `typeof loaded.parseEdgeActionV1 !== "function"` and `parseCanonicalEdgeActionV1`. +- **Disposition:** Delete lines 2802-2929 of `bindings/wasm/auths-proof-wasm/src/lib.rs` and drop the five profile imports and five `reference_canonicalize_*` imports at `:37-41` (retaining only what a surviving MCP path needs). Regenerate `bindings/typescript/api/public-api.txt` and the vendored `dist/` artifacts from the reduced ABI rather than hand-editing them. +- **Rationale:** Ten symbols for five profiles that no qualified production vertical uses is pure surface-area cost against the SIMPLE goal, and it is the cross-language half of the same ownership leak: once a browser consumer can call `parseDeploymentActionV1`, the deployment profile's meaning is a published cross-language contract owned by a shared reference crate, and the `reference_canonicalize_*` variants (which take a different code path from `DomainProfile::canonicalize` — see auths-profile-domains:135-156 vs :61-82) become independently frozen behavior. + +### `three-incompatible-effect-vocabularies` — Three mutually incompatible RetryClass/EffectState vocabularies ship in one wheel; the publicly exported one is not the registry's +- **Lane:** python · **Goal:** secure · **Area:** python +- **Evidence:** `bindings/python/python/auths/_errors.py:8-11`: +``` +RetryClass = Literal["never", "safe", "conditional", "unknown"] +EffectState = Literal[ + "not-started", "in-progress", "completed", "failed", "outcome-unknown" +] +``` +`bindings/python/python/auths/_product_errors.py:20-31`: +``` +class RetryClass(str, Enum): + NEVER = "never"; SAFE = "safe"; CONDITIONAL = "conditional"; UNKNOWN = "unknown" +class EffectState(str, Enum): + NOT_APPLIED = "not-applied"; POSSIBLE = "possible"; APPLIED = "applied"; UNKNOWN = "unknown" +``` +`bindings/python/python/auths/_production_client.py:31`: +``` +RetryClass = Literal["never", "backoff", "resume", "reconcile"] +``` +`bindings/python/python/auths/__init__.py:122` maps the public name to the third one: `"RetryClass": "._production_client"`. Verified at runtime: `auths.RetryClass` is `typing.Literal['never','backoff','resume','reconcile']`; `auths.RetryClass is _product_errors.RetryClass` -> `False`; `_errors.EffectState` -> `Literal['not-started','in-progress','completed','failed','outcome-unknown']`. Canonical vocabulary in `product/errors/v1/registry.json` is retry `never|safe|conditional|unknown`, effect `not-applied|possible|applied|unknown`. TypeScript derives both from the registry: `bindings/typescript/src/product-errors.ts:7-8` `export type RetryClass = Definition["outcomes"][number]["retry"]` / `EffectState = Definition["outcomes"][number]["effect"] | "unknown"`. +- **Disposition:** Delete `_errors.RetryClass` and `_errors.EffectState` outright. Delete `_production_client.RetryClass`. Make `_product_errors.RetryClass`/`EffectState` the single vocabulary, and derive both from `_error_registry.ERROR_REGISTRY` the way `bindings/typescript/src/product-errors.ts:7-8` does rather than hand-typing the enum members. Export both from `auths`. The production client's `never|backoff|resume|reconcile` axis must be replaced by the registry `retry` plus `effect`, not aliased to it. +- **Rationale:** `_errors.EffectState` is not an effect axis at all — it is a task-lifecycle axis. `"failed"` and `"in-progress"` say nothing about whether the real-world effect happened, which is the one question the registry exists to answer. Python has three answers to "did it apply?" and the one reachable from `import auths` is the one with no effect concept whatsoever. Semantics for effect classification are Rust-owned; Python is inventing two extra ones locally. + +### `public-authserror-catches-nothing` — auths.AuthsError is never raised; the exception the SDK actually raises is an unrelated class not reachable from the public package +- **Lane:** python · **Goal:** secure · **Area:** python +- **Evidence:** Two unrelated `Exception` subclasses named `AuthsError`: `bindings/python/python/auths/_product_errors.py:110` `class AuthsError(Exception)` and `bindings/python/python/auths/_errors.py:30` `class AuthsError(Exception)`. `bindings/python/python/auths/__init__.py:92` binds the public name to the former. Runtime check: `issubclass(auths._errors.AuthsError, auths.AuthsError)` -> `False`. The public one is constructible only via `AuthsError.parse()` (`_product_errors.py:115-117`); grep across `bindings/python/python/` shows the only producers of that schema string are `_product_errors.py:85` (its own `to_dict`) and `:209` (its own parser) — no SDK code path ever emits such a dict, and there is no `raise AuthsError` anywhere. Meanwhile the public `Auths.execute` (`_product.py:204-231`) calls `execute_mcp_closed`, which raises `_errors.AuthsWorkflowError` at `profiles/_mcp.py:913,917,924,934,967,992,1014,1100,1176,1447,1452,1479,1521,1532,1549,1555,1560` and `ProviderOperationError` at `_plan.py:79,81,83,87,89,115,125`. Neither is importable from `auths` (`__init__.py:50-86` `__all__`). Same defect in TypeScript: `AuthsError` is exported at `bindings/typescript/src/index.ts:11` and referenced nowhere else in `src/`. +- **Disposition:** Delete `_errors.AuthsError`/`AuthsWorkflowError`/`RuntimeStateError`. Reparent `ProviderOperationError` and every `raise` site onto `_product_errors.AuthsError`, constructing it from a registry code so `AuthsError.details.effect` is populated. Public `auths.AuthsError` must be the class that is actually raised, or it should not be exported. +- **Rationale:** `try: ... except auths.AuthsError:` — the idiom the public surface advertises — catches nothing. Every failure escapes as an exception type the caller cannot name without reaching into a private module, so callers will fall back to bare `except Exception`, which discards the retry/effect classification entirely. That is the failure mode the error registry was built to prevent. + +### `effect-state-unreachable-from-public-api` — No public Python API can tell a caller the effect MIGHT have happened +- **Lane:** python · **Goal:** secure · **Area:** python +- **Evidence:** `bindings/python/python/auths/_product.py:69-79` — the two failure members of the public `ExecutionResult` union carry no effect information at all: +``` +class Denied: + kind: Literal["denied"] + code: str +class Indeterminate: + kind: Literal["indeterminate"] + code: str +``` +`Auths.execute` returns these (`_product.py:151-158, 204-231, 409-429`). The `code` is a registry code that can be `mcp.reconciliation-pending`, `mcp.handler-failed`, `mcp.handler-timeout`, `mcp.invalid-handler-output`, `core.outcome-unknown`, `core.observation-pending`, `core.observation-inconclusive` — all registered `{retry: unknown, effect: possible, recommendedAction: resume-and-reconcile}` in `product/errors/v1/registry.json`, and `mcp.receipt-persist-failed` registered `{retry: conditional, effect: applied}`. To map a code to its effect a caller needs `ERROR_REGISTRY`, which lives only in the private `auths._error_registry`, or `EffectState`/`AuthsErrorDetails`, which live only in the private `auths._product_errors`. Runtime check against `auths.__all__`: symbols containing `effect` -> `[]`; symbols containing `registry`/`lookup` -> `[]`. `bindings/python/tests/test_product_errors.py:8` has to import from `auths._product_errors` to reach `EffectState` at all. +- **Disposition:** Add `effect: EffectState` (and `retry`, `recommended_action`) as fields on `Denied` and `Indeterminate` in `_product.py:69-79`, populated from the registry entry for `code` at projection time in `_project_execution` (`_product.py:409-429`). Export `EffectState` from `auths`. Do not make callers look codes up themselves. +- **Rationale:** The whole point of the `effect` axis is that `unknown`/`possible` means "we do not know whether the real-world action happened." A caller that receives `Indeterminate(kind='indeterminate', code='mcp.handler-timeout')` and retries has just double-applied an effect. The information exists in the wheel and is deliberately withheld from the public surface. Lossy here is exactly the security finding the spec warns about. + +### `native-generic-vertical-leak` — auths_profile_domains reference verticals (HttpAction, EdgeAction) are compiled into _native and exposed as 18 typed, callable Python symbols +- **Lane:** python · **Goal:** secure · **Area:** python +- **Evidence:** `bindings/python/src/http.rs:15`: `use auths_profile_domains::{HttpAction, HttpCommand, HttpProfile};` and `src/http.rs:702-719` registers `HttpCall`, `HttpPreparedAction`, `NativeHttpPlan`, `HttpCommand`, `HttpPlanCommand`, `HttpGatewayRequest`, `http_call`, `review_http_call`, `commit_http_plan`, `prepare_http_action`, `authorize_http`, `inspect_http_action`, `consume_http_command`, `seal_http_plan_command`, `consume_http_plan_command`. `bindings/python/src/domains.rs:2`: `use auths_profile_domains::{EdgeAction, EdgeProfile, reference_canonicalize_edge};` and `src/domains.rs:104-108` registers `DomainActionProjection`, `canonicalize_edge_action_v1`, `parse_canonical_edge_action_v1`. Both are wired into the extension at `src/lib.rs:39,40`. They are fully typed for consumers in `bindings/python/python/auths/_native.pyi:528-576` and `:884-920`, and `tools/check_wheel.py:13` REQUIRES `auths/_native.pyi` in the wheel. Verified at runtime: `dir(auths._native)` yields 147 symbols including `['DomainActionProjection','HttpAction','HttpCall','HttpCommand','HttpGatewayRequest','HttpPlanCommand','NativeHttpPlan','authorize_http','canonicalize_edge_action_v1','commit_http_plan','consume_http_command','consume_http_plan_command','http_call','inspect_http_action','parse_canonical_edge_action_v1','prepare_http_action','review_http_call','seal_http_plan_command']`. The Python-level wrappers were already deleted — `tools/check_wheel.py:54-55` lists `auths/profiles/domains.py` and `auths/profiles/http.py` under `REMOVED_PUBLIC_FILES` — but the native machinery still ships. `bindings/python/docs/INTEGRATION_RECIPES.md:33-37` states the opposite policy in prose: "Do not model a Cloudflare, Stripe, Kubernetes or database effect as a generic HTTP callback. A new public profile requires Rust-owned semantics." +- **Disposition:** Delete `bindings/python/src/http.rs` and `bindings/python/src/domains.rs`, their `mod` declarations and `register` calls at `src/lib.rs:9,8,39,40`, the `auths-profile-domains` dependency from `bindings/python/Cargo.toml:26`, and the corresponding blocks in `_native.pyi:528-576,884-920`. Then extend `tools/check_public_api.py:9-16` to snapshot `dir(auths._native)` as well, so the 147-symbol native surface is guarded at the symbol level like the 159-symbol Python surface. +- **Rationale:** This is the leak the invariant predicts: `HttpAction` is effect-free, passes any "no I/O, no clocks, no credentials" test, and still moves the meaning of "what was authorized" into the generic crate. The deletion of `auths/profiles/http.py` and `auths/profiles/domains.py` made the leak invisible to a Python-source audit while leaving it fully callable via `from auths import _native`. Because `check_public_api.py` walks only `bindings/public-topology-v1.json`, no existing gate covers it. + +### `python-can-define-a-vertical` — define_profile plus _native.application_action let a Python caller define a whole vertical with canonicalization in a Python callback +- **Lane:** python · **Goal:** secure · **Area:** python +- **Evidence:** `bindings/python/python/auths/_application_profile.py:84-89`: +``` +@dataclass(frozen=True) +class ProfileDefinition(Generic[InputT, CommandT]): + id: str + version: int + canonicalize: Callable[[InputT], CanonicalProfileAction] + decode_verified: Callable[[CanonicalProfileAction], CommandT] +``` +`_application_profile.py:865-870` `def define_profile(definition: ProfileDefinition[...]) -> ApplicationProfile[...]`. The Rust side accepts whatever Python hands it — `bindings/python/src/application.rs:317-355`: +``` +fn application_action(profile_id: &str, profile_version: u16, media_type: &str, body: &[u8], + capability: &str, resource: &str, budget: Option<(String,u64)>, + resource_namespace: &str, audience: &str) -> PyResult +``` +It only parses the strings for well-formedness (`ProfileId::parse`, `MediaType::parse`, `CapabilityId::parse`) and wraps the caller-supplied `body` in `CanonicalAction::new`. Nothing checks the profile id against the qualified roster in `bindings/public-topology-v1.json` (`auths.github.issue-address/1`, `auths.mcp/1`, `auths.opentofu.saved-plan-apply/1`, `auths.postgresql.bounded-update/1`). The generic `Profile(id: str, version: int)` type at `_workflow.py:340-346` validates only length, and `AuthsClient.attach_agent` at `_workflow.py:843-854` accepts any such `Profile`. `_application_profile.py` is 1185 lines with 28 `__all__` entries (`:1156`), is imported by no module in the package, and is nonetheless mandatory in the wheel per `tools/check_wheel.py:15`. +- **Disposition:** Delete `bindings/python/python/auths/_application_profile.py` and remove it from `tools/check_wheel.py:15`. Delete `application_action` (`src/application.rs:317-355`) and the rest of the profile-agnostic `application_*` pyfunctions at `src/application.rs:809-822` unless a qualified vertical proves it needs them; if kept, gate `profile_id` against the qualified roster in Rust and reject anything else. +- **Rationale:** Canonicalization is the definition of "what exactly was authorized." Here it is a `Callable[[InputT], CanonicalProfileAction]` supplied by the Python caller, with Rust reduced to a byte-wrapper. That inverts the ownership rule: the file-touch cost of a new vertical becomes zero Rust files, which reads as extensibility and is actually the loss of semantic ownership. That the module is dead code today makes it cheap to delete and dangerous to leave, since it ships to every consumer. + +### `typestub-hides-production-surface` — __init__.pyi omits 19 of 35 public symbols, so the entire production client is invisible to type checkers in a py.typed wheel +- **Lane:** python · **Goal:** simple · **Area:** python +- **Evidence:** `bindings/python/python/auths/__init__.pyi:1-18` re-exports only 16 names, ending at `from ._workflow import Approval as Approval`. `bindings/python/python/auths/__init__.py:50-86` declares 35 in `__all__` and `bindings/python/api/public-api.txt:1-36` snapshots all 35. The 19 missing from the stub are `ProductStep`, `ProductionAuths`, `ProductionAuthority`, `ProductionAuthorityResult`, `ProductionCompleted`, `ProductionDenied`, `ProductionExecutionResult`, `ProductionIndeterminate`, `ProductionReceipt`, `ProductionRecoverable`, `ProductionRecoveryReference`, `ProductionRejected`, `ProductionTransport`, `ProductionTransportRequest`, `ProductionTransportResponse`, `ProductionVerificationResult`, `ProductionVerified`, `RetryClass`, `create_auths`. Because a `.pyi` shadows its sibling `.py`, the runtime `__getattr__` at `__init__.py:127-133` is invisible to checkers. Proven with the repo's own mypy: +``` +probe.py:1: error: Module "auths" has no attribute "create_auths" [attr-defined] +probe.py:1: error: Module "auths" has no attribute "ProductionAuths" [attr-defined] +probe.py:1: error: Module "auths" has no attribute "RetryClass" [attr-defined] +``` +The wheel ships `auths/py.typed` (`tools/check_wheel.py:35`) and `pyproject.toml:24` declares `Classifier: Typing :: Typed`, enforced at `tools/check_wheel.py:137`. `bindings/python/README.md:72` claims "All public modules have explicit `__all__` and typed installed-wheel coverage." +- **Disposition:** Delete `bindings/python/python/auths/__init__.pyi` entirely and let the inline annotations plus the `TYPE_CHECKING` block at `__init__.py:8-48` be the single source of truth (that block already lists all 35 correctly). Then add a check to `tools/check_public_api.py` that fails when a checker-visible symbol set diverges from `__all__` — the current snapshot compares `__all__` to `api/public-api.txt` and cannot see this class of drift. +- **Rationale:** Two declarations of the same public surface, and they disagree. Every typed consumer of the production client — which is the entire non-development story — gets a hard type error on the import line. The `Typing :: Typed` classifier is a promise the wheel does not keep, and the existing public-API gate structurally cannot catch it. + +### `transport-failure-downgrades-effect-to-not-applied` — Production client maps every transport failure and every bad HTTP response to retry=backoff, asserting not-applied when the effect is genuinely possible +- **Lane:** cross · **Goal:** secure · **Area:** python +- **Evidence:** `bindings/python/python/auths/_production_client.py:272-285`: +``` +try: + response = await self._transport.send(request) +except Exception: + return MappingProxyType({"contractVersion": 1, "kind": "indeterminate", + "code": "core.runtime-unavailable", "retry": "backoff", + "recoveryReference": None, "value": None, "receipt": None}) +``` +and `:286-302` returns the same shape with `"code": "core.malformed-input", "retry": "backoff"` for any non-2xx status, wrong content type, empty body, or oversized body. The default transport (`:338-372`) is a blocking `urllib` call with a read timeout, so a socket timeout *after* the POST was delivered and applied lands in that `except Exception`. `product/errors/v1/registry.json` registers `core.runtime-unavailable` as `operation: execute, stages: [lifecycle-store], outcomes: [{retry: safe, effect: not-applied}], recommendedAction: retry-execution`, and `core.malformed-input` as `operation: verify, stages: [parse], outcomes: [{retry: never, effect: not-applied}]`. The registry's code for this situation is `core.outcome-unknown` — `{retry: unknown, effect: possible, recommendedAction: resume-and-reconcile, allowsExecutionReference: true}`. Python's own invariant checker agrees these are incompatible: `_product_errors.py:245-246` raises on `retry SAFE` with effect other than `NOT_APPLIED` — but that checker is never on this path, because the synthesized dict goes through `_execution_result`/`_authority_failure` (`:448-488`), not through `AuthsError.parse`. Identical construction in TypeScript at `bindings/typescript/src/production-client.ts:276-292`. +- **Disposition:** Replace both synthesized projections with the effect-preserving classification: on a transport exception or a malformed response for `execute`/`resume`, emit `core.outcome-unknown` with the possible-effect classification and surface a recovery reference; reserve `core.runtime-unavailable`/`retry=backoff` for the pre-send failures where not-applied is actually provable (endpoint validation, request encoding). Route the synthesized dicts through the same registry validation `_parse_details` performs so a code cannot be paired with an outcome the registry does not list. Fix `bindings/typescript/src/production-client.ts:276-292` the same way in the same change. +- **Rationale:** This fails open. The client tells the caller "not applied, safe to retry" for precisely the class of failure where it cannot know — a response lost after the server committed the effect. A caller following the returned `retry` class will re-apply. It also stamps registry codes onto operations and stages the registry does not permit (`core.malformed-input` is registered `operation: verify, stage: parse`, emitted here for `create`/`delegate`/`execute`/`resume`), so the code is not a reliable oracle even for a caller who does the registry lookup manually. + +### `locally-minted-error-codes` — Python mints 25 error codes that exist in no registry, on the code path reachable from the public execute() +- **Lane:** python · **Goal:** secure · **Area:** python +- **Evidence:** AST scan of every `AuthsWorkflowError(...)` first argument in `bindings/python/python/auths/` yields 25 distinct codes, none of which appear in `product/errors/v1/registry.json` (whose 45 codes are all namespaced `core.*`, `mcp.*`, `plan.*`, `custody.*`): `approval-cancelled`, `approval-failed`, `approval-policy-mismatch`(x4), `approval-rejected`, `approval-response-mismatch`(x3), `authority-mismatch`(x2), `authority-source-failed`, `authority-source-timeout`, `cleanup-failed`(x2), `delegation-expanded`, `disposed`(x6), `gateway-conflict`(x2), `invalid-action`, `invalid-authority`(x4), `invalid-delegation`, `invalid-principal`(x3), `invalid-profile`(x14), `invalid-provider`, `invalid-trusted-authority`, `native-authorization-failed`(x7), `profile-mismatch`(x6), `signer-failed`, `signer-response-mismatch`(x3), `transaction-consumed`(x2), `transaction-expired`(x2). Plus two more minted in subclasses: `bindings/python/python/auths/profiles/_mcp.py:408-423` `McpGatewayError` uses code `"gateway-failed"` with `retry="unknown", effect_state="outcome-unknown"`, and `:673-690` `McpGatewayCancelled` uses `"gateway-cancelled"` with the same. These are raised from `profiles/_mcp.py`, which is what public `Auths.execute` (`_product.py:225,229`) calls. Meanwhile `bindings/python/python/auths/_error_registry.py` is generated and byte-identical to the canonical registry (verified: `json.loads(...) == json.load(open('product/errors/v1/registry.json'))` -> `True`, 45 definitions each, regenerated by `xtask/src/error_registry.rs:10` and semantic-frozen at `xtask/src/semantic_freeze.rs:423`). +- **Disposition:** Map every one of these 27 codes onto an existing registry code, or add the missing ones to `product/errors/v1/registry.json` and regenerate via `xtask/src/error_registry.rs`. Then assert at construction time that the code is in `ERROR_REGISTRY` — a raise with an unregistered code should be an internal invariant failure, not a silently-accepted string. +- **Rationale:** The registry is the single Rust-owned enumeration of what can go wrong and what the caller may safely do next. A caller that receives `code='gateway-failed'` or `code='disposed'` has no registry entry to consult, so the retry class and effect state attached to it were decided in Python. `disposed` alone appears 6 times spanning lifecycle, client and agent teardown — three different conditions collapsed into one non-registry token. Twenty-seven codes is a second, undocumented, Python-owned error vocabulary. + +### `provider-error-asserts-non-effect` — ProviderOperationError hardcodes effect=not-started/none and the application gateway signs a "failed" receipt for a possibly-applied provider call +- **Lane:** security · **Goal:** secure · **Area:** error-model +- **Evidence:** bindings/python/python/auths/_errors.py:82-107 — `class ProviderOperationError(AuthsError)` takes only `kind`; line 92 `retry = "safe" if kind in ("unavailable", "timeout") else "never"`; line 102 passes the literal `"not-started"` as `effect_state` for EVERY kind, including `timeout`. There is no constructor parameter that can change it. bindings/python/python/auths/_application_profile.py:597-616 — the `except Exception as error:` around `await self._options.execute(command, credential, context)` computes `outcome = "failed" if isinstance(error, ProviderOperationError) and error.effect_state == "not-started" else "outcome-unknown"` (lines 599-603), then for `"failed"` calls `self._execution_receipt(..., "failed", None)` (605-611) and `self._finish(context, outcome, ...)` (616) — a terminal, signed attestation of non-effect. TypeScript is the same shape but slightly less broken: bindings/typescript/src/workflow/errors.ts:112 `this.effect = context.effect ?? (kind === "timeout" || kind === "cancelled" ? "possible" : "none")`, so `unavailable`/`rejected`/`unsupported` default to `"none"`, and bindings/typescript/src/profiles/application/index.ts:732-733 `const definitelyFailed = error instanceof ProviderOperationError && error.effect === "none"; const outcome = definitelyFailed ? "failed" : "outcome-unknown";`. `ProviderOperationError` is the documented adapter contract: it is a public export of `auths.framework` (bindings/python/python/auths/framework.py:8,22) and of `@auths-dev/sdk/framework` (bindings/typescript/src/framework.ts:6; bindings/typescript/api/public-api.txt:161). +- **Disposition:** Delete `ProviderOperationError` from `_errors.py` and `workflow/errors.ts` and from both `framework` exports. Replace the adapter contract with the MCP profile's already-correct shape: adapters return/raise a value carrying an explicit Rust-owned `EffectState`, and the gateway treats anything other than an explicit `not-applied` as `possible`. Until that lands, the `definitelyFailed`/`effect_state == "not-started"` predicates at profiles/application/index.ts:732 and _application_profile.py:601-602 must be deleted so every provider exception becomes `outcome-unknown`. +- **Rationale:** This is the exact invariant the Rust registry exists to protect. `mcp.handler-timeout` and `mcp.handler-failed` are `possible/unknown/resume-and-reconcile` (auths-errors/src/lib.rs:555-580) precisely because a provider call that threw after transmission gives no evidence of non-effect. Here an adapter author writing the obvious `raise ProviderOperationError("timeout")` — with no way in Python to say otherwise — causes Auths to sign an execution receipt asserting the effect did not happen and to close the workflow terminally, destroying the reconciliation path. A proof-carrying system that mints false non-effect proofs is worse than one that has no proofs. + +### `production-client-transport-catch` — Both production clients relabel every transport failure and non-2xx response as not-applied, safe-to-retry, using registry codes whose declared semantics contradict the situation +- **Lane:** security · **Goal:** secure · **Area:** error-model +- **Evidence:** bindings/typescript/src/production-client.ts:269-286 — the `await this.#transport.send(...)` for every step including `execute` is wrapped in `} catch {` returning `{kind:"indeterminate", code:"core.runtime-unavailable", retry:"backoff"}`. Lines 287-297 return `{kind:"indeterminate", code:"core.malformed-input", retry:"backoff"}` for any non-2xx status or content-type mismatch. bindings/python/python/auths/_production_client.py:272-299 is the byte-for-byte mirror (`except Exception:` → `core.runtime-unavailable`/`backoff`; non-2xx → `core.malformed-input`/`backoff`). The default TS transport aborts on timeout (production-client.ts:311-312 `setTimeout(() => controller.abort(), request.timeoutMs)`), so the canonical outcome-unknown case — an `execute` POST that timed out in flight — lands in that bare catch. The registry says `core.runtime-unavailable` is `retry: safe, effect: not-applied`, operation `execute`, stage `lifecycle-store`, explanation "The durable runtime could not complete an operation before provider entry" (product/errors/v1/registry.json:166-187); `core.malformed-input` is `retry: never`, operation `verify`, stage `parse` (registry.json:74-95). Neither claim is knowable from a dead socket. The server also returns 408 for its own request timeout (product/runtime/auths-node/src/api.rs:140-142 `TimeoutLayer::with_status_code(StatusCode::REQUEST_TIMEOUT, ...)`), which the client turns into `core.malformed-input`. +- **Disposition:** Replace both bare handlers with typed classification: cancellation, timeout, connection-refused, and any failure after the request bytes were written on a non-idempotent step must project `core.outcome-unknown` (possible/unknown/resume-and-reconcile) and must surface a recovery reference or force a status lookup. Only a failure provably before transmission may claim `core.runtime-unavailable`. Non-2xx must not reuse `core.malformed-input` — it needs its own code, and 5xx/408 on `execute` is outcome-unknown. +- **Rationale:** docs/target-state/PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:452-465 names this exact anti-pattern: "Replace broad `catch { ... }` and `except Exception: ...` translation" with typed adapter errors, explicit cancellation and timeout handling, and a sanitized unknown category. The current code does the opposite — it collapses the entire space of transport ambiguity onto the two most reassuring codes in the registry. A caller that follows `retry: backoff` after an execute timeout double-applies the effect. It also swallows programmer errors from a caller-supplied `ProductionTransport` implementation and reports them as an authorization outcome. + +### `authserror-has-no-producer` — AuthsError — the only registry-bound projection in either binding — is exported publicly but never constructed by any SDK code path +- **Lane:** cross · **Goal:** secure · **Area:** error-model +- **Evidence:** TypeScript: the only reference to `AuthsError` outside its defining module is the re-export at bindings/typescript/src/index.ts:11. `grep -rn "AuthsError" bindings/typescript/src` returns product-errors.ts plus that one line; nothing calls `AuthsError.parse`. Its unit test imports it by deep path `../../dist/product-errors.js` (bindings/typescript/test/unit/product-errors.test.js:10). Python: `_product_errors.AuthsError` is referenced only by `auths/__init__.py:23,92` and `bindings/python/tests/test_product_errors.py:9`; the `AuthsError` imported by `_workflow.py:58-59` is the unrelated `_errors.AuthsError`. What callers actually receive carries no recovery data at all: bindings/typescript/src/product.ts:70-78 `interface Denied { kind; code: string }` / `interface Indeterminate { kind; code: string }`, produced by product.ts:385-387 `return Object.freeze({ kind: value.kind, code: value.code });`, and bindings/python/python/auths/_product.py:69-78 plus `_project_execution` at _product.py:417-420. No `effect`, no `retry`, no `recommendedAction`, no `entered`, no `executionReference`. +- **Disposition:** Make the registry projection the only failure carrier on both public surfaces. `Denied`, `Indeterminate`, `ProductionDenied`, `ProductionIndeterminate`, `ProductionRejected` must embed `AuthsErrorDetails` (code, family, retry, effect, entered, recommendedAction, references) rather than a bare `code: string`, and the MCP/application/production paths must construct it from the Rust-owned classification instead of dropping it. +- **Rationale:** The entire safety argument of the error model — that a caller can distinguish applied / not-applied / possible and act on `recommendedAction` — is delivered by exactly one class in each binding, and that class is unreachable in practice. The registry, the generator, the fixtures, and the drift check all exist and all pass while the shipped API cannot express the distinction they encode. This is the single largest gap between the specified error model and the one an application developer actually programs against. + +### `three-shadow-error-taxonomies` — Three additional retry/effect vocabularies are defined independently in the bindings, and the public root RetryClass is one of them +- **Lane:** cross · **Goal:** simple · **Area:** error-model +- **Evidence:** Registry vocabulary: `RetryClass{never,safe,conditional,unknown}` / `EffectState{not-applied,possible,applied}` (product/errors/auths-errors/src/lib.rs:29-44). Shadow #1: bindings/typescript/src/production-client.ts:12 `export type RetryClass = "never" | "backoff" | "resume" | "reconcile";` and bindings/python/python/auths/_production_client.py:31 `RetryClass = Literal["never", "backoff", "resume", "reconcile"]`, mirroring the Rust `auths_production_client::RetryClass{Never,Backoff,Resume,Reconcile}` (product/runtime/auths-production-client/src/lib.rs:415-442). This is the `RetryClass` the public root exports — bindings/typescript/src/index.ts:47 and bindings/python/python/auths/__init__.py:45,122 both take it from the production client, not from the registry. Shadow #2: bindings/typescript/src/workflow/errors.ts:47-48 `RetryClass = "never"|"safe"|"conditional"|"unknown"` (redefined, not imported) and `EffectState = "none" | "possible" | "occurred"`, with 37 ad-hoc `WorkflowErrorCode` values (errors.ts:1-36) and retry derived from the code string's suffix: errors.ts:128-132 `if (code.endsWith("-timeout") || code.endsWith("-cancelled")) return "conditional"; if (code.endsWith("-failed") ...) return "safe";`. Shadow #3: bindings/python/python/auths/_errors.py:8-11 `EffectState = Literal["not-started", "in-progress", "completed", "failed", "outcome-unknown"]`. Shadow #4 (dead but public): bindings/typescript/src/profiles/mcp/index.ts:162-167 `McpGatewayError` with `effect: "not-applied" | "applied" | "unknown"` — no `possible` — never referenced anywhere in src or test, yet listed at bindings/typescript/api/public-api.txt:133; the same name in Python is a live exception class (bindings/python/python/auths/profiles/_mcp.py:408-420). +- **Disposition:** Delete bindings/typescript/src/workflow/errors.ts and bindings/python/python/auths/_errors.py outright and rewrite their call sites onto the generated registry types. Delete the dead `McpGatewayError` interface. Rename or eliminate the production-client retry vocabulary; if the wire genuinely needs `backoff/resume/reconcile` it must be a generated projection of `(RetryClass, EffectState, RecommendedAction)` with a different name, and the public root must export only the registry `RetryClass`. +- **Rationale:** AGENTS.md forbids duplicate vocabularies and dual paths prelaunch, and the product rule is that TypeScript and Python are projections of Rust-owned meaning and may never independently define semantics. Here each binding independently defines the two enums that encode whether a real-world effect happened, and the one the public root exports under the registry's name is the one that cannot express `possible`. Worse, `workflowRetry` infers retry class from a substring of the code name, so adding a code named `*-failed` silently declares it retry-safe. `gateway-failed` — the code the MCP profile throws for an unknown provider outcome (profiles/mcp/index.ts:995) — is classified `safe` with effect `none` by errors.ts:129. + +### `ts-effect-state-never-reaches-caller` — The effect axis is unreachable from every TypeScript public surface; AuthsError is dead code +- **Lane:** cross · **Goal:** secure · **Area:** typescript +- **Evidence:** `src/product-errors.ts:8` `export type EffectState = Definition["outcomes"][number]["effect"] | "unknown";` and `:77` `get effect(): EffectState { return this.details.effect; }` — AuthsError is the only type in the binding that carries the registry effect axis. `src/index.ts:10-14` root-exports `AuthsError`, `AuthsErrorCode`, `RecommendedAction` — and NOT `EffectState`, `AuthsErrorDetails`, `isAuthsError`, or the registry. `EffectState` appears nowhere in `api/public-api.txt` (195 lines, all 7 subpaths). Repository-wide grep for `AuthsError.parse`: the only call sites are `test/unit/product-errors.test.js:20,34,35,36,40,70`. The only non-test import of `product-errors.js` outside `index.ts` is `src/profiles/mcp/index.ts:22`, which imports `causeCategoryFrom` alone. No SDK operation constructs, throws, or returns an AuthsError. What a caller actually receives instead: `src/product.ts:75-78` `export interface Indeterminate { readonly kind: "indeterminate"; readonly code: string; }` — two fields, no effect, no retry, no recommendedAction, no execution reference; and `src/product.ts:123-127` `RecoveryResult { kind: "recoverable" | "not-applied" | "exact-replay" | "conflict"; executionId; reference? }`. `src/product.ts:385-387` `if (value.kind === "denied" || value.kind === "indeterminate") return Object.freeze({ kind: value.kind, code: value.code });` discards every other field. The registry entry the design exists for, `core.outcome-unknown` (`product/errors/v1/registry.json`: retry `unknown`, effect `possible`, recommendedAction `resume-and-reconcile`, allowsExecutionReference `true`), reaches the caller as the bare word `"recoverable"`. +- **Disposition:** Make effect a first-class field on every result the caller can observe. Add `readonly effect: EffectState` and `readonly recommendedAction: RecommendedAction` to `src/product.ts:70-78` (`Denied`, `Indeterminate`) and `:123-127` (`RecoveryResult`), sourced from the registry entry for the returned `code` rather than from the union tag. Collapse `RecoveryResult.kind`'s four-valued mixture of effect state and replay disposition into `{ effect, replay }`. Either wire `AuthsError.parse` into the actual failure paths in `src/profiles/mcp/index.ts` and `src/production-client.ts`, or delete `src/product-errors.ts` and its root exports at `src/index.ts:10-14` — a root-exported error class no code path can produce is worse than none, because it advertises a guarantee the SDK does not deliver. Export `EffectState`, `RetryClass`, `RecommendedAction`, and `AuthsErrorDetails` from the root and add them to `api/public-api.txt`. +- **Rationale:** 'effect: possible' is the single value that tells a caller a real-world side effect may have occurred and must be reconciled rather than retried. The registry is designed around it; TypeScript cannot express it. A caller who receives `{kind:"recoverable"}` and reads the word 'recoverable' will retry, because nothing in the type says an effect may already have happened. This is the exact failure the effect axis exists to prevent, and it damages SECURE at the API boundary rather than in the kernel. + +### `ts-production-client-fabricates-registry-codes` — production-client synthesizes registry error codes whose registered outcome is not-applied for genuinely unknown outcomes +- **Lane:** cross · **Goal:** secure · **Area:** typescript +- **Evidence:** `src/production-client.ts:269-286`: `try { response = await this.#transport.send(...) } catch { return Object.freeze({ contractVersion: 1, kind: "indeterminate", code: "core.runtime-unavailable", retry: "backoff", recoveryReference: null, value: null, receipt: null }); }`. `product/errors/v1/registry.json` registers `core.runtime-unavailable` with a single outcome `(retry: safe, effect: not-applied)` and recommendedAction `retry-execution`. The catch fires on the AbortController timeout at `:312` `const timeout = setTimeout(() => controller.abort(), request.timeoutMs)` — i.e. after the POST body has been transmitted, when the server may have fully applied the effect. `src/production-client.ts:287-297`: any non-2xx status or content-type mismatch returns `code: "core.malformed-input", retry: "backoff"`; the registry registers `core.malformed-input` as operation `verify`, outcome `(retry: never, effect: not-applied)`, recommendedAction `correct-input`. A 500 or 504 returned from `/v1/profiles/postgresql/bounded-update/execute` after the row was updated is projected as 'nothing applied, back off and retry'. Both objects are hand-built `NativeProjection` literals that bypass the registry validator entirely — `src/product-errors.ts:176` `if (!definition.outcomes.some((outcome) => outcome.retry === retry && outcome.effect === effect)) throw new TypeError("Auths error recovery classification is not registered")` would reject `core.malformed-input` with `retry: backoff`, but nothing on this path calls it. `src/production-client.ts:382-392` `parseProjection` validates only `contractVersion`, `kind`, and `retry`; `code` is never checked against the registry. +- **Disposition:** Delete both fabricated projections at `src/production-client.ts:276-286` and `:287-297`. A transport exception or a non-2xx response after `execute`/`resume` is an unknown outcome: return the registered unknown-outcome shape (`core.outcome-unknown`, retry `unknown`, effect `possible`, recommendedAction `resume-and-reconcile`) carrying the recovery reference required to reconcile, and reserve `core.runtime-unavailable` for pre-transmission failures the client can actually prove did not reach the server (e.g. endpoint parse failure, encode failure). Route every synthesized failure through `AuthsError.parse` so the registry validator at `src/product-errors.ts:176-199` is the single gate — that check already refuses `effect: "possible"` without an execution reference and an entered provider boundary. +- **Rationale:** The client asserts a registered code whose sole registered outcome is `not-applied` for the one case where the effect state is genuinely unknown, and it does so on the remote path where the effect is a Postgres commit or an OpenTofu apply. It is a fail-open classification produced by the SDK itself, not by the server, and the existing registry validator that would catch it is bypassed. This directly violates 'everything fails closed'. + +### `ts-dual-root-sdk` — The root entry point publishes two complete unrelated SDKs; 19 of 41 root symbols are a Production* mirror of the other 14 +- **Lane:** cross · **Goal:** simple · **Area:** typescript +- **Evidence:** `api/public-api.txt:3-43` enumerates 41 root symbols. Local SDK (14): `Actor, approval, ApprovalPolicy, Authority, Auths, AuthsConfiguration, Completed, createAuths, Denied, ExecutionReference, ExecutionResult, Indeterminate, Receipt, RecoveryResult`. Remote SDK (19): `ProductionAuthority, ProductionAuthorityResult, ProductionAuths, ProductionAuthsOptions, ProductionCompleted, ProductionDenied, ProductionExecutionResult, ProductionIndeterminate, ProductionReceipt, ProductionRecoverable, ProductionRecoveryReference, ProductionRejected, ProductionTransport, ProductionTransportRequest, ProductionTransportResponse, ProductionVerificationResult, ProductionVerified, ProductStep, RetryClass`. Nine concept pairs are named twice from one import site: `Authority`/`ProductionAuthority` (`src/product.ts:40`, `src/production-client.ts:39`), `Receipt`/`ProductionReceipt` (`:47`, `:57`), `Completed`/`ProductionCompleted` (`:49`, `:115`), `Denied`/`ProductionDenied` (`:70`, `:93`), `Indeterminate`/`ProductionIndeterminate` (`:75`, `:100`), `RecoveryResult`/`ProductionRecoverable` (`:123`, `:107`), `ExecutionResult`/`ProductionExecutionResult` (`:129`, `:136`), `Auths`/`ProductionAuths` (`:133`, `:139`). The two interfaces share zero methods: `Auths` is `execute({action,provider}) / resume / recover / delegate / close` over `McpAction` (`src/product.ts:137-163`); `ProductionAuths` is `create / delegate / execute / resume / verify` over raw `Uint8Array` (`src/production-client.ts:140-149`). Their `code` fields draw from disjoint code spaces: `Denied.code` carries verifier tokens (`test/integration/verifier.test.js:105` asserts `"verifier-configuration-mismatch"`), `ProductionDenied.code` carries dotted registry codes (`core.*`, `mcp.*`). +- **Disposition:** Split the two into separate subpaths and delete the `Production` prefix. Move the remote client to a dedicated `@auths-dev/sdk/service` entry (add to `bindings/public-topology-v1.json:3-28`, which `tools/public-api.mjs:14-21` enforces), rename its types off the prefix (`ServiceAuthority`, `ServiceReceipt`, …), and remove all 19 symbols from `src/index.ts:29-49`. Delete `src/product.ts:25-29` (the import of `production-client`) and `:354`/`:358` so the local product facade has no edge to the remote client at all. Unify the two `code` spaces on the registry before splitting, or the split just relocates the ambiguity. +- **Rationale:** 'One way to do each thing' is violated at the most visible point in the package. A caller doing `import {...} from "@auths-dev/sdk"` sees two spellings of authority, receipt, completed, denied, indeterminate, and recovery, with no type-level signal which one their code path produces, and identically named `code` fields holding values from two disjoint vocabularies. + +### `ts-retry-effect-vocabulary-fork` — Four RetryClass definitions and four effect vocabularies; workflow/errors.ts derives them from string prefixes of the code +- **Lane:** cross · **Goal:** simple · **Area:** typescript +- **Evidence:** RetryClass, four definitions: `src/product-errors.ts:7` registry-derived (`never|safe|conditional|unknown`); `src/workflow/errors.ts:47` `export type RetryClass = "never" | "safe" | "conditional" | "unknown";` (hand-written duplicate of the same four); `src/production-client.ts:12` `export type RetryClass = "never" | "backoff" | "resume" | "reconcile";` (disjoint — none of backoff/resume/reconcile appear in `product/errors/v1/registry.json`); `src/verifier/result.ts:19-23` `Explanation { readonly retryable: boolean }` (a lossy boolean). `src/index.ts:48` root-exports the `production-client` one, so the root `RetryClass` is the wrong type for `AuthsError.retry` (`src/product-errors.ts:76`) — the two are simultaneously root-exported and mutually unassignable. Effect, four vocabularies: registry `applied|not-applied|possible`; `src/product-errors.ts:8` adds `unknown`; `src/workflow/errors.ts:48` `export type EffectState = "none" | "possible" | "occurred";` (renames both terminal values); `src/profiles/mcp/index.ts:166` `readonly effect: "not-applied" | "applied" | "unknown";` on `McpGatewayError` — drops `possible` entirely; `src/profiles/mcp/index.ts:158` `readonly outcome: "executed" | "failed" | "outcome-unknown";` on `McpReceipt` — a fifth spelling. ErrorFamily, two taxonomies: registry families are `configuration|input|internal|profile|provider|runtime|state`; `src/workflow/errors.ts:39-45` declares `configuration|authority|approval|custody|provider|transaction` — only two overlap. The workflow taxonomy is computed in TypeScript from the code string: `src/workflow/errors.ts:118-126` `if (code.startsWith("approval-")) return "approval"; if (code.startsWith("signer-")) return "custody"; ... if (code.endsWith("-failed") || code === "invalid-provider") return "provider";` and `:128-132` `if (code.endsWith("-timeout") || code.endsWith("-cancelled")) return "conditional";`, and effect at `:112` `this.effect = context.effect ?? (kind === "timeout" || kind === "cancelled" ? "possible" : "none")`. +- **Disposition:** Delete `src/workflow/errors.ts:39-48` and `:118-138` outright and import `ErrorFamily`, `RetryClass`, `EffectState` from `src/product-errors.ts` (registry-derived). Delete `src/production-client.ts:12` and map the Rust `auths-production-client` retry enum onto the registry vocabulary in Rust, not in TypeScript — `product/runtime/auths-production-client/src/lib.rs:417-421` currently owns a second retry enum, which is the upstream cause and must be resolved in the rust lane. Add `possible` to `src/profiles/mcp/index.ts:166` and delete the `unknown` member. Replace `src/profiles/mcp/index.ts:158`'s `outcome` with the registry `effect` triple. Replace `Explanation.retryable: boolean` (`src/verifier/result.ts:21`) with the four-valued `RetryClass`. +- **Rationale:** Two names for one concept anywhere is a v1.0 blocker; there are four here for retry and four for effect. Worse, `workflow/errors.ts:118-138` does not project a Rust-owned classification — it re-derives family, retry, and effect from lexical suffixes of the code string. That is TypeScript defining the meaning of a failure. `McpGatewayError` dropping `possible` means a gateway that times out is structurally unable to report the one state that matters. `sdk-capability.json:45` cites `src/workflow/errors.ts` as the evidence for `typeSafety: verified`, so the fork is currently recorded as a passing claim. + +### `ts-receipt-and-development-name-collisions` — Two structurally identical receipt types published as `Receipt` at two subpaths, and two different `development` values at two subpaths +- **Lane:** cross · **Goal:** simple · **Area:** typescript +- **Evidence:** `src/internal/receipt-attestation.ts:10-13` `export interface LinkedAttestedReceipt { readonly decision: AttestedApplicationReceipt; readonly execution: AttestedApplicationReceipt; }`. `src/profiles/mcp/index.ts:258-261` `export interface McpAttestedReceipt { readonly decision: AttestedApplicationReceipt; readonly execution: AttestedApplicationReceipt; }` — byte-for-byte the same shape. Both are published under the name `Receipt`: `src/product.ts:47` `export type Receipt = McpAttestedReceipt;` reaching the root via `src/index.ts:26`, and `src/verify.ts:26` `type LinkedAttestedReceipt as Receipt` reaching `./verify`. `api/public-api.txt:40` (`. Receipt`) and `:91` (`./verify Receipt`) confirm both ship. `./profiles` additionally exports `McpAttestedReceipt` (`api/public-api.txt:120`) and a third unrelated `McpReceipt` (`:139`, defined at `src/profiles/mcp/index.ts:155-159`). Separately, `api/public-api.txt:152` `./integrations development` is `src/integrations.ts:115-134` `{createAuths, createRecoverableAuths}`, while `api/public-api.txt:187` `./testkit development` is `src/testkit/index.ts:154-176` `{ephemeralSigner, receiptAttestor, approve, reject, approval}` — same identifier, two public subpaths, disjoint shapes. `AtomicReservationRecord` is likewise exported from both `./framework` (`api/public-api.txt:157`) and `./testkit` (`:167`). +- **Disposition:** Delete `McpAttestedReceipt` (`src/profiles/mcp/index.ts:258-261`) and have the MCP profile import `LinkedAttestedReceipt`; keep exactly one name for the decision+execution receipt pair and export it under that one name from both `.` and `./verify` (no `as Receipt` aliasing at `src/verify.ts:26`, no `type Receipt =` at `src/product.ts:47`). Rename `./testkit`'s `development` to `fixtures` (`src/testkit/index.ts:154`) so the two subpaths cannot collide in a single import scope. Remove the duplicate `AtomicReservationRecord` re-export at `src/testkit/index.ts:50` and have testkit consumers import it from `./framework`. +- **Rationale:** A caller who imports `Receipt` from the root and `Receipt` from `/verify` in the same module gets a name conflict on two types that are the same thing, and a caller who imports `development` from `/integrations` and `/testkit` gets a name conflict on two things that are not. The receipt case is the worse of the two because the duplicate definitions can drift silently — nothing structurally ties `McpAttestedReceipt` to `LinkedAttestedReceipt`. + +### `budget-ceiling-inert-when-action-requests-none` — A grant's budget ceiling is unenforceable whenever the action carries no requested budget — which every money-moving Stripe profile and the shipped MCP profile mandate +- **Lane:** cross · **Goal:** secure · **Area:** security +- **Evidence:** core/crates/auths-model/src/lib.rs:929-937 — `pub fn optional_budget_covers(ceiling, requested) { match (ceiling, requested) { (_, None) | (None, Some(_)) => true, (Some(c), Some(r)) => c.covers(r) } }`. The arm `(_, None) => true` means a bounded ceiling is satisfied by an action that requests nothing. +core/crates/auths-authority/src/lib.rs:304 — terminal coverage calls exactly that: `if !optional_budget_covers(authority.budget_ceiling, action.requested_budget) { return CoverageDecision::Denied(BudgetCeilingExceeded); }`. +core/crates/auths-verifier/src/lib.rs:2543 — the registry-backed algebra check is also skipped: `if let (Some(ceiling), Some(requested)) = (parent, action.envelope().requested_budget())`. +product/integrations/auths-stripe/src/treasury/payout/profile.rs:110 passes `None` as the requested budget, and :135 rejects any action that carries one: `|| canonical.requested_budget().is_some()`. Identical rejections at product/integrations/auths-stripe/src/connect/transfer/profile.rs:130, mandate/profile.rs:125, subscription/create/profile.rs:127, subscription/modify/profile.rs:130, issuing/purchase_authorization/profile.rs:127, merchant/cancel/profile.rs:126. +product/profiles/auths-profile-mcp/src/lib.rs:348 passes `None`, and :368 rejects `action.requested_budget().is_some()`. +product/stores/auths-stores/src/lib.rs:76-79 and :243-246 — both stateful ledgers short-circuit: `let Some(requested) = requested else { return BudgetClaim::Claimed; };`. +product/runtime/auths-runtime/src/lib.rs:863-866 feeds them `verified.canonical_action().requested_budget()`. +Contrast product/integrations/auths-stripe/src/merchant/collect/profile.rs:103-108, which does bind the money: `Some(BudgetCeiling::new(BudgetAlgebraId::parse(PAYMENT_BUDGET_ALGEBRA)?, action.amount_minor()))`. +- **Disposition:** Change `optional_budget_covers` in core/crates/auths-model/src/lib.rs:929-937 so the `(Some(_), None)` case returns `false` (a bounded authority requires a bounded request; only `(None, _)` is permissive), and make core/crates/auths-verifier/src/lib.rs:2543 deny with `BudgetCeilingExceeded` when `parent` is `Some` and the envelope's requested budget is `None`. Then delete the `requested_budget().is_some()` rejections in the seven money-moving profiles and have each derive an explicit `BudgetCeiling` from its own amount field, exactly as merchant/collect already does. Make `BudgetLedger::claim` take a non-optional ceiling so product/stores/auths-stores/src/lib.rs:76-79 and :243-246 cannot return `Claimed` for an unbudgeted action. +- **Rationale:** Budget is one of the six advertised attenuation axes and the only one that can express 'how much'. Making it opt-in by the party being constrained means a delegation cannot actually narrow along that axis for the verticals where narrowing matters most. Worse, this fails silently and passes every existing test: the pure verifier returns Authorized, the ledger returns Claimed, and no invariant is visibly violated. Concrete attack: an operator issues a grant with `BudgetCeiling(numeric-ceiling-v1, 10_000)` (intended: $100), `ActionConstraint::AnyBody`, permission `stripe.payout/create` on `stripe-payout://acct_X/destinations/ba_Y`. The agent submits `StripeExactPayoutV1 { amount_minor: 100_000_000 }`. The profile canonicalizes it with `requested_budget = None`; `optional_budget_covers(Some(10_000), None)` returns true; `validate_budget_constraints` skips the algebra entirely; `InMemoryBudgetLedger::claim` returns `Claimed`. A $1,000,000 payout executes under a $100 ceiling. The same reasoning makes the entire budget subsystem of auths-runtime and auths-stores dead code for the reference MCP profile. + +### `effect-state-five-vocabularies` — EffectState — the effect-occurred claim — has five incompatible value sets across three languages +- **Lane:** security · **Goal:** secure · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** Rust OWNER, closed, three variants: product/errors/auths-errors/src/lib.rs:38-44 `pub enum EffectState { NotApplied, Possible, Applied }` (serde kebab-case -> "not-applied"|"possible"|"applied"). The registry corpus confirms only those three: product/errors/v1/registry.json yields effects {applied, not-applied, possible} over all 45 definitions. `ErrorEnvelope` is `deny_unknown_fields` (lib.rs:127-145), so any other value fails to decode. + +Projections: +| lang | symbol | file:line | value set | +| Rust | `auths_errors::EffectState` | product/errors/auths-errors/src/lib.rs:38 | not-applied, possible, applied | +| TS | `EffectState` (product) | bindings/typescript/src/product-errors.ts:8 | Rust's 3 + `"unknown"` | +| TS | `EffectState` (workflow) | bindings/typescript/src/workflow/errors.ts:48 | `"none" | "possible" | "occurred"` | +| PY | `EffectState` (product) | bindings/python/python/auths/_product_errors.py:27-31 | NOT_APPLIED, POSSIBLE, APPLIED, UNKNOWN | +| PY | `EffectState` (workflow) | bindings/python/python/auths/_errors.py:9-11 | not-started, in-progress, completed, failed, outcome-unknown | + +The `unknown` fallbacks are constructed, not merely typed: bindings/typescript/src/product-errors.ts:248 emits `effect: "unknown"`; bindings/python/python/auths/_product_errors.py:280-296 `_unknown_details` emits `EffectState.UNKNOWN`. Field spelling also diverges: `effect` in TS both places and in Python `_product_errors.py:74`, but `effect_state` in Python `_errors.py:24`. +- **Disposition:** Freeze one enum: Rust `auths_errors::EffectState { NotApplied, Possible, Applied }` stays the sole owner and does NOT gain `Unknown`. Delete `bindings/typescript/src/workflow/errors.ts:48` and `bindings/python/python/auths/_errors.py:9-11` outright. TS spells it `EffectState` with exactly `"not-applied" | "possible" | "applied"`; Python spells it `EffectState` as the same three-member enum. Field name is `effect` (TS) / `effect` (Python) everywhere — delete `effect_state`. Unrecognized codes must map to `effect: "possible"` (fail closed), never to a fourth value; if a distinct 'we could not classify' signal is genuinely needed, Rust must add it first and the fixtures must move with it. +- **Rationale:** This is the spine of safe API design and it has no single meaning. `"none"`, `"not-applied"`, and `"not-started"` are three different claims: "the effect definitely did not happen", versus "we never began". `"failed"` and `"in-progress"` are not effect claims at all — they conflate operation status with effect status, which is precisely the conflation that makes a caller retry a possibly-applied action. Any envelope a binding produces with effect="unknown" cannot round-trip back into Rust's deny_unknown_fields decoder, so the fail-closed path is unreachable from the binding side. Three implementation agents reading three different files will each pick a different set. + +### `auths-error-homonym-python` — Python ships two unrelated classes named AuthsError; `except auths.AuthsError` misses every workflow failure +- **Lane:** python · **Goal:** secure · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** bindings/python/python/auths/_errors.py:29 `class AuthsError(Exception)` — base of `AuthsWorkflowError` (_errors.py:52), `ProviderOperationError` (_errors.py:82), `RuntimeStateError` (_errors.py:110-ish); attributes `.family/.code/.operation/.stage/.correlation_id/.retry/.effect_state/.remediation/.cause_codes`. +bindings/python/python/auths/_product_errors.py:110 `class AuthsError(Exception)` — unrelated; attributes `.details: AuthsErrorDetails`, classmethod `.parse()`. +The package root binds the SECOND one: bindings/python/python/auths/__init__.py:92 `"AuthsError": "._product_errors"`. The FIRST one is simultaneously re-exported through the workflow surface: bindings/python/python/auths/_workflow.py:59 imports it and _workflow.py:1759 lists `"AuthsError"` in `__all__`. +TypeScript has the same two hierarchies but at least under distinct names: bindings/typescript/src/product-errors.ts:60 `class AuthsError extends Error` vs bindings/typescript/src/workflow/errors.ts:60 `class AuthsWorkflowError extends Error` (neither extends the other). +- **Disposition:** One error base per language. Keep the registry-backed type as the single `AuthsError` (Python `_product_errors.AuthsError`, TS `product-errors.ts` `AuthsError`). Delete `bindings/python/python/auths/_errors.py` entirely and re-parent `AuthsWorkflowError`, `ProviderOperationError`, `RuntimeStateError` onto the registry-backed `AuthsError`, populating `AuthsErrorDetails` from the registry code. In TypeScript make `AuthsWorkflowError extends AuthsError` for the same reason. Remove `"AuthsError"` from `_workflow.py:1759`. +- **Rationale:** A caller who writes `try: await auths.execute(...) except AuthsError:` using the documented root import catches nothing raised by the development/MCP path, because those raise `_errors.AuthsError` subclasses. The exception escapes as an unhandled error at exactly the moment an effect may be in flight. Two classes with one name in one installable package is a duplicate vocabulary in the strongest possible sense. + +### `provider-timeout-retry-fail-open-python` — Python classifies a provider timeout as retry=safe / effect=not-started; TypeScript classifies it conditional / possible +- **Lane:** security · **Goal:** secure · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** bindings/python/python/auths/_errors.py:92 `retry: RetryClass = "safe" if kind in ("unavailable", "timeout") else "never"`, and _errors.py:102 passes the literal `"not-started"` as the effect state for EVERY `ProviderFailureKind`, including `timeout` and `cancelled`. +bindings/typescript/src/workflow/errors.ts:134-138 `providerRetry`: `unavailable -> "safe"`, `timeout | cancelled -> "conditional"`, else `"never"`; and workflow/errors.ts:112 `effect: context.effect ?? (kind === "timeout" || kind === "cancelled" ? "possible" : "none")`. +Rust's own rule agrees with TypeScript, not Python: product/errors/auths-errors/src/lib.rs:317 `if input.retry == RetryClass::Safe && input.effect != EffectState::NotApplied { ... }` — `Safe` is only legal when the effect provably did not apply. Rust's provider taxonomy makes the same call: product/runtime/auths-lifecycle/src/model.rs:55-64 `ProviderRetryClass::ObserveBeforeRetry` ("Reconciliation must prove non-effect before retry"). +Signature shape also diverges: TS `constructor(kind: ProviderFailureKind, context: ErrorContext = {})` (workflow/errors.ts:104) vs Python `def __init__(self, kind: ProviderFailureKind)` (_errors.py:83) — Python cannot carry an effect override at all. +- **Disposition:** Delete Python's local mapping. Derive both bindings' provider classification from one Rust-owned table keyed by `ProviderFailureKind`: `unavailable -> (safe, not-applied)`, `rejected|unsupported -> (never, not-applied)`, `timeout|cancelled -> (conditional, possible)`. Give Python `ProviderOperationError.__init__(self, kind, *, context: ErrorContext | None = None)` to match TS's second parameter, and assert the mapping in the shared conformance corpus. +- **Rationale:** A provider timeout is the canonical possibly-applied case: the request left the process and no response came back. Python tells the caller the effect never started and that retrying is safe. That is a fail-open classification of the exact condition Auths exists to make safe, and it is reached without any adversarial input — an ordinary network timeout produces it. Two languages giving opposite retry advice for identical wire conditions also breaks the spec's 'retry classes match Rust exactly' cross-language test. + +### `retry-class-three-vocabularies` — RetryClass names three different closed sets; both bindings export two of them under the same identifier +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** Rust has three: +| owner | file:line | values | +| `auths_errors::RetryClass` | product/errors/auths-errors/src/lib.rs:29-36 | never, safe, conditional, unknown | +| `auths_production_client::RetryClass` | product/runtime/auths-production-client/src/lib.rs:415-422 | never, backoff, resume, reconcile | +| `auths_lifecycle::ProviderRetryClass` | product/runtime/auths-lifecycle/src/model.rs:55-64 | exact-idempotent, conditional, observe-before-retry, non-retryable | + +Bindings collapse the first two onto one identifier: +| lang | symbol | file:line | which set | +| TS | `RetryClass` | bindings/typescript/src/production-client.ts:12 | never/backoff/resume/reconcile — this is the one re-exported at the package root, bindings/typescript/src/index.ts:48 | +| TS | `RetryClass` | bindings/typescript/src/product-errors.ts:7 | never/safe/conditional/unknown | +| TS | `RetryClass` | bindings/typescript/src/workflow/errors.ts:47 | never/safe/conditional/unknown, re-exported again via workflow-client.ts | +| PY | `RetryClass` | bindings/python/python/auths/_production_client.py:31 | Literal never/backoff/resume/reconcile — root-exported, __init__.py:122 | +| PY | `RetryClass` | bindings/python/python/auths/_product_errors.py:20-24 | str Enum NEVER/SAFE/CONDITIONAL/UNKNOWN | +| PY | `RetryClass` | bindings/python/python/auths/_errors.py:8 | Literal never/safe/conditional/unknown | + +The two sets are not even the same kind of statement: {never,safe,conditional,unknown} answers 'may I retry', {never,backoff,resume,reconcile} answers 'what should I call next'. +- **Disposition:** Split by question and freeze both names. Keep `auths_errors::RetryClass` as `RetryClass` (never|safe|conditional|unknown) — the retry-permission answer. Rename `auths_production_client::RetryClass` to `NextCall` in Rust, `NextCall` in TS, `NextCall` in Python (values never|backoff|resume|reconcile). Delete bindings/typescript/src/workflow/errors.ts:47 and bindings/python/python/auths/_errors.py:8 and import the single definition. `ProviderRetryClass` stays Rust-reference-tier and is not projected. +- **Rationale:** One identifier, two answers to two different questions, in the same installable package, both reachable from the documented import path. A caller who narrows on `RetryClass` gets whichever one their import happened to reach. Python compounds it by making one a Literal and one an Enum, so `retry == RetryClass.NEVER` and `retry == "never"` are both live idioms. + +### `recover-sixth-operation-invented-by-bindings` — `recover` is a sixth product operation invented identically by both bindings with no Rust owner +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** Rust's closed verb set is exactly five: product/runtime/auths-production-client/src/lib.rs:130-136 `pub enum ProductVerb { Create, Delegate, Execute, Resume, Verify }`; the node routes exactly those (product/runtime/auths-node/src/api.rs:190,194,198,210,247). The Rust MCP session exposes `begin`, `begin_plan_member`, `resume`, `next_step`, `accept_*`, `checkpoint`, `terminal` (product/profiles/auths-profile-mcp/src/session.rs:313,345,440,589,650,674,706,721) — there is no `recover`. + +Both bindings add one anyway, on the public product facade: +| lang | symbol | file:line | +| Rust | (none) | — | +| TS | `Auths.recover(input: { action, provider, requestId? }): Promise` | bindings/typescript/src/product.ts:151-155 (impl :254), backed by `recoverMcpClosed` at bindings/typescript/src/profiles/mcp/index.ts:699 | +| PY | `Auths.recover(*, action, provider, request_id=None) -> ExecutionResult` | bindings/python/python/auths/_product.py:257-263, backed by `recover_mcp_closed` at bindings/python/python/auths/profiles/_mcp.py:1073 | + +The implementation is a binding-owned composition: re-authorize the action, call `beginMcpExecutionV1` purely to re-derive the execution id, throw the fresh session away (mcp/index.ts:732-733 `const executionId = fresh.executionId; fresh.free?.();`), `loadPending(executionId)`, then `resumeMcpExecutionV1`. The decision of what identity to recover under lives in TypeScript and Python, not in Rust. +- **Disposition:** Move the composition into Rust as `McpExecutionSession::recover(...)` (or fold it into `resume` taking either a reference or a canonical action) and expose it through the existing WASM/native ABI. If it is genuinely a sixth product operation, add `ProductVerb::Recover` and a registry `operation: "recover"` in the same change. Until then delete `Auths.recover` from bindings/typescript/src/product.ts:151 and bindings/python/python/auths/_product.py:257 and delete `recoverMcpClosed`/`recover_mcp_closed`. +- **Rationale:** The rule is that semantics are owned by Rust and projected. Here the re-derivation of an execution identity from an action, and the choice to treat a fresh authorization as addressing a pending durable checkpoint, is meaning — it decides which in-flight effect a caller re-attaches to. It is defined twice, in two languages, and if they ever drift the two SDKs will re-attach to different executions. It also breaks the five-verb story the whole product surface is built on. + +### `op-vocabulary-three-way-split` — The operation identifier has three vocabularies: ProductVerb, ProductStep, and an untyped registry `operation` that has `sign` but no `delegate` +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | symbol | file:line | values | +| Rust | `ProductVerb` | product/runtime/auths-production-client/src/lib.rs:130-136 | Create, Delegate, Execute, Resume, Verify | +| Rust | `ErrorDefinition.operation: &'static str` | product/errors/auths-errors/src/lib.rs:86 | UNTYPED string; corpus over product/errors/v1/registry.json yields {create, execute, resume, sign, verify} | +| TS | `ProductStep` | bindings/typescript/src/production-client.ts:11 | "create"|"delegate"|"execute"|"resume"|"verify" | +| PY | `ProductStep` | bindings/python/python/auths/_production_client.py:30 | same five | + +The registry set is neither a subset nor a superset: it lacks `delegate` and adds `sign`. Yet `ProductionDenied.step: ProductStep` (bindings/typescript/src/production-client.ts:95, bindings/python/python/auths/_production_client.py:98) can carry `step: "delegate"` alongside a `code` drawn from a registry in which no definition declares `operation: "delegate"`. The node maps delegate onto a policy stage anyway: product/runtime/auths-node/src/api.rs:313 `ProductVerb::Create | ProductVerb::Delegate => OperationalStage::Policy`. +A further spelling exists on the wire encoder: production-client.ts:154 sends the field as `verb`, while the surrounding type calls it `step`. +- **Disposition:** One name, one set. Rust keeps `ProductVerb` but the enum becomes the type of `ErrorDefinition.operation` (replacing `&'static str`), and gains whatever the registry legitimately needs — add `Delegate` definitions and decide whether `sign` is a sixth verb or a stage of `create`/`delegate`. Bindings spell it `ProductVerb` (TS type `ProductVerb`, Python `ProductVerb`), delete `ProductStep`, and the wire field is `verb` in all three. Regenerate the registry snapshot in the same change. +- **Rationale:** 'What operation was this' is the primary key for every error, telemetry event and receipt. Three sets means the code<->step pair can never be cross-validated, and the spec's required 'known error code sets match Rust exactly' test cannot be written for `delegate` at all. `sign` in the registry but not in the verb enum means custody failures are attributed to an operation the product surface cannot name. + +### `createauths-overload-and-root-asymmetry` — createAuths is an endpoint-sniffing overload in TypeScript and a production-only function in Python; the two roots are not the same product +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | symbol | file:line | behavior | +| TS | `createAuths(configuration: AuthsConfiguration): Promise` / `createAuths(configuration: ProductionAuthsOptions): ProductionAuths` | bindings/typescript/src/product.ts:353-362 | overloaded; dispatches with `if ("endpoint" in configuration) return createProductionAuths(configuration);` (product.ts:358). One overload returns a Promise, the other returns a client synchronously. | +| PY | `create_auths(*, endpoint, identity, profile, transport=None, timeout_seconds=15.0) -> ProductionAuths` | bindings/python/python/auths/_production_client.py:306-322, root-bound at bindings/python/python/auths/__init__.py:123 | production ONLY; the local path is private `_create_auths` (bindings/python/python/auths/_product.py:391) reachable only through `integrations.development` | + +The development entry points also disagree in return type: +| TS | `development.createAuths(options): Promise` | bindings/typescript/src/integrations.ts:116 | +| PY | `development.create_auths(...) -> _PendingAuths` | bindings/python/python/auths/integrations.py:329-347 — an awaitable-AND-async-context-manager hybrid defined at integrations.py:302 | +TS therefore requires `await using auths = await development.createAuths(...)`; Python requires `async with development.create_auths(...) as auths`. Neither idiom exists in the other language. +TS also exports `ProductionAuthsOptions` (index.ts:35) as the config type; Python has no such type — the options are bare keyword arguments. +- **Disposition:** Root `createAuths` / `create_auths` accepts ONLY the production options contract and returns one `Auths` client, in both languages — matching Python's current root and the spec. Delete the TS overload at product.ts:353-354 and the `"endpoint" in configuration` sniff at product.ts:358. Introduce `AuthsOptions` (TS) / `AuthsOptions` (Python dataclass) as the single named options type in both. Local composition stays only at `integrations.development.createAuths` / `.create_auths`, and both must return the same shape: pick the awaitable form (`Promise[Auths]` / coroutine returning `Auths`) and delete Python's `_PendingAuths` hybrid. +- **Rationale:** The overload is the exact anti-pattern the spec forbids (`docs/target-state/PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:333` and the 'no overloaded constructor that guesses' rule). Worse, the two languages already disagree about what the root product IS: in Python the root is the production client and the local MCP composition is private; in TypeScript the root is both, selected by duck-typing. A caller reading the TS README and the Python README learns two different products. + +### `auths-homonym-typescript` — TypeScript exports two unrelated types both named `Auths`: the product facade and the verifier engine +- **Lane:** typescript · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** bindings/typescript/src/product.ts:133 `export interface Auths` — the product facade with `execute/resume/recover/delegate/close`. +bindings/typescript/src/verifier/result.ts:122 `export class Auths` — the capability-minting WASM verifier with `verify()`/`verifyMany()`; re-exported through bindings/typescript/src/verifier/client.ts:5 and then aliased at bindings/typescript/src/verify.ts:3 as `Auths as Verifier`. +So `@auths-dev/sdk` gives you `Auths` = facade and `@auths-dev/sdk/verify` gives you `Verifier` which is really a class literally named `Auths` in its own source and in its emitted `.d.ts`. +Python has neither collision nor either concept in that form: bindings/python/python/auths/_product.py:191 `class Auths` is the facade, and there is NO verifier class at all — bindings/python/python/auths/verify.py:105 `def verify(...)` and verify.py:114 `def verify_many(...)` are free functions. +Rust's SDK names it plainly: product/sdk/auths-sdk/src/lib.rs:253 `pub struct Verifier`. +- **Disposition:** Rust already has the right name. Rename bindings/typescript/src/verifier/result.ts:122 `class Auths` to `class Verifier` and drop the alias at verify.ts:3. `Auths` means the product facade only, in all three languages. +- **Rationale:** The same identifier naming both the effect-capable product handle and the effect-free verification engine is the worst possible collision in this product, because the entire safety story is that those are different things with different capabilities. Type errors and stack traces will not distinguish them. + +### `op-verify-shape` — verify: a capability-minting class in TypeScript, free functions in Python, four entry points in Rust — and Python has no VerifiedAction +- **Lane:** cross · **Goal:** multiple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | symbol | file:line | signature | +| Rust | `auths_verifier::verify` | core/crates/auths-verifier/src/lib.rs:393 | `fn verify(...) -> VerificationOutcome` (also `verify_explained` :416, `verify_v1` :888, `verify_v1_sealed` :909, `verify_portable` :971) | +| Rust | `auths_sdk::Verifier::verify` | product/sdk/auths-sdk/src/lib.rs:299-305 | `fn verify(&self, proof: &[u8], canonical_action: &CanonicalAction, request: &RequestContext, profile: &P) -> Result, SdkError>` | +| Rust | product verb | product/runtime/auths-production-client/src/lib.rs:135 | `ProductVerb::Verify` | +| TS | `Auths.verify` (class) | bindings/typescript/src/verifier/result.ts:145-150 | `verify(proofCbor, canonicalActionCbor, trustedContextCbor, options: VerificationOptions = {}): VerificationResult` — SYNC on a class you can only obtain from `loadVerifier()` (verifier/client.ts:26) | +| TS | `Auths.verifyMany` | bindings/typescript/src/verifier/result.ts:172 | `async verifyMany(items, options): Promise` | +| TS | `ProductionAuths.verify` | bindings/typescript/src/production-client.ts:148 | `verify(value: ProductionAuthority | ProductionReceipt | Uint8Array): Promise` | +| PY | `verify` | bindings/python/python/auths/verify.py:105-109 | `def verify(proof_cbor, canonical_action_cbor, trusted_context_cbor) -> VerificationResult` — free function, no options, no minting | +| PY | `verify_many` | bindings/python/python/auths/verify.py:114 | `def verify_many(inputs) -> Tuple[...]` — SYNC (TS is async) | +| PY | `ProductionAuths.verify` | bindings/python/python/auths/_production_client.py:228 | same as TS | + +Divergences beyond shape: TS `VerifiedAction` is a sealed class (bindings/typescript/src/verifier/result.ts:35) carried on `AuthorizedResult.action` (:73). Python has NO `VerifiedAction` — bindings/python/python/auths/verify.py:132 explicitly reads `native.action`, raises if absent, and then THROWS IT AWAY; the returned `Authorized` (verify.py:66-74) has no action field. TS `VerificationResult` carries `correlationId` (verifier/result.ts:61); Python's does not. `VerificationInput` is an object in TS (result.ts:97-101) and a bare 3-tuple in Python (verify.py:102). Rust's result type is `VerifyResult` (auths-sdk/src/lib.rs:331) / `VerificationOutcome` (auths-verifier/src/lib.rs:53); the bindings call it `VerificationResult`. +- **Disposition:** One name `VerificationResult` and one shape in all three languages (rename Rust `VerifyResult`/`VerificationOutcome` to `VerificationResult` at product/sdk/auths-sdk/src/lib.rs:331). Python gains a sealed `VerifiedAction` class and `Authorized.action`, minted only from the native result. Python gains a `Verifier` class obtained from `load_verifier()` mirroring TS, so the capability-minting property is expressed in both. `verify_many` becomes async in Python or `verifyMany` becomes sync in TS — pick sync in both, since Rust is sync and the batch API adds no I/O. `VerificationInput` becomes a named record in Python (dataclass with `proof_cbor`, `canonical_action_cbor`, `trusted_context_cbor`). Python adds `correlation_id` and a `VerificationOptions` parameter. +- **Rationale:** Rust's whole reason for `VerifiedAction` is that a command may be derived only from sealed verifier output. TypeScript preserves that; Python discards it, so a Python caller has no type-level way to distinguish 'bytes the verifier authorized' from 'bytes I had lying around'. That is the central invariant of the product missing from one of two projections. Separately, the sync/async and class/function split means the same operation is written three unrecognizably different ways. + +### `execution-reference-two-formats` — The resume token has two names and two incompatible wire formats, and the profile-neutral one is MCP-namespaced +- **Lane:** cross · **Goal:** extensible · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | symbol | file:line | format | +| Rust | `auths_production_client::RecoveryReference` | product/runtime/auths-production-client/src/lib.rs:210, parse :219 | 32 bytes, non-zero, unpadded base64url | +| Rust | `auths_runtime::OpaqueRecoveryReference` | product/runtime/auths-runtime/src/production.rs:29, :37 `parse_url_token`, :48 `to_url_token` | 32 bytes | +| Rust | `McpExecutionReference` | product/profiles/auths-profile-mcp/src/session.rs:61 | profile-owned string | +| TS | `ExecutionReference` (product) | bindings/typescript/src/product.ts:80, validator :110-119 | exactly 134 bytes matching `/^mcp1\.[0-9a-f]{64}\.[0-9a-f]{64}$/` | +| TS | `ProductionRecoveryReference` (production) | bindings/typescript/src/production-client.ts:75, validator :446 | `/^[A-Za-z0-9_-]{43}$/` | +| PY | `ExecutionReference` | bindings/python/python/auths/_product.py:81, validator :112-125 | same 134-byte `mcp1.` form | +| PY | `ProductionRecoveryReference` | bindings/python/python/auths/_production_client.py:82, validator :528 | same 43-char base64url | + +The encode/decode method names also diverge across languages: TS `ExecutionReference.decode(input)` / `.encode()` (product.ts:91,95) vs Python `ExecutionReference.from_bytes(value)` / `.to_bytes()` (_product.py:98,102). +Both `Production*` classes and both `ExecutionReference` classes are exported from the package root (bindings/typescript/src/index.ts:23,31; bindings/python/python/auths/__init__.py:98,115). +- **Disposition:** One name `RecoveryReference` (matching Rust's `auths_production_client::RecoveryReference`) in both bindings, one format — the Rust-owned 32-byte base64url token — and the profile-specific `mcp1.` string becomes internal to the MCP profile, never surfaced. Delete `ExecutionReference` (product.ts:80, _product.py:81) and `ProductionRecoveryReference` as separate types. Method names: TS `RecoveryReference.decode(bytes)` / `.encode()`; Python `RecoveryReference.decode(value)` / `.encode()` — drop `from_bytes`/`to_bytes`. +- **Rationale:** A supposedly profile-neutral product type hard-codes the literal `mcp1.` prefix in its validator in both bindings. Adding a second vertical means either widening that regex in two languages or introducing a third reference type — that is the file-touch cost of a new profile made concrete. Meanwhile the root of each package exports two different opaque handles for 'the thing you pass to resume', with different names and different lengths, and a caller cannot tell from the type which resume accepts which. + +### `outcome-noun-parallel-production-prefix` — Outcome nouns exist twice per binding — bare and `Production`-prefixed — with different fields for the same decision +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** Rust has one outcome enum: product/runtime/auths-production-client/src/lib.rs:379-388 `ClientOutcomeKind { Completed, Denied, Indeterminate, Recoverable, Verified, Rejected }`. + +| concept | TS bare | TS Production | PY bare | PY Production | +| completed | `Completed` product.ts:49 (`kind, executionId, result, receipt`) | `ProductionCompleted` production-client.ts:115 (`kind, step, value?, receipt`) | `Completed` _product.py:61 | `ProductionCompleted` _production_client.py:120 | +| denied | `Denied` product.ts:70 (`kind, code`) | `ProductionDenied` production-client.ts:93 (`kind, step, code, retry`) | `Denied` _product.py:69 | `ProductionDenied` _production_client.py:95 | +| indeterminate | `Indeterminate` product.ts:75 | `ProductionIndeterminate` production-client.ts:100 | `Indeterminate` _product.py:75 | `ProductionIndeterminate` _production_client.py:103 | +| recoverable | `RecoveryResult` product.ts:123 (`kind` is a 4-way union incl. not-applied/exact-replay/conflict) | `ProductionRecoverable` production-client.ts:107 (`kind: "recoverable"` only) | `RecoveryResult` _product.py:128 | `ProductionRecoverable` _production_client.py:111 | +| verified/rejected | — | `ProductionVerified` :122 / `ProductionRejected` :128 | — | `_production_client.py:128/135` | + +Every one of these twelve TypeScript names and thirteen Python names is exported from the package root (bindings/typescript/src/index.ts:15-49; bindings/python/python/auths/__init__.py:50-86). The bare `Denied` carries no `step` and no `retry`; the prefixed one carries both. `RecoveryResult` folds four distinct outcomes (`recoverable`, `not-applied`, `exact-replay`, `conflict`) into one type while `ProductionRecoverable` names only one. +- **Disposition:** Drop the `Production` prefix per the spec and delete the bare set: root exports become `Completed`, `Denied`, `Indeterminate`, `Recoverable`, `Verified`, `Rejected`, `ExecutionResult`, `AuthorityResult`, `VerificationResult` — each carrying `kind`, `verb`, `code`, `next` (see retry-class finding). Fold `not-applied` / `exact-replay` / `conflict` into named members of the same union rather than a fourth type; they are outcomes, not a separate result category. +- **Rationale:** Two parallel result vocabularies at the root of both packages, for one Rust enum, is the single largest driver of surface-area bloat and the thing most likely to cause three implementation agents to write three different result models. The bare set additionally loses `step` and `retry`, so a caller handling the local product cannot answer 'what do I call next' without a second lookup. + +### `telemetry-event-divergence` — AuthsEvent has different fields in each binding; Python hardcodes correlationId and invents its own support-bundle schema +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | symbol | file:line | fields | +| Rust | `project_sdk_event_v2` | product/runtime/auths-production-client/src/lib.rs:83 | emits `{schemaVersion: "auths.telemetry/2", event}` | +| TS | `AuthsEvent` | bindings/typescript/src/observability.ts:18-28 | schemaVersion, name, timestamp, correlationId, operation, stage: TelemetryStage, outcome: TelemetryOutcome, durationMs?, attributes: Record | +| PY | `AuthsEvent` | bindings/python/python/auths/_observability.py:29-36 | name, operation, stage: str, outcome: str, observed_at, attributes: tuple-of-pairs — NO schemaVersion, NO correlationId, NO durationMs | + +Python does not merely omit correlationId, it fabricates one: bindings/python/python/auths/_observability.py:46 `"correlationId": "python-sdk"` for every event ever emitted. +Support bundle: +| TS | `createSupportBundle(input): Promise` | bindings/typescript/src/observability.ts:88, schema `"auths.support/1"` at :79/:103 | returns a structured object | +| PY | `support_bundle(events, *, runtime) -> bytes` | bindings/python/python/auths/_observability.py:82-109, schema `"auths.python-support-bundle/1"` at :95 | returns raw JSON bytes | +Python also uses `observedAt` where TS uses `timestamp` (_observability.py:103 vs observability.ts:21), and defines `DecisionTimeline` (_observability.py:67) with no TS counterpart. Port naming: TS `TelemetryPort` (observability.ts:30, `emit` may return a Promise) vs Python `Telemetry` (_observability.py:63, `emit` returns None). +- **Disposition:** One `AuthsEvent` shape in both bindings, matching the Rust projection exactly: `schemaVersion`, `name`, `timestamp` (delete `observed_at`), `correlationId` (required, caller-supplied — delete the `"python-sdk"` literal), `operation` typed as `ProductVerb`, `stage` typed as `Stage`, `outcome` typed as `TelemetryOutcome` (closed in Python too), `durationMs?`, `attributes`. One port name `TelemetryPort` in both. Delete `auths.python-support-bundle/1`; Python's `support_bundle` returns a `SupportBundle` record under `auths.support/1` and byte-comparison of the two languages' output becomes a CI test. Delete `DecisionTimeline` or add the identical type to TS. +- **Rationale:** The spec requires telemetry and support-bundle projections to be byte-equivalent across languages. They are not even field-equivalent, and Python declares a language-specific schema identifier — `auths.python-support-bundle/1` — which by construction can never match. A hardcoded correlation id destroys the ability to join a Python-side event to a server-side decision, which is the only thing a correlation id is for. + +### `approval-mode-headless-missing-python` — Python's ApprovalMode omits `headless` while its own runtime validator accepts it +- **Lane:** python · **Goal:** extensible · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** bindings/typescript/src/workflow/contracts.ts:18-25 `export type ApprovalMode = "none" | "grant-only" | "risk-based" | "every-action" | "plan-once" | "headless" | "custom"` (7). +bindings/python/python/auths/_workflow.py:68-70 `ApprovalMode = Literal["none", "grant-only", "risk-based", "every-action", "plan-once", "custom"]` (6 — `headless` absent). +Python's own validator contradicts its type: bindings/python/python/auths/_workflow.py:1488-1497 checks `approval.policy.mode not in ("none","grant-only","risk-based","every-action","plan-once","headless","custom")` — seven values. +Builder parity is also incomplete: TS `approvalPolicy` exposes `none/grantOnly/everyAction/riskBased/planOnce/headless/custom` (bindings/typescript/src/approvals.ts:73-95); Python `Approval` exposes `none/grant_only/risk_based/every_action/plan_once/...` as staticmethods (bindings/python/python/auths/_workflow.py:187-300) with no `headless` builder. +- **Disposition:** Add `"headless"` to bindings/python/python/auths/_workflow.py:68-70 and add `Approval.headless(...)`. Better: make `ApprovalMode` a generated projection of a Rust-owned enum (there is currently no Rust `ApprovalMode` enum at all — the mode is only a string fed to `commitApprovalPolicyV1`), so the list cannot be restated in three places. +- **Rationale:** `headless` is the mode an autonomous agent runs in — the product's headline use case. A Python user cannot express it through the typed surface even though the native layer accepts it, and mypy will reject the string. A type that disagrees with the validator sitting eight hundred lines below it in the same file is also a straightforward internal defect. + +### `disclosure-ports-sync-in-python` — Receipt-disclosure protector and store ports are async in TypeScript and synchronous in Python, so a KMS-backed protector is unimplementable in Python +- **Lane:** python · **Goal:** extensible · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | port | file:line | signature | +| TS | `ReceiptDisclosureProtector` | bindings/typescript/src/receipt-inspection.ts:82-85 | `protect(tenant, receiptId, plaintext): Promise` / `reveal(...): Promise` | +| TS | `ReceiptDisclosureStore` | bindings/typescript/src/receipt-inspection.ts:87-91 | `put/get/delete` all `Promise` | +| PY | `ReceiptDisclosureProtector` | bindings/python/python/auths/_receipts.py:156-159 | `def protect(self, tenant, receipt_id, plaintext) -> bytes` — SYNC | +| PY | `ReceiptDisclosureStore` | bindings/python/python/auths/_receipts.py:162-166 | `put/get/delete` all SYNC | +Same split on the operations: TS `createReceiptDisclosure(input): Promise` (receipt-inspection.ts:92-107, single object arg) vs Python `create_receipt_disclosure(receipt, *, profile_id, profile_version, command, result=None) -> bytes` (bindings/python/python/auths/_receipts.py:197-204, SYNC, positional+kw); TS `inspectReceipt(input): Promise` (receipt-inspection.ts:110-114) vs Python `inspect_receipt(receipt, *, mode="opaque", disclosure=None)` (_receipts.py:218-223, SYNC). +The same pattern repeats on `verifyReceipt` (async, product.ts:364) vs `verify_receipt` (sync, _product.py:397) and `inspectDecision` (async, bindings/typescript/src/inspection.ts:29) vs `inspect_decision` (sync, bindings/python/python/auths/_inspection.py:132) and `doctor` (async, bindings/typescript/src/doctor.ts:28) vs `doctor` (sync, bindings/python/python/auths/_doctor.py:28). +- **Disposition:** State the rule explicitly and apply it uniformly: every port a user implements is `async` in both languages; pure projections over already-loaded native state are sync in both. Concretely, make Python `ReceiptDisclosureProtector` and `ReceiptDisclosureStore` async (`async def protect/reveal/put/get/delete`). Make TypeScript's `inspectDecision`, `inspectReceipt`, `verifyReceipt`, `doctor` and `createReceiptDisclosure` sync by hoisting the one-time `loadPackagedWorkflowEngine()` into an explicit init, matching Python — these do no I/O and their async-ness is an artifact of lazy WASM loading, not of semantics. Adopt one argument convention (single options record) for all five in both languages. +- **Rationale:** These are user-implemented ports, not internal helpers. A disclosure protector wraps a KMS or HSM — inherently I/O-bound. TypeScript users can implement one; Python users must block the event loop inside an otherwise async SDK, which under an `asyncio` server means stalling every other in-flight execution. The blanket sync/async asymmetry also means no code sample, no documentation snippet and no conformance test transfers between the two languages. + +--- + +## MAJORS + +### `identity-authority-dead-public` — `auths-identity-authority` is published, frozen, and named a public root, with zero consumers anywhere in the 114-member workspace +- **Lane:** cross · **Goal:** simple · **Area:** rust-surface +- **Evidence:** Reverse-dependency scan across every Cargo.toml: `auths-identity-authority` appears only in the root workspace manifest (Cargo.toml:135 area, workspace.dependencies) — no crate, binding, demo, testkit, xtask, or fuzz target depends on it. Its entire public API is core/adapters/auths-identity-authority/src/lib.rs:20 `pub struct AuthorityIdentity`, :54 `pub trait PrincipalFromIdentity`, :68 `pub struct RawKeyV2AuthorityBridge`, :110 `pub enum IdentityPromotionError`. `RawKeyV2AuthorityBridge::promote` (line 71) is called only from its own `#[cfg(test)]` module (lines 167, 175). Despite this it is in xtask/src/semantic_freeze.rs:37 (PUBLIC_RUST_CLOSURE), xtask/src/semantic_freeze.rs:12 (PUBLIC_RUST_ROOTS — one of only ten declared roots), release/public-naming.toml:246 (release_order tier 1), and its README/Cargo.toml are byte-frozen at xtask/src/semantic_freeze.rs:~225. +- **Disposition:** Delete core/adapters/auths-identity-authority entirely, and remove it from Cargo.toml members, workspace.dependencies, PUBLIC_RUST_ROOTS, PUBLIC_RUST_CLOSURE (dropping the count to 41), release_order tier 1, and the `auths.modular-components` freeze entry. If the identity->authority promotion is a real product requirement, re-add it inside auths-sdk with an actual caller and an actual fixture. +- **Rationale:** Default for anything with no clear audience is NOT PUBLIC, and this crate has no audience at all — not even an internal one. Publishing it commits the project to a semver promise on a bridge between two vocabularies (auths-identity and auths-model) that nothing has ever exercised end to end, so the first real consumer will discover the contract is wrong after it is frozen. Declaring it one of ten PUBLIC_RUST_ROOTS also actively misdirects auditors about where the reference surface is. + +### `suite-id-constant-sprawl` — The protocol string "ed25519-v1" has six published spellings across four crates, three of them independent literals that can drift +- **Lane:** cross · **Goal:** simple · **Area:** rust-surface +- **Evidence:** Independent literal definitions: core/crates/auths-signature-core/src/lib.rs:11 `pub const ED25519_V1: &str = "ed25519-v1";`, core/crates/auths-raw-key-core/src/lib.rs:22 `pub const ED25519_V1: &str = "ed25519-v1";`, core/crates/auths-multikey/src/lib.rs:18 `pub const ED25519_SUITE: &str = "ed25519-v1";`. Re-export spellings: core/crates/auths-signature/src/lib.rs:8 `pub use auths_signature_core::{ED25519_V1, P256_SHA256_V1};`, core/adapters/auths-signature-ed25519/src/lib.rs:9 `pub use auths_signature_core::ED25519_V1;`, core/adapters/auths-did-keri/src/lib.rs:26 `pub use auths_signature_core::{ED25519_V1 as ED25519_SUITE, P256_SHA256_V1 as P256_SUITE};` (a renaming re-export). All six crates are in PUBLIC_RUST_CLOSURE. `"p256-sha256-v1"` follows the same pattern, plus a seventh unpublished redefinition at core/adapters/auths-webauthn/src/lib.rs:38. +- **Disposition:** Make auths-model the single owner of registry identifier strings (it already owns SignatureSuiteId at product/sdk/auths-sdk/src/lib.rs:10's import site). Delete the literals in auths-raw-key-core:22,24, auths-multikey:18,20 and auths-webauthn:38 and have those crates import from the owner. Delete the renaming re-export at auths-did-keri:26 — a second name for a frozen protocol string is a second vocabulary. Add the constants to the rust-surface symbol gate's re-export allowlist so this cannot regress. +- **Rationale:** These are registry identifiers that appear in AcceptedRegistries and get compared byte-for-byte during verification (see product/sdk/auths-sdk/src/lib.rs:114-115 parsing them into SignatureSuiteId). Three independently authored literals means a typo or a versioning bump in one crate silently produces a suite id that no longer matches the verifier's accepted set — a fail-closed denial in the best case, and in the delegation path a mismatch that is hard to diagnose. Six public spellings also means a downstream reader cannot tell which crate owns the meaning. + +### `custody-public-api-requires-telemetry-and-is-uncallable-from-sdk` — `auths-custody`'s signing functions take a telemetry port in their public signature, and the type is unnameable through the `auths-sdk` facade that re-exports them +- **Lane:** cross · **Goal:** simple · **Area:** rust-surface +- **Evidence:** product/integrations/auths-custody/src/lib.rs:500-513 is a macro whose expansion is `pub fn $name(request: ExternalSigningRequest<$input>, signer: &dyn ExternalSigner, verifier: &dyn CustodySignatureVerifier, events: &dyn auths_operations::EventSink) -> Result, CustodyError>`, instantiated at :517 `sign_grant`, :518 `sign_action`, :519-523 `sign_principal_status`, :524 `sign_grant_status`. auths-custody has no `pub use` statements at all, so `auths_operations::EventSink` is not re-exported. product/sdk/auths-sdk/src/lib.rs:21 does `pub use auths_custody as custody;`, but product/sdk/auths-sdk/Cargo.toml:12-28 has no `auths-operations` dependency. A crate depending only on auths-sdk therefore cannot name the fourth argument of `auths_sdk::custody::sign_grant`. +- **Disposition:** Remove `events: &dyn auths_operations::EventSink` from all four signing signatures. Return the operational observation as part of the result (or expose a separate `observe(&result, &dyn EventSink)`), so the caller owns telemetry. Then drop `auths-operations` from product/integrations/auths-custody/Cargo.toml, which also removes the tier-7-depends-on-tier-6 edge in release/public-naming.toml:260,262. +- **Rationale:** This is a broken facade with a compile-time proof: the curated Tier-2 path re-exports an API that Tier-2 users cannot call. It also puts an observability port inside the required arguments of a signing operation — telemetry is not part of what it means to sign, and forcing it into the signature means the security-critical function cannot be used without wiring an unrelated subsystem. A signing crate should not depend on a metrics crate at all. + +### `domain-canonicalizer-owns-five-verticals` — One shared canonicalizer in auths-profile-domains owns 'what was authorized' for five domains, behind a public generic type whose sealing trait is private +- **Lane:** cross · **Goal:** extensible · **Area:** rust-surface +- **Evidence:** product/profiles/auths-profile-domains/src/lib.rs:19 `trait DomainMeaning: Clone + DeserializeOwned + Serialize` — private, no `pub`. product/profiles/auths-profile-domains/src/lib.rs:34 `pub struct DomainProfile(PhantomData);` and :44 `pub struct DomainCommand` — both public and generic. The single `impl ActionProfile for DomainProfile where T: DomainMeaning` at :55-57 supplies ONE `canonicalize` body (:60-75: the 256KB bound, serde_json parse, serde_json_canonicalizer, Malformed/LimitExceeded mapping) for all five verticals implemented at :353 HttpAction, :469 GitAction, :612 DeploymentAction, :724 SupplyChainAction, :818 EdgeAction. `fn profile` (:106) and `fn decode_action` (:114) are likewise shared. Ten public aliases at :858-867 present these as five distinct profiles. The generic pair leaks into the product facade at product/sdk/auths-sdk/src/lib.rs:27 `pub use auths_profile_domains::{DeploymentAction, DomainCommand, DomainProfile};` under a doc comment reading 'Re-exported closed deployment action and profile'. +- **Disposition:** Give each vertical its own crate-owned canonicalize/decode, per the vertical-package-first rule in docs/target-state/PROFILE_AND_DOMAIN_ABSTRACTION_BOUNDARY_PLAN.md. Move DeploymentAction and its canonicalizer into product/integrations/auths-deployment (its only shipping consumer), and either move each remaining action to its own vertical or drop it. Delete `DomainProfile`/`DomainCommand` from the public surface and from product/sdk/auths-sdk/src/lib.rs:27; the SDK should re-export a concrete `DeploymentProfile`/`DeploymentCommand` owned by the vertical, or nothing at all. +- **Rationale:** This is the semantic-ownership failure the brief names: the crate is effect-free (no async, no HTTP, no clocks) and still owns the meaning of 'what was authorized' for five domains. A change to the shared canonicalize body at :60-75 silently changes the authorized bytes for a shipping Deployment vertical. The public generic also gives the wrong extensibility signal in both directions — rustdoc shows `DomainProfile`, an apparent extension point, while the private `DomainMeaning` trait means the file-touch cost of a new vertical is 'edit this one shared file', not 'add a package'. The doc comment at auths-sdk:27 says 'closed'; the type it names is the generic one. + +### `transport-closure-published-with-no-published-consumer` — Five exchange/transport crates are published while every implementation of the protocol they describe is publish = false +- **Lane:** cross · **Goal:** simple · **Area:** rust-surface +- **Evidence:** Published: exchange/crates/auths-byte-channel, exchange/adapters/auths-byte-channel-memory, exchange/adapters/auths-iroh, exchange/crates/auths-proof-exchange-model, exchange/crates/auths-proof-exchange-port (all `publish` unset; all in xtask/src/semantic_freeze.rs PUBLIC_RUST_CLOSURE). Everything that implements them is publish = false: exchange/crates/auths-proof-exchange/Cargo.toml, -codec, -framing, exchange/testkit/auths-proof-exchange-testkit, and all six adapters (-memory, -iroh, -tcp, -unix, -file, -https). Reverse-dep scan: `auths-byte-channel` has no published consumer (only auths-byte-channel-memory, auths-iroh, demos/identity-iroh); `auths-byte-channel-memory` has none (only demos/identity-iroh); `auths-iroh` has none (only the unpublished auths-proof-exchange-iroh, demos/identity-iroh, demos/cross-company-incident-response/edgeshield-service). Only auths-proof-exchange-model and -port have published consumers, and only one each: product/runtime/auths-runtime/Cargo.toml:19-20 and product/config/auths-config/Cargo.toml:14. release/public-naming.toml:212-218 acknowledges 'Only the model and port are in the first-RC dependency closure.' +- **Disposition:** Set publish = false on auths-byte-channel, auths-byte-channel-memory, and auths-iroh, and remove them from PUBLIC_RUST_ROOTS (xtask/src/semantic_freeze.rs:9,10,15) and PUBLIC_RUST_CLOSURE. For auths-proof-exchange-model and -port, either publish at least one adapter so the ports are usable, or absorb the two types auths-runtime and auths-config actually consume (ChannelBindingPolicy, PeerObservation, MAX_BODY_BYTES, MAX_PROOF_BYTES) into the consuming crates and unpublish the whole exchange family. +- **Rationale:** Publishing a port with no published implementation promises an integration path that a crates.io user cannot complete: they can name `BoundedByteChannel` and `ProofExchangeService` and cannot obtain a single working channel. Three of the five have no published consumer at all, which means their only demonstrated audience is in-repo demos — the brief's explicit 'no clear audience -> NOT PUBLIC' default. Each one is also a permanent semver obligation acquired for nothing. + +### `iroh-third-party-reexport` — `auths-iroh` re-exports third-party `iroh` types from its published root, binding Auths's 1.0 semver to an external crate's +- **Lane:** cross · **Goal:** secure · **Area:** rust-surface +- **Evidence:** exchange/adapters/auths-iroh/src/lib.rs:16 `pub use iroh::{Endpoint, EndpointAddr};`. exchange/adapters/auths-iroh/Cargo.toml:18 `iroh.workspace = true`. The crate is publishable (no `publish = false`), is in xtask/src/semantic_freeze.rs:19 PUBLIC_RUST_CLOSURE and :15 PUBLIC_RUST_ROOTS, and release/public-naming.toml:227 lists it under `modular-rust-components` for 1.0.0-rc.1 publication. Its own description at exchange/adapters/auths-iroh/Cargo.toml:9 is 'Semantics-free bounded byte exchange over Iroh'. xtask/src/fixtures.rs:1820 even builds a packaged-consumer fixture that depends on auths-byte-channel and auths-iroh together. +- **Disposition:** Delete the `pub use iroh::{Endpoint, EndpointAddr};` line. Accept iroh's types only in constructor parameters or behind an Auths-owned newtype (the crate already does this correctly for peers — exchange/adapters/auths-iroh/src/lib.rs:102 `pub struct ReceivedBytes` wraps `peer_endpoint_id: [u8; 32]`). If the crate stays published at all (see transport-closure finding), it must expose no foreign type in its root. +- **Rationale:** A `pub use` of a foreign type makes that crate's semver Auths's semver: an iroh major bump becomes an Auths breaking change even though no Auths code changed, and two versions of iroh in a consumer's tree produce type mismatches against a crate frozen at 1.0. It also violates the crate's own stated contract — 'semantics-free' is false when the public API hands out a transport library's endpoint type. + +### `auths-runtime-is-actually-mcp-runtime` — `auths-runtime` carries the most generic product name in the workspace while its public surface is MCP-specific +- **Lane:** cross · **Goal:** simple · **Area:** rust-surface +- **Evidence:** product/runtime/auths-runtime/Cargo.toml:9 already admits it: description = 'Authorization-before-execution service for Auths-protected MCP tools'. Of 30 root-level `pub` items in product/runtime/auths-runtime/src/lib.rs, eight are MCP-named: :373 `pub trait McpToolExecutor`, :379 `pub struct McpServiceConfig`, :483 `pub struct McpAuthorizationService`, :498 `pub struct McpRuntimeDependencies`, :510 `pub struct McpRequestStateDependencies`, :536 `pub struct McpExecutionDependencies`, :1174 `pub const fn mcp_profile_binding()`, :1184 `pub fn inspect_mcp_body()`. There is no non-MCP service type. It also re-exports two other crates from its root: :6 `pub use auths_production_client as production_client;` and :10 `pub use auths_kernel_runtime::AuthsKernel;`. It is published at release/public-naming.toml:260 (tier 7) and is a PUBLIC_RUST_ROOT (xtask/src/semantic_freeze.rs:16). +- **Disposition:** Rename the crate to `auths-mcp-runtime` (prelaunch, so a direct rename with no alias), or split: keep the domain-neutral `production` module (product/runtime/auths-runtime/src/production.rs — LifecycleCoordinator, recovery, clocks) under a neutral name and move every `Mcp*` symbol into a vertical package. Drop the `pub use auths_kernel_runtime::AuthsKernel` re-export at :10 — AuthsKernel already reaches users through auths-sdk (product/sdk/auths-sdk/src/lib.rs:5,260), so it currently has three public spellings. +- **Rationale:** This is the exact defect the consolidation spec already documents for TypeScript at bindings/typescript/src/product.ts:35-124 ('generic product names backed by MCP-specific authority'), reproduced in Rust and never called out. A Rust developer picking `auths-runtime` for a Stripe or Kubernetes vertical discovers the whole service layer is MCP-shaped. The name is also the most valuable coordinate in the namespace and it is spent on one profile. + +### `identity-second-principal-vocabulary` — `auths-identity` publishes a second, parallel principal/verification vocabulary alongside auths-model + auths-ports, and nothing in the verification path uses it +- **Lane:** cross · **Goal:** simple · **Area:** rust-surface +- **Evidence:** core/crates/auths-identity/src/lib.rs defines a full parallel stack: :90 `pub struct IdentityDescriptor`, :304 `pub struct VerificationRelationship`, :371 `pub struct VerificationMaterial`, :405 `pub trait IdentityDescriptorMethod`, :437 `pub struct PublicIdentity`, :549 `pub struct ValidatedIdentity`, :592 `pub trait IdentityMethod`, :603 `pub trait SignatureVerifier`. The Auths-owned equivalents already exist in the reference tier: core/crates/auths-model/src/lib.rs:172 `pub struct PrincipalId`, core/crates/auths-ports/src/lib.rs:194 `pub trait PrincipalMethod`, :244 `pub trait SignatureSuite`. No published crate consumes auths-identity in a normal dependency: core/crates/auths-signature/Cargo.toml:25 has it under `[dev-dependencies]` only; the remaining consumers are bindings/python, bindings/wasm/auths-proof-wasm, demos/identity-iroh, and the dead auths-identity-authority. The bridge that would reconcile the two vocabularies (core/adapters/auths-identity-authority/src/lib.rs:54 `PrincipalFromIdentity`) has zero consumers. +- **Disposition:** Decide whether identity is a separate shipped product or not, and record it. If it is not: set publish = false on auths-identity and auths-identity-raw-key, delete auths-identity-authority, and remove all three from PUBLIC_RUST_ROOTS/PUBLIC_RUST_CLOSURE. If it is: give it its own release_order tier with its own facade crate and at least one non-demo consumer, and make the auths-model bridge a tested, exercised path rather than a dead one. +- **Rationale:** Two published vocabularies for 'who is this principal and did they sign' is the definitive duplicate-vocabulary case, and the fact that the bridge between them is dead code proves nothing has ever needed both. It fails the audience test: the only consumers are language bindings and one demo. Publishing it now freezes an identity model that the verification path has never had to satisfy. + +### `auths-config-published-with-no-shipping-consumer` — `auths-config` publishes a full TOML application-configuration vocabulary that no shipping binary reads +- **Lane:** cross · **Goal:** simple · **Area:** rust-surface +- **Evidence:** product/config/auths-config/src/lib.rs exports :29 `pub struct AuthsConfig`, :172 `ProfileConfig`, :195 `RuntimeConfig`, :258 `ChannelPolicyConfig`, :268 `StoreConfig`, :304 `ReceiptPolicy`, :311 `CompiledConfig`, :373 `BoundConfiguration`, :440 `ConfigError`, plus eleven more re-exported at :7-11 (`ProductionCandidate`, `ProductionCandidateInput`, `SdkLanguage`, …). Reverse-dep scan gives exactly two consumers: product/operations/auths-operations (product/operations/auths-operations/src/lib.rs:12 `use auths_config::BoundConfiguration;` used once, at :134 as a `&BoundConfiguration` parameter) and xtask. The reference server does NOT use it — product/runtime/auths-node/Cargo.toml:11-30 lists auths-operations, auths-operations-otel, auths-production-client, axum, toml … and no auths-config. It is published at release/public-naming.toml:256 (tier 5). +- **Disposition:** Set publish = false on auths-config and remove it from PUBLIC_RUST_CLOSURE and release_order tier 5. The single type auths-operations needs (`BoundConfiguration`, used at product/operations/auths-operations/src/lib.rs:134) should move to auths-operations or be replaced by an explicit parameter struct. If a published config format is genuinely wanted, it must first be the format auths-node actually reads. +- **Rationale:** A configuration schema is one of the hardest things to change after publication, and this one is frozen at 1.0 without a single shipping binary validating that it is the right schema — the reference node parses its own TOML instead. Nineteen-plus public symbols promised to nobody, and a config vocabulary competing with the one auths-node uses is a second way to configure the same system. + +### `freeze-roots-contradict-tier-model` — The declared PUBLIC_RUST_ROOTS omit every protocol/reference crate and include a dead bridge, so the checked-in roots contradict the decided tier model +- **Lane:** cross · **Goal:** simple · **Area:** rust-surface +- **Evidence:** xtask/src/semantic_freeze.rs:8-19 `const PUBLIC_RUST_ROOTS: [&str; 10] = ["auths", "auths-byte-channel", "auths-byte-channel-memory", "auths-identity", "auths-identity-authority", "auths-identity-raw-key", "auths-iroh", "auths-runtime", "auths-sdk", "auths-signature-ed25519"]`, mirrored in release/semantic-freeze.json `publicSurface.rustRoots`. Absent: auths-model, auths-codec, auths-ports, auths-registries, auths-verifier, auths-author, auths-proof — i.e. the entire Tier-1 protocol/reference surface described in docs/target-state/PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:52 ('Complete deterministic model, codec, authoring, verifier stages, registries, ports, profile contracts, fixtures, and conformance tools'). Present: auths-identity-authority, which has zero consumers, and three transport crates with zero published consumers. +- **Disposition:** Rewrite PUBLIC_RUST_ROOTS to name the two intended entry points per tier and nothing else: Tier 1 = auths (or auths-proof, after the dual-coordinate fix) + auths-model + auths-verifier + auths-author; Tier 2 = auths-sdk. Everything else becomes a closure member reachable through a root, or is unpublished. Bump FREEZE_VERSION (xtask/src/semantic_freeze.rs:7) and regenerate release/semantic-freeze.json. +- **Rationale:** The 'roots' list is the machine-readable statement of where the public surface starts, and it currently points auditors at a dead bridge and three unused transports while omitting the crates that own protocol meaning. Any tooling built on this list — docs generation, the rust-surface symbol gate proposed above, dependency-closure review — inherits the wrong answer. Fixing the roots is a prerequisite for the symbol gate to be meaningful. + +### `python-endpoint-path-positional-index` — Python endpoint table is keyed by positional index into the profile tuple +- **Lane:** python · **Goal:** secure · **Area:** extensibility — the file-touch cost of adding a new profile vertical +- **Evidence:** bindings/python/python/auths/_production_client.py:375-379 defines `_PROFILE_IDS` as a 3-tuple, then :409-413: +``` + return { + _PROFILE_IDS[0]: "/v1/profiles/opentofu/saved-plan-apply/execute", + _PROFILE_IDS[1]: "/v1/profiles/postgresql/bounded-update/execute", + _PROFILE_IDS[2]: "/v1/profiles/github/issue-address/execute", + }[profile] +``` +The binding between a profile id and its execute path is ORDINAL, not nominal. Inserting a new profile anywhere but the end of `_PROFILE_IDS` silently re-points every subsequent path to the wrong profile, and nothing in the file or in xtask compares this table against product/runtime/auths-production-client/src/lib.rs:200-206. +- **Disposition:** Key the dict by the literal profile id string, not by tuple position — or better, delete the table entirely and read the Rust-owned descriptor (previous finding). Either way, add a test that asserts the Python path table equals what Rust `execute_path` returns for every profile. +- **Rationale:** A reordering that a reviewer would read as cosmetic silently changes which endpoint each authorization is submitted to, with no compile error and no test failure. Positional coupling between two literals in the same file is a trap; positional coupling to a security-relevant routing decision is a defect. + +### `xtask-hardcoded-vertical-lists` — Four separate hardcoded vertical lists plus an embedded JavaScript assertion inside xtask Rust source +- **Lane:** rust · **Goal:** extensible · **Area:** extensibility — the file-touch cost of adding a new profile vertical +- **Evidence:** Adding a vertical requires editing Rust in the build tool in at least five places. (1) xtask/src/bounded_domains.rs:3-11 `const REQUIRED_DOMAIN_IDS: [&str; 7] = ["github", "kubernetes", "opentofu", "postgresql", "radicle", "records-api", "stripe"]` — note the length is baked into the type. (2) xtask/src/bounded_benchmark.rs:18-24 repeats the identical seven ids, plus a hand-written per-vertical benchmark arm for each (stripe :129-132, kubernetes :154-157, postgresql :175-178, opentofu :188-191, github :217-220, radicle :247-250, records-api :269-308). (3) xtask/src/fixtures.rs:557 restates them a third time inside the embedded registry TOML: `domains = ["github", "kubernetes", "opentofu", "postgresql", "radicle", "records-api", "stripe"]`. (4) xtask/src/assurance.rs:15-19 `const REQUIRED_PROFILES: [&str; 3]`. (5) xtask/src/checks.rs:231-234 embeds a JavaScript smoke test inside a Rust string literal asserting each production profile helper exists by name: +``` + if (typeof profiles.opentofuSavedPlanApply !== 'function' || profiles.opentofuSavedPlanApply().id !== 'auths.opentofu.saved-plan-apply/1') throw new Error('OpenTofu profile missing');\n\ +``` +Also xtask/Cargo.toml must gain a dependency line. Two vertical-name special cases have already leaked into generic logic: xtask/src/bounded_domains.rs:481 `&& domain_id != "records-api"` and :525 `let report_name = if domain == "records-api"`. +- **Disposition:** Replace all five with iteration over the collected descriptors. `REQUIRED_DOMAIN_IDS` becomes a derived set rather than a fixed-length array; the per-vertical benchmark arms become a loop over descriptor-declared evaluator entry points; the JS assertion in checks.rs is generated from the descriptor list rather than typed by hand. Delete the two `records-api` special cases by promoting whatever they encode into a descriptor field. +- **Rationale:** This is the answer to assignment item 4, and it is unambiguous: yes, adding a vertical requires editing Rust source in xtask, in five places. `[&str; 7]` means the array length itself is a merge conflict. The hardcoded special cases at bounded_domains.rs:481 and :525 are the early symptom of exactly the 'generic runtime that dispatches on a domain tag' shape the boundary plan prohibits at :125-155 — it has appeared in the tooling rather than the runtime, which is where nobody is looking for it. + +### `registry-generated-vs-hand-classification` — Registry-by-registry: which of the extension-tax artifacts are genuinely generated +- **Lane:** cross · **Goal:** extensible · **Area:** extensibility — the file-touch cost of adding a new profile vertical +- **Evidence:** HAND-MAINTAINED, enforced, real tax: Cargo.toml (members :64-70, workspace deps :196-214); architecture.toml [layers] — completeness enforced at xtask/src/architecture.rs:59-62 `let missing: Vec<_> = workspace_names.difference(&classified)...` "architecture package classification drift"; compliance.toml — enforced at xtask/src/compliance.rs:258-261 "Cargo compliance inventory drift; missing={missing:?}", and each package block is ~15 fields plus a claims table (records-api at compliance.toml:957-985); bounded-domains.toml — a 25-field `[[domains]]` record per vertical, fields enumerated at xtask/src/bounded_domains.rs:53-79, with package_path/demo_path/fixture_dir all existence-checked at :249-251 and the spec doc at :188,:638-652; .github/ci/phase-ownership.toml (records at :42, :183-198, :323-324); .github/workflows/ci.yml; release/open-production-candidate.toml `[[profiles]]` blocks at :50-72. GENERATED, no hand-edit, acceptable: Cargo.lock; architecture/dependency-graph.{json,dot} via `cargo xtask arch --update` (xtask/src/architecture.rs:222-231); release/semantic-freeze.json — genuinely derived, xtask/src/semantic_freeze.rs:900-903 maps implemented domains out of bounded-domains.toml and :936-939 discovers fixture manifests by directory walk; bindings/{typescript,python}/api/public-api.txt via bindings/typescript/tools/public-api.mjs:67 and bindings/python/tools/check_public_api.py:35; bindings/customer-journey-matrix-v1.json baseline via `cargo xtask sdk-experience --update` (xtask/src/sdk_experience.rs:115-122). GENERATED-FROM-HAND-WRITTEN-RUST, i.e. laundering: product/fixtures/v1//* and product/fixtures/v1/bounded-policy/registry.toml (from xtask/src/fixtures.rs literals); product/conformance/v1/mechanism-profile-conformance.json plus its TS and Python emissions (xtask/src/mechanism_conformance.rs:22-26) from the hardcoded `&["auths.mcp/1", "auths.records/1"]` at core/testkit/auths-testkit/src/mechanism_conformance.rs:48,54. NOT A TAX: release/public-naming.toml contains no per-vertical entries (grep for records/radicle/kubernetes/opentofu/stripe/postgres returns only URL and prose matches at :4,:111,:164,:667). +- **Disposition:** Treat the three laundering artifacts as hand-maintained in any accounting of extension cost, and re-source them from the vertical descriptor. Keep the five genuinely-generated ones as they are — semantic-freeze.json in particular is a model for how the rest should work, since it already derives its vertical list from bounded-domains.toml instead of restating it. +- **Rationale:** This is the answer to assignment item 3. The honest split is 8 hand-maintained registries, 5 genuinely generated artifacts, and 3 that are nominally generated but require a hand-edit to a Rust literal first. Counting the third category as 'generated' understates the real extension tax by about a third and, more importantly, hides that a vertical's canonical fixture bytes are authored outside the vertical. + +### `action-profile-carries-no-identity` — The ActionProfile contract specifies 3 methods; a vertical must declare ~81 more facts out of band +- **Lane:** rust · **Goal:** extensible · **Area:** extensibility — the file-touch cost of adding a new profile vertical +- **Evidence:** product/profiles/auths-profile-api/src/lib.rs is 117 lines total. The entire contract is `trait ActionProfile` at :52-85: one associated type `Command` (:54) and three methods — `canonicalize(&self, untrusted: &[u8]) -> Result` (:62), `review_display(&self, action: &CanonicalAction) -> Result` (:70-73), `decode_verified(&self, action: &VerifiedAction) -> Result` (:81-84). Plus `ReviewDisplay` (:11-49) and the six-variant closed `ProfileContractError` (:88-102: LimitExceeded, Malformed, Ambiguous, UnsupportedProfile, NonCanonical, MeaningMismatch). What the trait does NOT carry: the profile id, the profile version, the evaluator entry point, the execute path, the policy/evidence/state/receipt schema ids, the reservation algebra, the provider contract, the fixture location. Every one of those is instead declared out of band — 25 fields in bounded-domains.toml per domain (xtask/src/bounded_domains.rs:53-79) and 28 fields per profile in the xtask/src/fixtures.rs registry literal (records-api create at :594-622). For a two-profile vertical that is 25 + 2x28 = 81 hand-typed declarations, none of which the compiler relates to the impl. There are 20 real implementors of ActionProfile across product/integrations and product/profiles. +- **Disposition:** Add `const DESCRIPTOR: VerticalDescriptorV1` as an associated const on `ActionProfile` (or on a companion `ProfileDescriptor` trait the vertical must also implement). The compiler then forces every implementor to state its identity, and xtask reads it instead of a TOML transcription. +- **Rationale:** This is the answer to assignment item 2, and it is the root cause of items 1, 3, and 4. The trait is small and clean — that is correct and should not change. The problem is that it is small because ~81 facts per vertical were pushed into text files the compiler cannot see. Putting the identity back on the trait is what makes every downstream registry derivable, and it is the difference between 'the registry claims this vertical uses evaluator X' and 'the compiler knows it does'. + +### `release-profile-inventory-lists-generic-machinery-not-verticals` — The release manifest's profile list is derived from the generic action-spec directory, so no shipping vertical appears in it +- **Lane:** cross · **Goal:** extensible · **Area:** extensibility — the file-touch cost of adding a new profile vertical +- **Evidence:** xtask/src/release.rs:1079-1091 builds the release `profiles` list by reading the directory: +``` +let mut profiles = fs::read_dir(root().join("product/spec/v1")) + ... + .filter(|path| path.extension()... == Some("md")) + .filter(|path| path.file_stem()... != Some("receipts")) +``` +The .md files in product/spec/v1 are: deployment-action.md, edge-action.md, git-action.md, http-action.md, mcp-tools-call.md, supply-chain-action.md. Those are the GENERIC `auths_profile_domains` action types. There is no github-issue-address.md, no radicle, no kubernetes, no records-api, no opentofu, no postgresql, no stripe entry — every shipping vertical is absent from the directory the release manifest treats as the profile inventory. Adding a real vertical therefore does not add it to the release profile list at all; the only way in is to add a .md alongside the generic action specs. +- **Disposition:** Stop deriving the release profile inventory from a directory listing. Derive it from the collected vertical descriptors, so the release manifest names the profiles that actually ship. Separately, move the generic action-type specs (deployment-action, edge-action, git-action, http-action, supply-chain-action) out of the release-facing profile directory — they document reference machinery, not products. +- **Rationale:** The release surface currently presents the generic reference types as the product's profiles while the seven real verticals are invisible to it. That is the facade-relabeling problem showing up on the release artifact rather than in a re-export, and it means a directory listing — the weakest possible coupling — is what decides what the release claims to authorize. It also guarantees the number stays wrong as verticals are added, because nothing forces a new vertical to appear. + +### `shared-contract-change-costs-every-vertical` — The inverse tax: one change to ActionProfile required editing 17 vertical profile.rs files in a single commit +- **Lane:** rust · **Goal:** extensible · **Area:** extensibility — the file-touch cost of adding a new profile vertical +- **Evidence:** Commit a8a6348 "Separate review displays from approval" changed product/profiles/auths-profile-api/src/lib.rs and, in the same commit, had to edit: product/integrations/auths-github/src/profile.rs, auths-kubernetes/src/profile.rs, auths-opentofu/src/profile.rs, auths-postgresql/src/profile.rs, auths-radicle/src/profile.rs, and eleven auths-stripe profile.rs files (src/profile.rs, connect/transfer, issuing/purchase_authorization, mandate, merchant/{authorize,cancel,capture,collect}, subscription/{cancel,create,modify}, treasury/payout) — plus product/profiles/auths-profile-domains/src/lib.rs, auths-profile-mcp/src/lib.rs, product/sdk/auths-profile-kit/src/lib.rs, product/integrations/auths-enforcement/src/lib.rs, and bindings/wasm/auths-proof-wasm/src/lib.rs. Similarly d51e5ae "feat: add durable workflow recovery" touched src/lifecycle.rs in six verticals at once (github, kubernetes, opentofu, postgresql, radicle, stripe). There are 20 ActionProfile implementors today. +- **Disposition:** Not a defect to fix by shrinking the trait — a 3-method contract is already minimal and the boundary plan is right that verticals should own their semantics. The correct disposition is to freeze `ActionProfile` at v1.0 and record its O(N) blast radius explicitly in docs/target-state/PROFILE_AND_DOMAIN_ABSTRACTION_BOUNDARY_PLAN.md so future changes to it are treated as a semantic-freeze event, not a refactor. +- **Rationale:** Extensibility has two directions and only one is being measured. Adding a vertical costs 28 files outside the package; changing the shared contract costs one file inside every vertical. Both numbers matter for v1.0 readiness, and the second one is the argument for freezing the contract before shipping rather than after. Note this is NOT an argument that the verticals are too duplicated — the boundary plan at :281-300 correctly says small visible duplication beats a wrong security abstraction, and I am not proposing to collapse the 17 profile.rs files. + +### `boundary-plan-followed-in-verticals-violated-in-tooling` — The vertical-first process is followed inside the packages and violated in the tooling and bindings around them +- **Lane:** cross · **Goal:** extensible · **Area:** extensibility — the file-touch cost of adding a new profile vertical +- **Evidence:** Following the plan: docs/target-state/PROFILE_AND_DOMAIN_ABSTRACTION_BOUNDARY_PLAN.md:84-102 requires domain semantics to live in one cohesive package — auths-records-api does, with 14 files and Cargo.toml depending only on auths-bounded-policy, auths-lifecycle, auths-model, auths-profile-api, auths-sdk, auths-signature-core. The plan at :281-300 forbids 'generic workflow services that accept an operation tag and callbacks' — auths-lifecycle honors this exactly, product/runtime/auths-lifecycle/src/registry.rs:8-10 stating provider functions and callbacks 'are intentionally absent', and lib.rs:1-5 confining the crate to 'deterministic lifecycle mechanics only'. Violating the plan: :451-494 'Machine-enforced inventories' requires a machine-readable profile inventory that CI validates for 'inventory/specification mismatch' — the inventory exists (bounded-domains.toml) but nothing compares it to the vertical's own consts (no hits for IMPLEMENTATION_ID, RESERVATION_ALGEBRA_ID, CANONICALIZATION_ID or POLICY_TYPE_ID anywhere in xtask/ or product/policy/). :125-155 prohibits selecting behavior by domain tag inside a generic runtime — xtask/src/bounded_domains.rs:481 and :525 already do this by vertical name. :104-122 says every profile owns 'its fixtures, mutation corpus, live contract, and demo' — the fixtures are authored in xtask/src/fixtures.rs:890-1677 instead. And the plan's own header at :9 names GitHub, Radicle, Stripe, Kubernetes, OpenTofu, PostgreSQL as the verticals it generalizes, while product/sdk/auths-sdk/src/lib.rs:27 still re-exports `DomainProfile` — the generic type the plan's prohibited-shape diagram at :131-145 describes. +- **Disposition:** Extend the plan's machine-enforced-inventory requirement to cover the tooling: CI must assert that every string in bounded-domains.toml and in the bounded-policy evaluator registry equals the corresponding `VerticalDescriptorV1` field exported by the vertical crate. Add a check that xtask source contains no per-vertical string literal outside a generated file. Both are cheap once the descriptor exists. +- **Rationale:** This is the answer to assignment item 6, and the honest answer is 'yes in the packages, no in the tooling'. The plan's authors got the hard part right — the vertical packages really are cohesive and auths-lifecycle really is declarative. What leaked is the metadata: the identity of each vertical is restated in build-tool source and in two binding languages, none of which the plan's enforcement section reaches. Leaks travel through whatever surface nobody is auditing, and here that surface is xtask. + +### `facade-exports-uninhabitable-generic-instead-of-existing-narrow-alias` — auths-sdk re-exports DomainProfile/DomainCommand, which are generic over a PRIVATE trait, while the narrow aliases that already exist go unexported +- **Lane:** rust · **Goal:** simple · **Area:** rust-ownership +- **Evidence:** `product/profiles/auths-profile-domains/src/lib.rs:19` — the bound is not public: +```rust +trait DomainMeaning: Clone + DeserializeOwned + Serialize { +``` +and `:55-58` is the only `ActionProfile` impl: +```rust +impl ActionProfile for DomainProfile where T: DomainMeaning { +``` +So `DomainProfile` is inhabitable only by the five types inside that crate — the generic parameter grants a consumer exactly zero extensibility. Meanwhile the narrow aliases already exist at `:858-867`: +```rust +pub type DeploymentProfile = DomainProfile; +pub type DeploymentCommand = DomainCommand; +``` +but `product/sdk/auths-sdk/src/lib.rs:26-27` exports the wide names instead, under a doc comment that describes the narrow ones: +```rust +/// Re-exported closed deployment action and profile. +pub use auths_profile_domains::{DeploymentAction, DomainCommand, DomainProfile}; +``` +The consequence is visible in the only consumer, which is forced to spell the generic: `product/integrations/auths-deployment/src/lib.rs:65,74` write `Enforcement>` and `CommandExecutor>` where `DeploymentProfile`/`DeploymentCommand` would have said the same thing. Note also that only DeploymentAction of the five is exported — HTTP, Git, supply-chain, and edge are not — so the facade is already inconsistent about which domain it curates. +- **Disposition:** Delete line 27 of `product/sdk/auths-sdk/src/lib.rs` entirely, together with its doc comment at `:26`. Advanced Rust consumers who genuinely need the reference profiles should depend on `auths-profile-domains` directly, per PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:520-523 ('Prefer a separately imported reference crate or an explicit advanced module over root-level re-exports'). This becomes a no-op deletion once auths-deployment is removed. +- **Rationale:** Beyond the mislabeling already known, the specific defect is that the facade pays the full cost of a generic name — every consumer must write the type parameter, every reader must ask what else `T` could be — for a type parameter that is sealed shut by a private trait and can never take another value from outside the crate. It exports the widest possible spelling of the narrowest possible capability. The correctly-scoped aliases were already written twelve lines apart from each other and simply were not used. + +### `sdk-omits-the-profile-contract-its-own-signatures-require` — auths-sdk requires ActionProfile and ProfileContractError in its public signatures but re-exports neither, forcing every vertical onto a second dependency +- **Lane:** rust · **Goal:** simple · **Area:** rust-ownership +- **Evidence:** `product/sdk/auths-sdk/src/lib.rs:13`: +```rust +use auths_profile_api::{ActionProfile, ProfileContractError}; +``` +Both appear in the public API — `:299-305`: +```rust +pub fn verify(&self, proof: &[u8], canonical_action: &auths_model::CanonicalAction, request: &RequestContext, profile: &P) -> Result, SdkError> +``` +and `:448-450`: +```rust +/// Authorized bytes could not be decoded by the selected profile. +#[error("verified action does not satisfy the selected profile: {0}")] +Profile(#[from] ProfileContractError), +``` +Neither is in the re-export block at `:18-31`, which lists `authority`, `custody`, `errors`, `model`, `DeploymentAction`/`DomainCommand`/`DomainProfile`, `McpCommand`/`McpProfile`/`McpToolCall`, and `VerifiedAction`. `ReviewDisplay` is likewise absent despite `auths-enforcement` returning `&ReviewDisplay` from `PreparedAction::review_display` (`product/integrations/auths-enforcement/src/lib.rs:100-104`). Result: 13 Cargo.toml files declare a direct `auths-profile-api` dependency to name types the facade already forces on them, and every vertical straddles both crates — e.g. `product/integrations/auths-github/src/profile.rs:8-9`: +```rust +use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_sdk::VerifiedAction; +``` +- **Disposition:** Add `pub use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay};` to `product/sdk/auths-sdk/src/lib.rs` in the same block as the other curated exports, and drop the direct `auths-profile-api` dependency from the 13 integration and demo manifests that only need those three names. Do this in the same change that removes line 27, so the facade ends up exporting the contract and not the implementation. +- **Rationale:** The curation is currently backwards: the facade exports a sealed generic implementation nobody can extend, and withholds the trait every real vertical must implement plus the error type its own `SdkError` wraps. A consumer holding `SdkError::Profile(e)` cannot name `e`'s type from `auths_sdk`. This is a direct symbol-count-at-the-public-surface defect and it is what makes the boundary look arbitrary to anyone reading the facade for the first time. + +### `trusted-context-builder-hardcodes-the-registry-set` — TrustedContextBuilder hardcodes one resource matcher, one budget algebra, and one profile policy, and has exactly one consumer +- **Lane:** rust · **Goal:** extensible · **Area:** rust-ownership +- **Evidence:** `product/sdk/auths-sdk/src/lib.rs:214-232`: +```rust +let accepted = AcceptedRegistries::new( + auths_registries::TARGET_V1_REGISTRY_MANIFEST, + principal_methods.into_iter().collect(), + self.signature_suites.into_iter().collect(), + self.evidence_types.into_iter().collect(), + principal_status_methods.into_iter().collect(), + Vec::new(), + assurance_claims.into_iter().collect(), + Vec::new(), + vec![ResourceMatcherId::parse(auths_registries::URI_NAMESPACE_V1)?], + vec![BudgetAlgebraId::parse(auths_registries::NUMERIC_CEILING_V1)?], + self.critical_extensions.into_iter().collect(), + profiles.into_iter().collect(), + vec![ProfilePolicyId::parse(auths_registries::EXACT_PROFILE_V1)?], +)?; +``` +There are `with_principal_status`, `with_grant_status`, `with_channel_policy`, `with_limits`, `accept_evidence_type`, and `accept_critical_extension` builder methods (`:139-179`) but no way to accept a different resource matcher, budget algebra, or profile policy, and two categories are passed as `Vec::new()` with no accessor at all. A repo-wide grep for `TrustedContextBuilder` returns three hits: the definition at `:79`, the impl at `:93`, and one consumer — `bindings/python/src/authoring.rs:994`. No Rust vertical uses it. Similarly `Verifier::self_contained` at `:274-287` hardcodes exactly three principal methods and two signature suites. +- **Disposition:** Either add explicit `accept_resource_matcher` / `accept_budget_algebra` / `accept_profile_policy` methods mirroring `accept_evidence_type` at `:167-172`, or delete `TrustedContextBuilder` from the facade and let its single Python consumer construct `VerifierContext` directly. Do not leave a builder in the product facade whose defaults are the whole story and whose only caller is a binding. +- **Rationale:** This is the mechanism behind the deploy-blast-radius failure and it will reproduce for the next vertical: the accepted-registry set is a product-facade constant, so any vertical needing a resource matcher or budget algebra other than `uri-namespace-v1` / `numeric-ceiling-v1` cannot use the supported context builder and must reimplement `AcceptedRegistries::new` with fourteen positional arguments. The file-touch cost of a new vertical includes editing `auths-sdk`, which is precisely what the EXTENSIBLE goal is measuring. + +### `semantic-freeze-cannot-see-symbol-level-leaks` — The freeze and architecture gates operate on file digests and the crate dependency graph, so a facade-laundered symbol leak passes both +- **Lane:** rust · **Goal:** secure · **Area:** rust-ownership +- **Evidence:** `xtask/src/semantic_freeze.rs:789-799` compares only the set of publishable crate NAMES, and `:801-860` walks the cargo resolve graph to check the dependency CLOSURE of `PUBLIC_RUST_ROOTS`. `:979-1046` (`freeze_entry` / `digest_owners`) hashes file contents under owner paths. Neither mechanism inspects a `pub use` or a type parameter. The failure is demonstrable: `auths-deployment` is absent from `PUBLIC_RUST_CLOSURE` (`:20-63`, 42 entries) and its `Cargo.toml` declares only `auths-enforcement` and `auths-sdk` — so the crate-graph check sees a clean edge while `src/lib.rs:65` reads `Enforcement>`. The freeze entry `auths.product.public-sdk-contract` (`:263-283`) hashes `product/sdk/auths-sdk/src` and `product/profiles/auths-profile-domains/src` under one identity, so the two can be edited together and only require a version bump — it detects change, never direction of ownership. `architecture.toml:119,145` records only a layer string (`auths-deployment = "product"`, `auths-profile-domains = "product"`). `compliance.toml:1034-1051` classifies `auths-profile-domains` as `layer = "product"` and lists `profiles = ["auths.deploy/1", "auths.edge/1", "auths.git/1", "auths.http/1", "auths.supply-chain/1"]` — one crate declaring ownership of five profiles is recorded as normal, not flagged. PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:906-912 already asks for a repository-hygiene check rejecting `DomainProfile` and generic domain parser symbols in shipping code; no such check exists. +- **Disposition:** Add a symbol-level xtask check that (a) parses every `pub use` in `product/sdk/auths-sdk/src/lib.rs` against an explicit allowlist committed alongside it, and (b) greps the source — not the manifests — of every crate under `product/integrations/` for `DomainProfile`, `DomainCommand`, `HttpAction`, `GitAction`, `DeploymentAction`, `SupplyChainAction`, `EdgeAction`, and `DomainReceiptInspector`, whether imported directly or via `auths_sdk::`. Add a compliance rule that no single package may declare more than one entry in its `profiles = [...]` list unless explicitly classified as reference tier. +- **Rationale:** Every finding above survived the existing gates. The freeze passes, the publishable closure passes, the architecture layer map passes, and cargo's dependency graph shows nothing — because the leak travels through a facade re-export, which is a source-level fact about symbols and not a build-level fact about crates. Until enforcement reads symbols, the next re-export added to auths-sdk will reintroduce exactly this class of coupling with a green CI. + +### `two-product-waists-one-namespace` — Auths and ProductionAuths are two parallel product waists with disjoint vocabularies exported from the same top-level namespace +- **Lane:** python · **Goal:** simple · **Area:** python +- **Evidence:** `auths.Auths` (`_product.py:191-366`) returns `Completed|PlanCompleted|Denied|Indeterminate|RecoveryResult|PlanRecoveryResult` (`:151-158`); `auths.ProductionAuths` (`_production_client.py:157-303`) returns `ProductionCompleted|ProductionDenied|ProductionIndeterminate|ProductionRecoverable`, `ProductionAuthorityResult`, `ProductionVerificationResult` (`:143-154`). Same concepts, prefixed duplicates: `Completed`/`ProductionCompleted`, `Denied`/`ProductionDenied`, `Indeterminate`/`ProductionIndeterminate`, `Receipt`/`ProductionReceipt`, `Authority`/`ProductionAuthority`, `ExecutionReference`/`ProductionRecoveryReference`, `RecoveryResult`/`ProductionRecoverable`. Both families are in `auths.__all__` (`__init__.py:50-86`) — 16 local + 19 production symbols, so 19 of the 35 top-level symbols exist only to name the second copy. Their failure shapes differ structurally: `ProductionDenied`/`ProductionIndeterminate` carry `step` and `retry` (`:96-109`); `Denied`/`Indeterminate` carry neither (`:69-79`). Their lifecycle differs: `Auths` has `aclose`/`__aenter__`/`__aexit__` (`_product.py:335-354`), `ProductionAuths` has none. +- **Disposition:** Pick one product waist. If `ProductionAuths` is the real production story, `Auths` plus `integrations.development` should be reframed as a testkit-layer composition and moved off the top-level namespace; if `Auths` is the product, `ProductionAuths` should become a `ProductionTransport`-backed `_AuthsResources` implementation feeding the same `Auths` class and the same six result types, and the 19 `Production*` symbols should be deleted. +- **Rationale:** "One way to do each thing" is violated at the widest possible point: the entry namespace. A reader of `import auths` sees two complete, incompatible vocabularies for authority, receipts, execution results and recovery, with no signal about which one is the product. This is the Python analogue of the TypeScript `product.ts` vs `production-client.ts` split, but worse, because both surfaces are flattened into one namespace instead of separate entry points. + +### `dead-production-composition` — auths.integrations.production is unreachable public API that crashes with AttributeError +- **Lane:** python · **Goal:** simple · **Area:** python +- **Evidence:** `bindings/python/python/auths/integrations.py:385-390`: +``` +class _Production: + def create_auths(self, configuration: AuthsConfiguration) -> _PendingAuths: + if configuration.mode != "production": + raise TypeError("production composition rejects development capabilities") + return _PendingAuths(configuration) +``` +exported at `:392` and `:671`, and listed in `bindings/python/api/public-api.txt` under `[auths.integrations]`. The only constructor of `AuthsConfiguration` is `_product._create_auths_configuration` (`_product.py:369-388`), private, and its sole caller is `integrations.py:491` `return _create_auths_configuration("development", diagnostics, open_resources)` — hardcoded. Grep across `python/`, `tests/`, `typecheck/`, `external/` finds no other call site. So no configuration with `mode == "production"` can be constructed by any consumer, and `production.create_auths` can only ever raise. Verified at runtime: `integrations.production.create_auths(None)` -> `AttributeError: 'NoneType' object has no attribute 'mode'` — the guard at `:387` dereferences `.mode` before validating the argument type, so it does not even fail closed with the intended `TypeError`. +- **Disposition:** Delete `_Production` (`integrations.py:385-390`), the `production` binding at `:392`, and its `__all__` entry at `:671`; regenerate `api/public-api.txt`. If a production composition is intended for 1.0, it needs a real public constructor for a production `AuthsConfiguration` — otherwise the symbol is a promise the package cannot keep. +- **Rationale:** A public symbol in the topology-declared surface that is unsatisfiable by construction. It also demonstrates the guard is untested: an argument-order bug means the documented `TypeError` never fires, which would matter the moment a production configuration did exist. Prelaunch, with zero external users, this is a clean delete. + +### `denied-indeterminate-name-collision-and-ts-disagreement` — auths.verify.Denied/Indeterminate collide with auths.Denied/Indeterminate; TypeScript deliberately avoids this with *Result suffixes +- **Lane:** python · **Goal:** simple · **Area:** python +- **Evidence:** `bindings/python/python/auths/_product.py:69-79` — `Denied(kind, code)` and `Indeterminate(kind, code)`. `bindings/python/python/auths/verify.py:75-95` — `Denied(kind, code, stage, explanation, metrics, required_configuration, local_configuration, result_cbor)` and `Indeterminate(...)` with the same eight fields. Both pairs are in `bindings/python/api/public-api.txt` (`[auths]` and `[auths.verify]`). TypeScript names the verification variants differently for exactly this reason: `bindings/typescript/src/verify.ts:6,7,9` export `type AuthorizedResult`, `type DeniedResult`, `type IndeterminateResult`, while `bindings/typescript/src/index.ts:22,25` export `type Denied`, `type Indeterminate` for the product results. Python's `auths.verify` also exports `Authorized` where TS exports `AuthorizedResult`. Additional collisions across the public surface: `Receipt` in `[auths]` and `[auths.verify]`; `verify_receipt` reachable as `auths.verify.verify_receipt` (aliased from `_receipts.verify_linked_receipt` at `verify.py:38`) and as `_product.verify_receipt` (`_product.py:397-398`); `AtomicReservationRecord` in `[auths.framework]` and `[auths.testkit]`; `CONFORMANCE_CATALOG` in `_conformance.py:664`, `_mechanism_conformance.py:213` and `[auths.testkit]`. +- **Disposition:** Rename `auths.verify.Authorized/Denied/Indeterminate` to `AuthorizedResult/DeniedResult/IndeterminateResult` to match `bindings/typescript/src/verify.ts:6,7,9`. Drop the `verify_linked_receipt`->`verify_receipt` / `decode_linked_receipt`->`decode_receipt` / `encode_linked_receipt`->`encode_receipt` renames at `verify.py:35-38` and use one name per operation across both bindings. Delete `_product.verify_receipt`/`encode_receipt`/`decode_receipt` (`_product.py:397-406`) as duplicate paths. +- **Rationale:** `from auths import Denied` and `from auths.verify import Denied` bind two different dataclasses with different arities in the same program — a silent `isinstance` hazard and a straightforward violation of one-name-per-concept. TypeScript already solved it; Python diverged, so the two bindings now disagree on the name of the same concept, which breaks the parity story the topology file asserts. + +### `execution-result-members-not-exported` — Two of the six members of the public ExecutionResult union are not importable from auths +- **Lane:** python · **Goal:** simple · **Area:** python +- **Evidence:** `bindings/python/python/auths/_product.py:151-158`: +``` +ExecutionResult = Union[Completed, PlanCompleted, Denied, Indeterminate, RecoveryResult, PlanRecoveryResult] +``` +`PlanCompleted` (`:135-140`) and `PlanRecoveryResult` (`:142-149`) are in `_product.__all__` (`:473-474`) but absent from both `auths.__all__` (`__init__.py:50-86`) and `_OWNERS` (`__init__.py:88-124`), so `auths.PlanCompleted` raises `AttributeError` from `__getattr__` at `:130`. They are returned by the public `Auths.execute(plan=...)` path via `_project_plan_execution` (`_product.py:432-456`, called at `:229`). TypeScript exports the narrowed aliases Python lacks — `bindings/typescript/src/product.ts:130-131` `SingleExecutionResult` and `PlanExecutionResult`. +- **Disposition:** Add `PlanCompleted` and `PlanRecoveryResult` to `__init__.py` `__all__`/`_OWNERS` and regenerate `api/public-api.txt`; also add `SingleExecutionResult`/`PlanExecutionResult` aliases to match `bindings/typescript/src/product.ts:130-131`, or split `execute` into `execute_action`/`execute_plan` so the return type is exact and the six-way union disappears. +- **Rationale:** A caller cannot annotate or `isinstance`-narrow two of the values the public API returns without importing from `auths._product`. The single overloaded `execute(action=..., plan=...)` with a mutual-exclusion check at runtime (`_product.py:223-231`) is what forces the six-way union in the first place; TypeScript at least ships the narrowed aliases. + +### `create-auths-two-meanings` — create_auths names two different operations in the same package, and disagrees with TypeScript's createAuths +- **Lane:** python · **Goal:** simple · **Area:** python +- **Evidence:** `bindings/python/python/auths/_production_client.py:306-322` `def create_auths(*, endpoint, identity, profile, transport, timeout_seconds) -> ProductionAuths` — a synchronous factory for the HTTPS client, exported as `auths.create_auths` (`__init__.py:123`). `bindings/python/python/auths/integrations.py:327-345` `_Development.create_auths(*, authority, approval, observer) -> _PendingAuths` — an awaitable/async-context factory for the local `Auths`, exported as `auths.integrations.development.create_auths`. `bindings/python/README.md:27` uses `async with development.create_auths(authority=...)` and `README.md:43-46` uses `from auths import create_auths` / `create_auths(endpoint=..., identity=..., profile=...)` — the same identifier, two unrelated meanings, in one document. TypeScript instead overloads a single symbol: `bindings/typescript/src/product.ts:353-355` `createAuths(configuration: AuthsConfiguration): Promise` / `createAuths(configuration: ProductionAuthsOptions): ProductionAuths`, dispatching on shape. So `createAuths` in TS can return the local waist; `create_auths` in Python never can. +- **Disposition:** Resolve alongside the two-waists finding: one `create_auths` with one meaning. Do not port TypeScript's shape-sniffing overload — that is the TypeScript defect, not the target. Whichever waist survives should own the name; the other factory should be named for what it is or removed. +- **Rationale:** The top-level `create_auths` and the composition-layer `create_auths` differ in return type, sync-vs-async shape, and lifecycle contract. A reader who learns one has learned nothing about the other, and the two bindings' identically-named entry points return different things — the parity claim in `bindings/public-topology-v1.json` does not hold for the single most-used symbol. + +### `vertical-roster-triplicated-in-python` — The qualified production vertical roster is hardcoded three times in Python and is Rust-owned data +- **Lane:** python · **Goal:** extensible · **Area:** python +- **Evidence:** Copy 1 — `bindings/python/python/auths/profiles/__init__.py:24-28`: +``` +ProductionProfileId = Union[Literal["auths.opentofu.saved-plan-apply/1"], + Literal["auths.postgresql.bounded-update/1"], Literal["auths.github.issue-address/1"]] +``` +plus three constructors at `:36-45`. Copy 2 — `bindings/python/python/auths/_production_client.py:375-379` `_PROFILE_IDS = (...)` with the same three strings, used for validation at `:169`. Copy 3 — `_production_client.py:409-413`, a dict literal mapping each id to its endpoint path. A fourth roster, with a different membership, lives at `bindings/public-topology-v1.json` `qualifiedProfiles` (four entries, including `auths.mcp/1`). A fifth, disagreeing, roster is at `bindings/python/python/auths/_doctor.py:41` `profiles=("mcp/1",)`. TypeScript duplicates the first copy at `bindings/typescript/src/profiles.ts:4-22`. Contrast `_error_registry.py` and `_mcp_profile.py`, which are generated from Rust by `xtask/src/error_registry.rs:10` and `xtask/src/mcp_session_contract.rs:6` and semantic-frozen at `xtask/src/semantic_freeze.rs:300,423`. +- **Disposition:** Generate the roster from Rust the way `_error_registry.py` already is: emit a `_profiles.py` from an xtask task, derive `ProductionProfileId`, the constructors, `_PROFILE_IDS` and the endpoint-path map from it, and add it to the semantic-freeze set. Fix `_doctor.py:41` to read the generated roster instead of the hardcoded `("mcp/1",)`. +- **Rationale:** Adding one qualified vertical currently means editing at least three Python locations plus the topology file plus the doctor, with nothing that fails when they drift — and they have already drifted, since `doctor()` reports only `mcp/1` to a caller who is about to use `github_issue_address()`. The repo already has the right pattern for exactly this kind of Rust-owned table; the profile roster just was not put on it. + +### `private-surface-larger-than-public` — 362 __all__-declared symbols in underscore modules versus 159 public, none covered by any gate +- **Lane:** python · **Goal:** simple · **Area:** python +- **Evidence:** `bindings/python/api/public-api.txt` totals 159 symbols across the 7 modules in `bindings/public-topology-v1.json`: `auths` 35, `auths.identity` 19, `auths.verify` 36, `auths.profiles` 20, `auths.integrations` 5, `auths.framework` 11, `auths.testkit` 33. Measured at runtime, the underscore modules declare 362 `__all__` symbols: `_workflow` 55, `profiles/_mcp` 47, `_authority` 38, `_application_profile` 28, `_receipts` 25, `_trust` 24, `_inspection` 19, `_production_client` 19, `_product` 18, `_lifecycle` 17, `_approvals` 12, `_product_errors` 12, `_conformance` 10, `_custody` 9, `_errors` 8, `_diagnostics` 7, `_observability` 4, `_bootstrap` 2, `_development` 2, `_mechanism_conformance` 1. Plus 147 in `auths._native`. All 24 underscore modules are mandatory wheel contents (`tools/check_wheel.py:8-41`). `tools/check_public_api.py:9-16` reads only the topology, so none of the 509 non-topology symbols is snapshotted. Concretely private-in-name-only: `_workflow.py` is imported by 12 modules including `_product.py:39` and `integrations.py:44`; `_workflow.AuthsClient` is the real client class and is not public at all, giving four names for the client concept (`Auths`, `AuthsClient`, `AttachedAgent`, `ProductionAuths`). Tests reach past the boundary as a matter of course: `tests/test_product_errors.py:8` imports from `auths._product_errors`, `:20` from `auths._workflow`. +- **Disposition:** Delete `_application_profile.py` (28 symbols, imported by nothing — see the define_profile finding) and `_errors.py` (8 symbols — see the vocabulary finding). Strip `__all__` from the remaining underscore modules so they stop advertising a surface, or move genuinely internal machinery under `auths/_internal/`. Extend `tools/check_public_api.py` to snapshot `auths._native` and to fail when an underscore module grows a new `__all__` entry. +- **Rationale:** A shadow surface 3.2x the size of the public one, entirely unguarded, is where every leak in this audit lives — the generic vertical machinery, the second error vocabulary, the generic profile factory. `__all__` on a private module is a contradiction: it declares intent to export from something that claims not to be exported. The one gate that exists is scoped so it cannot see any of it. + +### `authserrorcode-is-bare-str` — AuthsErrorCode is an alias for str, so Python cannot distinguish known registry codes from unknown ones +- **Lane:** python · **Goal:** secure · **Area:** python +- **Evidence:** `bindings/python/python/auths/_product_errors.py:13`: `AuthsErrorCode = str`. It is exported as a headline public symbol at `__init__.py:24,93` and `bindings/python/api/public-api.txt:6`. TypeScript derives the union from the generated registry — `bindings/typescript/src/product-errors.ts:5`: `export type AuthsErrorCode = Definition["code"] | (string & {});` — which gives autocomplete over all 45 codes, exhaustiveness checking, and still tolerates forward-compatible unknowns. Python has the registry available in the same package (`_error_registry.ERROR_REGISTRY`, 45 definitions, byte-identical to `product/errors/v1/registry.json`) and does not use it for typing. The runtime path does distinguish known from unknown correctly (`_product_errors.py:212-214` falls back to `_unknown_details` at `:280-306`, yielding `family="unknown", retry=UNKNOWN, effect=UNKNOWN, recommended_action=CONTACT_SUPPORT` — a correct fail-closed default), so the gap is purely at the type level and purely self-inflicted. +- **Disposition:** Generate `AuthsErrorCode` as a `Literal[...]` of the 45 registry codes from `_error_registry.ERROR_REGISTRY` in the same xtask pass that writes it (`xtask/src/error_registry.rs:10`), matching `bindings/typescript/src/product-errors.ts:5`. Keep the runtime `_unknown_details` fallback exactly as-is for forward compatibility. +- **Rationale:** `AuthsErrorCode = str` conveys nothing a bare `str` annotation would not, so exporting it as a public type is pure noise — it is one of only 35 top-level symbols. More importantly it means a Python caller writing a code handler gets no compile-time signal when they misspell a code or fail to handle a possible-effect one, while a TypeScript caller does. Same registry, same codes, two different guarantees. + +### `two-classes-named-authserror-python` — Python ships two different classes named AuthsError; `except auths.AuthsError` does not catch what the SDK raises +- **Lane:** python · **Goal:** secure · **Area:** error-model +- **Evidence:** bindings/python/python/auths/_product_errors.py:110 `class AuthsError(Exception)` (registry-bound) is what `auths.AuthsError` resolves to — auths/__init__.py:22-26, 54, 92. bindings/python/python/auths/_errors.py:30 `class AuthsError(Exception)` (shadow vocabulary) is what `auths._workflow.AuthsError` resolves to — _workflow.py:58-59 imports it and re-exports it in `__all__` at _workflow.py:1759. Every exception the workflow, plan, approval, and application-profile layers actually raise derives from the second one: `AuthsWorkflowError` (_errors.py:52), `ProviderOperationError` (_errors.py:82), `RuntimeStateError` (_errors.py:110), `McpGatewayError`/`McpGatewayCancelled` (profiles/_mcp.py:408, 673). Neither class is a subclass of the other. +- **Disposition:** Delete `_errors.AuthsError` along with the rest of `_errors.py`; make every raised SDK exception a subclass of the single `_product_errors.AuthsError`, carrying registry-bound `AuthsErrorDetails`. +- **Rationale:** A developer who reads the public package docs and writes `except auths.AuthsError:` around an execute call catches nothing — the raised exception is a same-named class from a private module. This is a silent catastrophic failure of the one thing an error hierarchy is for, and it is invisible to a reader because the names are identical. + +### `unknown-code-drops-recovery-handle` — The forward-compatibility path for unknown codes discards executionReference, making a future possible-effect code unreconcilable +- **Lane:** cross · **Goal:** secure · **Area:** error-model +- **Evidence:** bindings/typescript/src/product-errors.ts:228-253 `parseUnknownDetails` returns `family:"unknown", operation:"unknown", stage:"unknown", summary:"Unknown Auths error code", retry:"unknown", effect:"unknown", recommendedAction:"contact-support"` and constructs the frozen object with no `executionReference`, `decisionReference`, or `receiptReference` key at all — the incoming reference is read only by `parseToken` on operation/stage and then dropped. bindings/python/python/auths/_product_errors.py:280-306 `_unknown_details` is identical and sets `execution_reference=None` at line 302. The behavior is pinned as intended by bindings/typescript/test/unit/product-errors.test.js:61-77 (`assert.equal(error.executionReference, undefined)`) and bindings/python/tests/test_product_errors.py:72-87 (`assert error.execution_reference is None`). +- **Disposition:** Preserve the token-validated `executionReference` on the unknown-code path and set `recommendedAction` to `resume-and-reconcile` when a reference is present. Model "unknown code" as an explicit third state rather than by nulling the recovery handle. +- **Rationale:** The forward-compat story is otherwise good — both bindings refuse to infer retry/effect for an unregistered code and fall back to the conservative `unknown`/`contact-support`. But an older binding meeting a newer `possible`-effect code loses the only handle that can resolve the ambiguity, so the workflow becomes permanently stuck rather than reconcilable. `contact-support` is fail-closed but is not a recovery path, and the reference has already been validated as a bounded token by `parseToken`/`_token` before being thrown away. + +### `binding-invented-effectstate-unknown` — Both bindings add an EffectState variant that Rust does not have, then emit it in support bundles Rust cannot parse +- **Lane:** cross · **Goal:** secure · **Area:** error-model +- **Evidence:** Rust `EffectState` is closed at three variants — product/errors/auths-errors/src/lib.rs:38-44 `NotApplied, Possible, Applied`. TypeScript widens it: bindings/typescript/src/product-errors.ts:8 `export type EffectState = Definition["outcomes"][number]["effect"] | "unknown";` and product-errors.ts:6 does the same for `ErrorFamily`. Python widens it as a real enum member: bindings/python/python/auths/_product_errors.py:27-31 `class EffectState(str, Enum): ... UNKNOWN = "unknown"`. Those values are emitted through `AuthsErrorDetails.to_dict()` (_product_errors.py:93) / `toJSON()` (product-errors.ts:81-83) and embedded in support bundles (`create_support_bundle` _product_errors.py:182; `createSupportBundle` product-errors.ts:143-148). Round-tripping such a bundle through `auths_errors::ErrorEnvelope::parse` fails, because serde has no `unknown` variant for `EffectState`. The token also collides with `RetryClass::Unknown`, which is a real Rust variant. +- **Disposition:** Add the unknown-code projection to the Rust registry crate — either an `EffectState::Unknown` variant plus the corresponding envelope rule, or a distinct `UnregisteredError` shape — and generate the binding types from it. No binding may add a variant to a Rust-owned closed enum. +- **Rationale:** Rust owns the semantics of "what we know about the effect." Two bindings independently invented a fourth state for it, with a token that already means something else in the sibling enum. The result is a support bundle — the artifact intended for cross-team incident analysis — that the reference implementation cannot deserialize. + +### `recommended-action-unreachable-outside-rust` — recommendedAction and effect are computed in Rust but reachable by no TypeScript or Python caller +- **Lane:** cross · **Goal:** secure · **Area:** error-model +- **Evidence:** Rust computes them: product/runtime/auths-runtime/src/production.rs:1132-1149 `status_classification` maps `LifecycleState` to `(EffectState, RecommendedAction)` — `Committed => (Applied, InspectReceipt)`, `Executing | OutcomeUnknown => (Possible, ResumeAndReconcile)` — exposed via `WorkflowStatus::effect_state`/`recovery_action` (production.rs:1066-1073); `auths-operations/src/lib.rs:584-585,639-645` does the same. The node HTTP surface has the endpoint: product/runtime/auths-node/src/api.rs:135 `.route("/v1/workflows/{reference}", get(workflow_status))`. Neither SDK client can call it: bindings/typescript/src/production-client.ts:139-149 `interface ProductionAuths` has only create/delegate/execute/resume/verify, and `endpointPath` (production-client.ts:372-380) has no status route; bindings/python/python/auths/_production_client.py is the same. Nothing in the local path carries it either — see product.ts:70-78 and _product.py:69-78. Consequently `RecommendedAction` is exported as a public root type (bindings/typescript/api/public-api.txt:41) that no value in the API ever has. +- **Disposition:** Add a `status(reference)` operation to `ProductionAuths` in both bindings returning the Rust-owned `(state, effect, recommendedAction, receiptId)` projection, and attach `recommendedAction`/`effect` to every failure result on the local path. +- **Rationale:** Item 6 of the audit brief asked whether `RetryExecution`/`ResumeAndReconcile`/`InspectReceipt` are reachable in all three languages. They are Rust-only. A TypeScript caller who receives `ProductionIndeterminate { retry: "reconcile" }` is told to reconcile and given neither a reference nor a status call with which to do it. + +### `indeterminate-reconcile-has-no-reference` — The wire contract forbids a recovery reference on exactly the outcome that instructs the caller to reconcile +- **Lane:** rust · **Goal:** secure · **Area:** error-model +- **Evidence:** product/runtime/auths-production-client/src/lib.rs:494-500 — the `ClientOutcomeKind::Indeterminate` shape requires `recovery_reference.is_none()` while allowing `retry` to be `Backoff | Reconcile`. product/runtime/auths-node/src/profiles.rs:34-41 maps `RuntimeFailure::Indeterminate => RetryClass::Reconcile`, and `failure_response` (profiles.rs:272-294) passes `None` for the reference. Projected to callers as bindings/typescript/src/production-client.ts:100-105 `ProductionIndeterminate { kind; step; code; retry: "backoff" | "reconcile" }` and bindings/python/python/auths/_production_client.py:102-107 — no reference field exists on the type. The wire also carries no effect state at all: `NativeProjection` (production-client.ts:166-174) is `{contractVersion, kind, code, retry, recoveryReference, value, receipt}`. +- **Disposition:** Either require a recovery reference whenever `retry == Reconcile` (mirroring the Rust invariant at auths-errors/src/lib.rs:320-328 that `effect == Possible` implies `execution_reference.is_some()`), or delete the `Reconcile` retry class from the indeterminate shape and force those outcomes to `Recoverable`. +- **Rationale:** The Rust registry enforces that a possible effect always carries the handle needed to resolve it. The production wire contract enforces the exact opposite for the outcome that means the same thing, so the strongest safety invariant in the codebase is dropped at the one boundary where it is most needed. + +### `unregistered-codes-on-the-wire` — The node runtime emits codes that are not in the registry, and no layer checks registry membership +- **Lane:** rust · **Goal:** secure · **Area:** error-model +- **Evidence:** product/runtime/auths-node/src/profiles.rs:20-32 `RuntimeFailure::code()` returns `"authority.denied"`, `"authority.indeterminate"`, `"profile.disabled"`, `"workflow.unknown"`, `"receipt.unknown"`, `"receipt.disclosure-denied"` — none of these owners exist in product/errors/v1/registry.json, whose only owners are `core`, `mcp`, `plan`, `custody`. The wire validator accepts anything: product/runtime/auths-production-client/src/lib.rs:893-898 `fn valid_code` checks only length 3..=128 and the character class `[a-z0-9.-]`. `RuntimeFailure` also carries no effect state (profiles.rs:34-41 maps only to a retry class). The free-string drift continues in the status projection: profiles.rs:52-63 `WorkflowProjection { state: String, effect: String, retry: String }`, populated at product/runtime/auths-node/src/sandbox.rs:313-335 with `effect: "unknown"` and `effect: "succeeded"` — neither is an `EffectState` value — and at api.rs:473-481 with `effect: "unknown", retry: "resume"`. +- **Disposition:** Register the runtime failure codes in `auths-errors` (or map each to an existing registry code), type `WorkflowProjection.effect`/`.retry` as the Rust enums rather than `String`, and make `valid_code` a registry-membership check so an unregistered code cannot be encoded onto the wire. +- **Rationale:** The registry's value is that a code implies a fixed `(retry, effect, recommendedAction)` triple. A parallel code space that never touches `auths-errors`, validated only by a character-class regex and projected through untyped `String` effect fields, means the production surface has stable-looking codes with no stable meaning — while `cargo xtask error-registry` reports success. + +### `denied-indeterminate-code-is-plain-string` — The known/unknown code typing required by the consolidation spec is unimplemented in Python and only cosmetic in TypeScript +- **Lane:** cross · **Goal:** extensible · **Area:** error-model +- **Evidence:** docs/target-state/PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:470-489 requires `KnownAuthsErrorCode` / `UnknownAuthsErrorCode = string & { readonly __unknownAuthsCode: unique symbol }`, states "Application-authored arbitrary strings must not type-check as an `AuthsErrorCode`", requires a Python `Literal`/enum plus an explicit unknown type, and requires that "Denial and indeterminacy result types in both bindings must use the same code type." Actual: bindings/typescript/src/product-errors.ts:5 `export type AuthsErrorCode = Definition["code"] | (string & {});` — every string type-checks, and there is no branded unknown type. bindings/python/python/auths/_product_errors.py:13 `AuthsErrorCode = str`. Result types use neither: bindings/typescript/src/product.ts:72,77 `readonly code: string`, production-client.ts:96,102,110,131 `readonly code: string`, bindings/python/python/auths/_product.py:72,78 `code: str`, _production_client.py:97,105,113,139 `code: str`. +- **Disposition:** Generate `KnownAuthsErrorCode` as a literal union / `Literal` from the registry in `xtask/src/error_registry.rs`, add the branded `UnknownAuthsErrorCode` constructible only by the parser, and retype every `code` field on every denial/indeterminacy/recoverable result in both bindings. +- **Rationale:** Without this, a caller's exhaustive `switch` over codes silently accepts typos and cannot be made non-exhaustive-safe when the registry grows. It is also the mechanism by which a new Rust code is forced to surface at the binding boundary instead of flowing through as an anonymous string — the forward-compatibility property the brief asked about. + +### `ts-mcp-outcome-duck-typing` — TypeScript classifies a tool's own result as a handler outcome by duck-typing an `effect` property; Python uses nominal typing +- **Lane:** typescript · **Goal:** secure · **Area:** error-model +- **Evidence:** bindings/typescript/src/profiles/mcp/index.ts:981-985 `function isMcpOutcome(value: unknown): value is McpHandlerOutcome { ... const effect = (value as {effect?: unknown}).effect; return effect === "applied" || effect === "not-applied" || effect === "possible"; }`, consumed at index.ts:957-970 — a handler result that merely contains an `effect` key with one of those three values is treated as a control-plane outcome, its payload discarded (`acceptApplied(session, observed.result)` with `result` undefined encodes `"null"`), and `{effect:"not-applied"}` is accepted as an authoritative claim of non-effect. Python uses `isinstance(observed, McpHandlerOutcome)` — bindings/python/python/auths/profiles/_mcp.py:1337 — so an equivalent dict is treated as a plain result. +- **Disposition:** Make `McpHandlerOutcome` a sealed class or brand it with a non-enumerable symbol, and have `isMcpOutcome` test that brand. Never infer control-plane meaning from the shape of provider payload data. +- **Rationale:** Tool results describing infrastructure changes plausibly contain a field named `effect`. In TypeScript such a result silently becomes a non-effect declaration — the result is lost and the workflow records that nothing happened — while the identical handler in Python records the effect as applied. Two bindings disagreeing about whether an effect occurred, from the same provider payload, is a semantic divergence in the highest-stakes field in the system. + +### `ts-createauths-endpoint-sniffing` — createAuths dispatches on the presence of an `endpoint` property and returns a Promise in one arm and a bare object in the other +- **Lane:** cross · **Goal:** simple · **Area:** typescript +- **Evidence:** `src/product.ts:353-362`: +```ts +export function createAuths(configuration: AuthsConfiguration): Promise; +export function createAuths(configuration: ProductionAuthsOptions): ProductionAuths; +export function createAuths(configuration: AuthsConfiguration | ProductionAuthsOptions): Promise | ProductionAuths { + if ("endpoint" in configuration) return createProductionAuths(configuration); + const resources = configurationResources.get(configuration); + if (resources === undefined) throw new TypeError("Auths configuration was not created by an integration"); + return resources.open().then((opened) => new AuthsFacade(opened, resources.diagnostics)); +} +``` +The two arms return unrelated interfaces (`src/product.ts:133-163` vs `src/production-client.ts:139-149`) and differ in asynchrony. Concrete caller failures: (1) a wrapper `function make(c: AuthsConfiguration | ProductionAuthsOptions) { return createAuths(c); }` does not compile — no overload accepts the union — forcing a cast, and a cast to the wrong arm silently succeeds because `await` on a non-thenable yields the value, so `const a: Auths = await createAuths(prodOptions as never)` produces a `ProductionAuthsClient` typed as `Auths`; the first `a.execute({action, provider})` lands on `ProductionAuthsClient.execute(authority, action)` with the options object bound to `authority` and fails at `src/production-client.ts:464` with `"forged Auths authority"` — a message that describes nothing about the actual mistake. (2) A typo in `endpoint` (`endPoint`, `url`) skips the production branch and dies at `:360` with `"Auths configuration was not created by an integration"`, again describing the wrong error. (3) The SDK's own tests cannot tell the arms apart: `test/integration/production-client.test.js:56` writes `const auths = await createAuths({ endpoint: "https://operator.example", ... })`, awaiting a synchronously returned object. (4) `"production"` names two unrelated things — `src/integrations.ts:136-143` `production.createAuths(configuration)` requires `configuration.mode === "production"` and returns a local `Auths`, while `createAuths({endpoint})` returns the remote client. +- **Disposition:** Delete the overload at `src/product.ts:353-362` and remove `createAuths` from `src/index.ts:16`. Replace with two explicitly named async constructors, one per authoritative module, both returning a Promise so the call shape is uniform: `openLocalAuths(configuration: AuthsConfiguration): Promise` exported only from `@auths-dev/sdk/integrations` (it already gates on the `configurationResources` WeakMap at `src/product.ts:31`, so it belongs beside `development`/`production` which mint the configuration), and `connectAuthsService(options: AuthsServiceOptions): Promise` exported only from the new `@auths-dev/sdk/service` subpath. Delete `src/product.ts:25-29` so the local facade no longer imports the remote client. Update `api/public-api.txt` and `test/package/packed-consumer.test.js:32,47`. +- **Rationale:** Runtime property sniffing to choose between two unrelated implementations is exactly the dual-path construction the prelaunch policy forbids, and it defeats the type system in the one place a caller most needs it: a mis-typed configuration selects a different transport, a different code space, and a different set of security assumptions, and every resulting error message points at the wrong cause. The mixed Promise/non-Promise return means neither the compiler nor `await` can catch the mistake. + +### `ts-domains-generic-vertical-leak` — profiles/domains builds five verticals on one generic ApplicationAction, with zero importers anywhere in the repository +- **Lane:** cross · **Goal:** extensible · **Area:** typescript +- **Evidence:** `src/profiles/domains/index.ts:85-119` defines five verticals entirely as instantiations of one generic type family: `export type HttpAction = ApplicationAction` (`:85`), `GitAction = ApplicationAction` (`:92`), `DeploymentAction = ApplicationAction` (`:99`), `SupplyChainAction = ApplicationAction` (`:106`), `EdgeAction = ApplicationAction` (`:113`), each with a matching `ApplicationCommand<…>`, `ApplicationGateway<…>`, and `ApplicationProfile<…,…>`. The generic machinery is imported from the shared module at `:1-10` (`import { type ApplicationAction, type ApplicationCommand, type ApplicationGateway, type ApplicationProfile, type CanonicalProfileAction, defineProfile } from "../application/index.js"`), so canonicalization for deploy, git, edge, supply-chain, and http lives in `src/profiles/application/index.ts`, not in any vertical. Repository-wide grep for `profiles/domains` and `domains/index` across `src/`, `test/`, and `tools/` returns no importer other than the file itself. It is nonetheless compiled and shipped: `package.json` `"files": ["dist", …]` and `dist/profiles/` is present. `bindings/public-topology-v1.json:31-36` lists exactly four `qualifiedProfiles` (`auths.github.issue-address/1`, `auths.mcp/1`, `auths.opentofu.saved-plan-apply/1`, `auths.postgresql.bounded-update/1`) — none of the five domain verticals is qualified. `src/profile-kit.ts:1` `export * from "./profiles/application/index.js"` publishes the generic machinery as its own shipped module. +- **Disposition:** Delete `src/profiles/domains/index.ts` entirely — it has no consumer, no qualified profile, and no test. It is the TypeScript instance of the same generic-vertical coupling already confirmed at `product/sdk/auths-sdk/src/lib.rs:26` and `product/integrations/auths-deployment/src/lib.rs:7`, so removing it now avoids re-creating the leak in a second language. Delete `src/profile-kit.ts:1` (a one-line re-export with only two test consumers, `test/integration/inspection.test.js:10` and `test/integration/profiles/mcp.test.js:20`, which can import `src/profiles/application/index.js` directly) so the generic action machinery is not a shipped public module. If domain verticals are wanted later, add them one at a time under `bindings/public-topology-v1.json` `qualifiedProfiles` with vertical-owned canonicalization, per PROFILE_AND_DOMAIN_ABSTRACTION_BOUNDARY_PLAN. +- **Rationale:** Five shipping verticals canonicalize through one shared generic action type, which puts the meaning of 'what was authorized' for deployments, git pushes, and edge firmware into a generic module instead of each vertical. Because it travels through the `profile-kit.ts` facade re-export, a crate- or module-graph audit sees only `profiles/application` and misses the coupling — the leak is only visible in source, exactly as described for the Rust case. That it is dead code makes deletion cheap and makes retaining it pure risk. + +### `ts-npm-tarball-ships-orchestrator-hooks` — The published tarball ships dist/workflow.js, which star-exports capability-granting orchestrator hooks the exports map cannot protect +- **Lane:** cross · **Goal:** secure · **Area:** typescript +- **Evidence:** `src/workflow.ts` is two lines: `export * from "./workflow/contracts.js"; export * from "./workflow/internal/orchestrator.js";`. The orchestrator exports capability-granting accessors: `src/workflow/internal/orchestrator.ts:816` `export function signerForClient(client: AuthsClient): Signer`, `:825` `export function engineForClient(client: AuthsClient): WorkflowWasmEngine`, `:854` `export function trustedContextForClient`, `:882` `export function resourcesForAttachedAgent`, `:895` `export function createDelegatedAttachedAgent`, `:281` `export { registerProfileRuntime }`. `package.json` `"files": ["dist", …]` ships the whole tree; `dist/` contains `workflow.js`, `workflow-client.js`, `internal-sdk.js`, `mcp.js`, `profile-kit.js`, `authority.js`, `plans.js`, `authorization-plans.js`, `custody.js`, `product-errors.js`, `observability.js`, `diagnostics.js`, `inspection.js`, `receipt-inspection.js`, `runtime-contract.js`, `internal/`, `profiles/domains/` — none of which appear in the `package.json` `exports` map. The guard that is supposed to cover this checks the wrong thing: `test/package/package.test.js:124-140` `"published entry points omit package coordination hooks"` imports only `dist/index.js`, `dist/verify.js`, `dist/identity.js`, `dist/framework.js` and asserts the six hook names are not re-exported from those four — it never asserts the hooks are absent from the tarball, and `dist/workflow.js` re-exports all six. The exports map is not a boundary for non-Node consumers, and the SDK's own browser test proves it: `test/package/packed-browser.mjs:48-49` imports `/node_modules/@auths-dev/sdk/dist/integrations.js` and `/node_modules/@auths-dev/sdk/dist/profiles.js` by raw path. +- **Disposition:** Stop shipping unexported modules. Either narrow `package.json` `"files"` to the exact built entry points named in `bindings/public-topology-v1.json` plus their private chunks under a `dist/_internal/` prefix, or bundle each public entry so no internal module is separately addressable. Strengthen `test/package/package.test.js:124-140` to enumerate every `.js` in the packed tarball and assert that no packed module exports any of the six coordination hooks, rather than checking four hand-listed entry files. Delete `src/workflow.ts` as part of the alias-chain cleanup so `export * from "./workflow/internal/orchestrator.js"` no longer exists at all. +- **Rationale:** `signerForClient` returns the live Signer for an active client and `engineForClient` returns the WASM engine that selects the authorized branch; `createDelegatedAttachedAgent` mints delegated agents. These are capability grants, and the only thing preventing a consumer from reaching them is Node's exports map — which browsers and any bundler configured to ignore `exports` do not enforce, as the SDK's own browser test demonstrates by importing `dist/` paths directly. The existing test creates a false assurance that the hooks are not published. + +### `ts-production-routing-table-duplicated-with-open-fallback` — The production profile→route table is re-implemented in TypeScript with an unguarded fallback that routes unknown verticals to the GitHub executor +- **Lane:** cross · **Goal:** extensible · **Area:** typescript +- **Evidence:** `src/production-client.ts:372-380`: +```ts +function endpointPath(step: ProductStep, profile: ProductionProfile["id"]): string { + if (step === "create") return "/v1/authority/create"; + ... + if (profile === "auths.opentofu.saved-plan-apply/1") return "/v1/profiles/opentofu/saved-plan-apply/execute"; + if (profile === "auths.postgresql.bounded-update/1") return "/v1/profiles/postgresql/bounded-update/execute"; + return "/v1/profiles/github/issue-address/execute"; +} +``` +The final line is an unconditional fallback, not an exhaustiveness check — the function returns `string`, so adding a fourth id to `ProductionProfileId` (`src/profiles.ts:4-7`) and to the allowlist at `src/production-client.ts:364-370` compiles cleanly and silently POSTs every execution for the new vertical to the GitHub issue-address executor. Rust already owns this table and gets it right by construction: `product/runtime/auths-production-client/src/lib.rs:202` `Self::OpenTofuSavedPlanApply => "/v1/profiles/opentofu/saved-plan-apply/execute"` inside an exhaustive `match` on an enum. Python duplicates it a third time at `bindings/python/python/auths/_production_client.py:410`. Adding one production vertical today requires four TypeScript edits — `src/profiles.ts:4-7` (the id union), `src/profiles.ts:21-23` (a factory), `src/production-client.ts:364-370` (the allowlist), `src/production-client.ts:372-380` (the route) — plus the Rust and Python copies. +- **Disposition:** Delete `src/production-client.ts:364-380` and `src/profiles.ts:4-23`. The WASM boundary already carries the request encoder (`bindings/wasm/auths-proof-wasm/src/lib.rs:91` `encodeProductionRequestV1`); extend it with a `productionRoutePathV1(profileId, verb)` export backed by the exhaustive Rust match at `product/runtime/auths-production-client/src/lib.rs:202`, and have TypeScript call it. The profile-id allowlist then also comes from Rust, so adding a vertical is one Rust edit and zero binding edits. If the route must stay in TypeScript for any reason, make it exhaustive with a `never`-typed default so the compiler refuses an unhandled id. +- **Rationale:** The file-touch cost of adding a production vertical is currently six sites across three languages for a fact Rust already owns exhaustively, which is the EXTENSIBLE metric failing. The unguarded fallback converts that duplication into a security defect with a concrete trigger: a partially-completed vertical addition executes against the wrong service, and neither the compiler nor any test catches it. + +### `ts-locally-authored-decision-codes-and-explanations` — TypeScript mints decision codes and remediation prose that do not exist in Rust, and reduces retryability to a kind-keyed boolean +- **Lane:** cross · **Goal:** secure · **Area:** typescript +- **Evidence:** `src/workflow/internal/authority.ts:76-87` mints stage, code, verification state, and English prose in TypeScript: +```ts +explanation: Object.freeze({ + stage: "attach", + code: binding === "root" ? "root-authority-structurally-bound" : "delegated-authority-structurally-bound", + verification: "pending-authorization", + message: binding === "root" ? "Canonical root authority is bound; cryptographic and live checks remain pending authorization." : "Canonical delegated authority is bound; ...", +}) +``` +Repository-wide grep for `structurally-bound`, `structurally_bound`, and `pending-authorization` across all `*.rs` returns nothing — these codes exist only in TypeScript. `src/verifier/explanation.ts:3-11` authors the verifier's remediation text and retryability from the verdict kind alone, discarding the stable code: +```ts +export function explain(kind: VerdictKind, code: string): Explanation { + if (kind === "authorized") return { code, message: "the proof establishes exact authority for this action", retryable: false }; + if (kind === "indeterminate") return { code, message: "a required trustworthy fact or implementation is unavailable", retryable: true }; + return { code, message: "the supplied proof does not authorize this exact action", retryable: false }; +} +``` +Rust already owns per-reason remediation: `core/crates/auths-proof/src/lib.rs:235-266` maps each `DenialReason` to specific text (`D::PermissionNotGranted | D::ActionConstraintMismatch | D::BudgetCeilingExceeded | D::DelegationExpanded => "narrow the requested action or issue sufficient authority"`), and `requirement_message` does the same per `Requirement`. None of it is exposed over WASM — `bindings/wasm/auths-proof-wasm/src/lib.rs` has no explanation or remediation export. The locally-minted boolean is then published as evidence: `src/inspection.ts:78-83` `safeToLog: Object.freeze({ kind, stage, code, retryable: result.explanation.retryable })`. +- **Disposition:** Add a WASM export that returns the Rust-owned denial/requirement remediation for a decoded result (backed by `core/crates/auths-proof/src/lib.rs:235-266`), and replace `src/verifier/explanation.ts:3-11` with a call to it; delete the three hardcoded sentences. Replace `Explanation.retryable: boolean` (`src/verifier/result.ts:21`) with the registry `RetryClass`. Move the four `explanation` fields minted at `src/workflow/internal/authority.ts:76-87` into the native authority projection so `authoritySummary` copies them rather than authoring them, or delete the `explanation` field from `EffectiveAuthoritySummary` if no consumer needs it. +- **Rationale:** Rust owns meaning; these are three places where TypeScript states what a decision means. `retryable: true` for every indeterminate regardless of code is a fail-open-flavored default that ignores the stable code the kernel returned, and it is surfaced through `inspectDecision`'s `safeToLog` as if it were kernel-derived evidence. The `structurally-bound`/`pending-authorization` codes look like stable identifiers to a consumer and have no Rust definition to be stable against. + +### `ts-internal-sdk-second-export-list` — internal-sdk.ts is a second SDK-shaped export list with no source consumer, kept alive only so tests can bypass the public API +- **Lane:** cross · **Goal:** simple · **Area:** typescript +- **Evidence:** `src/internal-sdk.ts:1-25` exports a full client surface: `export * from "./verifier/authority.js"` plus `AuthsClient, AuthsWorkflowError, AttachedAgent, ProviderOperationError, SignedGrantSource, TrustedContextSource, loadAuths, signedGrantSource, trustedContextSource` and eleven types from `workflow-client.js`, plus `ProfilePlan, VerifiedPlanCommand` from `plans.js`. No file under `src/` imports it. Its only consumers reach into the build output directly: `test/integration/inspection.test.js:11`, `test/integration/workflow/delegation.test.js:9`, `test/integration/workflow/attach.test.js:11`, `test/integration/workflow/client.test.js:10`, `test/integration/helpers/mcp-fixture.js:7`, `test/integration/profiles/mcp.test.js:11` — all `from "../../dist/internal-sdk.js"`. It is absent from `package.json` `exports` and from `api/public-api.txt`. Ten further modules are exercised the same way by tests through `dist/` paths that no installed consumer can reach: `dist/mcp.js`, `dist/profile-kit.js`, `dist/diagnostics.js`, `dist/authorization-plans.js`, `dist/observability.js`, `dist/custody.js`, `dist/approvals.js`, `dist/inspection.js`, `dist/runtime-contract.js`, `dist/product-errors.js`. `src/workflow-client.ts` (67 lines re-exporting 50 names from `workflow.js` plus `loadAuths` at `:65`) has exactly two consumers: `src/integrations.ts:19` (which needs only `loadAuths`) and `src/internal-sdk.ts:12,24`. +- **Disposition:** Delete `src/internal-sdk.ts` and `src/workflow-client.ts`. Move `loadAuths` (`src/workflow-client.ts:65-67`) into `src/integrations.ts`, its only real caller. Re-point the six integration tests at the modules they actually exercise (`src/workflow/internal/orchestrator.js`, `src/plans.js`, `src/verifier/authority.js`) rather than at a bespoke barrel, and re-point the ten `dist/*` test imports at `src/` so the test suite stops validating a surface no consumer can reach. Add a package test that fails if any module in the tarball is importable but absent from `bindings/public-topology-v1.json`. +- **Rationale:** A second SDK-shaped export list that exists solely to let tests reach past the public API means the tests are not testing the shipped product, and it keeps two competing client barrels (`internal-sdk.ts` and `workflow-client.ts`) alive on top of the already-competing `product.ts`/`production-client.ts` pair. Deleting it removes the surface and forces the tests onto the real one. + +### `ts-alias-module-chain` — Five one-line alias modules and a four-hop re-export chain for the same nine custody symbols +- **Lane:** cross · **Goal:** simple · **Area:** typescript +- **Evidence:** Pure aliases with no content: `src/mcp.ts:1` `export * from "./profiles/mcp/index.js";` — duplicating `src/profiles.ts:2` which star-exports the same module; `src/profile-kit.ts:1` `export * from "./profiles/application/index.js";`; `src/authority.ts:1` `export * from "./authorization-plans.js";`; `src/workflow.ts:1-2` `export * from "./workflow/contracts.js"; export * from "./workflow/internal/orchestrator.js";`. The custody chain re-exports the identical nine symbols (`ProviderOperationError, ControlEvidence, PrincipalDescriptor, ProviderFailureKind, Signer, SignerLifecycle, SigningObjectKind, SigningRequest, SigningResponse`) four times: defined in `src/workflow/errors.ts` and `src/workflow/contracts.ts` → re-exported by `src/workflow/internal/orchestrator.ts:16-25` → `src/workflow.ts:2` → `src/custody.ts:1-11` → `src/framework.ts:5-15`. Two unrelated plan vocabularies coexist, neither reachable from any public subpath: `ProfilePlan`/`VerifiedPlanCommand`/`PlanAuthoritySummary` (`src/plans.ts:28,37`) and `AuthorizationPlan`/`AuthorizationPlanBuilder`/`AuthorizationPlanKind`/`ProofReference` (`src/authorization-plans.ts:8,19,42`). Two `createSupportBundle` functions with two schemas ship in the same package: `src/product-errors.ts:134` returning `schema: "auths.support/2"` and `src/observability.ts:88` returning `schemaVersion: "auths.support/1"`, alongside two `SupportBundleInput` and two `AuthsSupportBundle` interfaces (`src/product-errors.ts:111,123` and `src/observability.ts:70,78`). +- **Disposition:** Delete `src/mcp.ts`, `src/profile-kit.ts`, `src/authority.ts`, `src/workflow.ts`, `src/custody.ts` and have every importer name the defining module. Collapse the custody chain so `src/framework.ts` imports directly from `src/workflow/contracts.ts`. Pick one support-bundle schema — `auths.support/2` in `src/product-errors.ts:134` is the one that carries registry-validated error details — and delete `src/observability.ts:70-110` along with its duplicate `SupportBundleInput`/`AuthsSupportBundle`. Merge or rename the two plan vocabularies so `plan` means one thing. +- **Rationale:** Nothing in this set adds behavior; each alias adds an import path that must be kept consistent, and the four-hop custody chain means a change to a Signer type is invisible at three of the four sites that publish it. Two `createSupportBundle` functions emitting two different schema strings from one package is a diagnostic-surface fork that will produce unparseable support artifacts. + +### `lifecycle-acknowledgement-not-semantically-bound` — `execute_store_transaction` claims to reject a dishonest acknowledgement but never compares the returned record's execution intent with the intent it submitted +- **Lane:** cross · **Goal:** secure · **Area:** security +- **Evidence:** product/runtime/auths-lifecycle/src/sealed.rs:37-39 documents `StoredTransitionV1` as a trusted-adapter claim that "`execute_store_transaction` independently validates its identity, revision, and trace before granting a sealed side-effect token". +product/runtime/auths-lifecycle/src/sealed.rs:126-172 `validate_acknowledgement` checks only: `transaction.workflow_id != *stored.record.workflow_id()`; for `RecordDecision`, `input.workflow_id != transaction.workflow_id`; `stored.record.revision() != expected_revision + 1`; and that the last event and last receipt carry the record's revision. It never inspects `TransitionCommandV1::RecordExecutionIntent(ExecutionIntentV1)` against `stored.record.execution_intent()`. +product/runtime/auths-lifecycle/src/sealed.rs:192-218 `ExecutionAuthorizationV1::from_durable` then lifts `intent.intent_digest()`, `intent.provider_contract_id()`, and `intent.provider_request_digest()` straight out of the store-returned record. +product/runtime/auths-runtime/src/production.rs:648-660 `record_intent` submits the caller's `ExecutionIntentV1` and discards it; :668-681 `authorize_credential` derives the authorization solely `from_durable(&durable)` with no cross-check against the submitted intent. +product/runtime/auths-lifecycle/src/sealed.rs:362-372 `CredentialBroker::acquire(&ExecutionAuthorizationV1)` is keyed on that unvalidated payload. +- **Disposition:** In `validate_acknowledgement`, add semantic-payload equality for every command that carries one: when `transaction.command` is `RecordExecutionIntent(intent)`, require `stored.record.execution_intent() == Some(intent)`; when it is `Commit`/`Release`, require `stored.record.terminal_result() == Some(result_digest)`; when it is `Reconcile`, require the appended observation equals the submitted one. Anything the caller submitted and the store echoes back must be compared, not trusted. +- **Rationale:** The whole point of the sealed `DurableTransitionV1` -> `ExecutionAuthorizationV1` chain is that a compromised or buggy store cannot manufacture execution authority, and the doc comment asserts exactly that. Concrete attack: an attacker with write access to the lifecycle database (or a malicious `LifecycleStore` impl in a dependency) intercepts `RecordExecutionIntent` for a $5 transfer to `ba_alice` and returns a well-formed record for the same `workflow_id` at `expected_revision + 1` whose `execution_intent` names a $50,000 transfer to `ba_attacker`. `validate_acknowledgement` passes on identity and revision. The next `AuthorizeCredential` transition yields an `ExecutionAuthorizationV1` carrying the attacker's `provider_request_digest`, and `CredentialBroker::acquire` mints exact-operation credentials scoped to the substituted request. This is the substitution attack the sealed boundary exists to stop. + +### `definite-non-effect-is-a-caller-assertion` — The `NonEffectNotProved` gate that releases reserved capacity is satisfied by the caller passing `conclusion: NonEffect`; no evidence is bound +- **Lane:** cross · **Goal:** secure · **Area:** security +- **Evidence:** product/runtime/auths-lifecycle/src/transition.rs:303-304 — `definite_effect: conclusion == Some(EffectConclusion::Effect), definite_non_effect: conclusion == Some(EffectConclusion::NonEffect)`, where `conclusion` comes from product/runtime/auths-lifecycle/src/transition.rs:380-385 `command_conclusion`, which simply reads the variant: `Release { conclusion, .. } => Some(*conclusion)`. +product/runtime/auths-lifecycle/src/kernel.rs:241-248 — `(Some(LifecycleState::Executing), OperationCode::Release) => { if !gates.attempt_present { AttemptMissing } else if !gates.definite_non_effect { KernelCode::NonEffectNotProved } else { Applied(Released) } }`. +product/runtime/auths-lifecycle/src/kernel.rs:109-110 documents that code as "Definite non-effect evidence is absent." +product/runtime/auths-lifecycle/src/transition.rs:448-453 — `Release` then does `for reservation in &mut record.reservation_entries { reservation.mark_released(); }`, returning capacity. +product/runtime/auths-runtime/src/production.rs:905-922 — the private `release` helper hardcodes `conclusion: EffectConclusion::NonEffect`, and :806-814 `release_provider` exposes it for a `ProviderEntered` stage, i.e. after the provider call was entered. +By contrast the reconciliation path does bind evidence: transition.rs:308-313 `reconciliation_matches` requires `value.provider_request_digest == intent.provider_request_digest()`, and :306-307 `reconciliation_fresh` bounds the observation in time. +- **Disposition:** Make `TransitionCommandV1::Release` from `LifecycleState::Executing` carry the same evidence the `Reconcile` path already requires — a `ReconciliationObservationV1` whose `provider_request_digest` matches the recorded intent and whose freshness window contains `context.verifier_time` — and derive `definite_non_effect` from that observation rather than from the command variant. Remove `release_provider` from `LifecycleCoordinator` so a post-entry release cannot be expressed without evidence; leave `cancel_reserved` and `cancel_intent`, which are structurally pre-attempt. Rename `NonEffectNotProved` only after the gate actually proves something. +- **Rationale:** `EffectConclusion` has four variants including `Unknown` and `Inconclusive`, and `MarkOutcomeUnknown` exists precisely for post-entry ambiguity, so the design clearly intends a post-entry release to be evidence-backed. As written the gate is circular. Concrete attack: an agent's action is authorized, capacity for one $10,000 payout is reserved, the provider call is entered and the payout applies. The agent (or a bug in the caller) invokes `release_provider`, which submits `Release { conclusion: NonEffect }`. The kernel sees `definite_non_effect == true`, transitions to `Released`, and `mark_released()` returns the capacity — so the same ceiling now funds a second $10,000 payout. The workflow terminates recorded as a non-effect, so reconciliation is never scheduled and the receipt chain asserts that nothing happened. + +### `bindings-invent-an-unknown-effect-and-fabricate-not-entered` — TypeScript and Python invent a fourth `effect` value Rust does not own and hardcode `entered.provider = false` for unrecognized error codes, discarding the wire value +- **Lane:** cross · **Goal:** secure · **Area:** security +- **Evidence:** product/errors/auths-errors/src/lib.rs:40-44 — Rust owns a closed three-variant enum: `pub enum EffectState { NotApplied, Possible, Applied }`. product/errors/v1/registry.json contains only `not-applied`, `possible`, `applied`. +product/errors/auths-errors/src/lib.rs:316-332 `validate_recovery` enforces the invariant that makes `Possible` actionable: `if input.effect == EffectState::Possible && (input.retry != RetryClass::Unknown || input.recommended_action != RecommendedAction::ResumeAndReconcile || input.execution_reference.is_none() || !input.entered.provider || input.receipt_reference.is_some()) { return Err(UnsafeRetry); }`. +bindings/typescript/src/product-errors.ts:8 — `export type EffectState = Definition["outcomes"][number]["effect"] | "unknown";` invents the fourth value. +bindings/typescript/src/product-errors.ts:227-251 `parseUnknownDetails` never reads `value.entered` and returns `retry: "unknown", effect: "unknown", entered: Object.freeze({ approval: false, signer: false, state: false, credential: false, provider: false }), recommendedAction: "contact-support"` with no `executionReference`. +bindings/python/python/auths/_product_errors.py:27-31 — `class EffectState(str, Enum): NOT_APPLIED / POSSIBLE / APPLIED / UNKNOWN`. +bindings/python/python/auths/_product_errors.py:280-306 `_unknown_details` likewise ignores `item.get("entered")` and returns `effect=EffectState.UNKNOWN, entered=EnteredBoundaries(False, False, False, False, False), execution_reference=None, recommended_action=CONTACT_SUPPORT`. +The registry itself anticipates skew: `core.unsupported-semantic-subject` and `core.unsupported-abi` exist for it. +- **Disposition:** Delete `"unknown"` from `EffectState` in both bindings so the projection matches Rust's three variants. Replace the fabricated `EnteredBoundaries` in `parseUnknownDetails` / `_unknown_details` with the parsed wire value — an unrecognized code must still carry the boundary flags the runtime reported. If a binding genuinely cannot classify an envelope, it must refuse to project it (raise, as it already does for a bad schema at product-errors.ts:162) rather than synthesize a safer-looking one. Add `entered` to whatever fixture-driven conformance corpus covers `product/errors/v1/registry.json` so a code the binding does not know still round-trips its boundary flags. +- **Rationale:** This is the effect-ambiguity class the whole error registry exists to prevent, and it is the one place where a caller can conclude an effect did NOT happen when it might have. Rust guarantees `Possible => entered.provider && execution_reference.is_some() && recommendedAction == resume-and-reconcile`. The bindings' fallback produces the precise inverse shape: effect not classifiable, provider boundary asserted false, no execution reference to resume from, and 'contact support'. Concrete attack path: a node running a newer Rust runtime returns a provider-ambiguity code the pinned SDK does not have in its generated registry — the realistic version-skew case, since the registry is code-generated per release. An older `@auths-dev/sdk` or `auths` wheel decodes it via the unknown branch. The application's recovery logic reads `error.entered.provider === false` and `error.executionReference === undefined`, concludes the payout never reached Stripe, and retries. The payout runs twice. `effect: "unknown"` alone would be defensible; pairing it with a positively false `entered.provider` is not, because the binding is asserting a fact it discarded. + +### `budget-algebra-id-collides-across-incompatible-units` — `numeric-ceiling-v1` means money-minor-units in one profile and an operation count in five others, while attenuation and the stateful ledger key on the algebra string alone +- **Lane:** cross · **Goal:** secure · **Area:** security +- **Evidence:** core/crates/auths-model/src/lib.rs:906-910 — `pub fn budget_ceiling_attenuates(child, parent) -> bool { byte_slices_equal(child.algebra.0.as_bytes(), parent.algebra.0.as_bytes()) && child.value <= parent.value }`. Equality of the identifier string is the only type check. +Money semantics: product/integrations/auths-stripe/src/merchant/collect/profile.rs:14 `const PAYMENT_BUDGET_ALGEBRA: &str = "numeric-ceiling-v1";` with :107 `action.amount_minor()`. +Count semantics under the same identifier: product/integrations/auths-stripe/src/profile.rs:16 `REFUND_BUDGET_ALGEBRA = "numeric-ceiling-v1"` with :104-108 hardcoding value `1`; product/integrations/auths-kubernetes/src/profile.rs:16 `ROLLOUT_BUDGET_ALGEBRA`; product/integrations/auths-opentofu/src/profile.rs:18 `EFFECT_BUDGET_ALGEBRA`; product/integrations/auths-radicle/src/profile.rs:16 `PUBLICATION_BUDGET_ALGEBRA`; product/integrations/auths-records-api/src/profile.rs:150 and :177. +The stateful ledgers accumulate all of them into one counter keyed by that string: product/stores/auths-stores/src/lib.rs:79 `let algebra = requested.algebra().as_str();` then :89 `let consumed = state.consumed.get(algebra).copied().unwrap_or_default();` and :96 `state.consumed.insert(algebra.to_string(), next);` (same at :246-265). +A trust anchor may allow several profiles at once and starts with `profile: None`: core/crates/auths-authority/src/lib.rs:337-338 `allowed_profiles: anchor.profiles().to_vec(), profile: None`, with selection deferred to the first grant (:124-133 `selected_profile_attenuates`). +- **Disposition:** Give each unit its own budget algebra identifier — e.g. `stripe-minor-units-v1` for money and `operation-count-v1` for counts — registered separately in `auths_registries::TARGET_V1_REGISTRY_MANIFEST` and listed individually in `AcceptedRegistries` (product/sdk/auths-sdk/src/lib.rs:226-228 currently hardcodes the single `NUMERIC_CEILING_V1`). Keep `budget_ceiling_attenuates` as-is; the string equality check then becomes a real unit check. Key the ledger counters on `(algebra, profile_id)` rather than the algebra alone. +- **Rationale:** The brief's core invariant is that semantic ownership must live in the vertical, not in a shared generic. `BudgetAlgebraId` is exactly such a shared generic: six verticals canonicalize through one identifier, so the meaning of 'what was authorized' along the budget axis lives in the identifier string rather than in the vertical that issued it. Two concrete consequences. (a) Cross-profile widening: an operator issues an anchor or root grant with `BudgetCeiling(numeric-ceiling-v1, 50_000)` meaning '$500'; because the anchor lists several profiles and the profile is only pinned at the first delegation, the same ceiling admits 50,000 Kubernetes rollouts or 50,000 refunds (each costing exactly 1) under the count-semantics profiles. (b) Shared accumulator: `InMemoryBudgetLedger`/`PersistentBudgetLedger` sum dollars and rollout counts into one `consumed["numeric-ceiling-v1"]` value, so a run of cheap rollouts silently exhausts the payment budget and vice versa. Neither is detectable by a dependency-graph audit, since the coupling is a string constant duplicated across six crates. + +### `system-clock-fails-open-to-zero` — Three product-tier clocks substitute `0` for an unreadable system time, disabling challenge expiry and durable-lifecycle expiry, while five integration clocks correctly fail closed +- **Lane:** cross · **Goal:** secure · **Area:** security +- **Evidence:** product/runtime/auths-runtime/src/lib.rs:57-63 — `impl Clock for SystemClock { fn now(&self) -> u64 { SystemTime::now().duration_since(UNIX_EPOCH).map_or(0, |d| d.as_secs()) } }`. +product/runtime/auths-runtime/src/production.rs:102-108 — `impl TrustedClock for SystemTrustedClock` uses the same `map_or(0, ...)`; this is the clock feeding the durable lifecycle coordinator. +product/runtime/auths-node/src/sandbox.rs:647-651 — same pattern. +Consequences at `now == 0`: product/runtime/auths-runtime/src/lib.rs:703 `if now > challenge.expires_at()` can never be true, so every issued challenge is permanently unexpired; product/runtime/auths-lifecycle/src/transition.rs:290 `not_expired: context.verifier_time <= record.input.expires_at` is always true, so durable authority expiry is removed; product/runtime/auths-runtime/src/lib.rs:989 and :1019 stamp receipts with `Timestamp::new(0)`. +The correct pattern already exists five times in the same repository: product/integrations/auths-stripe/src/adapters.rs:52-59, auths-kubernetes/src/adapters.rs:55-62, auths-opentofu/src/adapters.rs:64-71, auths-postgresql/src/adapters.rs:55-62, auths-radicle/src/adapters.rs:107-114 all use `fn now(&self) -> Result` with `.map_err(|_| PortError::InvalidConfiguration)`. +- **Disposition:** Change `Clock::now` in product/runtime/auths-runtime/src/lib.rs:49-52 and `TrustedClock::now` in production.rs to return `Result` / `Result`, matching the five integration `Clock` ports, and propagate the failure to a refusal (`RefusalKind::ApplicationPolicy` for MCP, `CoordinatorError` for the lifecycle). Do the same for the free function at auths-node/src/sandbox.rs:647. There is no correct infallible value for 'what time is it'. +- **Rationale:** `SystemTime::now().duration_since(UNIX_EPOCH)` fails exactly when the host clock is before 1970 — a dead RTC, a container started with a bogus clock, or an operator/attacker with `CAP_SYS_TIME` or control of NTP. In every one of those cases this code answers 'the epoch' instead of 'I do not know', and every time-based control it feeds resolves permissive. Concrete attack: an attacker who can move the host clock backwards past 1970 for the duration of one request makes the MCP service accept a challenge issued arbitrarily long ago (replay window unbounded) and makes the lifecycle kernel treat every expired reservation as live. I am marking this PLAUSIBLE on exploitability because it needs host-level clock control, but the fail-open direction is not in doubt and the fix is already written five times elsewhere in the tree. + +### `post-execution-failures-reported-as-refusals` — The MCP runtime returns a refusal after the executor has already applied the effect, and its executor port has no way to express an ambiguous outcome +- **Lane:** cross · **Goal:** secure · **Area:** security +- **Evidence:** product/runtime/auths-runtime/src/lib.rs:930-943 — after `self.executor.execute(executable).await` returned `Ok(result)`, a `record_execution` failure returns `Self::refusal(RefusalKind::ApplicationPolicy, None, "receipt store unavailable", ...)`. +product/runtime/auths-runtime/src/lib.rs:946-955 — an oversized result likewise becomes `ExchangeOutcome::refused(RefusalKind::ApplicationPolicy, ..., "tool result exceeds exchange limit")`, again after the effect applied. +product/runtime/auths-runtime/src/lib.rs:373-376 — the port itself cannot express ambiguity: `async fn execute(&self, action: ExecutableAction) -> Result, String>`. +product/runtime/auths-runtime/src/lib.rs:900-922 — consequently an `Err(message)` is unconditionally recorded as `ReceiptExecutionOutcome::Failed`. +product/errors/v1/registry.json classifies the corresponding codes as `mcp.handler-failed` → `retry: unknown, effect: possible` and `mcp.handler-timeout` → `retry: unknown, effect: possible`, i.e. the exact opposite of `Failed`. +The TypeScript port models this correctly and can be copied: bindings/typescript/src/profiles/mcp/index.ts:169-172 `McpHandlerOutcome` = `{effect:"applied", result}` | `{effect:"not-applied", cause?}` | `{effect:"possible", cause?}`, and :928/:953/:977 map timeouts, thrown errors, and unencodable results to `"possible"`. +- **Disposition:** Change `McpToolExecutor::execute` to return an outcome enum mirroring the registry — `Applied(Vec) | NotApplied(cause) | Possible(cause)` — matching the TypeScript `McpHandlerOutcome`. Add an `Unknown` variant to `ReceiptExecutionOutcome` so product/runtime/auths-runtime/src/lib.rs:907-913 can record `possible` instead of `Failed`. For the two post-execution failures at :930-955, emit a completed-with-degraded-receipt outcome carrying an execution reference rather than a `RefusalKind`, since a refusal is read by every caller as 'not applied'. +- **Rationale:** The error registry's `effect` field is described in the brief as the spine of safe API design, and `possible` exists specifically because a caller must not conclude non-occurrence. This runtime — the reference implementation that owns the semantics the bindings project — cannot represent `possible` at all at its executor boundary, and actively reports 'refused' for two paths that run strictly after the provider effect. Concrete attack/failure: an MCP tool executes a transfer successfully and returns a 2 MB result that exceeds the exchange limit. Line 946 converts the completed outcome into `refused(ApplicationPolicy, ..., "tool result exceeds exchange limit")`. The client sees a refusal, treats it as not-applied, and resubmits with a fresh challenge; the transfer runs twice. The same happens on a transient receipt-store failure at line 937. + +### `trusted-context-defaults-to-no-revocation-and-no-channel-binding` — The default trusted context ships an empty, never-expiring status snapshot and `none-v1` channel binding, so a deployment that accepts the defaults never consults revocation +- **Lane:** cross · **Goal:** secure · **Area:** security +- **Evidence:** product/sdk/auths-sdk/src/lib.rs:129-131 — `TrustedContextBuilder::new` defaults to `principal_status: empty_principal_status()?, grant_status: empty_grant_status()?, channel_policy: ChannelBindingId::parse("none-v1")?`. +product/sdk/auths-sdk/src/lib.rs:410-428 — those snapshots are empty and valid forever: `PrincipalStatusSnapshot::new(StatusSnapshotId::new([0; 32]), Timestamp::new(0), Timestamp::new(u64::MAX), Vec::new(), Vec::new())`, likewise for grants. +bindings/python/python/auths/_trust.py:201 — `compile_trust(..., channel_policy: str = "none-v1", ...)`; :65 — `TrustAnchor.status: Optional[Tuple[str, int]] = None`, which maps to `StatusPolicy::ExpiryOnly`. +core/crates/auths-verifier/src/lib.rs:2379-2381 and :2418-2420 — under `ExpiryOnly` both status checks return immediately with no evaluation: `let StatusPolicy::SnapshotRequired { method, .. } = policy else { return Ok(()); };`. +product/sdk/auths-sdk/src/lib.rs:200-207 — accepted principal-status methods are derived only from anchors that already chose `SnapshotRequired`, so an all-`ExpiryOnly` context cannot later accept a status snapshot without rebuilding. +The mechanism itself is correct when selected: core/crates/auths-verifier/src/lib.rs:2454-2459 maps `StatusDecision::Missing` to `Indeterminate`, and core/crates/auths-model/src/lib.rs:989-1006 `status_policy_attenuates` correctly refuses `(ExpiryOnly, SnapshotRequired)`. +- **Disposition:** Remove the defaults. Make `TrustedContextBuilder::new` require the caller to pass a `StatusConfiguration` and a `ChannelBindingId` explicitly, and make `TrustAnchor.status` a required field in bindings/python/python/auths/_trust.py:52-65 (and its TypeScript peer). Keep `ExpiryOnly` and `none-v1` as reachable choices — they are legitimate for offline verification — but make choosing them an act, not an omission. Since this is prelaunch, change the signatures directly rather than adding a second constructor. +- **Rationale:** An unsafe default in a trust configuration is indistinguishable from a correct one at the call site, and this one silently removes the entire revocation axis: a compromised agent's principal or a revoked grant keeps its authority until the validity window expires, which for a root anchor is typically far in the future. The channel-binding default compounds it — `none-v1` combined with product/runtime/auths-runtime/src/lib.rs:1046 `ChannelBindingPolicy::None => true` means the peer is never authenticated. Nothing in `build()` forces the operator to make either decision, and the failure is invisible: verification returns Authorized. Note this is a defaults problem, not a mechanism problem — every fail-closed path around it (Missing → Indeterminate, no weakening on delegation) is correct. + +### `op-create-matrix` — create: Rust owns two unrelated create-shaped surfaces (grant authoring vs. remote verb) and the bindings project only the second +- **Lane:** cross · **Goal:** extensible · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | symbol | file:line | signature | +| Rust (verb) | `ProductVerb::Create` | product/runtime/auths-production-client/src/lib.rs:131 | routed at product/runtime/auths-node/src/api.rs:190 to `/v1/authority/create` | +| Rust (authoring) | `auths_author::prepare_grant` | core/crates/auths-author/src/lib.rs:1001-1004 | `fn prepare_grant(statement: GrantStatement, descriptor: SignatureDescriptor) -> Result, AuthorError>` | +| Rust (authoring) | `auths_author::GrantRequest::new` | core/crates/auths-author/src/lib.rs:368 | 11 positional fields | +| TS | `ProductionAuths.create` | bindings/typescript/src/production-client.ts:140 (impl :199) | `create(request: Uint8Array): Promise` | +| PY | `ProductionAuths.create` | bindings/python/python/auths/_production_client.py:183 | `async def create(self, request: bytes) -> ProductionAuthorityResult` | +| TS (local) | `prepareRawKeyAuthority` | bindings/typescript/src/verifier/authority.ts (used integrations.ts:170) | object-arg | +| PY (local) | `prepare_raw_key_authority` | bindings/python/python/auths/_bootstrap.py (used integrations.py:434) | keyword-arg | + +The remote `create` is fully aligned (both take opaque bytes, both return `ProductionAuthorityResult`). The authoring surface is not projected at all at the product waist: neither binding exposes `GrantRequest`/`prepare_grant`; Python surfaces them only through the private `_authority.py` (`GrantRequest`, `GrantPlan`, `root_grant`, `plan_child` at _authority.py:12-24, `__all__` at :177-217), which is not reachable from any of the seven public Python modules. TypeScript has no counterpart at all. +- **Disposition:** Decide once: either grant authoring is reference-tier (Rust only) — then delete bindings/python/python/auths/_authority.py and its native re-exports — or it is product surface, in which case TypeScript gains the identical module. Given the spec's topology (seven modules, none of which is 'authoring'), delete it from Python. Keep `create(request: bytes) -> AuthorityResult` as the single product-level create in both bindings, and drop the `Production` prefix per the spec. +- **Rationale:** This is a legitimate reference-tier/product split for the most part — Rust breadth is a feature. But the asymmetry is not: Python ships a fully-typed private grant-authoring module with no TypeScript peer, so 'how do I mint a root authority' has a Python-shaped answer and no TypeScript answer. That is an accidental parity gap in the product surface, not deliberate reference breadth. + +### `op-delegate-matrix` — delegate: three different signatures at the product waist plus a Rust name (`plan_child_grant`) the bindings never use +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | symbol | file:line | signature | +| Rust (semantics) | `auths_author::plan_child_grant` | core/crates/auths-author/src/lib.rs:537-540 | `fn plan_child_grant(parent: &GrantStatement, request: GrantRequest) -> Result` — refuses widening before any signer runs | +| Rust (verb) | `ProductVerb::Delegate` | product/runtime/auths-production-client/src/lib.rs:132 | body encoded by `encode_delegation_body` at :753 | +| TS (remote) | `ProductionAuths.delegate` | bindings/typescript/src/production-client.ts:141-145 (impl :207) | `delegate(authority: ProductionAuthority, subject: Uint8Array, attenuation?: Uint8Array): Promise` — positional, default `new Uint8Array([0x80])` | +| PY (remote) | `ProductionAuths.delegate` | bindings/python/python/auths/_production_client.py:189-194 | `async def delegate(self, authority, subject: bytes, attenuation: bytes = b"\x80") -> ProductionAuthorityResult` — positional, same default | +| TS (local) | `Auths.delegate` | bindings/typescript/src/product.ts:156-160 (impl :276) | `delegate(input: { authority: McpToolAuthority; name?: string; expiresInSeconds?: number }): Promise` — single object arg | +| PY (local) | `Auths.delegate` | bindings/python/python/auths/_product.py:281-287 | `async def delegate(self, *, authority, name: str = "delegated-agent", expires_in_seconds: int = 300) -> Auths` — keyword-only | +| TS (workflow) | `DelegationOptions

` | bindings/typescript/src/workflow/contracts.ts:297 | object | +| PY (workflow) | `DelegatedAuthority` | bindings/python/python/auths/_workflow.py:454 | dataclass | + +The local default is duplicated, not shared: TS product.ts:287 `input.expiresInSeconds ?? 300` and Python _product.py:286 `expires_in_seconds: int = 300`; the bound check 1..86_400 is written twice (product.ts:288, _product.py:293-297). The delegated-agent default name is `"delegated-agent"` in both but stated in two places (product.ts:294, _product.py:286). +NAME: TS `DelegatedAuthorityRequest` (contracts.ts:286) vs Python `DelegatedAuthority` (_workflow.py:454) for the same object. +- **Disposition:** One signature per language for the single product `delegate`: TS `delegate(input: { authority, subject, attenuation? }): Promise`, Python `async def delegate(self, *, authority, subject, attenuation=None) -> AuthorityResult`. Move the 1..86_400 expiry bound and remaining-depth policy into Rust behind the delegation encoder so both bindings read it rather than restate it. Rename Python `DelegatedAuthority` -> `DelegatedAuthorityRequest` to match TS and Rust's `GrantRequest` role. +- **Rationale:** Delegation is the operation where 'can only narrow, never widen' is enforced, and the bindings' local delegate re-implements the expiry and depth policy in each language rather than deferring to Rust's `plan_child_grant`. Two hand-written copies of a narrowing bound is exactly how they drift. The remote/local signature split (positional bytes vs. object/keyword) means one operation has two calling conventions inside one SDK. + +### `op-execute-matrix` — execute: TypeScript overloads action-vs-plan into two result types; Python returns one undiscriminated union +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | symbol | file:line | signature | +| Rust (verb) | `ProductVerb::Execute` | product/runtime/auths-production-client/src/lib.rs:133 | routed per profile, product/runtime/auths-node/src/api.rs:210,225,240 | +| Rust (session) | `McpExecutionSession::begin` / `begin_plan_member` | product/profiles/auths-profile-mcp/src/session.rs:313, :345 | separate entry points for single vs plan member | +| TS (remote) | `ProductionAuths.execute` | bindings/typescript/src/production-client.ts:146 (impl :221) | `execute(authority: ProductionAuthority, action: Uint8Array): Promise` | +| PY (remote) | `ProductionAuths.execute` | bindings/python/python/auths/_production_client.py:208-210 | `async def execute(self, authority, action: bytes) -> ProductionExecutionResult` | +| TS (local) | `Auths.execute` overload 1/2 | bindings/typescript/src/product.ts:137-146 | `execute({ action, provider, requestId? }) -> Promise` OR `execute({ plan, provider, requestId? }) -> Promise` | +| PY (local) | `Auths.execute` | bindings/python/python/auths/_product.py:204-211 | `async def execute(self, *, action=None, plan=None, provider, request_id=None) -> ExecutionResult` — ONE union covering both, both args optional, mutual exclusion enforced at runtime (_product.py:231 `raise TypeError`) | + +`ExecutionResult` itself differs: TS defines three unions (`ExecutionResult`, `SingleExecutionResult`, `PlanExecutionResult` at product.ts:129-131) and only exports the broad one at the root (index.ts:26); Python defines one (`_product.py:151-158`) and exports it (__init__.py:99). +The underlying profile functions also diverge in arity: `executeMcpClosed(agent, action, resources)` (bindings/typescript/src/profiles/mcp/index.ts:661-665) vs `execute_mcp_closed(agent, action, resources, request: Optional[AuthorizationRequest] = None)` (bindings/python/python/auths/profiles/_mcp.py:1043-1048) — Python has a fourth parameter and a whole type, `AuthorizationRequest` (profiles/_mcp.py:72-87, carrying `challenge` and `evaluation_time`), that does not exist anywhere in TypeScript. +- **Disposition:** Split the operation rather than the result: `execute(action)` and `executePlan(plan)` / `execute_plan(plan)` as two methods in both bindings, returning `ExecutionResult` and `PlanExecutionResult` respectively; delete the optional-both-args form at _product.py:204 and the TS overload pair at product.ts:137-146. Delete Python's `AuthorizationRequest` and the fourth parameter of `execute_mcp_closed`; if callers must control challenge/evaluation time, that belongs to a Rust-owned `RequestContext` projected identically into both. +- **Rationale:** In TypeScript the compiler tells you a plan execution returns plan-shaped results; in Python you get a six-member union and must isinstance your way out, including against `Completed` vs `PlanCompleted` which differ only in field names. `AuthorizationRequest` is worse: it lets a Python caller supply the replay challenge and evaluation time for an authorization, which is a security-relevant input no TypeScript caller can supply and no Rust product API accepts at that layer (Rust's equivalent is `auths_sdk::RequestContext`, product/sdk/auths-sdk/src/lib.rs:35, constructed by the verifier host, not the caller). + +### `op-resume-matrix` — resume: Python's profile-level resume drops the agent parameter TypeScript requires +- **Lane:** cross · **Goal:** secure · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | symbol | file:line | signature | +| Rust | `McpExecutionSession::resume` | product/profiles/auths-profile-mcp/src/session.rs:440 | `pub fn resume(...)` | +| Rust (verb) | `ProductVerb::Resume` | product/runtime/auths-production-client/src/lib.rs:134 | `/v1/workflows/resume`, product/runtime/auths-node/src/api.rs:247 | +| TS (remote) | `ProductionAuths.resume` | bindings/typescript/src/production-client.ts:147 (impl :225) | `resume(reference: ProductionRecoveryReference): Promise` | +| PY (remote) | `ProductionAuths.resume` | bindings/python/python/auths/_production_client.py:218-220 | `async def resume(self, reference: ProductionRecoveryReference) -> ProductionExecutionResult` | +| TS (local) | `Auths.resume` | bindings/typescript/src/product.ts:147-150 (impl :232) | `resume({ reference: ExecutionReference, provider }): Promise` | +| PY (local) | `Auths.resume` | bindings/python/python/auths/_product.py:233-238 | `async def resume(self, *, reference, provider) -> ExecutionResult` | +| TS (profile) | `resumeMcpClosed` | bindings/typescript/src/profiles/mcp/index.ts:804-808 | `(agent: AttachedAgent, reference: string, resources)` | +| PY (profile) | `resume_mcp_closed` | bindings/python/python/auths/profiles/_mcp.py:1170-1173 | `(reference: str, resources)` — NO agent | + +TypeScript uses the agent to obtain the engine bound to that client (`engineForClient(resourcesForAttachedAgent(agent).client)`, mcp/index.ts:813); Python resolves the engine from the module-level native import instead. +- **Disposition:** Python's `resume_mcp_closed` takes `(agent, reference, resources)` to match TypeScript and to make the binding explicit. Keep the product-level `resume(reference, provider)` identical in both. Rename Python's local `ExecutionReference` and remote `ProductionRecoveryReference` onto the single name below (see execution-reference-two-formats). +- **Rationale:** The agent parameter is what ties a resume to the client whose trust configuration authorized the original execution. Dropping it in Python means a resume is not structurally bound to an agent at all — the reference plus a session key is sufficient. Even if the session key currently makes this safe, the two languages encode different answers to 'what must you hold to resume an in-flight effect', and only one of them is checkable by a reviewer reading the signature. + +### `stage-four-vocabularies` — "Stage" names four disjoint closed sets plus one open string +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | owner | file:line | values | +| `auths_operations::OperationalStage` | product/operations/auths-operations/src/lib.rs:204-218 | Acquisition, Verification, Policy, DecisionPersistence, Reservation, ExecutionIntent, Credential, ProviderEntry, ProviderResult, Observation, Reconciliation, Receipt, Recovery (13) | +| `auths_operations::PublicWorkflowStage` | product/operations/auths-operations/src/lib.rs:519-530 | Received, Authorized, Reserved, ProviderPossible, Observing, Reconciling, Committed, Released, Failed (9) | +| error registry `stages` | product/errors/v1/registry.json (via `ErrorDefinition.stages: &'static [&'static str]`, product/errors/auths-errors/src/lib.rs:87) | 20 untyped strings: cancellation, central-validation, configuration, handler, handler-result, internal, key-lifecycle, lifecycle, lifecycle-store, parse, plan-commitment, plan-member, provider, provider-response, provider-result, receipt, reconciliation, reference, reservation, runtime | +| TS `TelemetryStage` | bindings/typescript/src/observability.ts:5-13 | acquisition, construction, approval, signing, verification, reservation, execution, receipt (8) | +| TS/PY `VerificationStage` | bindings/typescript/src/verifier/result.ts:12-17; bindings/python/python/auths/verify.py:42-44 | decode, resolve, principal-control, authority, complete (5) — this pair IS aligned | +| PY telemetry stage | bindings/python/python/auths/_observability.py:33 | `stage: str` — completely open | + +Only two of the eight sets share a single member with each other beyond `verification`/`receipt`. +- **Disposition:** One Rust-owned `Stage` enum covering the error registry's 20 values (they are the most complete and are already fixture-anchored), typed as `ErrorDefinition.stages: &'static [Stage]` instead of `&'static [&'static str]`. `VerificationStage` stays separate and keeps its five members — it names kernel phases, not product stages. Delete `TelemetryStage` (observability.ts:5) and Python's open `stage: str` (_observability.py:33) and project the one `Stage` enum into both. `PublicWorkflowStage` and `OperationalStage` stay Rust reference-tier and are not projected. +- **Rationale:** Every error, every telemetry event and every receipt carries a `stage`, and no two producers agree on the alphabet. `AuthsError.stage` is typed `string` in both bindings (bindings/typescript/src/product-errors.ts:35, bindings/python/python/auths/_product_errors.py:70), so nothing checks that an error's stage is a stage. Python's telemetry accepts any string at all. + +### `error-family-three-vocabularies` — ErrorFamily has three disjoint sets and Python's is an open string that emits values in none of them +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | owner | file:line | values | +| Rust OWNER | product/errors/auths-errors/src/lib.rs:17-27 | configuration, input, runtime, profile, provider, state, internal (7) | +| TS product | bindings/typescript/src/product-errors.ts:6 | Rust's 7 + `"unknown"`, emitted at product-errors.ts:241 | +| TS workflow | bindings/typescript/src/workflow/errors.ts:39-45 | configuration, authority, approval, custody, provider, transaction (6) — only `configuration` and `provider` overlap Rust | +| PY product | bindings/python/python/auths/_product_errors.py:67 | `family: str` — untyped; emits `"unknown"` at :292 | +| PY workflow | bindings/python/python/auths/_errors.py:19 | `family: str` — untyped; `AuthsWorkflowError` hardcodes `"workflow"` (_errors.py:69), `ProviderOperationError` hardcodes `"provider"` (:95), `RuntimeStateError` hardcodes `"runtime"` | +`"workflow"`, `"authority"`, `"approval"`, `"custody"` and `"transaction"` are not members of the Rust enum, which is `deny_unknown_fields` on the envelope (lib.rs:127-145). +- **Disposition:** Rust's 7-member `ErrorFamily` is the only one. Delete bindings/typescript/src/workflow/errors.ts:39-45 and type both Python `family` fields as the projected `ErrorFamily` enum. Map the workflow subsystems onto Rust families (authority/approval/transaction -> `input` or `state` as appropriate, custody -> `provider`). Do not add `"unknown"`: an unrecognized code maps to `family: "internal"`. +- **Rationale:** Family is the first thing a caller switches on to decide who is at fault. TypeScript's workflow set answers a different question entirely (which subsystem) than Rust's (what kind of fault). Python types it as `str` and then emits four values Rust cannot accept, so a Python-produced envelope will not decode server-side. + +### `plan-noun-three-names` — The proof-composition plan is AuthorizationPlan in Rust/TS and ProofPlan in Python; Rust's k_of_n is spelled threshold in both bindings +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | builder | plan type | combinators | file:line | +| Rust | `auths_author::PlanBuilder` | `AuthorizationPlan` | `proof` :640, `all_of` :649, `any_of` :661, **`k_of_n`** :673 | core/crates/auths-author/src/lib.rs:627-687 | +| TS | `AuthorizationPlanBuilder` | `AuthorizationPlan` (kind: "proof"|"all-of"|"any-of"|"threshold") | `proof` :97, `allOf` :106, `anyOf` :110, **`threshold`** :114 | bindings/typescript/src/authorization-plans.ts:42,77 | +| PY | **`ProofPlanBuilder`** | **`ProofPlan`** (`ProofPlanKind` same four strings) | `proof` :116, `all_of` :127, `any_of` :130, **`threshold`** :133 | bindings/python/python/auths/_authority.py:76,112 | + +Shape also diverges: TS exposes a separate `AuthorizationPlanSummary` obtained via `builder.summarize(plan)` (authorization-plans.ts:69,122) carrying `planId/canonicalPlan/proofReferences/leafCount/maximumDepth`; Python puts the same five on the plan itself as properties plus `canonical_bytes()` (_authority.py:92-109). TS `ProofReference` is a sealed class with a module function `proofReference(value: string)` (authorization-plans.ts:18,32); Python `ProofReference` is a frozen dataclass with a `parse` classmethod (_authority.py:56,66). +Separately there is a SECOND, unrelated plan concept: Rust `auths_author::ProfilePlanCommitment`/`ProfilePlanMember` (core/crates/auths-author/src/lib.rs:820,795), TS `ProfilePlan` (bindings/typescript/src/plans.ts:37), Python `McpPlan` (bindings/python/python/auths/profiles/_mcp.py:237). +- **Disposition:** Canonical name `AuthorizationPlan` / `AuthorizationPlanBuilder` in all three; rename Python `ProofPlan`->`AuthorizationPlan`, `ProofPlanBuilder`->`AuthorizationPlanBuilder`, `ProofPlanKind`->`AuthorizationPlanKind`. Canonical combinator name: pick `threshold` (both bindings and both kind-strings already use it) and rename Rust `PlanBuilder::k_of_n` -> `threshold`; update the `"threshold"` kind string nowhere, it is already correct. Expose the summary the same way in both — put `plan_id`/`leaf_count`/`maximum_depth`/`proof_references`/`canonical_bytes()` on the plan (Python's shape) and delete TS `AuthorizationPlanSummary`/`summarize`. For the second concept, name it `ProfilePlan` in Rust and both bindings and make Python's `McpPlan` an alias-free rename. +- **Rationale:** `k_of_n` versus `threshold` is Rust losing ownership of a combinator name to both projections simultaneously — the projections agreed with each other and disagreed with the owner. `ProofPlan` versus `AuthorizationPlan` for the same object is a straight name split. And the second plan concept is generic in TypeScript (`ProfilePlan`) but profile-specific in Python (`McpPlan`), so the file-touch cost of a new vertical differs between the two bindings for the same feature. + +### `receipt-noun-four-name-splits` — The receipt spine is structurally identical in both bindings and named differently in all four types +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | concept | TS | file:line | PY | file:line | +| receipt signer | `ApplicationReceiptSigner` (principal, verificationMethod, suite, evidence) | bindings/typescript/src/profiles/application/index.ts:175-180 | `ReceiptSigner` (principal, verification_method, suite, evidence) | bindings/python/python/auths/_receipts.py:15-21 | +| attestor port | `ApplicationReceiptAttestor` | .../application/index.ts:182-185 | `ReceiptAttestor` | _receipts.py:32-36 | +| one attested receipt | `AttestedApplicationReceipt` (kind, receiptId, bytes, signer) | .../application/index.ts:187-192 | `AttestedReceipt` (kind, receipt_id, bytes, signer) | _receipts.py:39-46 | +| the decision+execution pair | `LinkedAttestedReceipt` | bindings/typescript/src/internal/receipt-attestation.ts:10-13 | `Receipt` | _receipts.py:56-59 | +| the pair, again | `McpAttestedReceipt` | bindings/typescript/src/profiles/mcp/index.ts:258-261 | (same `Receipt`) | — | + +TypeScript then aliases BOTH pair types to the name `Receipt` in two different entry points: bindings/typescript/src/product.ts:47 `export type Receipt = McpAttestedReceipt` and bindings/typescript/src/verify.ts:26 `type LinkedAttestedReceipt as Receipt`. Python has one `Receipt` but two verify functions with the same stem: `_receipts.verify_receipt(AttestedReceipt)` (_receipts.py:163) and `verify_linked_receipt(Receipt)` (_receipts.py:172), the latter re-exported as `verify_receipt` at bindings/python/python/auths/verify.py:38. +- **Disposition:** Canonical set, all three languages: `ReceiptSigner`, `ReceiptAttestor`, `AttestedReceipt`, and `Receipt` for the decision+execution pair. Delete the `Application*` and `Mcp*` and `Linked*` prefixes (bindings/typescript/src/profiles/application/index.ts:175-192, internal/receipt-attestation.ts:10, profiles/mcp/index.ts:258) and the two conflicting `Receipt` aliases (product.ts:47, verify.ts:26). Rename Python's single-receipt verifier to `verify_attested_receipt` and keep `verify_receipt` for the pair only. +- **Rationale:** Field-for-field identical structures with four different names is pure vocabulary duplication, and `Receipt` meaning two different TypeScript types depending on which entry point you import from is a genuine correctness hazard. Python's `verify_receipt` homonym takes different argument types in the two places it is defined. + +### `approval-facade-shape-split` — approval builders return a policy in TypeScript and a policy+provider configuration in Python +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | symbol | file:line | returns | +| TS | `approvalPolicy.none(options?: ApprovalPolicyOptions)` etc. | bindings/typescript/src/approvals.ts:73-95 | `Promise` — provider supplied separately in `ApprovalConfiguration` | +| TS | `approval` | bindings/typescript/src/approvals.ts:152-155 | `{...approvalPolicy, threshold: thresholdApproval}` — root-exported at index.ts:1 | +| PY | `Approval.none(policy_id="approval.none", *, evaluator_version="1", expires_in_seconds=300)` | bindings/python/python/auths/_workflow.py:210-226 | `ApprovalConfiguration` (policy AND provider), SYNC — root-exported at __init__.py:48 | +| PY | `Approval.grant_only(policy_id, provider, *, ...)` | bindings/python/python/auths/_workflow.py:228-246 | `ApprovalConfiguration` | +| TS | `thresholdApproval(options: { threshold, providers })` | bindings/typescript/src/approvals.ts:115 | object arg | +| PY | `threshold_approval(providers, *, threshold)` | bindings/python/python/auths/_approvals.py:65-68 | positional + kw | +| TS | `BoundedApprovalSession` (class, AsyncDisposable) | bindings/typescript/src/approvals.ts:181, exported via testkit/index.ts:52 | | +| PY | `PlanApprovalSession` | bindings/python/python/auths/_plan.py:31 | different name, not in any `__all__` | +Python additionally has `Approval.response(request, *, decision)` (_workflow.py:198-208) with no TS counterpart; TS has `noApproval` (approvals.ts:98) with no Python public counterpart (Python hides it as `_NoApprovalProvider`, _workflow.py:216). +- **Disposition:** One facade name `approval` / `Approval` returning `ApprovalConfiguration` (policy + provider) in both, since that is what every call site actually needs. Signature `approval.none(options?)` / `Approval.none(*, options)` with one options record per language. `thresholdApproval(options)` / `threshold_approval(options)` both taking a single record with `threshold` and `providers`. Rename Python `PlanApprovalSession` -> `BoundedApprovalSession` and export it from testkit in both. Add `noApproval`/`no_approval` and `approval.response`/`Approval.response` to whichever language lacks it. +- **Rationale:** The builders return different things, so the two SDKs' approval wiring is not transferable between languages, and the async/sync split means the TS builder is a `await`-point while Python's is not. Two names for the plan-approval session and two argument conventions for threshold compound it. + +### `delegation-constraint-shape-split` — Delegated action/budget/status constraints are discriminated object literals in TypeScript and distinct classes in Python +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | file:line | representation | +| TS | bindings/typescript/src/workflow/contracts.ts:266-283 | `DelegatedActionConstraint`, `DelegatedBudget`, `DelegatedStatus` — unions of `{ kind: "inherit" }`-style object literals; call site product.ts:301-303 `actionConstraint: { kind: "inherit" }, budget: { kind: "inherit" }, status: { kind: "expiry-only" }` | +| PY | bindings/python/python/auths/_workflow.py:403-425, :454 | `InheritAction`, `InheritBudget`, `NoBudget`, `BudgetCeiling`, `ExpiryOnly`, `InheritStatus`, `SnapshotRequired`, `AnyBody`, `ExactBody`, `AllowedBodies` — ten separate classes; call site _product.py:308-310 `InheritAction(), InheritBudget(), ExpiryOnly()` | +| Rust | core/crates/auths-author/src/lib.rs:357-361 (`GrantRequest` fields `action_constraint: ActionConstraint`, `budget_ceiling: Option`, `status_policy: StatusPolicy`) | typed enums in `auths_model` | +TypeScript has no exported counterpart for `AnyBody`/`ExactBody`/`AllowedBodies`/`NoBudget`/`BudgetCeiling`/`SnapshotRequired` under any name (grep across bindings/typescript/src returns nothing). +- **Disposition:** One representation. Use Python's nominal form — it matches Rust's enums and is greppable: TypeScript gains exported classes/branded records `InheritAction`, `ExactBody`, `AllowedBodies`, `AnyBody`, `InheritBudget`, `NoBudget`, `BudgetCeiling`, `InheritStatus`, `ExpiryOnly`, `SnapshotRequired` mirroring bindings/python/python/auths/_workflow.py:403-425 exactly, with the same field names in camelCase. Delete the anonymous `{kind: ...}` unions at contracts.ts:266-283. +- **Rationale:** The narrowing dimensions are the heart of 'delegation can only narrow'. TypeScript can express `inherit` and little else through named types; Python exposes ten constructors. A reviewer comparing the two cannot tell whether the same set of narrowings is expressible, and adding a new narrowing dimension costs a union member in one language and a class in the other. + +### `trusted-context-noun-split` — "Trusted context" is called VerifierContext in the Rust model, TrustedContext in the Python native layer, and has no single TypeScript noun +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | symbol | file:line | +| Rust (model) | `VerifierContext` | used at product/sdk/auths-sdk/src/lib.rs:11 and returned by `TrustedContextBuilder::build` :187 — the builder says "trusted context", the type says "verifier context" | +| Rust (SDK) | `TrustedContextBuilder` | product/sdk/auths-sdk/src/lib.rs:79 | +| PY (native) | `TrustedContext` | bindings/python/python/auths/_native.pyi:470; helpers `trusted_context_bytes` / `parse_trusted_context_bytes` at bindings/python/python/auths/_inspection.py:216,228 | +| PY (trust) | `CompiledTrust`, `compile_trust`, `compile_trusted_context`, `TrustedAuthority`, `TrustedAuthoritySnapshot`, `TrustAnchor` | bindings/python/python/auths/_trust.py:137,191 and `__all__` :239-263 | +| TS | `TrustedContextSource` / `TrustedContextProvider` / `TrustedContextLoadRequest` / `TrustedContextSourceOptions` / `WorkflowTrustedContextCompilation` / `TrustedAuthority` / `TrustedAuthoritySnapshot` | bindings/typescript/src/workflow/trusted-context.ts:21; workflow/contracts.ts:200,207,211,658,112,126 — five source/provider types but no type named for the context itself | +Python's `_trust.py` (263 lines, 24 public names) has no TypeScript counterpart at all: `AssurancePolicy`, `AssuranceRequirement`, `AssuranceRole`, `AssuranceQuantifier`, `EvidenceProvider`, `EvidenceRequest`, `ResolvedEvidence`, `OfflineEvidenceBundle`, `PolicyReplacement`, `replace_policy`, `compile_trust`, `self_contained_configuration` — none appear in bindings/typescript/src. +- **Disposition:** Pick `TrustedContext` (it is what the SDK builder, the Python native layer and the TypeScript source-and-provider types all already say) and rename Rust `VerifierContext` -> `TrustedContext`. Both bindings expose one opaque `TrustedContext` type plus `TrustedContextSource`. Then classify `bindings/python/python/auths/_trust.py`: if assurance-policy authoring is reference-tier, delete it from Python; if it is product surface, add the identical module to TypeScript. Do not leave it Python-only. +- **Rationale:** Rust itself is inconsistent (builder named for one noun, product named for another), and the two bindings diverge from Rust and from each other. Python ships a whole assurance-policy authoring module that TypeScript lacks, which is either an accidental parity gap or dead Python weight — and neither module is reachable from the seven public entry points, so nobody has had to decide. + +### `budget-noun-near-absent-in-bindings` — Budget exists in Rust and Python but is almost entirely absent from the TypeScript surface +- **Lane:** typescript · **Goal:** extensible · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | symbols | file:line | +| Rust | `BudgetAlgebraId` (product/sdk/auths-sdk/src/lib.rs:7 import, :226 `NUMERIC_CEILING_V1`), `BudgetCeiling` (core/crates/auths-author/src/lib.rs:357 `budget_ceiling: Option`), `auths_runtime::BudgetLedger`/`BudgetClaim`/`NoBudgetLedger` (product/runtime/auths-runtime/src/lib.rs:190,200,207), `AuthorityDiff::budget_narrowed` (auths-author :477) | | +| PY | `BudgetCeiling` (_workflow.py:418), `NoBudget` (:413), `InheritBudget` (:408), `BudgetSummary` (:575), `_budget_fields` (:1534), `ProfileBudget` (_application_profile.py:65) | | +| TS | `DelegatedBudget` (workflow/contracts.ts:272 — a `{kind:"inherit"}`-shaped union only), `ProfileBudget` (profiles/application/index.ts:52), optional `budget?: { algebra, value }` on `PlanAuthoritySummary` (plans.ts:33) | | +TypeScript has no `BudgetCeiling`, no `NoBudget`, no `BudgetSummary`, and no budget ledger port; grep for `BudgetCeiling` across bindings/typescript/src returns nothing. +- **Disposition:** Project the same budget vocabulary into both bindings: `BudgetCeiling`, `NoBudget`, `InheritBudget`, `BudgetSummary` in TypeScript matching bindings/python/python/auths/_workflow.py:408-425,575, and add the budget ledger port to `framework`/`framework.py` in both if it is a supported extension point. If budget is deliberately not yet product surface, remove it from Python's `_workflow.py` public names too so the two bindings agree. +- **Rationale:** Budget is one of the named narrowing dimensions in Rust's `AuthorityDiff` (auths-author/src/lib.rs:477 `budget_narrowed`) and one of the accepted registries in the SDK context (auths-sdk/src/lib.rs:226). A TypeScript caller cannot state a budget ceiling on a delegation or read a budget summary from a delegation review; a Python caller can. That is a real capability gap, not a naming one. + +### `metrics-triplicated-in-python` — Python defines the same seven-field metrics record three times under three names; TypeScript defines it once +- **Lane:** python · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** Identical fields (proof_bytes, action_bytes, context_bytes, object_count, plan_leaves, plan_depth, work_units) in three Python types: +- `VerificationMetrics` — bindings/python/python/auths/verify.py:54-62 +- `InspectionMetrics` — bindings/python/python/auths/_inspection.py:44-52 +- `AuthorizationMetrics` — bindings/python/python/auths/profiles/_mcp.py:268-276 +TypeScript has exactly one: `VerificationMetrics` — bindings/typescript/src/verifier/result.ts:25-33 (bigint fields), reused by `DecisionInspection.metrics` via `VerificationResult["metrics"]` at bindings/typescript/src/inspection.ts:14. +Python's `DecisionInspection.metrics` is typed `InspectionMetrics` (_inspection.py:91) even though the value it receives originates from a `VerificationMetrics`-shaped source (_inspection.py:135). +- **Disposition:** Keep `VerificationMetrics` (bindings/python/python/auths/verify.py:54) as the single name. Delete `InspectionMetrics` (_inspection.py:44) and `AuthorizationMetrics` (profiles/_mcp.py:268) and reference `VerificationMetrics` from both call sites. Remove `"InspectionMetrics"` from bindings/python/python/auths/verify.py:204. +- **Rationale:** Three names for one record is the clearest possible SIMPLE violation, and the type mismatch between what `verify()` returns and what `inspect_decision()` declares means one of the two is lying about its input. + +### `mcp-profile-name-and-arity-splits` — The MCP profile surface diverges in type names, facade arity, plan sync/async, and cancellation support +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** NAME splits: +| concept | TS | file:line | PY | file:line | +| execution store port | `McpExecutionState` | bindings/typescript/src/profiles/mcp/index.ts:211 | `McpExecutionStore` | bindings/python/python/auths/profiles/_mcp.py:611 | +| plan type | `ProfilePlan` | bindings/typescript/src/plans.ts:37 | `McpPlan` | profiles/_mcp.py:237 | +| plan authority | `PlanAuthoritySummary` | bindings/typescript/src/plans.ts:28 | `McpPlanAuthority` | profiles/_mcp.py:245 | +| facade | `mcp` const object | bindings/typescript/src/profiles/mcp/index.ts:435 | `McpFacade` class + `mcp` instance | profiles/_mcp.py:799, :866 | + +SHAPE splits: +- `mcp.plan(actions)` is ASYNC in TS (`Promise>`, profiles/mcp/index.ts:464) and SYNC in Python (`-> McpPlan`, profiles/_mcp.py:856). +- `resourcesForMcpAuthority` returns a named object `{profile, permissions, resourceNamespaces, audiences}` (profiles/mcp/index.ts:472-476); `resources_for_mcp_authority` returns a bare 4-TUPLE (profiles/_mcp.py:866-869), destructured positionally at bindings/python/python/auths/_product.py:289. +- `McpExecutionResources` has `signal?: AbortSignal` in TS (profiles/mcp/index.ts:252); Python's has no cancellation field at all (profiles/_mcp.py:609-616). TS threads it into `driveMcpSession` (profiles/mcp/index.ts:834). +- `mcp.profile(options: McpProfileOptions)` takes one record in TS (profiles/mcp/index.ts:436) vs `mcp.profile(*, service, version=1)` keyword args in Python (profiles/_mcp.py:800). +- Default-service behavior differs: TS treats an explicit `service: "development"` as a NEW profile (profiles/mcp/index.ts:455 `options.service === undefined ? developmentProfile() : mintMcpProfile(...)`) while Python memoizes it (profiles/_mcp.py:835-839 `if service == "development"` -> `_development_mcp_profile()`). + +Python-only public names with no TS counterpart: `AuthorizationRequest`, `AuthorizationExplanation`, `AuthorizationMetrics`, `ApprovalSummary`, `McpAuthorized`, `McpDenied`, `McpIndeterminate`, `McpNotApplied`, `McpAuthorizationResult`, `McpPlanAuthority`, `McpReview`, `McpFacade`, `McpPlanMemberAuthorized`, `McpPlanMemberResult` (bindings/python/python/auths/profiles/_mcp.py:1620-1671). TS-only: `McpCommand`, `McpAuthority`, `McpProfileOptions`, `McpDevelopmentProviderOptions`, `McpAttestedReceipt`, `McpGatewayCall`, `McpHandlerCause`, `McpPlanClosedResult` (bindings/typescript/src/profiles/mcp/index.ts:79,124,430,268,258,112,174,263). +- **Disposition:** Rename to one set: `McpExecutionStore` (Python's — 'store' is what it is), `ProfilePlan` (used in Rust as `ProfilePlanCommitment`), `ProfilePlanAuthority`. Make `mcp.plan` sync in both (it is a pure commitment computation). Make `resources_for_mcp_authority` return a named record in Python. Add a cancellation parameter to Python's `McpExecutionResources` (an `asyncio.Event` or a `CancelScope`-compatible token). Make the explicit-`"development"` path identical in both — memoize in both. Then reconcile the two exported-name lists to one; every Python-only or TS-only name is either promoted to both or deleted. +- **Rationale:** MCP is the reference vertical — the shape every future profile copies. Its name splits and arity splits will be duplicated into every new vertical, and the missing `AbortSignal` means a Python caller has no way to cancel an in-flight tool call. The explicit-`"development"` behavioral difference is a live correctness divergence: the same call produces a memoized profile in one language and a fresh one in the other. + +### `production-timeout-unit-divergence` — The production client's timeout is milliseconds in TypeScript and seconds in Python, with different bounds +- **Lane:** cross · **Goal:** secure · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | field | file:line | unit / default / bounds | +| TS | `ProductionAuthsOptions.timeoutMs?: number` | bindings/typescript/src/production-client.ts:36; default `15_000` at :189; bounds `100 .. 120_000` at :190-192 | milliseconds | +| TS | `ProductionTransportRequest.timeoutMs` | bindings/typescript/src/production-client.ts:18 | milliseconds | +| PY | `create_auths(..., timeout_seconds: float = 15.0)` | bindings/python/python/auths/_production_client.py:312; bounds `0.1 .. 120` at :171-176 | seconds | +| PY | `ProductionTransportRequest.timeout_seconds: float` | bindings/python/python/auths/_production_client.py:39 | seconds | +The transport request record also diverges in the URL type: TS `url: URL` (production-client.ts:15), Python `url: str` (_production_client.py:36). +Bounds are not equivalent either: TS floor is 100ms, Python floor is 0.1s (=100ms, equal) but TS is integer-only (`Number.isSafeInteger`) while Python accepts any float. +- **Disposition:** One unit and one name in both: `timeoutMs` / `timeout_ms`, integer milliseconds, default 15000, bounds 100..120000, in `AuthsOptions` and `TransportRequest`. Change bindings/python/python/auths/_production_client.py:39,165,312 accordingly. Use `str` for the URL in both (TypeScript's `URL` object cannot cross the WASM boundary anyway) or a shared parsed-origin type. +- **Rationale:** `ProductionTransport` is a user-implemented port. Someone porting a transport implementation between the two SDKs will read `timeout` and get the unit wrong by a factor of a thousand — a 15-second timeout becomes 15 milliseconds or 15000 seconds. A timeout that fires too early on a mutating `execute` is exactly the `effect: possible` case this system exists to avoid. + +### `doctor-report-divergence` — DoctorReport reports ABI differently, is async in one language, and hardcodes version and profile lists in Python +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | symbol | file:line | shape | +| TS | `doctor(options: DoctorOptions = {}): Promise` | bindings/typescript/src/doctor.ts:28 | ASYNC; `portableAbi: { authoring, identity, compatible }` (:15-19); `sdkVersion` from `SDK_RUNTIME_CONTRACT` (:39); `profiles` derived from `SDK_RUNTIME_CONTRACT.profiles` (:43-44) | +| PY | `doctor(*, mode="unconfigured", state="unconfigured") -> DoctorReport` | bindings/python/python/auths/_doctor.py:28-30 | SYNC; `native_abi: int` + `native_abi_compatible: bool` (:18-19); `sdk_version="1.0.0rc1"` HARDCODED (:34); `profiles=("mcp/1",)` HARDCODED (:42); `compatible = abi == 2` HARDCODED (:32) | +`DoctorOptions` (bindings/typescript/src/doctor.ts:7) has no Python counterpart. `DoctorMode`/`DoctorState` are aligned (doctor.ts:4-5, _doctor.py:10-11) but only TS exports them at the root (bindings/typescript/src/index.ts:5-8); Python exports only `DoctorReport` (bindings/python/python/auths/__init__.py:59). +The rendered output also differs: bindings/typescript/src/doctor.ts:64 pluralizes (`warning${length === 1 ? "" : "s"}`), bindings/python/python/auths/_doctor.py:64 always says `warnings`; TS prints `Portable ABI`, Python prints `Native ABI`. +TS `package.json` declares version `1.0.0-rc.1`; Python's doctor says `1.0.0rc1`. +- **Disposition:** One `DoctorReport` shape: `sdkVersion`, `runtime`, `abi: { authoring, identity, compatible }`, `semanticSubject`, `profiles`, `mode`, `state`, `status`, `warnings`. Add `DoctorOptions`/`DoctorOptions` to Python and export `DoctorMode`/`DoctorState` from both roots. Derive `sdk_version`, `profiles` and the expected ABI from a generated runtime-contract module in Python exactly as bindings/typescript/src/runtime-contract.ts does. Make `doctor` sync in both. Byte-compare `render_doctor` output across languages in CI. +- **Rationale:** `doctor` is the first thing a user runs and the first thing pasted into a support ticket. The two languages produce different field names, different ABI models, and non-comparable text. Python's hardcoded version, profile list and ABI number will silently go stale on the next release — they are not derived from anything. + +### `identity-two-tier-typescript-vs-one-python` — TypeScript ships two parallel identity vocabularies (descriptor-tier and packet-tier); Python ships one, and neither matches +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** TypeScript, descriptor tier — bindings/typescript/src/identity.ts:30,48,53,59,65,91,118: `IdentityDescriptorInput`, `DecodedIdentityDescriptor`, `ResolvedIdentityDescriptor`, `ValidatedIdentityDescriptor`, `AuthenticatedDescriptorMessage`, `IdentityDescriptorMethodAdapter`, `DescriptorSignatureSuiteAdapter`. +TypeScript, packet tier — bindings/typescript/src/identity.ts:165,175,185,193,206,216: `DecodedIdentity`, `ValidatedIdentity`, `DecodedSignedIdentityMessage`, `AuthenticatedIdentityMessage`, `IdentityMethodAdapter`, `SignatureSuiteAdapter`. Two registries: `IdentityMethodRegistry` (:123) and `SignatureSuiteRegistry` (:137). Client: `IdentityClient` (:391), `loadIdentity()` (:778). +Python, one tier — bindings/python/python/auths/identity.py:148,190,214,267,279,289,314,339,367: `DecodedIdentity`, `ResolvedIdentity`, `ValidatedIdentity`, `AuthenticatedIdentity`, `IdentityPrincipal`, single `IdentityRegistry`, `RawKeyIdentityMethod`, `ResolverIdentityMethod`, `Ed25519SignatureSuite`; free functions `decode_identity` (:382), `encode_identity` (:410), `encode_raw_key_identity` (:422); `__all__` at :478-498. +No `ResolvedIdentity` in TypeScript (only `ResolvedIdentityDescriptor`); no `*Descriptor` types in Python; TS `AuthenticatedIdentityMessage` vs Python `AuthenticatedIdentity`; TS `IdentityClient`/`loadIdentity` vs Python `IdentityRegistry` and module functions; TS `DecodedSignedIdentityMessage` has no Python peer. +- **Disposition:** Collapse TypeScript to Python's single tier and align names: `DecodedIdentity`, `ResolvedIdentity`, `ValidatedIdentity`, `AuthenticatedIdentity`, `IdentityPrincipal`, one `IdentityRegistry`, `IdentityMethod`, `SignatureSuite`, `decodeIdentity`/`decode_identity`, `encodeIdentity`/`encode_identity`, `encodeRawKeyIdentity`/`encode_raw_key_identity`. Delete the seven `*Descriptor` types at bindings/typescript/src/identity.ts:30-118 and the second registry at :137, or, if the descriptor tier is genuinely the resolution-time model, name it explicitly (`IdentityResolution*`) and add it to Python. +- **Rationale:** `auths.identity` / `@auths-dev/sdk/identity` is one of the seven declared public modules. Its two vocabularies in one language and one in the other means the module's contract is not defined. TypeScript's own two tiers are also a duplicate vocabulary problem independent of Python. + +### `testkit-check-vs-certify-duplicate` — Python's testkit has two conformance vocabularies (check_* and certify_*); TypeScript has one and the two lists barely overlap +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | conformance entry points | file:line | +| TS | `certifyAtomicStore`, `certifyByteTransport`, `certifyMcpProvider`, `certifySigner`, `CONFORMANCE_CATALOG`, `custodyConformance`, `productWaistConformance`, `createDiagnosticVerifier` | bindings/typescript/src/testkit/index.ts:24,35,38-49,51-55 | +| PY | `certify_atomic_store`, `certify_byte_transport`, `certify_mcp_provider`, `certify_signer` AND `check_approval_provider`, `check_identity_method`, `check_signer`, `check_telemetry`, `product_waist_conformance`, `create_diagnostic_verifier` | bindings/python/python/auths/testkit.py:331-364 | +`check_signer` and `certify_signer` both exist in Python. `custodyConformance` (TS) has no Python peer. `check_approval_provider`, `check_identity_method`, `check_telemetry` (PY) have no TS peers. TS `InMemoryApplicationExecutionStore` (testkit/index.ts:81) and `development` (:154) vs Python `MemoryGateway`, `FixedClock`, `RecordingTelemetry`, `DevelopmentApproval`, `DevelopmentEd25519Signer`, `DevelopmentReceiptAttestor`, `DevelopmentIdentityMethod`, `DevelopmentSignatureSuite`, `DevelopmentSigner`, `ADAPTER_CONTRACT_VERSION` (testkit.py:332-347). +- **Disposition:** One verb: `certify*` / `certify_*`. Delete Python's `check_approval_provider`/`check_identity_method`/`check_signer`/`check_telemetry` (testkit.py:348-351) after folding their cases into the corresponding `certify_*`. Make the certifiable-port list identical in both: atomic store, byte transport, MCP provider, signer, approval provider, identity method, telemetry port, disclosure protector, disclosure store. Add `custodyConformance` to Python or fold it into `certify_signer` in both. +- **Rationale:** Testkit is how third parties prove their adapters are correct. Two verbs for the same job in one language, and a set of adapters that can be certified in one language but not the other, means 'conformant' does not mean the same thing across bindings — which defeats the purpose of a conformance kit. + +### `lifecycle-authoring-python-only-and-unreachable` — Python ships a 396-line revocation/rotation/compromise authoring module with no TypeScript peer and no public entry point +- **Lane:** python · **Goal:** extensible · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** bindings/python/python/auths/_lifecycle.py:376-396 `__all__` = `CriticalExtension, GrantStatusRequest, GrantStatusSnapshot, IdentityRotation, LifecycleAuthor, LifecycleState, PrincipalStatusRequest, PrincipalStatusSnapshot, ProtocolDigest, SignedGrantStatus, SignedPrincipalStatus, StatusSnapshot, StatusProvider, StatusTrustRule, grant_status_snapshot, principal_status_snapshot, record_compromise, rotate_identity, withdraw_delegation`. +Grep across bindings/typescript/src for `withdrawDelegation`, `rotateIdentity`, `recordCompromise`, `LifecycleAuthor`, `StatusProvider` returns zero hits. TypeScript's entire lifecycle presence is two functions: bindings/typescript/src/internal/lifecycle-resources.ts:5,9 `registerStatusSnapshot`, `statusSnapshotBytes`. +The Python module is not reachable from any of the seven public entry points either: the only importer is bindings/python/python/auths/_trust.py:11, which itself is private and not re-exported from `auths`, `auths.verify`, `auths.identity`, `auths.integrations`, `auths.framework`, `auths.testkit`, or `auths.profiles`. +Rust owns these: `auths_author::prepare_principal_status` (core/crates/auths-author/src/lib.rs:1041), `prepare_grant_status` (:1061), and `auths_lifecycle::LifecycleState` (product/runtime/auths-lifecycle/src/model.rs:21). +Note Python reuses the name `LifecycleState` (_lifecycle.py:382) for what Rust calls `LifecycleState` (model.rs:21) — verify these are the same enum before renaming. +- **Disposition:** Decide and act in one change. If lifecycle authoring is product surface, add a `lifecycle` module to both bindings with identical names (`withdrawDelegation`/`withdraw_delegation`, `rotateIdentity`/`rotate_identity`, `recordCompromise`/`record_compromise`, `StatusProvider`, `StatusSnapshot`) and add it to the seven-module topology in the spec. Otherwise delete bindings/python/python/auths/_lifecycle.py and the two TypeScript helpers, and keep status authoring Rust-reference-tier only. +- **Rationale:** Revocation and key rotation are security operations. Either they are product surface — in which case TypeScript users have no way to revoke a delegation and that is a blocker-shaped gap — or they are not, in which case Python carries 396 lines of unreachable public-shaped API. Nobody has had to choose because the module is private-but-complete. + +### `grant-vs-authority-noun` — Rust's core noun is `grant`; the entire product surface calls the same object `authority` +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** Rust: `GrantStatement`, `SignedGrant`, `GrantRequest` (core/crates/auths-author/src/lib.rs:350), `GrantPlan` (:496), `plan_child_grant` (:537), `prepare_grant` (:1001), `push_grant` (:152), `bind_grant_evidence` (:169), `GrantStatusSnapshot` (product/sdk/auths-sdk/src/lib.rs:8). +Product surface: `Authority` (bindings/typescript/src/product.ts:40 `export type Authority = McpToolAuthority`; bindings/python/python/auths/_product.py:58 `Authority = McpToolAuthority`), `ProductionAuthority` (production-client.ts:39, _production_client.py:56), `TrustedAuthority` (workflow/contracts.ts:112), `DelegatedAuthority` (_workflow.py:454), `McpToolAuthority` (profiles/mcp/index.ts:133, profiles/_mcp.py), `AuthorityDiff` (auths-author :439), `AuthorityDimension` (re-exported auths-author :31), `/v1/authority/create` and `/v1/authority/delegate` routes (product/runtime/auths-node/src/api.rs:190,194). +The bindings then mix both: TypeScript exposes `SignedGrantSource`, `SignedGrantMaterial`, `SignedGrantProvider`, `SignedGrantLoadRequest`, `WorkflowSignedGrantAuthority`, `WorkflowGrantPlan` (bindings/typescript/src/workflow/contracts.ts:179-195, :806, :837) alongside the authority names. Python does the same (`SignedGrantInput`, `SignedGrantSource`, ... at bindings/python/python/auths/_workflow.py:1784-1788). +Python's native layer drops the noun entirely: `plan_child` and `plan_child_statement` (bindings/python/python/auths/_authority.py:21-22) for Rust's `plan_child_grant`. +- **Disposition:** Choose `Authority` as the product-surface noun (it is what the routes, the root exports and every binding already say) and keep `grant` strictly as the Rust reference-tier protocol term for the signed statement. Then make the boundary explicit: every binding-visible name is `Authority*` (`AuthoritySource`, `AuthorityMaterial`, `AuthorityProvider`, `AuthorityLoadRequest`) and every `SignedGrant*` name in bindings/typescript/src/workflow/contracts.ts:179-195 and bindings/python/python/auths/_workflow.py:1784-1788 is renamed. Rename `plan_child`/`plan_child_statement` back to `plan_child_grant`/`plan_child_grant_statement` to match Rust. +- **Rationale:** One object, two nouns, used interchangeably in the same file (`WorkflowSignedGrantAuthority` contains both). This is the deepest naming split in the codebase and the reason 'what is an Authority' has no single answer — sometimes it is a signed grant, sometimes an MCP tool authority, sometimes opaque server bytes. + +### `ts-entry-module-alias-chains` — TypeScript has eight competing source-level entry modules including a three-hop alias chain that re-exports the same nine symbols +- **Lane:** typescript · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** bindings/typescript/src/framework.ts (15 lines) re-exports `ProviderOperationError, ControlEvidence, PrincipalDescriptor, ProviderFailureKind, Signer, SignerLifecycle, SigningObjectKind, SigningRequest, SigningResponse` from `./custody.js`. +bindings/typescript/src/custody.ts (11 lines) re-exports the identical nine from `./workflow.js`. +bindings/typescript/src/workflow.ts (2 lines) re-exports `* from ./workflow/contracts.js` and `* from ./workflow/internal/orchestrator.js`. +Three hops for nine symbols, and `framework` is a published entry point (`bindings/typescript/package.json` `exports` map) while `custody` and `workflow` are not. +The other competing surfaces: `authority.ts` (1 line, `export * from "./authorization-plans.js"`), `mcp.ts` (1 line, `export * from "./profiles/mcp/index.js"`), `profile-kit.ts` (1 line, `export * from "./profiles/application/index.js"`), `internal-sdk.ts` (25 lines, a second SDK-shaped surface), `workflow-client.ts` (67 lines, re-exports 50 names from workflow.js plus `loadAuths`). +`profiles.ts:2` also does a blanket `export * from "./profiles/mcp/index.js"`, so every MCP internal name (including `McpGatewayCall`, `McpHandlerCause`, `McpExecutionResources`) is published from `@auths-dev/sdk/profiles`, whereas Python's `profiles/__init__.py:47-71` publishes a deliberate 21-name list. +Python has exactly the seven modules the spec names, with explicit `__all__` in each. +- **Disposition:** Delete `internal-sdk.ts`, `workflow.ts`, `workflow-client.ts`, `profile-kit.ts`, `mcp.ts`, `authority.ts`, `custody.ts` per the spec, and have `framework.ts` import directly from `./workflow/contracts.js`. Replace `export *` in `profiles.ts:2` and `index.ts` with explicit named export lists mirroring Python's `__all__` exactly, and add a CI check that compares the two symbol lists. +- **Rationale:** Eight source-level entry modules for seven published ones, with blanket `export *` and multi-hop aliasing, makes the TypeScript public symbol count unknowable and unreviewable — you cannot count what `export *` publishes without building. Python already does this correctly, so the two languages have different notions of what is public. + +--- + +## MINORS + +### `cross-module-visibility-splinter-crates` — Six published crates exist only to give an implementation cross-module visibility; each has one or two internal consumers and no external audience +- **Lane:** cross · **Goal:** simple · **Area:** rust-surface +- **Evidence:** auths-signature-core (core/crates/auths-signature-core/src/lib.rs:69 `verify_ed25519`, :104 `verify_p256_sha256`) — consumers: auths-signature, auths-signature-ed25519, auths-identity-raw-key, auths-did-keri, and the unpublished auths-records-api. auths-raw-key-core (core/crates/auths-raw-key-core/src/lib.rs:129 `RawKeyDescriptorV2`) — consumers: auths-raw-key, auths-identity-raw-key, auths-identity-authority (dead). auths-multikey (core/crates/auths-multikey/src/lib.rs:58 `Multikey`) — only published consumer is auths-did-key; others are auths-testkit, auths-did-web (both publish=false) and core/fuzz. auths-assurance (core/crates/auths-assurance/src/lib.rs:21 `evaluate`) — single consumer auths-verifier. auths-composition (core/crates/auths-composition/src/lib.rs:73 `evaluate`) — consumers auths-verifier plus unpublished auths-formal-refinement and core/fuzz. auths-kernel-runtime (product/runtime/auths-kernel-runtime/src/lib.rs:12 `AuthsKernel`) — consumers auths-sdk and auths-runtime only, and it leaks into both facades (product/sdk/auths-sdk/src/lib.rs:260 `pub const fn new(kernel: Arc)` and product/runtime/auths-runtime/src/lib.rs:10 `pub use auths_kernel_runtime::AuthsKernel;`). All six are in PUBLIC_RUST_CLOSURE. +- **Disposition:** Absorb each into its single real owner and delete the coordinate: auths-signature-core -> auths-signature; auths-raw-key-core -> auths-raw-key; auths-multikey -> auths-did-key; auths-assurance and auths-composition -> auths-verifier (as `pub mod assurance` / `pub mod composition`, keeping the symbols visible to auditors under the reference crate that owns verification); auths-kernel-runtime -> auths-sdk with `AuthsKernel` constructed internally so `Verifier::self_contained` is the only entry point. Where a no_std split is the real reason for a `-core` crate, keep the split as a feature flag on the owner rather than a second published coordinate. +- **Rationale:** These are not 'breadth in the reference tier is a feature' — breadth means many symbols for auditors under a coherent owner, not many crates.io coordinates each holding one internal helper. Every extra coordinate is an independent semver promise, an independent docs.rs page, and one more place a reader must look to find where a meaning lives. The suite-id sprawl finding is a direct consequence of this splintering: `-core` crates redeclare constants because they cannot see their owner. + +### `lifecycle-and-policy-published-ahead-of-verticals` — `auths-lifecycle` and `auths-bounded-policy` are published tier 0/1 while every vertical that consumes them is publish = false +- **Lane:** cross · **Goal:** extensible · **Area:** rust-surface +- **Evidence:** auths-lifecycle consumers: product/runtime/auths-runtime (published) plus auths-stores, auths-radicle, auths-kubernetes, auths-opentofu, auths-stripe, auths-records-api, auths-github, auths-postgresql — all `publish = false` — plus bindings/python and six demos. auths-bounded-policy consumers: product/runtime/auths-lifecycle and product/runtime/auths-runtime (published) plus the same eight unpublished integrations, its own fuzz target, and demos/rest-api-authorization. Both are published: release/public-naming.toml:231 (tier 0, auths-bounded-policy) and :246 (tier 1, auths-lifecycle). Their public surfaces are large mechanism vocabularies — product/runtime/auths-lifecycle/src/lib.rs:53-90 alone freezes eleven contract-id strings and nine limit constants; product/policy/auths-bounded-policy/src/lib.rs:40-66 freezes eight contract ids and five limits. +- **Disposition:** Set publish = false on both until at least one vertical integration crate is itself published, then publish them together in the same release wave. Record the decision in release/public-naming.toml so the coupling is explicit: these are mechanisms for verticals, and a mechanism with no published vertical has no external caller. +- **Rationale:** docs/target-state/PROFILE_AND_DOMAIN_ABSTRACTION_BOUNDARY_PLAN.md is explicit that shared packages provide narrow mechanisms and must not acquire ownership of lifecycle states. Freezing the mechanism contract before any published vertical has exercised it inverts that: the first external vertical must conform to a lifecycle vocabulary chosen by an in-repo consumer. Since no vertical ships publicly today, unpublishing costs nothing and preserves the freedom to change the contract when the first one does. + +### `algebra-kernel-two-equal-entry-points` — `auths-algebra-kernel` publishes two entry points for one attenuation decision, and its own proof asserts they are identical +- **Lane:** cross · **Goal:** simple · **Area:** rust-surface +- **Evidence:** core/crates/auths-algebra-kernel/src/lib.rs:9-12 re-exports both `attenuation_accepts` and `attenuation_checks_accept` from generated.rs (defined at core/crates/auths-algebra-kernel/src/generated.rs:125 and :141). The Kani harness at core/crates/auths-algebra-kernel/src/lib.rs:69-73 computes both and asserts `generic == expected`, `concrete == expected`, `generic == concrete`. The crate also exposes `AttenuationChecks` with eleven public bool fields (core/crates/auths-algebra-kernel/src/generated.rs:52-75). +- **Disposition:** Export one. Keep `attenuation_accepts` (the generic form the verifier uses) and demote `attenuation_checks_accept` to a private helper, or vice versa — but publish exactly one name for 'does this attenuation hold'. Keep the Kani harness by calling the private form directly. +- **Rationale:** Two public functions that are formally proven to return the same value for all inputs are two ways to do one thing, in the crate that defines the single most security-relevant predicate in the system (delegation can only narrow). A caller reading the docs cannot tell which one is authoritative, and a future divergence between them would be a silent authority-widening bug rather than a compile error. + +### `algebra-kernel-predecessor-schema-string` — A published tier-0 crate freezes a predecessor-named schema identifier that public-naming.toml elsewhere requires replacing +- **Lane:** cross · **Goal:** simple · **Area:** rust-surface +- **Evidence:** core/crates/auths-algebra-kernel/src/generated.rs:4 `pub const CONTRACT_SCHEMA: &str = "auths-proof-algebra-contract/v1";`. release/public-naming.toml treats repository-scoped `auths-proof-*` schema ids as replace targets: :175-181 (`release-evidence-schema`, current 'auths-proof-release-evidence/v1' -> target 'auths.release-evidence/1', state 'replace') and :183-189 (`platform-artifact-schema`, 'auths-proof-platform/v1' -> 'auths.platform/1', state 'replace'), with the justification 'No release candidate has been published with the repository-scoped schema identifier.' The algebra contract schema is not listed among the surfaces at all, yet it is published in release_order tier 0 (release/public-naming.toml:231). +- **Disposition:** Rename to `auths.algebra-contract/1` in the generator that produces core/crates/auths-algebra-kernel/src/generated.rs (and in the Lean/Aeneas source of truth), then add a `[[surfaces]]` entry to release/public-naming.toml recording the replacement so xtask public-naming covers it. +- **Rationale:** public-naming.toml claims to be 'the sole machine-readable authority for public Auths names' (release/public-naming.toml:4), and this identifier escaped the inventory. It is a frozen contract string in a published crate, so the window to rename it closes at 1.0 — after which the product ships a schema id naming a repository the project has already decided is a temporary coordinate. + +### `doctor-underreports-profiles` — doctor() hardcodes a single profile and never mentions the three production verticals the same package exposes +- **Lane:** python · **Goal:** secure · **Area:** python +- **Evidence:** `bindings/python/python/auths/_doctor.py:41`: `profiles=("mcp/1",),` — a literal, unconditional. `DoctorReport.profiles` is `Tuple[str, ...]` (`:22`) and is rendered to the operator at `:61` `f"Profiles {', '.join(report.profiles)}"`. The same wheel exposes `auths.profiles.opentofu_saved_plan_apply()`, `postgresql_bounded_update()`, `github_issue_address()` (`profiles/__init__.py:36-45`), all usable through `auths.create_auths(profile=...)` (`_production_client.py:306-322`). `bindings/public-topology-v1.json` `qualifiedProfiles` lists four: `auths.github.issue-address/1`, `auths.mcp/1`, `auths.opentofu.saved-plan-apply/1`, `auths.postgresql.bounded-update/1`. Note also the format disagreement: doctor says `mcp/1`, the topology says `auths.mcp/1`. +- **Disposition:** Derive `DoctorReport.profiles` from the generated roster (see the triplication finding) rather than the literal at `_doctor.py:41`, and use the topology's fully-qualified id format. +- **Rationale:** `doctor()` is the pre-flight diagnostic an operator runs to confirm what the installed wheel can do. Reporting one profile when the wheel supports four means the one tool built to answer "is this install capable of X" answers wrong for three of four verticals — including all three production ones. + +### `lifecycle-shape-inconsistency` — ProductionAuths has no close or context-manager protocol while every other resource-holding type does, and _PendingAuths implements two conflicting protocols +- **Lane:** python · **Goal:** simple · **Area:** python +- **Evidence:** Async lifecycle across the package: `Auths.__aenter__/__aexit__/aclose` (`_product.py:335-354`), `AuthsClient` and `AttachedAgent` both `__aenter__`/`__aexit__`/`aclose` (`_workflow.py`), `DevelopmentMcpProvider.__aenter__/__aexit__/aclose` (`profiles/_mcp.py`), `DevelopmentEd25519Signer.aclose`/`DevelopmentReceiptAttestor.aclose` (`_development.py`). `ProductionAuths` (`_production_client.py:157-303`) has no `aclose`, no `__aenter__`, no `__aexit__`, despite owning a `_UrlLibProductionTransport` (`:180`) and accepting a caller-supplied `ProductionTransport` (`:164`) that may hold connections. `LifecycleAuthor` uses a sync `def close()` (`_lifecycle.py`) where everything else is `async def aclose()`. `_PendingAuths` (`integrations.py:302-323`) is simultaneously `Awaitable[Auths]` (`__await__` at `:306`) and an async context manager (`__aenter__`/`__aexit__` at `:313-323`), and the two paths differ in ownership: `async with` closes the `Auths` at `:322`, plain `await` hands the caller an `Auths` it must close itself, with no signal which is intended. +- **Disposition:** Give `ProductionAuths` `aclose`/`__aenter__`/`__aexit__` and a `ProductionTransport.aclose` hook so caller-supplied transports can release resources. Rename `LifecycleAuthor.close` to `aclose` and make it async to match. Drop `__await__` from `_PendingAuths` (`integrations.py:306-311`) so composition has exactly one shape — `async with` — rather than two with different ownership semantics. +- **Rationale:** Three lifecycle shapes for the same job means a caller cannot generalize: `async with` works for `Auths` but not `ProductionAuths`, `await` works for `_PendingAuths` but silently transfers cleanup responsibility. The dual protocol on `_PendingAuths` in particular is a leak waiting to happen, since the two spellings look interchangeable and are not. + +### `generated-modules-unmarked` — Generated projection modules are indistinguishable from hand-written private modules +- **Lane:** python · **Goal:** simple · **Area:** python +- **Evidence:** `bindings/python/python/auths/_error_registry.py` and `_mcp_profile.py` are generated and semantic-frozen (`xtask/src/error_registry.rs:10`, `xtask/src/mcp_session_contract.rs:6`, `xtask/src/semantic_freeze.rs:300,423`), and `_error_registry.py` is verified byte-identical to `product/errors/v1/registry.json`. Neither carries a do-not-edit marker: `_error_registry.py:1-6` opens with `from __future__ import annotations` / `import json` / `from typing import Any, Final` / `ERROR_REGISTRY: Final[dict[str, Any]] = json.loads(r'''{`. They sit inline in `python/auths/` alongside 22 hand-written underscore modules with identical naming. TypeScript segregates the same three artifacts into `bindings/typescript/src/generated/{error-registry.ts,mcp-profile.ts,mechanism-conformance.ts}`, where the directory name is the marker. Python's third generated-equivalent, `_mechanism_conformance.py`, is likewise inline. +- **Disposition:** Move `_error_registry.py`, `_mcp_profile.py` and `_mechanism_conformance.py` to `python/auths/_generated/`, mirroring `bindings/typescript/src/generated/`, and have the xtask emitters prepend a do-not-edit header. Update `tools/check_wheel.py:12,20,26` paths accordingly. +- **Rationale:** These three files are the only true Rust-owned projections in the Python package and they are the ones a maintainer is most likely to hand-edit, because nothing marks them. Making them visually distinct is also the cheapest way to make the projection boundary legible: everything in `_generated/` is Rust-owned, everything else is Python, and the ratio (3 files versus 22) tells the real story about how much of this binding is a projection. + +### `custody-unknown-declared-not-applied` — Three custody codes whose own explanations admit the provider gave no proof are registered as not-applied +- **Lane:** rust · **Goal:** secure · **Area:** error-model +- **Evidence:** product/errors/auths-errors/src/lib.rs:795-807 — `custody.provider-unknown`, title "Custody outcome unknown", explanation "The provider did not prove whether it produced a signature for the exact request", registered with `NOT_APPLIED_CONDITIONAL` and `RecommendedAction::ContactSupport`. Same pattern at lib.rs:756-768 `custody.unavailable` — "could not conclusively service the exact signing request" — `NOT_APPLIED_CONDITIONAL`; and lib.rs:743-755 `custody.throttled`. Contrast `core.outcome-unknown` (lib.rs:474-486), whose near-identical explanation carries `POSSIBLE_UNKNOWN` + `ResumeAndReconcile`. Because `allows_execution_reference` is false for all custody codes, `validate_recovery` (lib.rs:320-328) would reject them if they were reclassified as `Possible` — the current classification is what makes them pass validation. +- **Disposition:** Reclassify `custody.provider-unknown` (and reconsider `custody.unavailable`) as `possible/unknown/resume-and-reconcile`, set `allows_execution_reference = true`, and add the custody reconciliation path the reclassification implies. If custody deliberately treats an unproven signature as a non-effect, state that reasoning in the explanation instead of contradicting it. +- **Rationale:** An accepted-but-unreturned signature is a real-world artifact: it exists, it can be replayed, and it may have been billed and audit-logged. Declaring `effect: not-applied` on the strength of "the provider did not prove" asserts knowledge the code's own explanation denies having. This is the one place where the registry itself — the artifact everything else is measured against — flattens unknown into not-applied. + +### `docs-render-debug-tokens` — The generated error reference prints Rust Debug identifiers instead of the wire tokens developers must compare against +- **Lane:** rust · **Goal:** simple · **Area:** error-model +- **Evidence:** xtask/src/error_registry.rs:153 `format!("{:?} / {:?}", outcome.effect, outcome.retry).to_lowercase()` and error_registry.rs:156-163 `"| `{}` | `{}` | {} | `{:?}` | {} |"` with `definition.recommended_action`. Output at docs/reference/error-codes.md:7-20 reads ``| `core.invalid-configuration` | `create` | notapplied / never | `CorrectConfiguration` | ...``. The real serialized values are `"not-applied"` and `"correct-configuration"` (product/errors/v1/registry.json:14-19), since both enums are `#[serde(rename_all = "kebab-case")]` (auths-errors/src/lib.rs:39,47). +- **Disposition:** Serialize the values through serde in `render_docs` rather than `Debug`-formatting them, so the table prints `not-applied` and `correct-configuration`. +- **Rationale:** This is the only human-readable reference for the error model, and every effect and recommended-action token in it is wrong. `notapplied` is not a value in any vocabulary in the system, and a developer copying `CorrectConfiguration` into a comparison writes a branch that never fires. + +### `substring-and-concat-classification` — Contract violations are classified by substring matching on exception text and error codes are built by string concatenation +- **Lane:** python · **Goal:** secure · **Area:** error-model +- **Evidence:** bindings/python/python/auths/_workflow.py:1411-1421 `def _transaction_runtime_error(error: RuntimeError): if "expired" in str(error): return AuthsWorkflowError("transaction-expired", ...)` — otherwise `"transaction-consumed"`, so any unrelated `RuntimeError` is relabeled as a consumed transaction. _workflow.py:1396-1409 `_provider_failure` builds codes as `operation + "-" + suffix`; _workflow.py:1437-1440 builds `"authority-source-" + error.kind`, minting codes such as `authority-source-unsupported` that do not exist in TypeScript's closed `WorkflowErrorCode` union (bindings/typescript/src/workflow/errors.ts:1-36 has only `authority-source-failed`). Every code produced this way inherits the `AuthsWorkflowError` defaults `retry="never", effect_state="not-started"` (_errors.py:61-63), so a signer timeout is reported as never-retryable and definitely-not-started. Related broad relabels: _application_profile.py:898-902 `_state_call` `except Exception: return "unavailable"` (mirrored at bindings/typescript/src/profiles/application/index.ts:815-821) turns any store or adapter bug into `gateway-unavailable` with `retry="safe", effect_state="not-started"` (_application_profile.py:905-919; index.ts:823-833). +- **Disposition:** Delete `_transaction_runtime_error`'s substring test in favour of a typed transaction-state error from the native layer; stop constructing codes by concatenation — enumerate them in the registry; and let programmer/invariant errors from state adapters propagate instead of collapsing to `"unavailable"`. +- **Rationale:** PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md:456-465 requires typed validation, typed adapter errors, and programmer errors that "fail loudly ... rather than being mislabeled as an authorization denial." Classifying by exception message text is not a bounded cause category, concatenated codes cannot be enumerated or kept in sync across bindings, and the shared default of `not-started` means these paths assert non-effect for conditions where it has not been established. + +### `ts-root-executionresult-references-unexported-members` — The root ExecutionResult union names two unexported members and is not the return type of any method +- **Lane:** cross · **Goal:** simple · **Area:** typescript +- **Evidence:** `src/product.ts:129-131`: +```ts +export type ExecutionResult = Completed | PlanCompleted | Denied | Indeterminate | RecoveryResult | PlanRecoveryResult; +export type SingleExecutionResult = Completed | Denied | Indeterminate | RecoveryResult; +export type PlanExecutionResult = PlanCompleted | Denied | Indeterminate | PlanRecoveryResult; +``` +`src/index.ts:24` root-exports `ExecutionResult`, but `PlanCompleted` (`src/product.ts:56-60`) and `PlanRecoveryResult` (`:62-68`) are not in `src/index.ts` and do not appear in `api/public-api.txt:3-43`. Meanwhile no method returns `ExecutionResult`: `Auths.execute` is declared as `Promise` / `Promise` (`src/product.ts:141,146`), `resume` as `Promise` (`:150`), `recover` likewise (`:155`) — and none of those three type names is root-exported either. Also dead at the package boundary: `src/product.ts:364-374` `export async function verifyReceipt`, `export function encodeReceipt`, `export function decodeReceipt` — `src/index.ts:15-28` does not re-export them and the only other importer of `product.js` is `src/integrations.ts:3-9`, which takes only `createAuths`, `createAuthsConfiguration`, `Auths`, `AuthsConfiguration`, `AuthsResources`. The three names ship instead from `./verify` as aliases of the `internal/receipt-attestation` functions (`src/verify.ts:22-27`). +- **Disposition:** Export `SingleExecutionResult` and `PlanExecutionResult` from the root and delete `ExecutionResult` (`src/product.ts:129`) — a union that no signature produces and whose members are half-unexported has no caller. Or export `PlanCompleted` and `PlanRecoveryResult` and make `ExecutionResult` the declared return of `Auths.execute`. Delete the three dead receipt functions at `src/product.ts:364-374`; `./verify` is the single authoritative receipt surface. +- **Rationale:** A caller who annotates a variable as the root `ExecutionResult` cannot narrow it, because two of its six members have no importable name, and cannot assign to it from any SDK call, because no method returns it. It is a type that exists only in the export list. The three dead receipt functions are a second, unreachable copy of an operation `./verify` already publishes. + +### `ts-docs-reference-nonexistent-subpaths` — Published documentation instructs callers to import from two subpaths that do not exist and calls the root a compatibility surface +- **Lane:** cross · **Goal:** simple · **Area:** typescript +- **Evidence:** `docs/adoption-layers.md:17` documents layer 3 as `@auths-dev/sdk/authority` and `:18` documents layer 4 as `@auths-dev/sdk/approvals`. `package.json` `exports` declares exactly seven subpaths — `.`, `./identity`, `./verify`, `./profiles`, `./integrations`, `./framework`, `./testkit` — and `tools/public-api.mjs:19-21` hard-fails if that list drifts from `bindings/public-topology-v1.json:3-28`, which likewise lists only those seven. Both documented imports throw `ERR_PACKAGE_PATH_NOT_EXPORTED` at runtime. `docs/adoption-layers.md:19` further describes the root as `@auths-dev/sdk/profiles` "or the compatibility root". `src/authority.ts:1` and `src/approvals.ts` exist in `dist/` but are not exported, which is presumably why the docs believe the subpaths are real. +- **Disposition:** Correct `docs/adoption-layers.md:17-19` to the seven subpaths that `bindings/public-topology-v1.json` actually declares, and delete the phrase "compatibility root" — the root is the product surface, not a compatibility path, and the vocabulary is forbidden prelaunch. Add a docs test to `tools/` that extracts every `@auths-dev/sdk/*` specifier from `bindings/typescript/docs/` and `docs/` and asserts it resolves against `package.json` `exports`, so this class of drift fails CI alongside the existing `public-api.mjs` check. +- **Rationale:** Documented imports that throw at resolution are a first-contact failure for the exact caller the layered adoption story is meant to serve, and the word 'compatibility' applied to the root entry point contradicts the prelaunch policy that forbids compatibility surfaces. The existing `public-api.mjs` gate proves the enforcement pattern already exists; the docs are simply outside it. + +### `ts-testkit-published-as-public-subpath` — testkit is a first-class published subpath exporting 29 symbols including an in-memory execution store +- **Lane:** cross · **Goal:** secure · **Area:** typescript +- **Evidence:** `package.json` `exports` declares `"./testkit": { "types": "./dist/testkit/index.d.ts", "import": "./dist/testkit/index.js" }`, and `bindings/public-topology-v1.json:24-28` names it a topology layer, so `tools/public-api.mjs:19-21` requires it to stay. `api/public-api.txt:167-195` lists 29 exported symbols including `InMemoryApplicationExecutionStore` (`src/testkit/index.ts:81-126`), `development` fixtures that mint ephemeral signers and auto-approving approval providers (`:154-176`, `DevelopmentApprovalProvider` at `:61-79` returns `"approved"` unconditionally), and `createDiagnosticVerifier`/`DiagnosticVerifier` which accept a caller-supplied WASM engine. The design intent is documented and sound — `src/verifier/result.ts:118-121` states that caller-supplied engines "belong on `@auths-dev/sdk/testkit`, whose results are never effect-capable" — but the boundary is asserted in a doc comment, and `test/package/package.test.js` contains no test proving a testkit-minted verifier result cannot reach `Auths.execute`. `src/testkit/index.ts:50-59` also re-exports `AtomicReservationRecord` (duplicating `./framework`) and `BoundedApprovalSession` from `../approvals.js`. +- **Disposition:** Keep `./testkit` (conformance certification is a legitimate published capability) but prove the boundary rather than asserting it: add a package test that constructs a `DiagnosticVerifier` result from a caller-supplied engine and asserts it cannot be accepted by `Auths.execute` or by any `./integrations` composition. Move `InMemoryApplicationExecutionStore` and the auto-approving `development` fixtures behind a name that cannot be mistaken for production (`fixtures`, per the `development` collision finding), and drop the duplicate `AtomicReservationRecord` re-export at `src/testkit/index.ts:50`. +- **Rationale:** An always-approving approval provider, an in-memory execution store, and a verifier that accepts a caller-supplied engine are all published to installed consumers. The separation that makes this safe — testkit results are never effect-capable — is currently only a comment in `verifier/result.ts`, so a future refactor can erase it without any test failing. This is the one place where a test-shaped surface genuinely reaches the public package. + +### `execution-reference-decode-bypasses-its-own-seal` — `ExecutionReference.decode` mints a supposedly sealed reference from any well-formed hex string, bypassing the private token +- **Lane:** cross · **Goal:** secure · **Area:** security +- **Evidence:** bindings/typescript/src/product.ts:80-102 — the constructor is private and guarded (`if (token !== REFERENCE_TOKEN) throw new TypeError("sealed Auths execution reference")`), but `static create(token, value)` at :87-89 is `public`, and `static decode(input)` at :91-93 delegates to `decodeExecutionReference`. +bindings/typescript/src/product.ts:110-119 — `decodeExecutionReference` accepts any 134-byte input matching `/^mcp1\.[0-9a-f]{64}\.[0-9a-f]{64}$/` and then calls `ExecutionReference.create(REFERENCE_TOKEN, value)`. No integrity tag, MAC, or store lookup. +bindings/typescript/src/product.ts:232-251 — `AuthsFacade.resume` retrieves the string via `referenceResources.get(input.reference)` and passes it to `resumeMcpClosed`. +bindings/typescript/src/profiles/mcp/index.ts:804-811 — that string is used directly as a key: `const record = await resources.state.loadRecovery(reference);`, throwing `gateway-conflict` only when the application store returns `undefined`. +The error registry has a dedicated code for this: `core.forged-execution-reference` (`retry: never`, `effect: not-applied`). +- **Disposition:** Make `decode` verify rather than parse: bind the reference to the session with a MAC (the facade already holds `resources.sessionKey`, used at profiles/mcp/index.ts:783 `boundedSessionKey`) and reject on mismatch with `core.forged-execution-reference`. Make `ExecutionReference.create` private — `mintExecutionReference` via a `static {}` initializer, the pattern already used correctly for `VerifiedAction` (verifier/result.ts:50-53), `Auths` (result.ts:141-143), and `TrustedContextSource` (workflow/trusted-context.ts:48-51). +- **Rationale:** The class advertises itself as sealed and the private constructor plus token is clearly meant to enforce that, but `decode` hands out the token to anyone who can format 64 hex characters, and `create` is public on top of that. Exploitability depends entirely on the application's `McpExecutionState.loadRecovery`: any store that keys on a caller-derivable identifier (an execution id echoed in a log or an API response) lets an attacker resume another tenant's recoverable execution, which drives the provider call again through `driveMcpSession`. The registry already anticipates forged references, so the intended defence exists — it just is not wired up here. I could not construct a concrete cross-tenant exploit without assuming a specific store implementation, so this is PLAUSIBLE. + +### `root-preserved-is-a-hardcoded-constant` — The formal attenuation contract's `root_preserved` dimension is supplied as a literal `true`, so the Kani proof about it is vacuous +- **Lane:** cross · **Goal:** secure · **Area:** security +- **Evidence:** core/crates/auths-authority/src/lib.rs:200-201 — `let checks = AttenuationChecks { root_preserved: true, depth_decreases: parent.remaining_depth > 0 && grant.remaining_depth < parent.remaining_depth, ... }`. Every other field is a real computation; this one is a literal. +core/crates/auths-algebra-kernel/src/generated.rs:141-153 — `attenuation_checks_accept` conjoins `checks.root_preserved` with the other ten dimensions. +core/crates/auths-algebra-kernel/src/generated.rs:22-24 — the generated trait documents it as a real obligation: `/// Whether the trust root is preserved. fn root_preserved(&self) -> bool;`. +core/crates/auths-algebra-kernel/src/lib.rs:43-74 — the Kani harness `attenuation_accepts_exactly_the_conjunction` proves only that `attenuation_accepts` and `attenuation_checks_accept` both equal the eleven-way conjunction of arbitrary booleans; it says nothing about how any witness is produced. +The property does hold today by construction — core/crates/auths-authority/src/lib.rs:359-380 `delegate` mutates `subject`, `profile`, `permissions`, `validity`, `audiences`, `action_constraint`, `budget_ceiling`, `remaining_depth`, `last_grant`, `status_policy`, and `extensions`, but never `self.root`. +- **Disposition:** Either compute the witness — carry the parent root into `AuthorityStateView` and set `root_preserved: principal_id_equal(view.root, parent.root)` — or delete the dimension from the generated contract and the Kani harness so the formal claim matches what is actually checked. Do not leave a proven-looking conjunct whose witness is a literal. +- **Rationale:** This does not currently admit an attack, because `EffectiveAuthority::delegate` structurally cannot change the root. The problem is that the formal apparatus creates a false assurance signal: the contract, the trait doc, the Kani harness, and the generated conjunction all present root preservation as verified, when the only thing standing between the system and a root swap is that nobody has yet written `self.root = ...`. If `delegate` is later extended — for example to support cross-root grant bridging — the eleven-way conjunction will keep returning true and the proof will keep passing. A security invariant whose witness is `true` is a comment, and it should be labelled as one or made real. + +### `single-verify-path-does-not-copy-action-bytes-before-sealing` — The TypeScript single-verify path seals a `VerifiedAction` from the caller's live buffer after the engine call, unlike the batch path which copies first +- **Lane:** cross · **Goal:** secure · **Area:** security +- **Evidence:** bindings/typescript/src/verifier/result.ts:153-154 — `const bytes = this.#engine.verifyV1(proofCbor, canonicalActionCbor, trustedContextCbor); const result = verificationResult(bytes, canonicalActionCbor, correlationId);` — the caller's array is handed to the engine and then reused. +bindings/typescript/src/verifier/result.ts:256-263 — `verificationResult` mints from that same reference: `action: mintVerifiedAction(canonicalActionCbor)`. +bindings/typescript/src/verifier/result.ts:38-41 — the copy happens only inside the constructor, i.e. after the engine has already returned: `this.#canonicalAction = canonicalAction.slice();`. +The batch path gets this right: bindings/typescript/src/verifier/result.ts:201-205 copies every input up front (`canonicalActionCbor: item.canonicalActionCbor.slice()`) and mints from the copy at :214-218. +- **Disposition:** Copy once at entry in `Auths.verify`, mirroring `verifyMany`: take `const action = canonicalActionCbor.slice();` before calling `this.#engine.verifyV1`, pass that copy to both the engine and `verificationResult`. One line, and it makes the two paths structurally identical. +- **Rationale:** In single-threaded JavaScript no mutation can interleave between the synchronous `verifyV1` call and the `slice()` in the constructor, which is why this is minor. It stops being safe if the caller passes a `Uint8Array` backed by a `SharedArrayBuffer`: a worker thread can rewrite the buffer after the WASM engine has copied the original bytes into linear memory and decided 'authorized', but before `VerifiedAction` snapshots it. The resulting sealed action would report canonical bytes that were never verified, and `decode_verified` in a profile consuming `canonicalBytes()` would decode the substituted action. I could not disprove that a consumer passes shared memory, so this is PLAUSIBLE — but the batch path already demonstrates the intended discipline, so the inconsistency is worth closing regardless. + +### `decision-inspection-shape-split` — DecisionInspection is anonymous nested objects in TypeScript and five named dataclasses in Python +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** | lang | symbol | file:line | +| TS | `DecisionInspection` with inline `Readonly<{...}>` for decision / kernel / commitments / approval | bindings/typescript/src/inspection.ts:4-25 | +| PY | `DecisionInspection` composed of `DecisionSummary` (:56), `KernelSummary` (:61), `DecisionCommitments` (:67), `ApprovalInspection` (:83), `InspectionMetrics` (:45) | bindings/python/python/auths/_inspection.py:87-93 | +Python exports all five sub-names publicly (bindings/python/python/auths/verify.py:178-205: `ApprovalInspection`, `DecisionCommitments`, `DecisionSummary`, `InspectionMetrics`, `KernelSummary`); TypeScript exports only `DecisionInspection` (bindings/typescript/src/verify.ts:28-31). +Optionality also differs: TS omits `requiredConfiguration`/`action` keys when absent (inspection.ts:8-11 `?`, construction at :60); Python always carries them as `Optional[bytes] = None` (_inspection.py:70-71). +- **Disposition:** Name the sub-structures in both: TypeScript gains exported `DecisionSummary`, `KernelSummary`, `DecisionCommitments`, `ApprovalInspection` interfaces matching Python's field-for-field, and `DecisionInspection` references them. Adopt Python's always-present-with-null convention or TypeScript's omit convention in both — prefer present-with-null so the two serialize identically. +- **Rationale:** Four extra public symbols in one language for a type that is one symbol in the other. A caller writing cross-language documentation or a conformance test cannot name the same sub-structures. + +### `error-registry-45-not-20` — The error registry has 45 definitions across four owners, not 20 — any freeze based on a 20-code assumption will be wrong +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** product/errors/v1/registry.json `schema: "auths.error-registry/1"` contains 45 definitions with owners {core: 14, mcp: 8, plan: 5, custody: 18}. Codes: core.invalid-configuration, core.unsupported-abi, core.unsupported-semantic-subject, core.malformed-input, core.native-runtime-unavailable, core.forged-execution-reference, core.runtime-conflict, core.runtime-unavailable, core.runtime-cancelled, core.outcome-unknown, core.observation-pending, core.observation-inconclusive, core.workflow-terminal, core.internal-invariant, mcp.* (8), plan.* (5), custody.* (18). +Both bindings generate from it: bindings/typescript/src/generated/error-registry.js (consumed at bindings/typescript/src/product-errors.ts:1) and bindings/python/python/auths/_error_registry.py (1046 lines, consumed at bindings/python/python/auths/_product_errors.py:12). +Separately, `WorkflowErrorCode` (bindings/typescript/src/workflow/errors.ts:1-37) defines 35 MORE codes in a completely disjoint namespace (`disposed`, `invalid-provider`, `gateway-conflict`, ...) that are not in the registry at all, and Python's `AuthsWorkflowError` takes `code: str` untyped (bindings/python/python/auths/_errors.py:55). +- **Disposition:** Correct the working assumption to 45. Then bring the 35 `WorkflowErrorCode` values into `product/errors/v1/registry.json` under an explicit owner (e.g. `workflow`), give each one `family`/`operation`/`stages`/`outcomes`/`recommendedAction`/`fixtureId`, and regenerate both bindings so `WorkflowErrorCode` and Python's workflow `code` become generated projections rather than hand-written lists. Do not change any existing code string or fixture id. +- **Rationale:** The registry is the test oracle. Any consolidation plan sized against 20 codes will under-scope. More importantly the 35 workflow codes are a second, unregistered code namespace — untyped in Python — so a caller cannot enumerate the codes an SDK can raise. + +### `python-root-missing-config-and-options-types` — Python's package root omits four types TypeScript exports, and vice versa +- **Lane:** cross · **Goal:** simple · **Area:** vocabulary-matrix (cross-language: Rust reference / TypeScript projection / Python projection) +- **Evidence:** Present at the TypeScript root (bindings/typescript/src/index.ts) and absent from Python's (bindings/python/python/auths/__init__.py:50-86): +- `AuthsConfiguration` (index.ts:20) — Python has the class (bindings/python/python/auths/_product.py:174, listed in `_product.__all__` at :461) but does not surface it +- `ProductionAuthsOptions` (index.ts:35) — Python has no such type; `create_auths` takes bare kwargs (bindings/python/python/auths/_production_client.py:306-313) +- `DoctorMode`, `DoctorOptions`, `DoctorState` (index.ts:5-8) — Python defines `DoctorMode`/`DoctorState` (bindings/python/python/auths/_doctor.py:10-11) but exports only `DoctorReport` +Present at the Python root and absent from TypeScript's: +- `Approval` (bindings/python/python/auths/__init__.py:48,91) — TypeScript exports the lowercase value `approval` (index.ts:1) and the type `ApprovalPolicy` (index.ts:2), not a class +TypeScript exports 40 names from `.`; Python exports 36 from `auths`. +- **Disposition:** Make the two root lists identical by construction: add `AuthsOptions` (replacing `ProductionAuthsOptions`), `AuthsConfiguration`, `DoctorMode`, `DoctorOptions`, `DoctorState` to Python's `__all__` and `_OWNERS`; align the approval export name. Add a CI test that diffs the TypeScript root export list against Python's `auths.__all__` after case conversion. +- **Rationale:** The two roots are supposed to be the same product. A user cannot annotate a Python variable holding an Auths configuration, and cannot construct options as a value. diff --git a/product/README.md b/product/README.md index d9c083d5..07ab9ece 100644 --- a/product/README.md +++ b/product/README.md @@ -49,7 +49,6 @@ the release-candidate publication set): facade selected for the first public Rust surface; - `auths-enforcement`: HTTP, gRPC, CI, MCP, and service-local enforcement entry points; -- `auths-deployment`: replay- and blast-radius-safe deployment execution; - `auths-profile-kit`: deterministic fixtures and hostile-input scaffolding; - `@auths-dev/sdk`: precompiled WASM with an idiomatic TypeScript API; - `auths`: stable-ABI Python wheels; diff --git a/product/config/auths-config/src/lib.rs b/product/config/auths-config/src/lib.rs index 07e88aa9..9a99fb9f 100644 --- a/product/config/auths-config/src/lib.rs +++ b/product/config/auths-config/src/lib.rs @@ -13,7 +13,7 @@ pub use production::{ use auths_codec::context_digest; use auths_model::{ ChannelBindingId, ContextDigest, Digest, LimitKind, PROTOCOL_V1, ProfileId, ProfileRef, - RegistryManifestId, VerifierConfigurationId, VerifierContext, + RegistryManifestId, TrustedContext, VerifierConfigurationId, }; use auths_proof_exchange_model::{ChannelBindingPolicy, MAX_BODY_BYTES, MAX_PROOF_BYTES}; use serde::{Deserialize, Serialize}; @@ -256,9 +256,17 @@ impl RuntimeConfig { #[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pub enum ChannelPolicyConfig { + /// Accepts any transport, including unauthenticated ones. None, + /// Requires the transport to supply concrete peer material for the caller + /// that submitted the action: an Iroh endpoint identifier, a mutual-TLS + /// certificate digest, or operating-system peer credentials. Observations + /// that only authenticate the remote *server*, and free-form opaque + /// assertions, are refused. AuthenticatedPeer, + /// Requires a signed sender channel binding over an Iroh endpoint. SignedSender, + /// Requires a signed recipient channel binding over an Iroh endpoint. SignedRecipient, } @@ -335,7 +343,7 @@ impl CompiledConfig { &self.signed_channel_binding } - /// Binds this configuration to one immutable pure verifier context. + /// Binds this configuration to one immutable pure trusted context. /// /// # Errors /// @@ -343,7 +351,7 @@ impl CompiledConfig { /// required/executed verifier configuration disagrees with the context. pub fn bind_context( &self, - context: &VerifierContext, + context: &TrustedContext, executed_configuration: VerifierConfigurationId, ) -> Result { if self @@ -368,7 +376,7 @@ impl CompiledConfig { } } -/// Startup-ready binding of configuration, registries, and verifier context. +/// Startup-ready binding of configuration, registries, and trusted context. #[derive(Clone, Debug, Eq, PartialEq)] pub struct BoundConfiguration { config_digest: Digest, @@ -386,7 +394,7 @@ impl BoundConfiguration { self.config_digest } - /// Returns the complete pure verifier-context digest. + /// Returns the complete pure trusted-context digest. #[must_use] pub const fn context_digest(&self) -> ContextDigest { self.context_digest diff --git a/product/docs/developer-integration.md b/product/docs/developer-integration.md index e7383c17..6ad02e0d 100644 --- a/product/docs/developer-integration.md +++ b/product/docs/developer-integration.md @@ -124,7 +124,7 @@ Run the native package test and the shared corpus in the service repository: ```sh # Rust -cargo test -p auths-enforcement -p auths-deployment +cargo test -p auths-enforcement # TypeScript npm test diff --git a/product/integrations/auths-deployment/Cargo.toml b/product/integrations/auths-deployment/Cargo.toml deleted file mode 100644 index 8b361559..00000000 --- a/product/integrations/auths-deployment/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "auths-deployment" -version.workspace = true -edition.workspace = true -license.workspace = true -rust-version.workspace = true -publish = false -description = "Replay-safe Auths V1 internal deployment enforcement" - -[dependencies] -auths-enforcement.workspace = true -auths-sdk.workspace = true -thiserror.workspace = true - -[lints] -workspace = true diff --git a/product/integrations/auths-deployment/src/lib.rs b/product/integrations/auths-deployment/src/lib.rs deleted file mode 100644 index 5616f1ef..00000000 --- a/product/integrations/auths-deployment/src/lib.rs +++ /dev/null @@ -1,178 +0,0 @@ -//! Replay- and budget-safe internal deployment reference integration. - -#![forbid(unsafe_code)] - -use auths_enforcement::{CommandExecutor, Enforcement, EnforcementDecision, EnforcementError}; -use auths_sdk::{ - DomainCommand, DomainProfile, Explanation, RequestContext, Verifier, - model::{ActionId, ContextDigest, Digest, PlanId}, -}; -use thiserror::Error; - -/// Atomic state-gate outcome. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum GateClaim { - /// State was atomically reserved for this execution. - Claimed, - /// The challenge or budget was already consumed. - Rejected, - /// The state store could not make a trustworthy atomic decision. - Unavailable, -} - -/// Atomic challenge-consumption port. -pub trait ReplayStore: Send + Sync { - /// Claims an authorized request challenge exactly once. - fn claim(&self, challenge: [u8; 32], evaluation_time: u64) -> GateClaim; -} - -/// Atomic deployment-budget port. -pub trait DeploymentBudgetStore: Send + Sync { - /// Claims the verified action's blast radius. - fn claim(&self, action: ActionId, blast_radius: u64) -> GateClaim; -} - -/// Privacy-preserving authorized decision inputs for receipt/audit systems. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct AuthorizedDecision { - /// Digest of the exact proof bundle. - pub proof_digest: Digest, - /// Digest of the public verifier context. - pub context_digest: ContextDigest, - /// Identifier of the satisfied authorization plan. - pub plan_id: PlanId, - /// Identifier of the exact signed action. - pub action_id: ActionId, - /// Deterministic kernel work charged. - pub work_units: u64, -} - -/// Audit/receipt port called before application execution. -pub trait DeploymentAuditSink: Send + Sync { - /// Records authorized decision inputs without proof or action secrets. - fn authorized(&self, decision: AuthorizedDecision); -} - -/// Audit sink for integrations that deliberately disable export. -pub struct NoopDeploymentAuditSink; - -impl DeploymentAuditSink for NoopDeploymentAuditSink { - fn authorized(&self, _decision: AuthorizedDecision) {} -} - -/// Complete internal deployment enforcement boundary. -pub struct DeploymentService { - enforcement: Enforcement>, - executor: E, - replay: R, - budgets: B, - audit: A, -} - -impl DeploymentService -where - E: CommandExecutor>, - R: ReplayStore, - B: DeploymentBudgetStore, - A: DeploymentAuditSink, -{ - /// Constructs a deployment boundary from explicit local dependencies. - #[must_use] - pub fn new(verifier: Verifier, executor: E, replay: R, budgets: B, audit: A) -> Self { - Self { - enforcement: Enforcement::new(verifier, DomainProfile::default()), - executor, - replay, - budgets, - audit, - } - } - - /// Verifies, state-gates, audits, and executes one deployment request. - /// - /// `deployment_json` must be the bytes derived from the real request. The - /// executor receives only the deployment command decoded from sealed - /// verifier output, never these original untrusted bytes. - /// - /// # Errors - /// - /// Returns a typed integration/profile failure or application executor - /// failure. Protocol and state-gate outcomes remain ordinary values. - pub fn execute( - &self, - proof_cbor: &[u8], - deployment_json: &[u8], - request: &RequestContext, - ) -> Result, DeploymentError> { - let decision = self - .enforcement - .verify(proof_cbor, deployment_json, request)?; - let authorized = match decision { - EnforcementDecision::Authorized(authorized) => authorized, - EnforcementDecision::Denied(explanation) => { - return Ok(DeploymentOutcome::Denied(explanation)); - } - EnforcementDecision::Indeterminate(explanation) => { - return Ok(DeploymentOutcome::Indeterminate(explanation)); - } - }; - let Some(action_id) = authorized.verified().action_ids().first().copied() else { - return Ok(DeploymentOutcome::StateUnavailable); - }; - match self.replay.claim( - *request.challenge().as_bytes(), - request.evaluation_time().get(), - ) { - GateClaim::Claimed => {} - GateClaim::Rejected => return Ok(DeploymentOutcome::ReplayRejected), - GateClaim::Unavailable => return Ok(DeploymentOutcome::StateUnavailable), - } - match self - .budgets - .claim(action_id, authorized.command().action().blast_radius()) - { - GateClaim::Claimed => {} - GateClaim::Rejected => return Ok(DeploymentOutcome::BudgetRejected), - GateClaim::Unavailable => return Ok(DeploymentOutcome::StateUnavailable), - } - self.audit.authorized(AuthorizedDecision { - proof_digest: authorized.verified().proof_digest(), - context_digest: authorized.verified().context_digest(), - plan_id: authorized.verified().plan_id(), - action_id, - work_units: authorized.verified().work_units(), - }); - Ok(DeploymentOutcome::Executed( - self.executor - .execute(authorized.command()) - .map_err(DeploymentError::Executor)?, - )) - } -} - -/// Deployment request outcome. -pub enum DeploymentOutcome { - /// Verified, state-gated command completed. - Executed(T), - /// Available facts denied authority; nothing executed. - Denied(Explanation), - /// Required trusted facts were unavailable; nothing executed. - Indeterminate(Explanation), - /// Challenge was unknown, expired, or already consumed. - ReplayRejected, - /// Stateful blast-radius budget was exhausted. - BudgetRejected, - /// Replay or budget state could not make an atomic decision. - StateUnavailable, -} - -/// Deployment integration or executor failure. -#[derive(Debug, Error)] -pub enum DeploymentError { - /// Auths enforcement/profile integration failed. - #[error("deployment authorization integration failed: {0}")] - Enforcement(#[from] EnforcementError), - /// Application deployment executor failed. - #[error("authorized deployment execution failed: {0}")] - Executor(E), -} diff --git a/product/integrations/auths-github/src/ports.rs b/product/integrations/auths-github/src/ports.rs index 5ec48f02..7b809c9e 100644 --- a/product/integrations/auths-github/src/ports.rs +++ b/product/integrations/auths-github/src/ports.rs @@ -103,7 +103,7 @@ pub struct ProofAuthorization { pub authorized: Authorized, /// Exact proof commitment. pub proof_digest: DigestHex, - /// Trusted verifier-context commitment. + /// Trusted-context commitment. pub context_digest: DigestHex, } diff --git a/product/operations/auths-operations/src/explanation.rs b/product/operations/auths-operations/src/explanation.rs index dc08c77d..80b5c4b4 100644 --- a/product/operations/auths-operations/src/explanation.rs +++ b/product/operations/auths-operations/src/explanation.rs @@ -1,7 +1,7 @@ //! Privacy-preserving causal explanation report. use auths_codec::{body_digest, context_digest, encode_canonical_action}; -use auths_model::{CanonicalAction, VerificationCode, VerificationDecision, VerifierContext}; +use auths_model::{CanonicalAction, TrustedContext, VerificationCode, VerificationDecision}; use auths_registries::ImmutableRegistries; use auths_verifier::{ ExplainedVerification, VerificationOutcome, @@ -196,7 +196,7 @@ pub fn explain( verification: &ExplainedVerification, proof: &[u8], action: &CanonicalAction, - context: &VerifierContext, + context: &TrustedContext, registries: &ImmutableRegistries<'_>, disclosure: DisclosurePolicy, ) -> Result { diff --git a/product/operations/auths-operations/src/lib.rs b/product/operations/auths-operations/src/lib.rs index c98789e5..06340962 100644 --- a/product/operations/auths-operations/src/lib.rs +++ b/product/operations/auths-operations/src/lib.rs @@ -10,7 +10,9 @@ pub mod explanation; pub mod render; use auths_config::BoundConfiguration; -use auths_errors::{EffectState, RecommendedAction}; +// Re-exported so operational consumers project the one Rust-owned effect +// vocabulary instead of minting a stringly-typed shadow of it. +pub use auths_errors::{EffectState, RecommendedAction}; use auths_model::ProfileRef; use std::{ collections::{BTreeMap, BTreeSet}, @@ -99,7 +101,7 @@ impl ReadinessReport { &self.config_digest_hex } - /// Returns the public verifier-context digest. + /// Returns the public trusted-context digest. #[must_use] pub fn context_digest_hex(&self) -> &str { &self.context_digest_hex diff --git a/product/profiles/auths-profile-domains/Cargo.toml b/product/profiles/auths-profile-domains/Cargo.toml index 29874ab4..cfb8ad27 100644 --- a/product/profiles/auths-profile-domains/Cargo.toml +++ b/product/profiles/auths-profile-domains/Cargo.toml @@ -19,5 +19,8 @@ serde_json.workspace = true serde_json_canonicalizer.workspace = true sha2.workspace = true +[dev-dependencies] +auths-registries.workspace = true + [lints] workspace = true diff --git a/product/profiles/auths-profile-domains/src/lib.rs b/product/profiles/auths-profile-domains/src/lib.rs index b08a0798..73f8b8cc 100644 --- a/product/profiles/auths-profile-domains/src/lib.rs +++ b/product/profiles/auths-profile-domains/src/lib.rs @@ -16,6 +16,14 @@ use std::{collections::BTreeMap, marker::PhantomData}; const MAX_ACTION_BYTES: usize = 256 * 1024; +/// Budget algebra used for the deployment blast-radius ceiling. +/// +/// This must name an algebra installed in the target V1 registry +/// (`auths_registries::NUMERIC_CEILING_V1`); an unregistered identifier makes +/// every ceiling comparison unresolvable and the action unauthorizable for a +/// reason unrelated to authority. +const BLAST_RADIUS_BUDGET_ALGEBRA: &str = "numeric-ceiling-v1"; + trait DomainMeaning: Clone + DeserializeOwned + Serialize { const PROFILE_ID: &'static str; const PROFILE_VERSION: u16 = 1; @@ -663,7 +671,7 @@ impl DomainMeaning for DeploymentAction { fn budget(&self) -> Result, ProfileContractError> { Ok(Some(BudgetCeiling::new( - BudgetAlgebraId::parse("deploy-blast-radius-v1") + BudgetAlgebraId::parse(BLAST_RADIUS_BUDGET_ALGEBRA) .map_err(|_| ProfileContractError::MeaningMismatch)?, self.blast_radius, ))) @@ -1097,6 +1105,41 @@ mod tests { } } + /// Regression: `DeploymentAction` used to mint the unregistered algebra + /// `deploy-blast-radius-v1`. No verifier registry installs that algebra, so + /// a ceiling-bearing grant could never resolve it (denied for a reason + /// unrelated to authority) and a ceiling-free chain skipped the check + /// entirely while an integration claimed real blast radius against it. + #[test] + fn every_profile_budget_uses_a_registered_algebra() { + let digest = "11".repeat(32); + let deployment = DeploymentProfile::default() + .canonicalize( + &serde_json::to_vec(&DeploymentAction::new( + "production".into(), + "eu-west-1".into(), + "deploy".into(), + digest.clone(), + digest.clone(), + digest, + "canary".into(), + 1_800_000_000, + 1_800_003_600, + 10, + )) + .unwrap(), + ) + .expect("valid deployment action"); + let budget = deployment + .requested_budget() + .expect("deployment binds a blast-radius ceiling"); + assert_eq!( + budget.algebra().as_str(), + auths_registries::NUMERIC_CEILING_V1 + ); + assert_eq!(budget.value(), 10); + } + #[test] fn unknown_fields_and_noncanonical_bytes_fail_closed() { let input = br#"{"profile":"auths.git","profile_version":1,"repository":"example/repository","operation":"push","reference":"heads/main","object_id":"1111111111111111111111111111111111111111111111111111111111111111","extra":true}"#; diff --git a/product/receipts/auths-receipts/src/lib.rs b/product/receipts/auths-receipts/src/lib.rs index c7685fa0..7d5c1ab9 100644 --- a/product/receipts/auths-receipts/src/lib.rs +++ b/product/receipts/auths-receipts/src/lib.rs @@ -13,8 +13,8 @@ pub use disclosure::{ use auths_model::{ CanonicalAction, ContextDigest, Digest, PROTOCOL_V1, PrincipalId, ProfileRef, ReceiptId, - SignatureBytes, SignatureSuiteId, StatusSnapshotId, Timestamp, VerificationMethod, - VerifierContext, + SignatureBytes, SignatureSuiteId, StatusSnapshotId, Timestamp, TrustedContext, + VerificationMethod, }; use auths_ports::{SignatureInput, SignatureSuite}; use minicbor::{Decoder, Encoder, data::Type}; @@ -74,7 +74,7 @@ impl PreparedReceipt { pub fn prepare_decision_receipt( authority_commitment: Digest, action: &CanonicalAction, - context: &VerifierContext, + context: &TrustedContext, decision: DecisionClass, reasons: Vec, decided_at: Timestamp, @@ -251,7 +251,7 @@ impl DecisionReceipt { self.action_digest } - /// Returns the public verifier-context digest. + /// Returns the public trusted-context digest. #[must_use] pub const fn context_digest(&self) -> ContextDigest { self.context_digest diff --git a/product/runtime/auths-kernel-runtime/src/lib.rs b/product/runtime/auths-kernel-runtime/src/lib.rs index 46bc21d6..8921022a 100644 --- a/product/runtime/auths-kernel-runtime/src/lib.rs +++ b/product/runtime/auths-kernel-runtime/src/lib.rs @@ -2,15 +2,15 @@ #![forbid(unsafe_code)] -use auths_model::{Audience, CanonicalAction, Challenge, DenialReason, Timestamp, VerifierContext}; +use auths_model::{Audience, CanonicalAction, Challenge, DenialReason, Timestamp, TrustedContext}; use auths_ports::{PrincipalMethod, SignatureSuite}; use auths_registries::ImmutableRegistries; use auths_verifier::{VerificationOutcome, verify}; use std::fmt; -/// Owned immutable verifier context and executable method registries. +/// Owned immutable trusted context and executable method registries. pub struct AuthsKernel { - context_template: VerifierContext, + context_template: TrustedContext, principal_methods: Vec>, signature_suites: Vec>, } @@ -22,7 +22,7 @@ impl AuthsKernel { /// /// Returns a configuration error when either executable registry is empty. pub fn new( - context_template: VerifierContext, + context_template: TrustedContext, principal_methods: Vec>, signature_suites: Vec>, ) -> Result { @@ -51,7 +51,7 @@ impl AuthsKernel { expected_audience: Audience, expected_challenge: Challenge, evaluation_time: Timestamp, - ) -> Result<(VerificationOutcome, VerifierContext), DenialReason> { + ) -> Result<(VerificationOutcome, TrustedContext), DenialReason> { let context = self .context_template .for_request(expected_audience, expected_challenge, evaluation_time) @@ -98,7 +98,7 @@ impl AuthsKernel { /// Returns the immutable context template. #[must_use] - pub const fn context_template(&self) -> &VerifierContext { + pub const fn context_template(&self) -> &TrustedContext { &self.context_template } } diff --git a/product/runtime/auths-node/src/api.rs b/product/runtime/auths-node/src/api.rs index 5bc28d4c..c2e2df64 100644 --- a/product/runtime/auths-node/src/api.rs +++ b/product/runtime/auths-node/src/api.rs @@ -423,6 +423,11 @@ fn status_for(error: RuntimeFailure) -> StatusCode { RuntimeFailure::Indeterminate | RuntimeFailure::Unavailable => { StatusCode::SERVICE_UNAVAILABLE } + // Deliberately not 503: proxies and clients treat 503 as a safe + // automatic retry, which is the exact behaviour an outcome-unknown + // provider result must not invite. The body carries the registry code + // and the reconcile retry class. + RuntimeFailure::ProviderOutcomeUnknown => StatusCode::INTERNAL_SERVER_ERROR, } } @@ -431,12 +436,7 @@ fn json_error(status: StatusCode, error: RuntimeFailure) -> Response { status, axum::Json(ApiError { code: error.code(), - retry: match error.retry() { - auths_production_client::RetryClass::Never => "never", - auths_production_client::RetryClass::Backoff => "backoff", - auths_production_client::RetryClass::Resume => "resume", - auths_production_client::RetryClass::Reconcile => "reconcile", - }, + retry: error.retry().as_str(), }), ) .into_response() @@ -458,7 +458,7 @@ mod tests { ProductionResponse::new( auths_production_client::ClientOutcomeKind::Completed, None, - auths_production_client::RetryClass::Never, + auths_production_client::NextCall::Never, None, Some(request.body().unwrap_or_default().to_vec()), Some(vec![1]), @@ -474,8 +474,8 @@ mod tests { reference: reference.as_str().into(), profile: QualifiedProfile::GitHubIssueAddress.as_str().into(), state: "outcome-unknown".into(), - effect: "unknown".into(), - retry: "resume".into(), + effect: auths_operations::EffectState::Possible, + retry: auths_production_client::NextCall::Resume, updated_at: 1, receipt_id: None, }) diff --git a/product/runtime/auths-node/src/profiles.rs b/product/runtime/auths-node/src/profiles.rs index 042399db..7e7573f1 100644 --- a/product/runtime/auths-node/src/profiles.rs +++ b/product/runtime/auths-node/src/profiles.rs @@ -1,15 +1,36 @@ +use auths_operations::EffectState; use auths_production_client::{ - ClientOutcomeKind, ProductVerb, ProductionRequest, ProductionResponse, QualifiedProfile, - RecoveryReference, RetryClass, + ClientOutcomeKind, NextCall, ProductVerb, ProductionRequest, ProductionResponse, + QualifiedProfile, RecoveryReference, }; use serde::Serialize; use std::{collections::BTreeSet, sync::Arc}; +/// Whether a failing provider call had already entered the provider. +/// +/// A profile port cannot report an honest effect state without stating this. +/// Before entry the runtime holds proof that nothing was applied; after entry it +/// holds none, and a failed call is indistinguishable from an applied call whose +/// acknowledgement was lost. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ProviderBoundary { + /// The exact request provably never reached the provider. + BeforeEntry, + /// The exact request entered, or may have entered, the provider. + AfterEntry, +} + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum RuntimeFailure { Denied, Indeterminate, + /// A bounded operation failed **before provider entry**. + /// + /// This is the only runtime failure that may claim a safe blind retry. A + /// failure after provider entry must use [`Self::ProviderOutcomeUnknown`]. Unavailable, + /// A provider call failed after entry with no evidence of non-effect. + ProviderOutcomeUnknown, Malformed, ProfileDisabled, UnknownWorkflow, @@ -18,12 +39,22 @@ pub enum RuntimeFailure { } impl RuntimeFailure { + /// Classifies one failed provider call by the exact boundary it crossed. + #[must_use] + pub const fn provider(boundary: ProviderBoundary) -> Self { + match boundary { + ProviderBoundary::BeforeEntry => Self::Unavailable, + ProviderBoundary::AfterEntry => Self::ProviderOutcomeUnknown, + } + } + #[must_use] pub const fn code(self) -> &'static str { match self { Self::Denied => "authority.denied", Self::Indeterminate => "authority.indeterminate", Self::Unavailable => "core.runtime-unavailable", + Self::ProviderOutcomeUnknown => "core.outcome-unknown", Self::Malformed => "core.malformed-input", Self::ProfileDisabled => "profile.disabled", Self::UnknownWorkflow => "workflow.unknown", @@ -33,11 +64,28 @@ impl RuntimeFailure { } #[must_use] - pub const fn retry(self) -> RetryClass { + pub const fn retry(self) -> NextCall { + match self { + Self::Unavailable => NextCall::Backoff, + Self::Indeterminate | Self::ProviderOutcomeUnknown => NextCall::Reconcile, + _ => NextCall::Never, + } + } + + /// Projects the Rust-owned effect state this failure is entitled to claim. + #[must_use] + pub const fn effect(self) -> EffectState { match self { - Self::Unavailable => RetryClass::Backoff, - Self::Indeterminate => RetryClass::Reconcile, - _ => RetryClass::Never, + // Authority verification and provider entry both end here without + // conclusive evidence, so neither may assert non-effect. + Self::Indeterminate | Self::ProviderOutcomeUnknown => EffectState::Possible, + Self::Denied + | Self::Unavailable + | Self::Malformed + | Self::ProfileDisabled + | Self::UnknownWorkflow + | Self::UnknownReceipt + | Self::DisclosureDenied => EffectState::NotApplied, } } } @@ -56,8 +104,9 @@ pub struct WorkflowProjection { pub reference: String, pub profile: String, pub state: String, - pub effect: String, - pub retry: String, + /// Rust-owned effect axis. Never a locally invented word. + pub effect: EffectState, + pub retry: NextCall, pub updated_at: u64, pub receipt_id: Option, } @@ -224,7 +273,7 @@ impl crate::api::NodeRuntime for ClosedProfileRegistry { ProductVerb::Verify => ProductionResponse::new( ClientOutcomeKind::Verified, None, - RetryClass::Never, + NextCall::Never, None, self.authority.verify(&request)?, None, @@ -258,7 +307,7 @@ fn completed_authority(value: Vec) -> Result ProductionResponse { | RuntimeFailure::UnknownWorkflow | RuntimeFailure::UnknownReceipt | RuntimeFailure::DisclosureDenied => ClientOutcomeKind::Denied, - RuntimeFailure::Indeterminate | RuntimeFailure::Unavailable => { - ClientOutcomeKind::Indeterminate - } + RuntimeFailure::Indeterminate + | RuntimeFailure::Unavailable + | RuntimeFailure::ProviderOutcomeUnknown => ClientOutcomeKind::Indeterminate, }; + debug_assert!( + !(error.effect() == EffectState::Possible && error.retry().asserts_non_effect()), + "a possible effect may never be projected with a non-effect retry class" + ); ProductionResponse::new( kind, Some(error.code().to_owned()), @@ -295,3 +348,96 @@ pub fn failure_response(error: RuntimeFailure) -> ProductionResponse { ) .expect("closed failure projections are valid") } + +#[cfg(test)] +mod tests { + use super::*; + + const EVERY_FAILURE: &[RuntimeFailure] = &[ + RuntimeFailure::Denied, + RuntimeFailure::Indeterminate, + RuntimeFailure::Unavailable, + RuntimeFailure::ProviderOutcomeUnknown, + RuntimeFailure::Malformed, + RuntimeFailure::ProfileDisabled, + RuntimeFailure::UnknownWorkflow, + RuntimeFailure::UnknownReceipt, + RuntimeFailure::DisclosureDenied, + ]; + + #[test] + fn a_failure_after_provider_entry_never_asserts_non_effect() { + let failure = RuntimeFailure::provider(ProviderBoundary::AfterEntry); + assert_eq!(failure.effect(), EffectState::Possible); + assert_eq!(failure.code(), "core.outcome-unknown"); + assert!(!failure.retry().asserts_non_effect()); + assert_eq!(failure.retry(), NextCall::Reconcile); + let response = failure_response(failure); + assert_eq!(response.code(), Some("core.outcome-unknown")); + assert!(!response.retry().asserts_non_effect()); + } + + #[test] + fn only_a_failure_before_provider_entry_claims_not_applied() { + let failure = RuntimeFailure::provider(ProviderBoundary::BeforeEntry); + assert_eq!(failure.effect(), EffectState::NotApplied); + assert_eq!(failure.code(), "core.runtime-unavailable"); + assert_eq!(failure.retry(), NextCall::Backoff); + assert_ne!( + RuntimeFailure::provider(ProviderBoundary::AfterEntry), + failure, + "the two provider boundaries must not collapse onto one failure" + ); + } + + #[test] + fn no_possible_effect_is_ever_paired_with_a_non_effect_retry_class() { + for failure in EVERY_FAILURE.iter().copied() { + if failure.effect() == EffectState::Possible { + assert!( + !failure.retry().asserts_non_effect(), + "{failure:?} told the caller nothing happened" + ); + } + let response = failure_response(failure); + assert_eq!(response.code(), Some(failure.code())); + assert_eq!(response.retry(), failure.retry()); + } + } + + /// The public status projection must speak the one Rust-owned effect + /// vocabulary. It previously carried a free `String` and shipped the locally + /// invented words `"unknown"` and `"succeeded"`. + #[test] + fn the_status_projection_speaks_only_the_rust_owned_effect_vocabulary() { + let tokens: Vec = [ + EffectState::NotApplied, + EffectState::Possible, + EffectState::Applied, + ] + .into_iter() + .map(|effect| { + let projection = WorkflowProjection { + reference: "reference".into(), + profile: "auths.github.issue-address/1".into(), + state: "outcome-unknown".into(), + effect, + retry: NextCall::Resume, + updated_at: 1, + receipt_id: None, + }; + serde_json::from_str::(&serde_json::to_string(&projection).unwrap()) + .unwrap()["effect"] + .as_str() + .unwrap() + .to_owned() + }) + .collect(); + assert_eq!(tokens, ["not-applied", "possible", "applied"]); + assert!( + !tokens + .iter() + .any(|token| token == "unknown" || token == "succeeded") + ); + } +} diff --git a/product/runtime/auths-node/src/sandbox.rs b/product/runtime/auths-node/src/sandbox.rs index 543e8c75..f4a91852 100644 --- a/product/runtime/auths-node/src/sandbox.rs +++ b/product/runtime/auths-node/src/sandbox.rs @@ -5,9 +5,10 @@ use crate::{ MemorySandboxStore, PendingEffect, PostgresSandboxStore, SandboxStore, StoredReceipt, }, }; +use auths_operations::EffectState; use auths_production_client::{ - ClientOutcomeKind, ProductVerb, ProductionRequest, ProductionResponse, QualifiedProfile, - RecoveryReference, RetryClass, decode_delegation_body, + ClientOutcomeKind, NextCall, ProductVerb, ProductionRequest, ProductionResponse, + QualifiedProfile, RecoveryReference, decode_delegation_body, }; use base64ct::{Base64UrlUnpadded, Encoding as _}; use ed25519_dalek::{Signature, Signer as _, SigningKey, Verifier as _, VerifyingKey}; @@ -168,7 +169,7 @@ impl SandboxRuntime { return ProductionResponse::new( ClientOutcomeKind::Recoverable, Some("provider.outcome-unknown".into()), - RetryClass::Resume, + NextCall::Resume, Some(reference), None, None, @@ -288,7 +289,7 @@ impl NodeRuntime for SandboxRuntime { return ProductionResponse::new( ClientOutcomeKind::Rejected, Some("verification.rejected".into()), - RetryClass::Never, + NextCall::Never, None, None, None, @@ -298,7 +299,7 @@ impl NodeRuntime for SandboxRuntime { ProductionResponse::new( ClientOutcomeKind::Verified, None, - RetryClass::Never, + NextCall::Never, None, None, None, @@ -314,8 +315,8 @@ impl NodeRuntime for SandboxRuntime { reference: reference.as_str().to_owned(), profile: pending.profile.as_str().into(), state: "outcome-unknown".into(), - effect: "unknown".into(), - retry: "resume".into(), + effect: EffectState::Possible, + retry: NextCall::Resume, updated_at: pending.created_at, receipt_id: None, }); @@ -328,8 +329,8 @@ impl NodeRuntime for SandboxRuntime { reference: reference.as_str().to_owned(), profile: completed.profile.as_str().into(), state: "committed".into(), - effect: "succeeded".into(), - retry: "never".into(), + effect: EffectState::Applied, + retry: NextCall::Never, updated_at: completed.completed_at, receipt_id: Some(hex::encode(digest(&completed.bytes))), }) @@ -404,7 +405,7 @@ fn authority_response(value: Vec) -> Result "verify", } } + + /// Reports whether a delivered request for this verb can change durable or + /// provider state. + /// + /// Only `verify` is effect-free. Every other verb mints authority, consumes + /// a use, or enters a provider, so a lost response for it leaves the effect + /// genuinely unknown rather than provably not applied. + #[must_use] + pub const fn applies_effect(self) -> bool { + !matches!(self, Self::Verify) + } } #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] @@ -412,27 +423,33 @@ impl ClientOutcomeKind { } } +/// What the caller should call next after one production response. +/// +/// This answers *what should I call next?*. It is a different closed set from +/// `auths_errors::RetryClass`, which answers *may I retry?*, and the two must +/// never share an identifier again. #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] #[serde(rename_all = "kebab-case")] -pub enum RetryClass { +pub enum NextCall { Never, Backoff, Resume, Reconcile, } -impl RetryClass { +impl NextCall { fn parse(value: &str) -> Result { match value { "never" => Ok(Self::Never), "backoff" => Ok(Self::Backoff), "resume" => Ok(Self::Resume), "reconcile" => Ok(Self::Reconcile), - _ => Err(ProductionClientError::UnknownRetryClass), + _ => Err(ProductionClientError::UnknownNextCall), } } - const fn as_str(self) -> &'static str { + #[must_use] + pub const fn as_str(self) -> &'static str { match self { Self::Never => "never", Self::Backoff => "backoff", @@ -440,13 +457,138 @@ impl RetryClass { Self::Reconcile => "reconcile", } } + + /// Reports whether this class tells the caller the request produced no + /// effect. + /// + /// `never` and `backoff` both mean "nothing happened": the first says do not + /// try again, the second says a blind retry is safe. Neither may be attached + /// to an outcome the runtime cannot prove was not applied. `resume` and + /// `reconcile` are the only classes that preserve a possible effect. + #[must_use] + pub const fn asserts_non_effect(self) -> bool { + matches!(self, Self::Never | Self::Backoff) + } +} + +/// Where one client-side failure occurred relative to request transmission. +/// +/// This boundary is the whole safety question. Before transmission the client +/// holds proof that nothing reached the server; after transmission it holds no +/// evidence at all, and a connection reset is indistinguishable from a response +/// lost after the effect was applied. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum TransmissionBoundary { + /// No request byte was written, so non-effect is provable. + BeforeTransmission, + /// Request bytes may already have reached the server. + AfterTransmission, +} + +/// Closed classification of one client transport failure. +/// +/// Every variant states what the transport can actually prove. A transport that +/// cannot distinguish its own failure modes must report [`Self::ConnectionFailed`], +/// which fails closed to a possible effect. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum TransportFailure { + /// The endpoint could not be resolved and no connection was attempted. + EndpointUnresolvable, + /// The peer refused the connection before any request byte was written. + ConnectionRefused, + /// The transport failed without proving whether request bytes were written. + ConnectionFailed, + /// The connection failed or closed after request bytes were written. + ConnectionLost, + /// No usable response arrived before the client deadline. + ResponseTimeout, + /// The caller cancelled the operation after the request was written. + Cancelled, + /// A response arrived that is not a bounded product response. + UnusableResponse, +} + +impl TransportFailure { + /// Reports the exact transmission boundary this failure crossed. + #[must_use] + pub const fn boundary(self) -> TransmissionBoundary { + match self { + Self::EndpointUnresolvable | Self::ConnectionRefused => { + TransmissionBoundary::BeforeTransmission + } + Self::ConnectionFailed + | Self::ConnectionLost + | Self::ResponseTimeout + | Self::Cancelled + | Self::UnusableResponse => TransmissionBoundary::AfterTransmission, + } + } + + #[must_use] + pub const fn as_str(self) -> &'static str { + match self { + Self::EndpointUnresolvable => "endpoint-unresolvable", + Self::ConnectionRefused => "connection-refused", + Self::ConnectionFailed => "connection-failed", + Self::ConnectionLost => "connection-lost", + Self::ResponseTimeout => "response-timeout", + Self::Cancelled => "cancelled", + Self::UnusableResponse => "unusable-response", + } + } +} + +/// Registry code for a failure that provably occurred before provider entry. +pub const TRANSPORT_NOT_APPLIED_CODE: &str = "core.runtime-unavailable"; +/// Registry code for a failure whose effect may already have occurred. +pub const TRANSPORT_OUTCOME_UNKNOWN_CODE: &str = "core.outcome-unknown"; + +/// Projects one transport failure into the bounded production response contract. +/// +/// Any failure that is not provably before transmission, on a verb that applies +/// an effect, is projected as `core.outcome-unknown` with +/// [`NextCall::Reconcile`]. Only a failure that provably occurred before any +/// request byte was written, or a failure of the effect-free `verify` verb, may +/// claim `core.runtime-unavailable`, whose registered effect is `not-applied`. +/// +/// # Panics +/// +/// Panics only if the closed transport projection stops satisfying the +/// production response invariant. +#[must_use] +pub fn transport_failure_response( + verb: ProductVerb, + failure: TransportFailure, +) -> ProductionResponse { + let effect_possible = + verb.applies_effect() && failure.boundary() == TransmissionBoundary::AfterTransmission; + let (code, retry) = if effect_possible { + (TRANSPORT_OUTCOME_UNKNOWN_CODE, NextCall::Reconcile) + } else { + (TRANSPORT_NOT_APPLIED_CODE, NextCall::Backoff) + }; + debug_assert!( + !(effect_possible && retry.asserts_non_effect()), + "a possible effect may never be projected with a non-effect next call" + ); + ProductionResponse::new( + ClientOutcomeKind::Indeterminate, + Some(code.to_owned()), + retry, + None, + None, + None, + ) + .expect("closed transport projections are valid") } #[derive(Clone, Debug, Eq, PartialEq)] pub struct ProductionResponse { kind: ClientOutcomeKind, code: Option, - retry: RetryClass, + retry: NextCall, recovery_reference: Option, value: Option>, receipt: Option>, @@ -458,11 +600,11 @@ impl ProductionResponse { /// # Errors /// /// Returns an error when a field exceeds its bound or the supplied fields - /// contradict the selected outcome or retry class. + /// contradict the selected outcome or next call. pub fn new( kind: ClientOutcomeKind, code: Option, - retry: RetryClass, + retry: NextCall, recovery_reference: Option, value: Option>, receipt: Option>, @@ -482,34 +624,34 @@ impl ProductionResponse { code.is_none() && recovery_reference.is_none() && receipt.is_some() - && retry == RetryClass::Never + && retry == NextCall::Never } ClientOutcomeKind::Denied | ClientOutcomeKind::Rejected => { code.is_some() && recovery_reference.is_none() && value.is_none() && receipt.is_none() - && retry == RetryClass::Never + && retry == NextCall::Never } ClientOutcomeKind::Indeterminate => { code.is_some() && recovery_reference.is_none() && value.is_none() && receipt.is_none() - && matches!(retry, RetryClass::Backoff | RetryClass::Reconcile) + && matches!(retry, NextCall::Backoff | NextCall::Reconcile) } ClientOutcomeKind::Recoverable => { code.is_some() && recovery_reference.is_some() && value.is_none() && receipt.is_none() - && retry == RetryClass::Resume + && retry == NextCall::Resume } ClientOutcomeKind::Verified => { code.is_none() && recovery_reference.is_none() && receipt.is_none() - && retry == RetryClass::Never + && retry == NextCall::Never } }; if !valid_shape { @@ -536,7 +678,7 @@ impl ProductionResponse { } #[must_use] - pub const fn retry(&self) -> RetryClass { + pub const fn retry(&self) -> NextCall { self.retry } @@ -586,7 +728,7 @@ struct ProductionResponseProjection<'a> { contract_version: u16, kind: ClientOutcomeKind, code: Option<&'a str>, - retry: RetryClass, + retry: NextCall, recovery_reference: Option<&'a str>, value: Option, receipt: Option, @@ -726,7 +868,7 @@ pub fn decode_response(input: &[u8]) -> Result "client.unknown-verb", Self::UnknownProfile => "client.unknown-profile", Self::UnknownOutcome => "client.unknown-outcome", - Self::UnknownRetryClass => "client.unknown-retry-class", + Self::UnknownNextCall => "client.unknown-retry-class", Self::InvalidIdentity => "client.invalid-identity", Self::InvalidBody => "client.invalid-body", Self::InvalidShape => "client.invalid-shape", @@ -1013,7 +1155,7 @@ mod tests { ProductionResponse::new( ClientOutcomeKind::Completed, None, - RetryClass::Never, + NextCall::Never, None, Some(vec![1]), Some(vec![2]), @@ -1022,7 +1164,7 @@ mod tests { ProductionResponse::new( ClientOutcomeKind::Denied, Some("authority.denied".into()), - RetryClass::Never, + NextCall::Never, None, None, None, @@ -1031,7 +1173,7 @@ mod tests { ProductionResponse::new( ClientOutcomeKind::Indeterminate, Some("provider.unknown".into()), - RetryClass::Reconcile, + NextCall::Reconcile, None, None, None, @@ -1040,7 +1182,7 @@ mod tests { ProductionResponse::new( ClientOutcomeKind::Recoverable, Some("workflow.recoverable".into()), - RetryClass::Resume, + NextCall::Resume, Some(reference()), None, None, @@ -1049,7 +1191,7 @@ mod tests { ProductionResponse::new( ClientOutcomeKind::Verified, None, - RetryClass::Never, + NextCall::Never, None, Some(vec![3]), None, @@ -1058,7 +1200,7 @@ mod tests { ProductionResponse::new( ClientOutcomeKind::Rejected, Some("verification.rejected".into()), - RetryClass::Never, + NextCall::Never, None, None, None, @@ -1141,4 +1283,93 @@ mod tests { Err(ProductionClientError::InvalidBody) ); } + + const EVERY_TRANSPORT_FAILURE: &[TransportFailure] = &[ + TransportFailure::EndpointUnresolvable, + TransportFailure::ConnectionRefused, + TransportFailure::ConnectionFailed, + TransportFailure::ConnectionLost, + TransportFailure::ResponseTimeout, + TransportFailure::Cancelled, + TransportFailure::UnusableResponse, + ]; + + const EVERY_VERB: &[ProductVerb] = &[ + ProductVerb::Create, + ProductVerb::Delegate, + ProductVerb::Execute, + ProductVerb::Resume, + ProductVerb::Verify, + ]; + + #[test] + fn transport_failure_after_transmission_never_asserts_non_effect() { + for verb in EVERY_VERB + .iter() + .copied() + .filter(|verb| verb.applies_effect()) + { + for failure in EVERY_TRANSPORT_FAILURE + .iter() + .copied() + .filter(|failure| failure.boundary() == TransmissionBoundary::AfterTransmission) + { + let response = transport_failure_response(verb, failure); + assert!( + !response.retry().asserts_non_effect(), + "{verb:?}/{failure:?} claimed a non-effect next call" + ); + assert_eq!(response.retry(), NextCall::Reconcile); + assert_eq!(response.code(), Some(TRANSPORT_OUTCOME_UNKNOWN_CODE)); + } + } + } + + #[test] + fn only_proven_pre_transmission_failures_claim_not_applied() { + for verb in EVERY_VERB.iter().copied() { + for failure in EVERY_TRANSPORT_FAILURE.iter().copied() { + let response = transport_failure_response(verb, failure); + let claims_not_applied = response.code() == Some(TRANSPORT_NOT_APPLIED_CODE); + let provable = failure.boundary() == TransmissionBoundary::BeforeTransmission + || !verb.applies_effect(); + assert_eq!( + claims_not_applied, provable, + "{verb:?}/{failure:?} misclassified the transmission boundary" + ); + assert_eq!(response.retry().asserts_non_effect(), provable); + } + } + } + + #[test] + fn an_unclassifiable_transport_failure_fails_closed_to_possible() { + assert_eq!( + TransportFailure::ConnectionFailed.boundary(), + TransmissionBoundary::AfterTransmission + ); + let response = + transport_failure_response(ProductVerb::Execute, TransportFailure::ConnectionFailed); + assert_eq!(response.code(), Some(TRANSPORT_OUTCOME_UNKNOWN_CODE)); + assert_eq!(response.retry(), NextCall::Reconcile); + } + + #[test] + fn a_non_product_http_response_is_not_malformed_input() { + let response = + transport_failure_response(ProductVerb::Execute, TransportFailure::UnusableResponse); + assert_ne!(response.code(), Some("core.malformed-input")); + assert_eq!(response.code(), Some(TRANSPORT_OUTCOME_UNKNOWN_CODE)); + } + + #[test] + fn transport_projections_round_trip_on_the_wire() { + for verb in EVERY_VERB.iter().copied() { + for failure in EVERY_TRANSPORT_FAILURE.iter().copied() { + let response = transport_failure_response(verb, failure); + let encoded = encode_response(&response).unwrap(); + assert_eq!(decode_response(&encoded).unwrap(), response); + } + } + } } diff --git a/product/runtime/auths-runtime/src/lib.rs b/product/runtime/auths-runtime/src/lib.rs index 1be08c6d..bfacee8c 100644 --- a/product/runtime/auths-runtime/src/lib.rs +++ b/product/runtime/auths-runtime/src/lib.rs @@ -7,10 +7,11 @@ pub use auths_production_client as production_client; use async_trait::async_trait; use auths_codec::context_digest; +use auths_errors::EffectState; pub use auths_kernel_runtime::AuthsKernel; use auths_model::{ ActionId, Audience, BudgetCeiling, Challenge, Digest, ReceiptId, SignatureBytes, Timestamp, - VerifierContext, + TrustedContext, }; use auths_operations::{ EventSink, NoopEventSink, OperationalEventV2, OperationalOutcome, OperationalReasonCode, @@ -45,6 +46,10 @@ use subtle::ConstantTimeEq as _; const REQUEST_ID_DOMAIN: &[u8] = b"AUTHS-APPS-REQUEST\x00\x01"; +/// Upper bound for an adapter-supplied failure summary. Kept below the exchange +/// refusal-message maximum so the projection can never fail. +const MAX_FAILURE_SUMMARY_BYTES: usize = 512; + /// Runtime clock effect. pub trait Clock: Send + Sync { /// Returns whole Unix seconds. @@ -294,7 +299,7 @@ fn evaluate_kernel( challenge: ChallengeNonce, audience: &ExchangeAudience, now: u64, -) -> Result<(VerificationOutcome, VerifierContext), auths_model::DenialReason> { +) -> Result<(VerificationOutcome, TrustedContext), auths_model::DenialReason> { let expected_audience = Audience::parse(audience.as_str()) .map_err(|_| auths_model::DenialReason::AudienceMismatch)?; kernel.verify_with_context( @@ -368,11 +373,139 @@ impl ExecutableAction { } } +/// Whether a failing provider call had already entered the provider. +/// +/// This boundary is the entire safety question for a failed execution. Before +/// entry the adapter holds proof that the exact effect was not applied. After +/// entry it holds none: a refused connection, a timeout, and a lost response for +/// an applied write are indistinguishable from inside the adapter. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ProviderBoundary { + /// The exact command provably never reached the provider. + BeforeEntry, + /// The exact command entered, or may have entered, the provider. + AfterEntry, +} + +/// Bounded executor failure that states exactly what the adapter can prove. +/// +/// There is deliberately no `From` and no default boundary. An adapter +/// must choose, because Auths cannot choose for it and the wrong default mints a +/// signed proof that a real-world effect did not happen. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ToolExecutionFailure { + boundary: ProviderBoundary, + summary: String, +} + +impl ToolExecutionFailure { + /// Declares a failure that provably occurred before provider entry. + /// + /// Use this only when the adapter can prove the exact command never reached + /// the provider: local validation, a policy refusal, or a connection that + /// was refused before any request byte was written. + #[must_use] + pub fn before_provider_entry(summary: impl AsRef) -> Self { + Self::new(ProviderBoundary::BeforeEntry, summary) + } + + /// Declares a failure whose exact effect may already have been applied. + /// + /// This is the correct answer for a timeout, a cancellation, a reset + /// connection, an unparsable provider response, and any failure the adapter + /// cannot place on the near side of provider entry. + #[must_use] + pub fn after_provider_entry(summary: impl AsRef) -> Self { + Self::new(ProviderBoundary::AfterEntry, summary) + } + + fn new(boundary: ProviderBoundary, summary: impl AsRef) -> Self { + Self { + boundary, + summary: bounded_summary(summary.as_ref()), + } + } + + /// Returns the exact provider boundary this failure crossed. + #[must_use] + pub const fn boundary(&self) -> ProviderBoundary { + self.boundary + } + + /// Returns the bounded, display-safe failure summary. + #[must_use] + pub fn summary(&self) -> &str { + &self.summary + } + + /// Projects the Rust-owned effect state this failure is entitled to claim. + #[must_use] + pub const fn effect(&self) -> EffectState { + match self.boundary { + ProviderBoundary::BeforeEntry => EffectState::NotApplied, + ProviderBoundary::AfterEntry => EffectState::Possible, + } + } +} + +/// Reports whether a failed execution may be recorded as a signed receipt +/// asserting the exact effect did not happen. +/// +/// `ExecutionOutcome::Failed` is an assertion of non-effect, not a description +/// of an error. Only a failure the adapter proved occurred before provider entry +/// may be minted as one. +const fn may_sign_non_effect_receipt(failure: &ToolExecutionFailure) -> bool { + matches!(failure.effect(), EffectState::NotApplied) +} + +/// Prefix that marks a refusal whose exact effect may already have been applied. +pub const OUTCOME_UNKNOWN_PREFIX: &str = "effect possible, reconcile before retry: "; + +/// Prefixes an unknown-effect summary so the refusal message cannot be read as +/// an assertion that nothing happened. +fn alloc_unknown_message(summary: &str) -> String { + let mut message = String::with_capacity(OUTCOME_UNKNOWN_PREFIX.len() + summary.len()); + message.push_str(OUTCOME_UNKNOWN_PREFIX); + message.push_str(summary); + message +} + +/// Normalizes an adapter-supplied summary into the bounded, control-character +/// free form the exchange refusal message requires. +/// +/// An adapter cannot crash the service with an empty, oversized, or control +/// character message. +fn bounded_summary(value: &str) -> String { + let mut summary = String::with_capacity(value.len().min(MAX_FAILURE_SUMMARY_BYTES)); + for character in value.chars() { + if character.is_control() { + continue; + } + if summary.len() + character.len_utf8() > MAX_FAILURE_SUMMARY_BYTES { + break; + } + summary.push(character); + } + if summary.is_empty() { + summary.push_str("provider adapter reported no summary"); + } + summary +} + /// MCP executor boundary. No API accepts original request bytes. #[async_trait] pub trait McpToolExecutor: Send + Sync { /// Executes a command decoded from a sealed verified action. - async fn execute(&self, action: ExecutableAction) -> Result, String>; + /// + /// # Errors + /// + /// Returns a [`ToolExecutionFailure`] that names the provider boundary the + /// failure crossed. Auths signs an execution receipt asserting non-effect + /// only for [`ProviderBoundary::BeforeEntry`]. + async fn execute( + &self, + action: ExecutableAction, + ) -> Result, ToolExecutionFailure>; } /// Static MCP runtime configuration. @@ -580,7 +713,7 @@ impl McpAuthorizationService { /// /// # Errors /// - /// Returns a configuration failure when the pure verifier context does + /// Returns a configuration failure when the pure trusted context does /// not require the channel-binding identifier selected by the service. pub fn new( config: McpServiceConfig, @@ -897,20 +1030,43 @@ impl ProofExchangeService for McpAuthorizationService { let execution_started = Instant::now(); let result = match self.executor.execute(executable).await { Ok(result) => result, - Err(message) => { + Err(failure) => { + let applied_is_possible = !may_sign_non_effect_receipt(&failure); self.observe( OperationalStage::ProviderResult, - OperationalOutcome::Failed, - OperationalReasonCode::ProviderFailed, + if applied_is_possible { + OperationalOutcome::OutcomeUnknown + } else { + OperationalOutcome::Failed + }, + if applied_is_possible { + OperationalReasonCode::ProviderUnknown + } else { + OperationalReasonCode::ProviderFailed + }, micros(execution_started.elapsed()), ); - let _ = self.record_execution( - decision_receipt_id, - lease_digest, - &verified, - ReceiptExecutionOutcome::Failed, - None, - ); + // A signed execution receipt has exactly two outcomes, + // `Succeeded` and `Failed` (auths_receipts::ExecutionOutcome). + // `Failed` asserts the exact effect did not happen. When the + // adapter cannot place the failure before provider entry, that + // assertion is unprovable, so no execution receipt is minted at + // all: the caller is told the outcome is unknown and must + // reconcile rather than being handed a false non-effect proof. + if !applied_is_possible { + let _ = self.record_execution( + decision_receipt_id, + lease_digest, + &verified, + ReceiptExecutionOutcome::Failed, + None, + ); + } + let message = if applied_is_possible { + alloc_unknown_message(failure.summary()) + } else { + failure.summary().to_owned() + }; return Self::refusal( RefusalKind::ApplicationPolicy, Some(verdict_summary(&VerificationOutcome::Authorized(Box::new( @@ -966,7 +1122,7 @@ impl McpAuthorizationService { &self, proof: &[u8], canonical_action: &auths_model::CanonicalAction, - context: &VerifierContext, + context: &TrustedContext, outcome: &VerificationOutcome, now: u64, ) -> Option { @@ -1037,6 +1193,33 @@ impl McpAuthorizationService { } } +/// Reports whether a transport observation authenticates the *remote peer that +/// submitted this action*. +/// +/// This deliberately does NOT use `PeerObservation::is_authenticated`. That +/// predicate is satisfied by two kinds of observation this service must never +/// accept as peer authentication: +/// +/// * `AuthenticatedOpaque { kind, identifier }` carries no transport material +/// this service can corroborate. Its variant and fields are public, so any +/// caller — including an in-process channel that performed no authentication +/// at all — can mint one and satisfy a blanket `is_authenticated()` gate. +/// * `ServerAuthenticated` and `HttpsServerCertificate` are *client-side* +/// observations: they record that the local side authenticated the remote +/// SERVER. This runtime is the responder, so they say nothing about the +/// caller submitting the action. +/// +/// Only observations that name concrete, transport-supplied peer material are +/// accepted. Unlisted and future observations fail closed. +const fn peer_is_authenticated_to_this_service(peer: &PeerObservation) -> bool { + matches!( + peer, + PeerObservation::IrohEndpoint(_) + | PeerObservation::MutualTlsCertificate(_) + | PeerObservation::UnixPeerCredentials { .. } + ) +} + fn channel_policy_satisfied( policy: ChannelBindingPolicy, peer: &PeerObservation, @@ -1044,7 +1227,9 @@ fn channel_policy_satisfied( ) -> bool { match policy { ChannelBindingPolicy::None => true, - ChannelBindingPolicy::RequireAuthenticatedPeer => peer.is_authenticated(), + ChannelBindingPolicy::RequireAuthenticatedPeer => { + peer_is_authenticated_to_this_service(peer) + } ChannelBindingPolicy::RequireSignedSenderBinding => { matches!(peer, PeerObservation::IrohEndpoint(_)) } @@ -1147,7 +1332,7 @@ pub enum ServiceConfigurationError { InvalidExchangeLimits, /// Channel policy lacks required local configuration. InvalidChannelPolicy, - /// Pure verifier context and signed outer channel policy disagree. + /// Pure trusted context and signed outer channel policy disagree. InvalidKernelChannelPolicy, } @@ -1186,3 +1371,167 @@ pub fn inspect_mcp_body( ) -> Result { McpToolCall::from_canonical_bytes(canonical_body) } + +#[cfg(test)] +mod channel_policy_tests { + use super::{ChannelBindingPolicy, PeerObservation, channel_policy_satisfied}; + + /// Regression for `peer-observation-fail-open`: `PeerObservation` is a + /// public enum with public variants, so any caller can mint an observation + /// that `PeerObservation::is_authenticated` reports as authenticated. The + /// service must not accept a free-form assertion as peer authentication. + #[test] + fn forgeable_observations_do_not_satisfy_require_authenticated_peer() { + let forged = PeerObservation::AuthenticatedOpaque { + kind: "memory-demo".into(), + identifier: vec![1], + }; + assert!( + forged.is_authenticated(), + "guard the premise: the model still reports this as authenticated" + ); + assert!(!channel_policy_satisfied( + ChannelBindingPolicy::RequireAuthenticatedPeer, + &forged, + None, + )); + } + + #[test] + fn client_side_observations_do_not_authenticate_the_submitting_peer() { + for peer in [ + PeerObservation::ServerAuthenticated, + PeerObservation::HttpsServerCertificate([3; 32]), + ] { + assert!(peer.is_authenticated()); + assert!( + !channel_policy_satisfied( + ChannelBindingPolicy::RequireAuthenticatedPeer, + &peer, + None, + ), + "{peer:?} authenticates the server, not the caller" + ); + } + } + + #[test] + fn unauthenticated_transports_are_refused() { + for peer in [ + PeerObservation::Unauthenticated, + PeerObservation::TcpEndpoint("127.0.0.1:9".into()), + PeerObservation::FileEnvelope { + digest: [4; 32], + sequence: 7, + }, + ] { + assert!(!channel_policy_satisfied( + ChannelBindingPolicy::RequireAuthenticatedPeer, + &peer, + None, + )); + } + } + + #[test] + fn transport_supplied_peer_material_is_accepted() { + for peer in [ + PeerObservation::IrohEndpoint([9; 32]), + PeerObservation::MutualTlsCertificate([9; 32]), + PeerObservation::UnixPeerCredentials { + uid: 501, + gid: 20, + pid: Some(4242), + }, + ] { + assert!(channel_policy_satisfied( + ChannelBindingPolicy::RequireAuthenticatedPeer, + &peer, + None, + )); + } + } +} + +#[cfg(test)] +mod provider_boundary_tests { + use super::{ + EffectState, MAX_FAILURE_SUMMARY_BYTES, OUTCOME_UNKNOWN_PREFIX, ProviderBoundary, + ToolExecutionFailure, alloc_unknown_message, may_sign_non_effect_receipt, + }; + use auths_proof_exchange_model::{ExchangeOutcome, RefusalKind}; + + /// Regression for `provider-error-asserts-non-effect`. The executor boundary + /// used to be `Result, String>`, so every adapter failure — including + /// a timeout after the provider had been entered — reached the gateway + /// indistinguishable from a local policy refusal and was minted as a signed + /// `ExecutionOutcome::Failed` receipt asserting the effect did not happen. + #[test] + fn a_failure_after_provider_entry_never_mints_a_non_effect_receipt() { + let failure = ToolExecutionFailure::after_provider_entry("provider call timed out"); + assert_eq!(failure.boundary(), ProviderBoundary::AfterEntry); + assert_eq!(failure.effect(), EffectState::Possible); + assert!( + !may_sign_non_effect_receipt(&failure), + "a possibly-applied effect was about to be signed as not applied" + ); + } + + #[test] + fn only_a_proven_pre_entry_failure_mints_a_non_effect_receipt() { + let failure = ToolExecutionFailure::before_provider_entry("command outside local policy"); + assert_eq!(failure.boundary(), ProviderBoundary::BeforeEntry); + assert_eq!(failure.effect(), EffectState::NotApplied); + assert!(may_sign_non_effect_receipt(&failure)); + } + + #[test] + fn the_two_provider_boundaries_never_collapse() { + let before = ToolExecutionFailure::before_provider_entry("same words"); + let after = ToolExecutionFailure::after_provider_entry("same words"); + assert_ne!(before, after); + assert_ne!(before.effect(), after.effect()); + assert_ne!( + may_sign_non_effect_receipt(&before), + may_sign_non_effect_receipt(&after) + ); + } + + #[test] + fn an_unknown_effect_refusal_message_does_not_read_as_failure() { + let failure = ToolExecutionFailure::after_provider_entry("connection reset"); + let message = alloc_unknown_message(failure.summary()); + assert!(message.starts_with(OUTCOME_UNKNOWN_PREFIX)); + assert!(message.contains("connection reset")); + } + + /// The adapter-supplied summary used to flow straight into + /// `ExchangeOutcome::refused(..).expect("static runtime refusal is bounded")`, + /// so an empty, oversized, or control-character summary panicked the + /// authorization service. + #[test] + fn an_adapter_summary_can_never_panic_the_refusal_projection() { + for raw in [ + String::new(), + "\u{0}\u{7}\n\r\t".to_owned(), + "x".repeat(64 * 1024), + ] { + for failure in [ + ToolExecutionFailure::before_provider_entry(&raw), + ToolExecutionFailure::after_provider_entry(&raw), + ] { + let message = if may_sign_non_effect_receipt(&failure) { + failure.summary().to_owned() + } else { + alloc_unknown_message(failure.summary()) + }; + assert!(!message.is_empty()); + assert!(message.len() <= OUTCOME_UNKNOWN_PREFIX.len() + MAX_FAILURE_SUMMARY_BYTES); + assert!(!message.bytes().any(|byte| byte.is_ascii_control())); + assert!( + ExchangeOutcome::refused(RefusalKind::ApplicationPolicy, None, message).is_ok() + ); + } + } + } +} diff --git a/product/sdk/auths-sdk/src/lib.rs b/product/sdk/auths-sdk/src/lib.rs index f59f72c9..e825e0bb 100644 --- a/product/sdk/auths-sdk/src/lib.rs +++ b/product/sdk/auths-sdk/src/lib.rs @@ -8,7 +8,7 @@ use auths_model::{ ChannelBindingId, CompositionRequirement, EvidenceTypeId, ExtensionId, GrantStatusSnapshot, PrincipalMethodId, PrincipalStatusSnapshot, ProfilePolicyId, ProfileRef, ResourceMatcherId, SignatureSuiteId, StatusMethodId, StatusPolicy, StatusSnapshotId, Timestamp, TrustAnchor, - VerifierConfigurationId, VerifierContext, VerifierLimits, + TrustedContext, VerifierConfigurationId, VerifierLimits, }; use auths_profile_api::{ActionProfile, ProfileContractError}; use auths_verifier::VerificationOutcome; @@ -23,8 +23,6 @@ pub use auths_custody as custody; pub use auths_errors as errors; /// Canonical protocol model used by explicit advanced configuration. pub use auths_model as model; -/// Re-exported closed deployment action and profile. -pub use auths_profile_domains::{DeploymentAction, DomainCommand, DomainProfile}; /// Re-exported MCP profile for the shortest supported reference integration. pub use auths_profile_mcp::{McpCommand, McpProfile, McpToolCall}; /// Sealed verifier output constructible only by the protocol kernel. @@ -95,7 +93,7 @@ impl TrustedContextBuilder { /// /// Mandatory V1 signature suites and self-describing evidence identifiers /// are accepted by default. Every value remains encoded into the returned - /// [`VerifierContext`]. + /// [`TrustedContext`]. /// /// # Errors /// @@ -178,13 +176,13 @@ impl TrustedContextBuilder { self } - /// Compiles one immutable verifier-context template. + /// Compiles one immutable trusted-context template. /// /// # Errors /// /// Returns a typed model failure if roots, profiles, status policy, /// registries, or limits disagree. - pub fn build(self) -> Result { + pub fn build(self) -> Result { let principal_methods: BTreeSet = self .trust_anchors .iter() @@ -230,7 +228,7 @@ impl TrustedContextBuilder { profiles.into_iter().collect(), vec![ProfilePolicyId::parse(auths_registries::EXACT_PROFILE_V1)?], )?; - Ok(VerifierContext::new( + Ok(TrustedContext::new( self.configuration, self.composition, self.trust_anchors, @@ -271,7 +269,7 @@ impl Verifier { /// /// Returns a typed error only if a compiled identifier or immutable /// kernel configuration is invalid. - pub fn self_contained(context: VerifierContext) -> Result { + pub fn self_contained(context: TrustedContext) -> Result { let methods: Vec> = vec![ Box::new(auths_raw_key::RawKeyMethod::new()?), Box::new(auths_did_key::DidKeyMethod::new()?), diff --git a/product/spec/v1/deployment-action.md b/product/spec/v1/deployment-action.md index 666c7b6f..c6fd3af8 100644 --- a/product/spec/v1/deployment-action.md +++ b/product/spec/v1/deployment-action.md @@ -13,7 +13,7 @@ non-canonical encodings are rejected. ```text capability = deploy/ resource = deploy:////artifacts/ -budget = deploy-blast-radius-v1: +budget = numeric-ceiling-v1: ``` The verified decoder re-derives the exact permission and stateful budget. diff --git a/product/stores/auths-stores/src/lib.rs b/product/stores/auths-stores/src/lib.rs index 486d39f8..751f4e15 100644 --- a/product/stores/auths-stores/src/lib.rs +++ b/product/stores/auths-stores/src/lib.rs @@ -41,6 +41,19 @@ struct BudgetState { claimed: BTreeSet, } +/// Decides an action that declares no requested budget. +/// +/// A ledger with no configured ceilings meters nothing and admits it. A ledger +/// that was configured with ceilings cannot account for an unbudgeted action +/// and fails closed. +const fn unmetered_claim(no_ceilings_configured: bool) -> BudgetClaim { + if no_ceilings_configured { + BudgetClaim::Claimed + } else { + BudgetClaim::Exhausted + } +} + impl InMemoryBudgetLedger { /// Constructs a duplicate-free set of exact algebra ceilings. /// @@ -75,7 +88,12 @@ impl InMemoryBudgetLedger { impl BudgetLedger for InMemoryBudgetLedger { fn claim(&self, action: ActionId, requested: Option<&BudgetCeiling>) -> BudgetClaim { let Some(requested) = requested else { - return BudgetClaim::Claimed; + // An action that declares no budget cannot be metered. A ledger + // that was configured with stateful ceilings therefore refuses it + // rather than passing it through un-metered: otherwise the + // configured ceiling is inert for exactly the actions that decline + // to state what they will spend. + return unmetered_claim(self.ceilings.is_empty()); }; let algebra = requested.algebra().as_str(); let Some(ceiling) = self.ceilings.get(algebra) else { @@ -242,7 +260,9 @@ impl PersistentBudgetLedger { impl BudgetLedger for PersistentBudgetLedger { fn claim(&self, action: ActionId, requested: Option<&BudgetCeiling>) -> BudgetClaim { let Some(requested) = requested else { - return BudgetClaim::Claimed; + // See `InMemoryBudgetLedger::claim`: a configured stateful ledger + // fails closed on an action that declares no budget. + return unmetered_claim(self.ceilings.is_empty()); }; let algebra = requested.algebra().as_str(); let Some(ceiling) = self.ceilings.get(algebra) else { @@ -758,6 +778,28 @@ mod tests { assert!(ledger.is_claimed(action)); } + /// Regression: a configured stateful ledger used to return `Claimed` for + /// any action that declared no requested budget, so the configured ceiling + /// was inert for exactly the actions that decline to state their spend. + #[test] + fn configured_ledger_refuses_an_action_without_a_requested_budget() { + let ledger = InMemoryBudgetLedger::new([("numeric-ceiling-v1".into(), 10)]).unwrap(); + assert_eq!( + ledger.claim(ActionId::new([9; 32]), None), + BudgetClaim::Exhausted + ); + assert!(!ledger.is_claimed(ActionId::new([9; 32]))); + } + + #[test] + fn unconfigured_ledger_meters_nothing_and_admits_unbudgeted_actions() { + let ledger = InMemoryBudgetLedger::new([]).unwrap(); + assert_eq!( + ledger.claim(ActionId::new([9; 32]), None), + BudgetClaim::Claimed + ); + } + #[test] fn budget_consumption_is_aggregate() { let ledger = InMemoryBudgetLedger::new([("numeric-ceiling-v1".into(), 10)]).unwrap(); diff --git a/release/docs-bundle/auths-docs-bundle-v1.tar.zst b/release/docs-bundle/auths-docs-bundle-v1.tar.zst new file mode 100644 index 0000000000000000000000000000000000000000..85fc8fe48c77c93006fa84cf72d3da69e2ba238e GIT binary patch literal 5080 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 + exit 1 +fi + +git switch "$protected_branch" + +# Remove metadata for worktrees whose directories no longer exist. Git refuses +# to delete branches that remain associated with stale worktree records. +git worktree prune + +deleted=0 +while IFS= read -r branch; do + if [[ "$branch" == "$protected_branch" ]]; then + continue + fi + + git branch -D -- "$branch" + deleted=$((deleted + 1)) +done < <(git for-each-ref --format='%(refname:short)' refs/heads) + +echo "Deleted $deleted local branches; kept '$protected_branch'." diff --git a/xtask/src/fixtures.rs b/xtask/src/fixtures.rs index ce586f79..caa13b1a 100644 --- a/xtask/src/fixtures.rs +++ b/xtask/src/fixtures.rs @@ -141,8 +141,8 @@ pub(crate) fn product_fixtures(update: bool) -> Result<(), String> { fn production_client_fixtures() -> Result>, String> { use auths_production_client::{ - ClientOutcomeKind, ProductVerb, ProductionRequest, ProductionResponse, QualifiedProfile, - RecoveryReference, RetryClass, decode_request, decode_response, encode_request, + ClientOutcomeKind, NextCall, ProductVerb, ProductionRequest, ProductionResponse, + QualifiedProfile, RecoveryReference, decode_request, decode_response, encode_request, encode_response, }; @@ -238,7 +238,7 @@ fn production_client_fixtures() -> Result>, String> { ProductionResponse::new( ClientOutcomeKind::Completed, None, - RetryClass::Never, + NextCall::Never, None, Some(vec![8; 32]), Some(vec![9; 96]), @@ -249,7 +249,7 @@ fn production_client_fixtures() -> Result>, String> { ProductionResponse::new( ClientOutcomeKind::Denied, Some("authority.denied".into()), - RetryClass::Never, + NextCall::Never, None, None, None, @@ -260,7 +260,7 @@ fn production_client_fixtures() -> Result>, String> { ProductionResponse::new( ClientOutcomeKind::Indeterminate, Some("provider.outcome-unknown".into()), - RetryClass::Reconcile, + NextCall::Reconcile, None, None, None, @@ -271,7 +271,7 @@ fn production_client_fixtures() -> Result>, String> { ProductionResponse::new( ClientOutcomeKind::Recoverable, Some("workflow.recoverable".into()), - RetryClass::Resume, + NextCall::Resume, Some(reference), None, None, @@ -282,7 +282,7 @@ fn production_client_fixtures() -> Result>, String> { ProductionResponse::new( ClientOutcomeKind::Verified, None, - RetryClass::Never, + NextCall::Never, None, Some(vec![10; 32]), None, @@ -293,7 +293,7 @@ fn production_client_fixtures() -> Result>, String> { ProductionResponse::new( ClientOutcomeKind::Rejected, Some("verification.rejected".into()), - RetryClass::Never, + NextCall::Never, None, None, None, From f5c3589832041c455e3fc762566b49770e7df66e Mon Sep 17 00:00:00 2001 From: bordumb Date: Sat, 15 Aug 2026 12:25:28 +0100 Subject: [PATCH 02/61] fix(corpus): converge three verifiers on absent-budget denial UNSIGNED (auths-sign requires an interactive passphrase unavailable here). Wave 1 changed the Rust verifier to DENY when a bounded terminal budget ceiling is present and the action requests no budget (auths-verifier/src/lib.rs:2543-2557). The two independent verifiers still AUTHORIZED that case, and the cross-language gate could not see the disagreement: all 102 canonical fixtures declared a non-null requested_budget, so the divergent branch was never exercised. Adds canonical fixture `action-budget-absent` (bounded ceiling present, requested budget ABSENT). This is an ADDITION, not a regeneration: no existing fixture changed, so it carries no protocol justification. With the fixture in place `cargo xtask cross-language` failed, empirically proving the divergence: action-budget-absent independently derived authorized/authorized, manifest requires denied/budget-ceiling-exceeded Both independents reproduced it. Both are now conformed to Rust, the semantic owner: a bounded ceiling does not vacuously cover an action that declares no budget. - core/testkit/auths-testkit/src/lib.rs: ActionVariation::BudgetAbsent - core/fixtures/v1: 5 new vectors + additive manifest entry - bindings/independent/go/auths/semantic.go:1341 budgetCovers - bindings/independent/typescript/semantic-verifier.ts:1499 budgetCovers - core/spec/v1/error-codes.md:32 widens budget-ceiling-exceeded prose BEHAVIOR CHANGE, stated explicitly: the pinned Go corpus digest in api_test.go:65 moves 102:114c0508... -> 103:636140b3... because the corpus legitimately gained a fixture. No assertion was weakened. Co-Authored-By: Claude Opus 5 --- bindings/independent/go/auths/api_test.go | 2 +- bindings/independent/go/auths/semantic.go | 9 ++- .../typescript/semantic-verifier.ts | 8 ++- .../denied/action-budget-absent.action.cbor | Bin 0 -> 122 bytes .../v1/denied/action-budget-absent.body.cbor | Bin 0 -> 24 bytes .../denied/action-budget-absent.context.cbor | Bin 0 -> 1158 bytes .../v1/denied/action-budget-absent.proof.cbor | Bin 0 -> 1453 bytes .../denied/action-budget-absent.result.cbor | Bin 0 -> 345 bytes core/fixtures/v1/manifest.json | 52 ++++++++++++++++++ core/spec/v1/error-codes.md | 2 +- core/testkit/auths-testkit/src/lib.rs | 33 ++++++++--- 11 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 core/fixtures/v1/denied/action-budget-absent.action.cbor create mode 100644 core/fixtures/v1/denied/action-budget-absent.body.cbor create mode 100644 core/fixtures/v1/denied/action-budget-absent.context.cbor create mode 100644 core/fixtures/v1/denied/action-budget-absent.proof.cbor create mode 100644 core/fixtures/v1/denied/action-budget-absent.result.cbor diff --git a/bindings/independent/go/auths/api_test.go b/bindings/independent/go/auths/api_test.go index 031913e7..9d1d7351 100644 --- a/bindings/independent/go/auths/api_test.go +++ b/bindings/independent/go/auths/api_test.go @@ -62,7 +62,7 @@ func TestSharedCorpusRunsInNativeGoTest(t *testing.T) { if err != nil { t.Fatal(err) } - const expected = "102:114c0508445854687020d96ac3f214c171a80407b2a6e6b185d99de533237cf0" + const expected = "103:636140b3f7a660cfbcedf51f8efa170df6fb9d521aa67e8950e66fcbf059c616" if digest != expected { t.Fatalf("semantic corpus digest mismatch: got %s", digest) } diff --git a/bindings/independent/go/auths/semantic.go b/bindings/independent/go/auths/semantic.go index a990dd44..735639e9 100644 --- a/bindings/independent/go/auths/semantic.go +++ b/bindings/independent/go/auths/semantic.go @@ -1338,10 +1338,17 @@ func budgetAttenuates(child, parent *budget) bool { return child != nil && child.algebra == parent.algebra && child.value <= parent.value } +// budgetCovers reports whether a bounded terminal ceiling covers the budget the +// action requests. An unbounded ceiling covers everything. A bounded ceiling +// does NOT vacuously cover an action that declares no budget: nothing bounds +// what such an action would spend, so it is not covered. func budgetCovers(ceiling, requested *budget) bool { - if requested == nil || ceiling == nil { + if ceiling == nil { return true } + if requested == nil { + return false + } return ceiling.algebra == requested.algebra && requested.value <= ceiling.value } diff --git a/bindings/independent/typescript/semantic-verifier.ts b/bindings/independent/typescript/semantic-verifier.ts index c4351673..1f8b8444 100644 --- a/bindings/independent/typescript/semantic-verifier.ts +++ b/bindings/independent/typescript/semantic-verifier.ts @@ -1496,9 +1496,13 @@ function budgetAttenuates(child?: Budget, parent?: Budget): boolean { return parent === undefined || (child !== undefined && child.algebra === parent.algebra && child.value <= parent.value); } +// An unbounded ceiling covers everything. A bounded ceiling does NOT vacuously +// cover an action that declares no budget: nothing bounds what such an action +// would spend, so it is not covered. function budgetCovers(ceiling?: Budget, requested?: Budget): boolean { - return requested === undefined || ceiling === undefined || - (ceiling.algebra === requested.algebra && requested.value <= ceiling.value); + if (ceiling === undefined) return true; + if (requested === undefined) return false; + return ceiling.algebra === requested.algebra && requested.value <= ceiling.value; } function requireBudgetAlgebra(value: Budget | undefined, contextValue: Context): void { if (value === undefined) return; diff --git a/core/fixtures/v1/denied/action-budget-absent.action.cbor b/core/fixtures/v1/denied/action-budget-absent.action.cbor new file mode 100644 index 0000000000000000000000000000000000000000..8354f34e8d4062404a4b1c287390e435b7a2a4cf GIT binary patch literal 122 zcmZ3+u!tcuv9u(kST8rZfRT~0LM^eNASW|9u_QA;ProcLMGr2ko1B=FqgQ6Aot%_k z#1tX1h#{pYH8F)TU%x1|Ait=jSijI%uOKCjc@aZaNq&A#u|7xxV-e6`D}A^ckQSD2 GtPKDxwJOa3 literal 0 HcmV?d00001 diff --git a/core/fixtures/v1/denied/action-budget-absent.body.cbor b/core/fixtures/v1/denied/action-budget-absent.body.cbor new file mode 100644 index 0000000000000000000000000000000000000000..3edfb10557fc6a4f3652c4b35c07d99bafcf284e GIT binary patch literal 24 fcmZ3)kW!SIn8KK^UzA#qUsO`8UudjXkdg)fYQYGM literal 0 HcmV?d00001 diff --git a/core/fixtures/v1/denied/action-budget-absent.context.cbor b/core/fixtures/v1/denied/action-budget-absent.context.cbor new file mode 100644 index 0000000000000000000000000000000000000000..2b5aedeba1b3f12a9e05eafc2f8e662f741e3d54 GIT binary patch literal 1158 zcmbVL&1(}u6rb6Ae^jHbI!RF%kHQ+0lG64Rsa8u7YN??XL?*kF>?XTA>Fi{a94v{R zdlCNuQ!VYG9t6RI;GqXEf_U&Cf?hm$S0Z?FHbtqe81Wr;_kGO#e!utLJ_gT5K??W* zfGHpYAoZWVgg+5T1jC_37|D?ofB;0197A#($wNp!lmZC=4x@Y`DS;y>e-z2X;gMt> z97FQ)@QHpFA(Tf+B%@SvXC5xe@9y_LYw4H6=eJs4*I$gB{y2B{$*ql9X3t;!@;1Nz zT6zeUg@M>tKGFXK&fmo3ZVpj(AU`p&G9N}O*uNw z=EkRPEH7LxE|fSNo+&ul+?=~Ox!@G8n3Y1|()8@&O!LA7{NIC8r^0YU z5vYor@pZbuz;&)W=Pb1bp@ zx`5eri!eh~RAQKhT^49?Bno4-pXf#G5RoA92LSlDMbpLx7~IQT$RJuam&;7{W$e-9 zZwK1JwdSZwZO*7^5~esDbE2qq#)!=o%vW^Gv<7BGd~@P(qFK7(a4P6xQ^Xi8l{{*S zD-@IrTr`O(l4ZkIinMNPPOWMvs*Z*4V$;yX?OL4?m#UZ>)D|hCR*5EVvKbZFUJQG9 z&6Lnw5-nleD7Cz<7@D9YFbHd&?9;S7iliy%5L+cmjZv3~)7+{%Ldh;6fz~j%O&mwz zIwOuwO--=~UujTQH7WLxcXfr@;ZCj248_J4acWrH=w>3FKMW_@j_q59ctukEc! Z)T_Tf!1mO~4zw};t35+yn|j-l`2q8VV@Qf;d4WN0g@0N~VZLXmQLu+@ykDSsieH7N ziC4C(uV=A)V6Ks$flD@XW@2ecMzLOQasdk?E6^2LCHeU|#rnyKIXR3)Kv64w{i4)@ z{GyU#Af1@PCc-Wez|okCBEt#tLxe(1^4TkV6OZcl$uVBfo7S`GteQ>^&w`ifUYw;1 z1^c;lxfU@L<(1~97G);uCZ}fRWag#omKic~aWn9I<6X$Wz*kU|Sgs2U0Ntd-;#8n? zDt`mxB8J5bSuk;+Y7~!>5<*$2DMqHIhL#{JBOIbPpT26LZrGG6H{a}lyxF|>6LZAM zcV@CBi#R{f)K5?@3pe$86DKKK%To4}DgFIfQUB)qIf@4p=BHd15AaUqW&#G)W}vST z!N_?EoLG{XpQm4zm!by`TwPFr>XjL4Cnx0>u@DX`R-|}f!x9hd-#F0X zAVNWj0N|3);FhrAK?y+;k_vBxg8BdFdS|XKU-RCscP7_O$BUP|-+2bl%8V)T&fhF^ z>1q^Tgo4)h*xL%VTld^MY`(cAp!}21m`oMCV={Gr&me$_w+Vp`OT3&czH^6cU02ma+ZIyc;0W(ys@~Bf68;F zNAJG}yE4?*OFe9jTw=azdX(bDd7BxINZl__;AURLzyM711bncNrD+L6ghFq@F{4dx z`=>Wc3%K-tv8~QHKC$xii#x|YeB|{zFxSbS5h;Z;RVZLjMbJzDOhn8PMvkE&9>Kan zj^VoAu92V;M8RasO%}tj)4AulUvp`CZV#O^g;)D@?!KPjm7AF=9xrzTxnt%xg^q7W zYJVsA%s+GZUHG%P1`k$zTrDVj49&Seq6B%RErn5lT&rjf7ncG7D7pxIXOdS+GgOVp&@IP5x7?Q|6mF xzYus-D0g<<^xbAEXNo{(5sC%22KEMy2F?hHMGPsRvW_tyUi1|j>lLJ=0RZW0F7N;V literal 0 HcmV?d00001 diff --git a/core/fixtures/v1/denied/action-budget-absent.result.cbor b/core/fixtures/v1/denied/action-budget-absent.result.cbor new file mode 100644 index 0000000000000000000000000000000000000000..942022621301abbf70b926480039bceee8079346 GIT binary patch literal 345 zcmdnMz{tqVvRsidOnmQwvwvTe2Z zSC+iL%Nn5&EX>yr{YHM9C(RrVPtN zDLxCN_vx{in3;1la5iu?a4%<&WL?WBQN<+5(#Fij!pO?VCdp{b6QN*C0PsdA+??|5 oMT-7$VS|;GUl#8dk-zWVyeWQ}XHi~&@8_!yi_h~BR>IE&03uV0FaQ7m literal 0 HcmV?d00001 diff --git a/core/fixtures/v1/manifest.json b/core/fixtures/v1/manifest.json index a232ac4b..7d106c69 100644 --- a/core/fixtures/v1/manifest.json +++ b/core/fixtures/v1/manifest.json @@ -2874,6 +2874,58 @@ "expected_decision": "denied", "expected_code": "budget-ceiling-exceeded" }, + { + "name": "action-budget-absent", + "class": "denied", + "proof": { + "path": "denied/action-budget-absent.proof.cbor", + "sha256": "b83cd035292a01f14c987032beed5134059ed35db6d34346ef40fab2a85932a1" + }, + "context": { + "path": "denied/action-budget-absent.context.cbor", + "sha256": "bba4e01d24b8031b68b728355c163d4ea5baf033bbc7e647666738492947166a" + }, + "canonical_action": { + "path": "denied/action-budget-absent.action.cbor", + "sha256": "2689e2076637a9d685e83f1118ebda241a62727b84752fca1d867ddfa974efdd", + "profile": "auths.mcp", + "profile_version": 1, + "media_type": "application/vnd.auths.mcp-call.v1+cbor", + "capability": "tools/call", + "resource": "mcp://reports/read", + "requested_budget": null + }, + "canonical_body": { + "path": "denied/action-budget-absent.body.cbor", + "sha256": "5c63cdd40e91c52d8e1e01d76e968cb2cd262c6c0ca0e9674a0975a1118f0a2d" + }, + "expected_result": { + "path": "denied/action-budget-absent.result.cbor", + "sha256": "73f83d1f9d6911976bc05bcdc1f680e3e919012169395e12a1dc6f6f02e3d983", + "stage": "authority", + "decision": "denied", + "code": "budget-ceiling-exceeded", + "proof_digest": "b83cd035292a01f14c987032beed5134059ed35db6d34346ef40fab2a85932a1", + "action_digest": "2689e2076637a9d685e83f1118ebda241a62727b84752fca1d867ddfa974efdd", + "context_digest": "524846ecba92c7e1d7b8793743915d83f735de0bdaa7cdd83e38c13975837671", + "plan_digest": "2af75ddb207db5bcdec335d9cc1ee40da5747e49fa927ea28dd1002d6aa9726f", + "result_digest": "133920946e8c2e9a9a3e77728381c874b214826839c2644ca01bbe2e04343637", + "authorized_branches": [], + "assurance_satisfactions": 0, + "resources": { + "proof_bytes": 1453, + "action_bytes": 122, + "context_bytes": 1158, + "object_count": 6, + "plan_leaves": 1, + "plan_depth": 1, + "work_units": 307 + }, + "registry_manifest": "3333333333333333333333333333333333333333333333333333333333333333" + }, + "expected_decision": "denied", + "expected_code": "budget-ceiling-exceeded" + }, { "name": "action-validity-expanded", "class": "denied", diff --git a/core/spec/v1/error-codes.md b/core/spec/v1/error-codes.md index 13bc2ce7..14b51284 100644 --- a/core/spec/v1/error-codes.md +++ b/core/spec/v1/error-codes.md @@ -29,7 +29,7 @@ verifier retains three diagnostic classes. | `delegation-expanded` | authority | A child widened an authority dimension | | `permission-not-granted` | authority | Terminal authority lacks the exact permission | | `action-constraint-mismatch` | authority | Body digest is outside the granted constraint | -| `budget-ceiling-exceeded` | authority | Requested budget exceeds or mismatches the signed ceiling | +| `budget-ceiling-exceeded` | authority | Requested budget exceeds or mismatches the signed ceiling, or the action declares no budget under a bounded ceiling | | `composition-requirement-not-met` | authority | Authorized branches do not satisfy the trusted expected plan, quorum, actor-diversity, or root-diversity obligation | | `plan-action-mismatch` | action | Branches bind different action meaning or plan | | `action-body-mismatch` | action | Canonical body digest differs | diff --git a/core/testkit/auths-testkit/src/lib.rs b/core/testkit/auths-testkit/src/lib.rs index 7bc07fec..1fab40d2 100644 --- a/core/testkit/auths-testkit/src/lib.rs +++ b/core/testkit/auths-testkit/src/lib.rs @@ -2525,6 +2525,7 @@ enum ActionVariation { Permission, Constraint, Budget, + BudgetAbsent, Validity, Actor, UnsupportedProfile, @@ -2556,10 +2557,13 @@ fn action_authority_fixture(name: &'static str, variation: ActionVariation) -> C } else { BODY.to_vec() }; - let action_budget = if matches!(variation, ActionVariation::Budget) { - 11 - } else { - 5 + // `BudgetAbsent` declares no budget at all while the terminal grant still + // carries a bounded ceiling. A bounded ceiling with nothing to compare + // against is not vacuously satisfied; the verifier must deny. + let action_budget = match variation { + ActionVariation::Budget => Some(11), + ActionVariation::BudgetAbsent => None, + _ => Some(5), }; let action_profile = if matches!(variation, ActionVariation::UnsupportedProfile) { ProfileRef::new(ProfileId::parse("auths.unknown").expect("profile"), 1) @@ -2572,10 +2576,12 @@ fn action_authority_fixture(name: &'static str, variation: ActionVariation) -> C MediaType::parse("application/vnd.auths.mcp-call.v1+cbor").expect("media type"), action_body, action_permission.clone(), - Some(BudgetCeiling::new( - BudgetAlgebraId::parse("numeric-ceiling-v1").expect("budget algebra"), - action_budget, - )), + action_budget.map(|value| { + BudgetCeiling::new( + BudgetAlgebraId::parse("numeric-ceiling-v1").expect("budget algebra"), + value, + ) + }), ) .expect("canonical action"); let proof_ref = ProofRef::new([0xa1; 32]); @@ -2674,7 +2680,9 @@ fn action_authority_fixture(name: &'static str, variation: ActionVariation) -> C let expected = match variation { ActionVariation::Permission => Expected::Denied(DenialReason::PermissionNotGranted), ActionVariation::Constraint => Expected::Denied(DenialReason::ActionConstraintMismatch), - ActionVariation::Budget => Expected::Denied(DenialReason::BudgetCeilingExceeded), + ActionVariation::Budget | ActionVariation::BudgetAbsent => { + Expected::Denied(DenialReason::BudgetCeilingExceeded) + } ActionVariation::Validity => Expected::Denied(DenialReason::ActionOutsideValidity), ActionVariation::Actor => Expected::Denied(DenialReason::BrokenGrantChain), ActionVariation::UnsupportedProfile => { @@ -2728,6 +2736,12 @@ action_fixture!( "action-budget-exceeded", Budget ); +action_fixture!( + /// Signed action declares no budget while terminal authority is bounded. + action_budget_absent, + "action-budget-absent", + BudgetAbsent +); action_fixture!( /// Signed action validity exceeds the grant window. action_validity_expanded, @@ -4660,6 +4674,7 @@ pub fn corpus() -> Vec { action_permission_not_granted(), action_constraint_mismatch(), action_budget_exceeded(), + action_budget_absent(), action_validity_expanded(), action_actor_mismatch(), unsupported_action_profile(), From 1a88ba3cac43fdf45687a106729179140a67c795 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sat, 15 Aug 2026 12:42:55 +0100 Subject: [PATCH 03/61] core: make root preservation a computed dimension and a real proof (UNSIGNED) `AttenuationChecks.root_preserved` was the literal `true` in auths-authority/src/lib.rs:201, and `AuthorityStateView` carried no root, so the eleventh dimension of the generated attenuation contract had no witness. The Kani harness quantified over free booleans and the Lean projection set `rootPreserved := true`, so neither could observe the hole. Empirically established first: `evaluate_grant_view` ACCEPTED a delegation from a chain state whose subject never received authority from the root it claims, reporting `root_preserved=true`. Recorded as the regression test `kernel_denies_a_delegation_whose_parent_state_is_not_rooted`. Also recorded as a corrected false positive: through the safe path (`from_anchor` + `delegate`) a foreign root was already caught by the issuer/subject linkage check, because `from_anchor` sets subject == root. The vacuity was exploitable only through the pure kernel entry point - which is exactly the function the Lean production refinement models and the Aeneas qualification qualifies. - auths-algebra-kernel gains `RootLinkage` / `root_preserved`, one definition shared by the shipping kernel and the bounded proofs. - `AuthorityStateView` gains `root`; the dimension is computed, and the linkage gate consumes it instead of recomputing a weaker condition. - `evaluate_action_coverage_view` gains the same gate: an unrooted authority authorized actions too. BEHAVIOUR CHANGE, both paths now deny BrokenGrantChain. - three Kani harnesses quantify over root identities. Mutation-checked: restoring the literal `true` fails all three while the pre-existing conjunction harness still passes. - Lean gains `rooted` / `rootPreserved`, `linked` is strengthened, and the root-preservation theorems are stated over all inputs in Attenuation.lean and Authority.lean. - `rich_projection_accepts_iff_scope_depth_checks` was itself the vacuity in theorem form - it proved an 11-dimension conjunction equivalent to 10. Renamed and given the `rootPreserved` conjunct. Verified: cargo check --workspace --all-targets; cargo test --workspace (144 suites, 0 failures); cargo kani -p auths-algebra-kernel (5/5); lake build (3284 jobs); cargo test -p auths-formal-refinement (4/4). `cargo xtask formal` still fails at the source-closure gate: the Aeneas translation of auths-authority predates the `root` field and must be regenerated with charon 0.1.225 + aeneas 3a8586f, which are not installed here. source-closure.json and the generated Lean were deliberately NOT hand-updated - that would assert a correspondence that does not hold. --- core/crates/auths-algebra-kernel/src/lib.rs | 120 +++++++++++- core/crates/auths-authority/src/lib.rs | 204 +++++++++++++++++++- formal/Auths/Attenuation.lean | 58 ++++++ formal/Auths/Authority.lean | 67 +++++++ formal/Auths/Refinement/Production.lean | 47 +++-- formal/Auths/Rich/Semantics.lean | 39 +++- formal/Auths/Rich/Theorems.lean | 141 +++++++++++++- 7 files changed, 646 insertions(+), 30 deletions(-) diff --git a/core/crates/auths-algebra-kernel/src/lib.rs b/core/crates/auths-algebra-kernel/src/lib.rs index 43fdba0a..5bb0c7bc 100644 --- a/core/crates/auths-algebra-kernel/src/lib.rs +++ b/core/crates/auths-algebra-kernel/src/lib.rs @@ -11,12 +11,130 @@ pub use generated::{ attenuation_accepts, attenuation_checks_accept, threshold_counts, }; +/// The chain-linkage facts one delegation edge presents to the trust-root +/// dimension of [`AttenuationChecks`]. +/// +/// The identity type is abstract on purpose: the production kernel supplies +/// borrowed `PrincipalId` values, bounded proofs supply small scalars, and both +/// obtain the same decision from [`root_preserved`]. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct RootLinkage { + /// Trust root the parent authority is anchored at. + pub parent_root: Identity, + /// Principal the parent authority currently speaks for. + pub parent_subject: Identity, + /// Whether the parent has already applied at least one grant, so its + /// subject was reached from `parent_root` by an accepted edge rather than + /// by being the root itself. + pub parent_delegated: bool, + /// Principal that issued the edge under evaluation. + pub grant_issuer: Identity, +} + +/// Accepts exactly when the edge continues the chain rooted at `parent_root`. +/// +/// Two independent facts are required, and neither is implied by the other: +/// +/// 1. the parent state genuinely descends from the root it claims — it is +/// either the root itself (no grant applied yet) or it reached its subject +/// through an accepted edge that already carried the root forward; and +/// 2. the edge is issued by the parent's own subject, so the authority being +/// extended is the one the root conferred. +/// +/// An accepted transition copies `parent_root` unchanged, so these two facts +/// are exactly what makes the child descend from the same root as the parent. +/// Because delegation acceptance is the conjunction over every dimension, a +/// false result here can never be rescued by any other dimension. +#[must_use] +pub fn root_preserved(linkage: &RootLinkage) -> bool { + (linkage.parent_delegated || linkage.parent_root == linkage.parent_subject) + && linkage.grant_issuer == linkage.parent_subject +} + #[cfg(kani)] mod kani_harnesses { use super::{ - AttenuationChecks, Truth, attenuation_accepts, attenuation_checks_accept, threshold_counts, + AttenuationChecks, RootLinkage, Truth, attenuation_accepts, attenuation_checks_accept, + root_preserved, threshold_counts, }; + /// Bounded identity carrier. Principals are compared only for equality, so + /// a scalar with more than two inhabitants is a faithful model: every + /// equal/distinct arrangement of the four identities in a `RootLinkage` is + /// reachable, and `kani::any()` explores all of them. + type Identity = u8; + + fn any_linkage() -> RootLinkage { + RootLinkage { + parent_root: kani::any(), + parent_subject: kani::any(), + parent_delegated: kani::any(), + grant_issuer: kani::any(), + } + } + + #[kani::proof] + fn root_preservation_requires_an_anchored_edge() { + let linkage = any_linkage(); + let preserved = root_preserved(&linkage); + + // An edge issued by anyone other than the parent's subject extends an + // authority the root never conferred. + if linkage.grant_issuer != linkage.parent_subject { + assert!(!preserved); + } + // A parent that has applied no grant and whose subject is not the root + // it claims descends from no root at all. + if !linkage.parent_delegated && linkage.parent_root != linkage.parent_subject { + assert!(!preserved); + } + // Preservation is not vacuous in the other direction either: whenever + // both facts hold the dimension must accept, so the check cannot be + // satisfied by refusing everything. + if linkage.grant_issuer == linkage.parent_subject + && (linkage.parent_delegated || linkage.parent_root == linkage.parent_subject) + { + assert!(preserved); + } + } + + #[kani::proof] + fn a_broken_root_cannot_be_rescued_by_any_other_dimension() { + let linkage = any_linkage(); + let checks = AttenuationChecks { + root_preserved: root_preserved(&linkage), + depth_decreases: kani::any(), + profile_attenuates: kani::any(), + permissions_attenuate: kani::any(), + validity_attenuates: kani::any(), + audiences_attenuate: kani::any(), + action_constraint_attenuates: kani::any(), + budget_attenuates: kani::any(), + status_attenuates: kani::any(), + assurance_attenuates: kani::any(), + extensions_attenuate: kani::any(), + }; + let unrooted_parent = + !linkage.parent_delegated && linkage.parent_root != linkage.parent_subject; + if unrooted_parent || linkage.grant_issuer != linkage.parent_subject { + assert!(!attenuation_accepts(&checks)); + assert!(!attenuation_checks_accept(&checks)); + } + } + + #[kani::proof] + fn an_accepted_edge_leaves_the_child_under_the_parent_root() { + let linkage = any_linkage(); + kani::assume(root_preserved(&linkage)); + // `acceptedNextState` copies the parent root, so the child root is + // `parent_root` by construction; the reachable content of the claim is + // that the edge starts at a principal the root actually authorised. + let child_root = linkage.parent_root; + assert!(child_root == linkage.parent_root); + assert!(linkage.grant_issuer == linkage.parent_subject); + assert!(linkage.parent_delegated || linkage.grant_issuer == linkage.parent_root); + } + #[kani::proof] fn threshold_partition_matches_contract() { let authorized: u16 = kani::any(); diff --git a/core/crates/auths-authority/src/lib.rs b/core/crates/auths-authority/src/lib.rs index c255edc7..434c8496 100644 --- a/core/crates/auths-authority/src/lib.rs +++ b/core/crates/auths-authority/src/lib.rs @@ -6,7 +6,9 @@ extern crate alloc; use alloc::vec::Vec; -use auths_algebra_kernel::{AttenuationChecks, attenuation_checks_accept}; +use auths_algebra_kernel::{ + AttenuationChecks, RootLinkage, attenuation_checks_accept, root_preserved, +}; use auths_model::{ ActionAuthorityView, ActionConstraint, ActionEnvelope, AssurancePolicyId, AudienceSet, BudgetCeiling, CriticalExtensions, DenialReason, GrantAuthorityView, GrantId, GrantStatement, @@ -106,6 +108,10 @@ pub enum AuthorScopeDecision { #[doc(hidden)] #[derive(Clone, Copy, Debug)] pub struct AuthorityStateView<'a> { + /// Trust root this authority is anchored at. Every accepted delegation + /// copies it forward unchanged, so it is the identity a chain must still + /// descend from after any number of edges. + pub root: &'a PrincipalId, pub subject: &'a PrincipalId, pub allowed_profiles: &'a [ProfileRef], pub profile: Option<&'a ProfileRef>, @@ -121,6 +127,34 @@ pub struct AuthorityStateView<'a> { pub extensions: Option<&'a CriticalExtensions>, } +/// Borrowed principal whose equality is the canonical protocol comparison. +/// +/// The derived `PartialEq` on [`PrincipalId`] is deliberately not used: every +/// authority decision in this crate compares principals through +/// [`principal_id_equal`], and the trust-root dimension must not become a +/// second, unmodelled comparison path. +#[derive(Clone, Copy, Debug)] +struct CanonicalPrincipal<'a>(&'a PrincipalId); + +impl PartialEq for CanonicalPrincipal<'_> { + fn eq(&self, other: &Self) -> bool { + principal_id_equal(self.0, other.0) + } +} + +/// Projects the chain-linkage facts the trust-root dimension consumes. +fn root_linkage<'a>( + parent: &AuthorityStateView<'a>, + issuer: &'a PrincipalId, +) -> RootLinkage> { + RootLinkage { + parent_root: CanonicalPrincipal(parent.root), + parent_subject: CanonicalPrincipal(parent.subject), + parent_delegated: parent.last_grant.is_some(), + grant_issuer: CanonicalPrincipal(issuer), + } +} + fn selected_profile_attenuates( selected: Option<&ProfileRef>, allowed_profiles: &[ProfileRef], @@ -198,7 +232,7 @@ pub fn evaluate_grant_view<'grant>( grant: GrantAuthorityView<'grant>, ) -> DelegationEvaluation<'grant> { let checks = AttenuationChecks { - root_preserved: true, + root_preserved: root_preserved(&root_linkage(&parent, grant.issuer)), depth_decreases: parent.remaining_depth > 0 && grant.remaining_depth < parent.remaining_depth, profile_attenuates: selected_profile_attenuates( @@ -224,9 +258,10 @@ pub fn evaluate_grant_view<'grant>( None => true, }, }; - if !principal_id_equal(grant.issuer, parent.subject) - || !optional_grant_id_equal(grant.parent, parent.last_grant) - { + // `root_preserved` subsumes the issuer/subject linkage and additionally + // rejects a parent state that never descended from the root it claims, so + // the linkage gate consumes it rather than recomputing a weaker condition. + if !checks.root_preserved || !optional_grant_id_equal(grant.parent, parent.last_grant) { return DelegationEvaluation { checks, outcome: DelegationOutcome::Denied(DenialReason::BrokenGrantChain), @@ -277,7 +312,10 @@ pub fn evaluate_action_coverage_view( authority: AuthorityStateView<'_>, action: ActionAuthorityView<'_>, ) -> CoverageDecision { - if !principal_id_equal(action.actor, authority.subject) + // Terminal coverage is the same chain claim as a delegation edge with the + // actor in the issuer position: an authority that never descended from the + // root it claims authorizes nothing. + if !root_preserved(&root_linkage(&authority, action.actor)) || !optional_grant_id_equal(action.terminal_grant, authority.last_grant) { return CoverageDecision::Denied(DenialReason::BrokenGrantChain); @@ -312,6 +350,7 @@ pub fn evaluate_action_coverage_view( #[must_use] pub fn authority_state_view(authority: &EffectiveAuthority) -> AuthorityStateView<'_> { AuthorityStateView { + root: &authority.root, subject: &authority.subject, allowed_profiles: &authority.allowed_profiles, profile: authority.profile.as_ref(), @@ -409,8 +448,8 @@ mod tests { use super::*; use alloc::vec; use auths_model::{ - Audience, CapabilityId, CriticalExtension, CriticalExtensions, ExtensionId, - PrincipalMethodId, ProfileId, ResourceId, Timestamp, TrustAnchorId, + ActionAuthorityView, Audience, CapabilityId, CriticalExtension, CriticalExtensions, + ExtensionId, PrincipalMethodId, ProfileId, ResourceId, Timestamp, TrustAnchorId, }; fn profile(name: &str) -> ProfileRef { @@ -503,6 +542,155 @@ mod tests { .expect("extensions") } + #[test] + fn delegation_denies_a_grant_issued_under_a_different_root() { + let mut authority = EffectiveAuthority::from_anchor(&anchor()); + assert_eq!( + authority.delegate( + GrantId::new([9; 32]), + &grant("did:key:other-root", "did:key:agent", "profile-a", 1, None), + ), + Err(DenialReason::BrokenGrantChain) + ); + } + + #[test] + fn kernel_denies_a_delegation_whose_parent_state_is_not_rooted() { + // A chain state that claims a root it never received authority from. + // `evaluate_grant_view` is the pure kernel entry point: nothing above + // it re-derives the root, so this state must be rejected here. + let anchor = anchor(); + let root = PrincipalId::parse("did:key:root").expect("root"); + let forged = PrincipalId::parse("did:key:attacker").expect("attacker"); + let permissions = permissions(); + let audiences = audiences(); + let profiles = [profile("profile-a"), profile("profile-b")]; + let constraint = ActionConstraint::AnyBody; + let assurance = AssurancePolicyId::parse("assurance-v1").expect("assurance"); + let status = StatusPolicy::ExpiryOnly; + let unrooted = AuthorityStateView { + root: &root, + subject: &forged, + allowed_profiles: &profiles, + profile: None, + permissions: &permissions, + validity: anchor.validity(), + audiences: &audiences, + action_constraint: &constraint, + budget_ceiling: None, + remaining_depth: 2, + last_grant: None, + assurance_policy: &assurance, + status_policy: &status, + extensions: None, + }; + let statement = grant("did:key:attacker", "did:key:victim", "profile-a", 1, None); + let evaluation = evaluate_grant_view( + unrooted, + GrantId::new([7; 32]), + grant_authority_view(&statement), + ); + let preserved = evaluation.checks.root_preserved; + assert!( + matches!( + evaluation.outcome, + DelegationOutcome::Denied(DenialReason::BrokenGrantChain) + ), + "unrooted parent state must not mint authority (root_preserved={preserved})" + ); + assert!( + !preserved, + "root preservation must be computed, not asserted" + ); + } + + #[test] + fn terminal_coverage_denies_an_authority_that_is_not_rooted() { + let anchor = anchor(); + let root = PrincipalId::parse("did:key:root").expect("root"); + let forged = PrincipalId::parse("did:key:attacker").expect("attacker"); + let permissions = permissions(); + let audiences = audiences(); + let profiles = [profile("profile-a"), profile("profile-b")]; + let constraint = ActionConstraint::AnyBody; + let assurance = AssurancePolicyId::parse("assurance-v1").expect("assurance"); + let status = StatusPolicy::ExpiryOnly; + let selected = profile("profile-a"); + let permission = auths_model::Permission::new( + CapabilityId::parse("deploy").expect("capability"), + ResourceId::parse("cluster://production").expect("resource"), + ); + let audience = Audience::parse("cluster://production").expect("audience"); + let action = ActionAuthorityView { + profile: &selected, + canonical_body_digest: auths_model::Digest::new([0; 32]), + permission: &permission, + requested_budget: None, + audience: &audience, + validity: anchor.validity(), + actor: &forged, + terminal_grant: None, + }; + let unrooted = AuthorityStateView { + root: &root, + subject: &forged, + allowed_profiles: &profiles, + profile: None, + permissions: &permissions, + validity: anchor.validity(), + audiences: &audiences, + action_constraint: &constraint, + budget_ceiling: None, + remaining_depth: 2, + last_grant: None, + assurance_policy: &assurance, + status_policy: &status, + extensions: None, + }; + assert_eq!( + evaluate_action_coverage_view(unrooted, action), + CoverageDecision::Denied(DenialReason::BrokenGrantChain) + ); + } + + #[test] + fn every_edge_of_a_rooted_chain_reports_root_preservation() { + // Guards the other direction: a check that denied everything would + // also make the exploit tests above pass. + let anchor = anchor(); + let mut authority = EffectiveAuthority::from_anchor(&anchor); + let first_id = GrantId::new([1; 32]); + let first = grant("did:key:root", "did:key:agent", "profile-b", 1, None); + assert!( + evaluate_grant(&authority, first_id, &first) + .checks + .root_preserved + ); + authority.delegate(first_id, &first).expect("first edge"); + let second = grant( + "did:key:agent", + "did:key:child", + "profile-b", + 0, + Some(first_id), + ); + assert!( + evaluate_grant(&authority, GrantId::new([2; 32]), &second) + .checks + .root_preserved + ); + assert_eq!(authority.root().as_str(), "did:key:root"); + } + + #[test] + fn a_broken_root_is_reported_on_the_dimension_not_only_in_the_reason() { + let authority = EffectiveAuthority::from_anchor(&anchor()); + let statement = grant("did:key:other-root", "did:key:agent", "profile-a", 1, None); + let evaluation = evaluate_grant(&authority, GrantId::new([9; 32]), &statement); + assert!(!evaluation.checks.root_preserved); + assert!(!attenuation_checks_accept(&evaluation.checks)); + } + #[test] fn first_grant_selects_one_allowed_profile_and_depth_strictly_decreases() { let mut authority = EffectiveAuthority::from_anchor(&anchor()); diff --git a/formal/Auths/Attenuation.lean b/formal/Auths/Attenuation.lean index 99b3a75c..81e596ed 100644 --- a/formal/Auths/Attenuation.lean +++ b/formal/Auths/Attenuation.lean @@ -4,4 +4,62 @@ import Auths.Rich.Theorems Rich target-V1 attenuation, coverage, transition, diagnostic, and well-founded-depth theorems. There is deliberately no `Nat` product-order surrogate in this module. + +## The trust-root dimension + +`Auths.Generated.AttenuationProjection` declares eleven dimensions, and +acceptance is their conjunction. The statements below pin the first of them — +`rootPreserved` — to a predicate over real root identities, so that the +generated conjunction cannot be satisfied by a dimension that is constantly +`true`. +-/ + +namespace Auths.Attenuation + +open Auths.Rich + +/-- +Acceptance of the generated attenuation contract implies the trust root is +preserved. With a constant `rootPreserved` this is provable but empty; it has +content exactly because `rootPreserved` is decided from `parent.root`, +`parent.subject`, `parent.lastGrant`, and `grant.issuer`. -/ +theorem attenuation_requires_trust_root {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) + (accepted : + Auths.Generated.attenuationAccepts + (delegationProjection parent grant) = true) : + rootPreserved parent grant := + ((rich_projection_accepts_iff_root_and_scope_depth_checks + parent grant).1 accepted).1 + +/-- +The contrapositive, stated for every input: a broken trust root denies the +whole projection no matter what the other ten dimensions report. +-/ +theorem attenuation_denied_when_root_broken {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) + (broken : ¬ rootPreserved parent grant) : + Auths.Generated.attenuationAccepts + (delegationProjection parent grant) = false := + broken_root_denies_every_projection parent grant broken + +/-- +The dimension is falsifiable. A grant issued by any principal other than the +one the parent speaks for drives it to `false`, so no implementation that +returns a literal `true` can satisfy this theorem. +-/ +theorem attenuation_root_dimension_is_not_a_literal {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) + (foreign : grant.issuer ≠ parent.subject) : + (delegationProjection parent grant).rootPreserved = false := + root_dimension_false_of_foreign_issuer parent grant foreign + +/-- The dimension reports the semantic predicate exactly, in both directions. -/ +theorem attenuation_root_dimension_is_exact {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) : + (delegationProjection parent grant).rootPreserved = true ↔ + rootPreserved parent grant := + root_dimension_is_exact parent grant + +end Auths.Attenuation diff --git a/formal/Auths/Authority.lean b/formal/Auths/Authority.lean index bba31593..54296136 100644 --- a/formal/Auths/Authority.lean +++ b/formal/Auths/Authority.lean @@ -1,8 +1,75 @@ import Auths.Rich.Semantics +import Auths.Rich.Theorems /-! The authority model is defined in `Auths.Rich`. This compatibility import intentionally contains no scalar authority coordinates: identities are opaque, sets are extensional `Finset` values, intervals and ceilings use their actual relations, and chain state is separate from ordered authority scope. + +## Root preservation + +`ChainState` carries the trust root the authority is anchored at. The +theorems below state, over all inputs, that a delegated authority still +descends from the same root as its parent — and, crucially, that an authority +which descends from no root can neither delegate nor authorize. +-/ + +namespace Auths.Authority + +open Auths.Rich + +/-- Every accepted edge leaves the child under the parent's trust root. -/ +theorem delegation_preserves_trust_root {v : Vocabulary} + {parent child : ChainState v} {grantId : GrantId v} {grant : Grant v} + (edge : delegates parent grantId grant child) : + child.root = parent.root := + delegate_preserves_root edge + +/-- +Every accepted edge starts from an authority that itself descends from that +root, and leaves the child in the same condition. This is the inductive +content that `delegation_preserves_trust_root` alone does not carry. -/ +theorem delegation_requires_and_preserves_rootedness {v : Vocabulary} + {parent child : ChainState v} {grantId : GrantId v} {grant : Grant v} + (edge : delegates parent grantId grant child) : + rooted parent ∧ rooted child := + ⟨delegate_requires_rooted_parent edge, delegate_preserves_rootedness edge⟩ + +/-- The first edge of a chain is issued by the root principal itself. -/ +theorem first_delegation_comes_from_the_root {v : Vocabulary} + {parent child : ChainState v} {grantId : GrantId v} {grant : Grant v} + (fresh : parent.lastGrant = none) + (edge : delegates parent grantId grant child) : + grant.issuer = parent.root := + first_edge_is_issued_by_the_root fresh edge + +/-- +Every state reachable by any number of delegations descends from the single +root the chain started at. Delegation can narrow authority; it can never +re-anchor it. +-/ +theorem chain_descends_from_one_root {v : Vocabulary} + {start : ChainState v} {rest : List (ChainState v)} + (chain : DelegationChain start rest) : + ∀ state ∈ rest, state.root = start.root ∧ rooted state := + fun state member => + ⟨chain_preserves_root chain state member, + chain_preserves_rootedness chain state member⟩ + +/-- An authority that descends from no root delegates nothing. -/ +theorem unrooted_authority_delegates_nothing {v : Vocabulary} + (parent : ChainState v) (grantId : GrantId v) (grant : Grant v) + (unrooted : ¬ rooted parent) : + evaluateGrant parent grantId grant = .denied .brokenGrantChain := + unrooted_parent_delegates_nothing parent grantId grant unrooted + +/-- An authority that descends from no root authorizes nothing. -/ +theorem unrooted_authority_authorizes_nothing {v : Vocabulary} + (authority : ChainState v) (action : Action v) + (unrooted : ¬ rooted authority) : + evaluateCoverage authority action = .denied .brokenGrantChain := + unrooted_authority_covers_nothing authority action unrooted + +end Auths.Authority diff --git a/formal/Auths/Refinement/Production.lean b/formal/Auths/Refinement/Production.lean index 77c899f7..2b3fb3e6 100644 --- a/formal/Auths/Refinement/Production.lean +++ b/formal/Auths/Refinement/Production.lean @@ -1628,6 +1628,23 @@ structure ActionAuthorityViewValid audience : StringBounded view.audience validity : ValidityWindowValid view.validity +/-- +The trust root supplied to `richAuthorityState` is the root the production +state actually descends from. + +`auths_authority::AuthorityStateView` gained a `root` field so the shipping +kernel can compute `root_preserved` instead of asserting it; the Aeneas +translation replayed here predates that field, so the correspondence between +the Rust root and the model root must be carried as an explicit hypothesis +until the translation is regenerated. Regenerating it lets this predicate be +discharged as `richPrincipal view.root = root` rather than assumed. +-/ +def AuthorityStateAnchored + (root : Auths.Rich.Principal ProductionVocabulary) + (view : auths_authority.AuthorityStateView) : Prop := + (view.last_grant.map richGrantId).isSome = true ∨ + root = richPrincipal view.subject + def richAuthorityState (root : Auths.Rich.Principal ProductionVocabulary) (view : auths_authority.AuthorityStateView) @@ -2100,7 +2117,8 @@ theorem translated_coverage_refines_rich_spec (authority : auths_authority.AuthorityStateView) (action : auths_model.ActionAuthorityView) (authorityValid : AuthorityStateViewValid authority) - (actionValid : ActionAuthorityViewValid action) : + (actionValid : ActionAuthorityViewValid action) + (anchored : AuthorityStateAnchored root authority) : auths_authority.evaluate_action_coverage_view authority action ⦃ result => result = productionCoverageDecision @@ -2176,7 +2194,7 @@ theorem translated_coverage_refines_rich_spec (authority.budget_ceiling.map richBudget) (action.requested_budget.map richBudget) := budgetIff.mp budgetCondition - simp_all [Auths.Rich.evaluateCoverage, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, productionCoverageDecision, richAuthorityState, richAction] · have budgetSemantic : @@ -2185,7 +2203,7 @@ theorem translated_coverage_refines_rich_spec (action.requested_budget.map richBudget) := by intro semantic exact budgetCondition (budgetIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, productionCoverageDecision, richAuthorityState, richAction] · have constraintSemantic : @@ -2196,7 +2214,7 @@ theorem translated_coverage_refines_rich_spec intro semantic exact constraintCondition (constraintIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, productionCoverageDecision, richAuthorityState, richAction] · have audienceSemantic : @@ -2205,7 +2223,7 @@ theorem translated_coverage_refines_rich_spec intro semantic exact audienceCondition (audienceIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, productionCoverageDecision, richAuthorityState, richAction] · have validitySemantic : @@ -2214,7 +2232,7 @@ theorem translated_coverage_refines_rich_spec (richWindow authority.validity authorityWindow) := by intro semantic exact validityCondition (validityIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, productionCoverageDecision, richAuthorityState, richAction] · have permissionSemantic : @@ -2222,7 +2240,7 @@ theorem translated_coverage_refines_rich_spec richPermissionSet authority.permissions := by intro semantic exact permissionCondition (permissionIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, productionCoverageDecision, richAuthorityState, richAction] · have profileSemantic : @@ -2232,21 +2250,21 @@ theorem translated_coverage_refines_rich_spec (richProfile action.profile) := by intro semantic exact profileCondition (profileIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, productionCoverageDecision, richAuthorityState, richAction] · have grantSemantic : action.terminal_grant.map richGrantId ≠ authority.last_grant.map richGrantId := by intro semantic exact grantCondition (grantIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, productionCoverageDecision, richAuthorityState, richAction] · have actorSemantic : richPrincipal action.actor ≠ richPrincipal authority.subject := by intro semantic exact actorCondition (actorIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, productionCoverageDecision, richAuthorityState, richAction] /-- @@ -2260,7 +2278,8 @@ theorem translated_delegation_refines_rich_spec (grantId : auths_model.GrantId) (grant : auths_model.GrantAuthorityView) (parentValid : AuthorityStateViewValid parent) - (grantValid : GrantAuthorityViewValid grant) : + (grantValid : GrantAuthorityViewValid grant) + (anchored : AuthorityStateAnchored root parent) : auths_authority.evaluate_grant_view parent grantId grant ⦃ result => result.outcome = productionDelegationOutcome @@ -2309,6 +2328,7 @@ theorem translated_delegation_refines_rich_spec ⟨scopeAccepted, scopeIff⟩ split <;> rename_i scopeCondition · simp_all [Auths.Rich.evaluateGrant, Auths.Rich.linked, + Auths.Rich.rootPreserved, Auths.Rich.rooted, AuthorityStateAnchored, Auths.Rich.scopeDepthChecks, Auths.Rich.grantScopeChecks, productionDelegationOutcome, expectedAcceptedTransition, extensionAwareDelegationDecision, @@ -2435,7 +2455,8 @@ theorem translated_delegation_refines_rich_spec assurance := grantAssurance extensions := grantExtensions }) := by - simp_all [Auths.Rich.linked, richAuthorityState, + simp_all [Auths.Rich.linked, Auths.Rich.rootPreserved, + Auths.Rich.rooted, AuthorityStateAnchored, richAuthorityState, richGrant] rcases scopeOrExtensions with ⟨scopeSemantic, extensionsSemantic⟩ | extensionsSemantic @@ -2446,10 +2467,12 @@ theorem translated_delegation_refines_rich_spec split <;> simp [productionDelegationOutcome, extensionAwareDelegationDecision, extensionsSemantic] · simp_all [Auths.Rich.evaluateGrant, Auths.Rich.linked, + Auths.Rich.rootPreserved, Auths.Rich.rooted, AuthorityStateAnchored, productionDelegationOutcome, extensionAwareDelegationDecision, richAuthorityState, richGrant] · simp_all [Auths.Rich.evaluateGrant, Auths.Rich.linked, + Auths.Rich.rootPreserved, Auths.Rich.rooted, AuthorityStateAnchored, productionDelegationOutcome, extensionAwareDelegationDecision, richAuthorityState, richGrant] diff --git a/formal/Auths/Rich/Semantics.lean b/formal/Auths/Rich/Semantics.lean index 34afe4c6..2628a22b 100644 --- a/formal/Auths/Rich/Semantics.lean +++ b/formal/Auths/Rich/Semantics.lean @@ -168,9 +168,40 @@ def semanticAttenuates {v : Vocabulary} (child parent : AuthorityScope v) : Prop := ∀ facts, admits child facts → admits parent facts +/-- +A chain state genuinely descends from the root it names. + +Either an accepted edge has already been applied — and `acceptedNextState` +copies the root forward, so the root was carried by that edge — or no edge has +been applied yet and the state must still *be* the root. A state with no +applied grant whose subject differs from its root descends from nothing. +-/ +def rooted {v : Vocabulary} (state : ChainState v) : Prop := + state.lastGrant.isSome = true ∨ state.root = state.subject + +instance {v : Vocabulary} (state : ChainState v) : Decidable (rooted state) := by + unfold rooted + infer_instance + +/-- +The trust-root dimension of the generated attenuation contract: this edge +continues the chain rooted at `parent.root`. + +Two independent facts are required and neither implies the other — the parent +must be rooted, and the edge must be issued by the parent's own subject. +-/ +def rootPreserved {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) : Prop := + rooted parent ∧ grant.issuer = parent.subject + +instance {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : + Decidable (rootPreserved parent grant) := by + unfold rootPreserved + infer_instance + def linked {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : Prop := - grant.issuer = parent.subject ∧ grant.parent = parent.lastGrant + rootPreserved parent grant ∧ grant.parent = parent.lastGrant instance {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : Decidable (linked parent grant) := by @@ -348,7 +379,8 @@ inductive CoverageDecision where /-- First-failure order used by the shipping terminal-coverage API. -/ def evaluateCoverage {v : Vocabulary} (authority : ChainState v) (action : Action v) : CoverageDecision := - if action.actor = authority.subject ∧ + if rooted authority ∧ + action.actor = authority.subject ∧ action.terminalGrant = authority.lastGrant ∧ profileAllows authority.scope.profileScope action.profile then if action.permission ∉ authority.scope.permissions then @@ -368,6 +400,7 @@ def evaluateCoverage {v : Vocabulary} def terminalCovers {v : Vocabulary} (authority : ChainState v) (action : Action v) : Prop := + rooted authority ∧ action.actor = authority.subject ∧ action.terminalGrant = authority.lastGrant ∧ actionCovers authority.scope action @@ -375,7 +408,7 @@ def terminalCovers {v : Vocabulary} def delegationProjection {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : Auths.Generated.AttenuationProjection where - rootPreserved := true + rootPreserved := decide (rootPreserved parent grant) depthDecreases := decide (0 < parent.remainingDepth ∧ grant.remainingDepth < parent.remainingDepth) diff --git a/formal/Auths/Rich/Theorems.lean b/formal/Auths/Rich/Theorems.lean index 2d60423a..fcbf9735 100644 --- a/formal/Auths/Rich/Theorems.lean +++ b/formal/Auths/Rich/Theorems.lean @@ -412,6 +412,125 @@ theorem delegate_preserves_root {v : Vocabulary} rcases accepted with ⟨_, ⟨_, rfl⟩⟩ rfl +/-! +### Trust-root preservation + +`delegate_preserves_root` alone is not the security claim: it holds for any +definition of `delegates` because `acceptedNextState` copies the root field. +The claim only has content once an edge is *required* to descend from that +root. The theorems below establish that requirement over all inputs. +-/ + +/-- An edge is only accepted from a parent that descends from its own root. -/ +theorem delegate_requires_rooted_parent {v : Vocabulary} + {parent child : ChainState v} {grantId : GrantId v} {grant : Grant v} + (accepted : delegates parent grantId grant child) : + rooted parent := + accepted.1.1.1 + +/-- An accepted edge is issued by the principal the parent speaks for. -/ +theorem delegate_requires_parent_issuer {v : Vocabulary} + {parent child : ChainState v} {grantId : GrantId v} {grant : Grant v} + (accepted : delegates parent grantId grant child) : + grant.issuer = parent.subject := + accepted.1.1.2 + +/-- Rootedness is closed under accepted edges, so the invariant is inductive. -/ +theorem delegate_preserves_rootedness {v : Vocabulary} + {parent child : ChainState v} {grantId : GrantId v} {grant : Grant v} + (accepted : delegates parent grantId grant child) : + rooted child := by + rcases accepted with ⟨_, ⟨_, rfl⟩⟩ + exact Or.inl rfl + +/-- +The first edge of any chain is issued by the root itself. This is the case +`delegate_preserves_root` cannot see: with an unrooted parent the model would +mint authority under a root that never conferred it. +-/ +theorem first_edge_is_issued_by_the_root {v : Vocabulary} + {parent child : ChainState v} {grantId : GrantId v} {grant : Grant v} + (fresh : parent.lastGrant = none) + (accepted : delegates parent grantId grant child) : + grant.issuer = parent.root := by + have issuer := delegate_requires_parent_issuer accepted + rcases delegate_requires_rooted_parent accepted with applied | isRoot + · rw [fresh] at applied + exact absurd applied (by simp) + · rw [issuer, ← isRoot] + +/-- Every state reachable from `start` descends from `start.root`. -/ +theorem chain_preserves_root {v : Vocabulary} + {start : ChainState v} {rest : List (ChainState v)} + (chain : DelegationChain start rest) : + ∀ state ∈ rest, state.root = start.root := by + induction chain with + | nil => simp + | cons parent child grantId grant rest edge tail inductionHypothesis => + intro state member + rcases List.mem_cons.1 member with head | inTail + · rw [head] + exact delegate_preserves_root edge + · exact (inductionHypothesis state inTail).trans + (delegate_preserves_root edge) + +/-- Every state reachable from a rooted `start` is itself rooted. -/ +theorem chain_preserves_rootedness {v : Vocabulary} + {start : ChainState v} {rest : List (ChainState v)} + (chain : DelegationChain start rest) : + ∀ state ∈ rest, rooted state := by + induction chain with + | nil => simp + | cons parent child grantId grant rest edge tail inductionHypothesis => + intro state member + rcases List.mem_cons.1 member with head | inTail + · rw [head] + exact delegate_preserves_rootedness edge + · exact inductionHypothesis state inTail + +/-- A parent that descends from no root delegates nothing, for every grant. -/ +theorem unrooted_parent_delegates_nothing {v : Vocabulary} + (parent : ChainState v) (grantId : GrantId v) (grant : Grant v) + (unrooted : ¬ rooted parent) : + evaluateGrant parent grantId grant = .denied .brokenGrantChain := by + simp [evaluateGrant, linked, rootPreserved, unrooted] + +/-- A parent that descends from no root authorizes no action either. -/ +theorem unrooted_authority_covers_nothing {v : Vocabulary} + (authority : ChainState v) (action : Action v) + (unrooted : ¬ rooted authority) : + evaluateCoverage authority action = .denied .brokenGrantChain := by + simp [evaluateCoverage, unrooted] + +/-- +The generated trust-root dimension reports exactly the semantic predicate. +This is what makes the dimension non-vacuous: it is `false` on a real class of +inputs, so a literal `true` would refute it. +-/ +theorem root_dimension_is_exact {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) : + (delegationProjection parent grant).rootPreserved = true ↔ + rootPreserved parent grant := by + simp [delegationProjection] + +/-- Witness that the dimension is falsifiable, stated over all inputs. -/ +theorem root_dimension_false_of_foreign_issuer {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) + (foreign : grant.issuer ≠ parent.subject) : + (delegationProjection parent grant).rootPreserved = false := by + simp [delegationProjection, rootPreserved, foreign] + +/-- +No other attenuation dimension can rescue a broken root: acceptance is the +conjunction, so the whole projection is rejected. +-/ +theorem broken_root_denies_every_projection {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) + (broken : ¬ rootPreserved parent grant) : + Auths.Generated.attenuationAccepts + (delegationProjection parent grant) = false := by + simp [Auths.Generated.attenuationAccepts, delegationProjection, broken] + theorem delegate_updates_subject_and_parent {v : Vocabulary} {parent child : ChainState v} {grantId : GrantId v} {grant : Grant v} (accepted : delegates parent grantId grant child) : @@ -467,11 +586,20 @@ theorem authorized_action_covered {v : Vocabulary} actionCovers parent.scope action := action_coverage_downward_closed (delegate_implies_scope_le accepted) authorized -theorem rich_projection_accepts_iff_scope_depth_checks {v : Vocabulary} +/-- +The generated conjunction accepts exactly the trust-root dimension together +with every scope and depth dimension. + +The `rootPreserved` conjunct is not redundant: before the trust root became a +computed dimension this theorem read `↔ scopeDepthChecks parent grant`, which +is precisely the vacuity — the eleven-dimension contract was proved equivalent +to ten dimensions. +-/ +theorem rich_projection_accepts_iff_root_and_scope_depth_checks {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : Auths.Generated.attenuationAccepts (delegationProjection parent grant) = true ↔ - scopeDepthChecks parent grant := by + rootPreserved parent grant ∧ scopeDepthChecks parent grant := by simp [Auths.Generated.attenuationAccepts, delegationProjection, scopeDepthChecks, grantScopeChecks] tauto @@ -485,9 +613,9 @@ theorem apply_grant_success_iff_linked_and_projection {v : Vocabulary} (delegationProjection parent grant) = true ∧ ∃ checks : scopeDepthChecks parent grant, child = acceptedNextState parent grantId grant checks := by - rw [rich_projection_accepts_iff_scope_depth_checks] + rw [rich_projection_accepts_iff_root_and_scope_depth_checks] simp only [evaluateGrant] - split_ifs with linkage checks <;> simp_all [eq_comm] + split_ifs with linkage checks <;> simp_all [linked, eq_comm] theorem apply_grant_success_iff_delegates {v : Vocabulary} (parent : ChainState v) (grantId : GrantId v) (grant : Grant v) @@ -495,8 +623,9 @@ theorem apply_grant_success_iff_delegates {v : Vocabulary} evaluateGrant parent grantId grant = .accepted child ↔ delegates parent grantId grant child := by rw [apply_grant_success_iff_linked_and_projection, - rich_projection_accepts_iff_scope_depth_checks] - simp [delegates] + rich_projection_accepts_iff_root_and_scope_depth_checks] + simp [delegates, linked] + tauto theorem apply_grant_success_unique {v : Vocabulary} {parent : ChainState v} {grantId : GrantId v} {grant : Grant v} From d735edaa7cf6907c09ad0957f01e89ef4e01921f Mon Sep 17 00:00:00 2001 From: bordumb Date: Sat, 15 Aug 2026 13:37:13 +0100 Subject: [PATCH 04/61] product: let the signed receipt say "unknown", and sign it only after every gate UNSIGNED (auths-sign requires an interactive passphrase unavailable here). Two ratified protocol/ordering changes, contract 5A.3 / 5A.4 / 10A decision 11.8. PART 1 - ExecutionOutcome::Indeterminate (decision 11.8). `ExecutionOutcome` had two variants, so a provider timeout could only be signed as `Failed` - an assertion of non-effect for an effect that may have applied. A prior wave worked around it by minting nothing, trading a false receipt for no receipt. Now: * auths_receipts::ExecutionOutcome gains `Indeterminate`, wire tag 2. * auths_proof_exchange_model::ExchangeOutcome gains `Indeterminate { verdict, message }`, outcome code 2, with a matching codec branch that refuses a smuggled refusal-kind or result. * The runtime projects the adapter's proven provider boundary onto the receipt through one total function, `receipt_outcome_for`. Both tag assignments are ADDITIVE. `cargo xtask wire` reports 516 golden vector files byte-stable and `cargo xtask product-fixtures` reports stable, so NO canonical fixture required regeneration and none was performed. Behaviour changes, named: 1. A post-provider-entry failure now mints a signed `Indeterminate` execution receipt. It previously minted none. 2. Its exchange response is `Indeterminate`, not `Refused`. Every caller reads a refusal as "nothing happened" and retries. 3. The two post-success failures - receipt store unavailable, and result over the exchange limit - also became `Indeterminate`. The effect had already applied; refusing them invited a duplicate side effect. PART 2 - the authorization receipt follows every refusing gate (5A.4). The `Authorized` decision receipt was written before the replay claim AND before the budget claim. Audit records asserted authorization for requests that were then refused, and each replay attempt was an unbounded write into the receipt sink. The write now happens after the last gate. The pure action-identifier check was hoisted above the replay claim so a verified action the runtime cannot lease no longer consumes the caller's challenge. Red-first evidence (all three failed before the fix): * a_provider_timeout_signs_a_receipt_that_does_not_claim_the_effect_failed left 0 execution receipts, expected 1. * a_replayed_request_writes_no_authorization_receipt sink write calls (2, 1), expected (1, 1). The pre-existing count-by-id assertion could not see this: a replay re-derives the same receipt id, so the deduplicating map never grew. * a_budget_refused_request_writes_no_authorization_receipt sink write calls (1, 0), expected (0, 0). Plus compile evidence that neither enum could name the third state at all. Verified serially (concurrent cargo jobs sharing target/ produce spurious xtask filesystem failures): cargo check --workspace --all-targets exit 0; cargo test --workspace --exclude auths-proof-fuzz --exclude auths-bounded-policy-fuzz --lib --tests exit 0, 144 suites ok, 0 failures. cargo xtask compliance passed. FrozenMeaning identities drifted by this change: auths.product.receipts v4, auths.portable-abi-bindings v49, auths.product.mcp-closed-execution v12, and auths.product.public-sdk-contract v40 (already a known-red baseline). Per the brief, no semantic-freeze regeneration was run. --- bindings/python/python/auths/_receipts.py | 4 +- bindings/python/src/receipts.rs | 3 +- bindings/typescript/src/receipt-inspection.ts | 2 +- bindings/wasm/auths-proof-wasm/src/lib.rs | 3 +- compliance.toml | 23 +- demos/live-lab/src/lib.rs | 9 + demos/live-service/src/lib.rs | 9 + demos/rest-api-authorization/src/app.rs | 21 +- demos/testkit/auths-apps-testkit/src/lib.rs | 368 +++++++++++++++++- docs/target-state/v1-api-contract.md | 23 +- .../auths-proof-exchange-codec/src/lib.rs | 75 ++++ .../auths-proof-exchange-model/src/lib.rs | 39 ++ exchange/spec/v1/protocol.md | 19 +- product/receipts/auths-receipts/src/lib.rs | 102 ++++- product/runtime/auths-runtime/src/lib.rs | 221 ++++++++--- 15 files changed, 835 insertions(+), 86 deletions(-) diff --git a/bindings/python/python/auths/_receipts.py b/bindings/python/python/auths/_receipts.py index 4c7ed06e..3e729f3d 100644 --- a/bindings/python/python/auths/_receipts.py +++ b/bindings/python/python/auths/_receipts.py @@ -97,7 +97,7 @@ class ReceiptInspectionMetadata: profile: ReceiptInspectionProfile decision: Literal["authorized", "denied", "indeterminate"] reasons: Tuple[str, ...] - outcome: Literal["succeeded", "failed"] + outcome: Literal["succeeded", "failed", "indeterminate"] decided_at: int completed_at: int decision_signer: ReceiptInspectionSigner @@ -353,7 +353,7 @@ def _parse_inspection_metadata( ), cast(Literal["authorized", "denied", "indeterminate"], value["decision"]), tuple(str(reason) for reason in cast(list[object], value["reasons"])), - cast(Literal["succeeded", "failed"], value["outcome"]), + cast(Literal["succeeded", "failed", "indeterminate"], value["outcome"]), int(cast(int, value["decidedAt"])), int(cast(int, value["completedAt"])), _parse_inspection_signer(cast(Mapping[str, object], value["decisionSigner"])), diff --git a/bindings/python/src/receipts.rs b/bindings/python/src/receipts.rs index 91a2f7a0..639836be 100644 --- a/bindings/python/src/receipts.rs +++ b/bindings/python/src/receipts.rs @@ -162,6 +162,7 @@ fn prepare_application_execution_receipt_v1( match outcome { "succeeded" => ExecutionOutcome::Succeeded, "failed" => ExecutionOutcome::Failed, + "indeterminate" => ExecutionOutcome::Indeterminate, _ => { return Err(PyValueError::new_err( "execution outcome cannot be attested", @@ -448,7 +449,7 @@ fn metadata_json(metadata: &VerifiedReceiptMetadata) -> Value { "profile": { "id": metadata.profile().id().as_str(), "version": metadata.profile().version() }, "decision": match metadata.decision() { DecisionClass::Authorized => "authorized", DecisionClass::Denied => "denied", DecisionClass::Indeterminate => "indeterminate" }, "reasons": metadata.reasons(), - "outcome": match metadata.outcome() { ExecutionOutcome::Succeeded => "succeeded", ExecutionOutcome::Failed => "failed" }, + "outcome": match metadata.outcome() { ExecutionOutcome::Succeeded => "succeeded", ExecutionOutcome::Failed => "failed", ExecutionOutcome::Indeterminate => "indeterminate" }, "decidedAt": metadata.decided_at().get().to_string(), "completedAt": metadata.completed_at().get().to_string(), "decisionSigner": signer_json(metadata.decision_signer()), diff --git a/bindings/typescript/src/receipt-inspection.ts b/bindings/typescript/src/receipt-inspection.ts index abdb7fd7..724d6306 100644 --- a/bindings/typescript/src/receipt-inspection.ts +++ b/bindings/typescript/src/receipt-inspection.ts @@ -31,7 +31,7 @@ export interface ReceiptInspectionMetadata { readonly profile: ReceiptInspectionProfile; readonly decision: "authorized" | "denied" | "indeterminate"; readonly reasons: readonly string[]; - readonly outcome: "succeeded" | "failed"; + readonly outcome: "succeeded" | "failed" | "indeterminate"; readonly decidedAt: bigint; readonly completedAt: bigint; readonly decisionSigner: ReceiptInspectionSigner; diff --git a/bindings/wasm/auths-proof-wasm/src/lib.rs b/bindings/wasm/auths-proof-wasm/src/lib.rs index 8877c4fe..70aa385b 100644 --- a/bindings/wasm/auths-proof-wasm/src/lib.rs +++ b/bindings/wasm/auths-proof-wasm/src/lib.rs @@ -3863,6 +3863,7 @@ pub fn prepare_application_execution_receipt_v1( match outcome { "succeeded" => ExecutionOutcome::Succeeded, "failed" => ExecutionOutcome::Failed, + "indeterminate" => ExecutionOutcome::Indeterminate, _ => { return Err(js_error(EngineError::Abi( "execution outcome cannot be attested", @@ -4176,7 +4177,7 @@ fn inspection_metadata_json(metadata: &VerifiedReceiptMetadata) -> Value { "profile": { "id": metadata.profile().id().as_str(), "version": metadata.profile().version() }, "decision": match metadata.decision() { DecisionClass::Authorized => "authorized", DecisionClass::Denied => "denied", DecisionClass::Indeterminate => "indeterminate" }, "reasons": metadata.reasons(), - "outcome": match metadata.outcome() { ExecutionOutcome::Succeeded => "succeeded", ExecutionOutcome::Failed => "failed" }, + "outcome": match metadata.outcome() { ExecutionOutcome::Succeeded => "succeeded", ExecutionOutcome::Failed => "failed", ExecutionOutcome::Indeterminate => "indeterminate" }, "decidedAt": metadata.decided_at().get().to_string(), "completedAt": metadata.completed_at().get().to_string(), "decisionSigner": inspection_signer_json(metadata.decision_signer()), diff --git a/compliance.toml b/compliance.toml index 95401d2e..059f818e 100644 --- a/compliance.toml +++ b/compliance.toml @@ -1271,7 +1271,11 @@ configuration_inputs = ["decision-digest", "executed-verifier-configuration", "r security_state = ["receipt-attestation-key"] [packages.auths-receipts.claims] -receipt-producer-consumer = ["product/receipts/auths-receipts/src/lib.rs#audit_bundle_verifies_links_and_redaction_offline"] +receipt-producer-consumer = [ + "product/receipts/auths-receipts/src/lib.rs#audit_bundle_verifies_links_and_redaction_offline", + "product/receipts/auths-receipts/src/lib.rs#execution_outcome_wire_tags_are_exactly_zero_one_two", + "product/receipts/auths-receipts/src/lib.rs#the_indeterminate_outcome_is_a_distinct_canonical_signed_value", +] [packages.auths-resolver-did-web] kind = "cargo" @@ -1354,9 +1358,20 @@ configuration_inputs = ["channel-policy", "executed-verifier-configuration", "re security_state = ["budget-ledger", "challenge-ledger", "receipt-sink"] [packages.auths-runtime.claims] -receipt-producer-consumer = ["demos/testkit/auths-apps-testkit/src/lib.rs#target_flow_is_transport_independent_and_replay_safe"] -runtime-enforcement-boundary = ["demos/testkit/auths-apps-testkit/src/lib.rs#authenticated_transport_does_not_upgrade_bad_proof"] -stateful-replay-budget-component = ["demos/testkit/auths-apps-testkit/src/lib.rs#concurrent_duplicate_executes_exactly_once"] +receipt-producer-consumer = [ + "demos/testkit/auths-apps-testkit/src/lib.rs#a_provider_timeout_signs_a_receipt_that_does_not_claim_the_effect_failed", + "demos/testkit/auths-apps-testkit/src/lib.rs#a_proven_pre_entry_failure_still_signs_a_non_effect_receipt", + "demos/testkit/auths-apps-testkit/src/lib.rs#target_flow_is_transport_independent_and_replay_safe", +] +runtime-enforcement-boundary = [ + "demos/testkit/auths-apps-testkit/src/lib.rs#a_budget_refused_request_writes_no_authorization_receipt", + "demos/testkit/auths-apps-testkit/src/lib.rs#a_denied_verification_still_records_a_denied_receipt", + "demos/testkit/auths-apps-testkit/src/lib.rs#authenticated_transport_does_not_upgrade_bad_proof", +] +stateful-replay-budget-component = [ + "demos/testkit/auths-apps-testkit/src/lib.rs#a_replayed_request_writes_no_authorization_receipt", + "demos/testkit/auths-apps-testkit/src/lib.rs#concurrent_duplicate_executes_exactly_once", +] [packages.auths-sdk] kind = "cargo" diff --git a/demos/live-lab/src/lib.rs b/demos/live-lab/src/lib.rs index c12f557c..80da25c6 100644 --- a/demos/live-lab/src/lib.rs +++ b/demos/live-lab/src/lib.rs @@ -373,6 +373,15 @@ fn response_projection(response: &ActionResponse) -> Value { })), "request_id": request_id, }), + ExchangeOutcome::Indeterminate { verdict, message } => json!({ + "outcome": "indeterminate", + "message": message, + "verdict": verdict.as_ref().map(|summary| json!({ + "decision": verdict_name(summary.decision()), + "reasons": summary.reasons(), + })), + "request_id": request_id, + }), } } diff --git a/demos/live-service/src/lib.rs b/demos/live-service/src/lib.rs index af177194..a1166177 100644 --- a/demos/live-service/src/lib.rs +++ b/demos/live-service/src/lib.rs @@ -486,6 +486,15 @@ fn response_projection(response: &ActionResponse) -> Value { })), "request_id": request_id, }), + ExchangeOutcome::Indeterminate { verdict, message } => json!({ + "outcome": "indeterminate", + "message": message, + "verdict": verdict.as_ref().map(|summary| json!({ + "decision": verdict_name(summary.decision()), + "reasons": summary.reasons(), + })), + "request_id": request_id, + }), } } diff --git a/demos/rest-api-authorization/src/app.rs b/demos/rest-api-authorization/src/app.rs index eb8a948f..b0eaa0f8 100644 --- a/demos/rest-api-authorization/src/app.rs +++ b/demos/rest-api-authorization/src/app.rs @@ -712,6 +712,11 @@ async fn send_via_iroh( ExchangeOutcome::Refused { message, .. } => { Err(ApiError::owned_bad_request(message.clone())) } + // The effect may already have been applied. A 4xx would tell the + // caller nothing happened and invite a duplicate write. + ExchangeOutcome::Indeterminate { message, .. } => { + Err(ApiError::owned_effect_unknown(message.clone())) + } } } @@ -874,7 +879,8 @@ pub async fn send_envelope_file( ExchangeOutcome::Completed { result } => { serde_json::from_slice(result).map_err(|_| "invalid records response".into()) } - ExchangeOutcome::Refused { message, .. } => Err(message.clone()), + ExchangeOutcome::Refused { message, .. } + | ExchangeOutcome::Indeterminate { message, .. } => Err(message.clone()), } } @@ -1195,6 +1201,19 @@ impl ApiError { } } + /// The upstream effect may or may not have been applied. + /// + /// Deliberately not a 4xx: every client reads a 4xx as "the request was + /// rejected and nothing happened", which is exactly the claim this runtime + /// cannot make. 502 with an explicit code tells the caller to reconcile. + fn owned_effect_unknown(detail: String) -> Self { + Self { + status: StatusCode::BAD_GATEWAY, + code: "effect-unknown", + detail, + } + } + fn detail_owned(&self) -> String { self.detail.clone() } diff --git a/demos/testkit/auths-apps-testkit/src/lib.rs b/demos/testkit/auths-apps-testkit/src/lib.rs index 1ad35210..47406492 100644 --- a/demos/testkit/auths-apps-testkit/src/lib.rs +++ b/demos/testkit/auths-apps-testkit/src/lib.rs @@ -35,7 +35,7 @@ use auths_proof_exchange_port::{ClientProofChannel, ProofExchangeService, serve_ use auths_raw_key::{RAW_KEY_MEDIA_TYPE, RAW_KEY_V1, RawKeyDescriptor, RawKeyMethod, RawKeyType}; use auths_receipts::{ReceiptSigner, decode_attested_decision, decode_attested_execution}; use auths_runtime::{ - AuthsKernel, ChallengeSource, ChallengeSourceError, Clock, ExecutableAction, + AuthsKernel, BudgetLedger, ChallengeSource, ChallengeSourceError, Clock, ExecutableAction, InMemoryChallengeLedger, McpAuthorizationService, McpExecutionDependencies, McpRequestStateDependencies, McpRuntimeDependencies, McpServiceConfig, McpToolExecutor, NoBudgetLedger, ReceiptAttestationError, ReceiptAttestor, ReceiptSink, ReceiptStoreError, @@ -144,10 +144,67 @@ impl McpToolExecutor for StaticReportExecutor { } } +/// Executor that always fails at a caller-chosen provider boundary. +/// +/// Exists so a test can drive the two halves of +/// [`auths_runtime::ProviderBoundary`] through the real service and observe +/// which signed execution receipt, if any, the runtime mints. +#[cfg(test)] +struct BoundaryFailureExecutor { + boundary: auths_runtime::ProviderBoundary, +} + +#[cfg(test)] +impl BoundaryFailureExecutor { + const fn new(boundary: auths_runtime::ProviderBoundary) -> Self { + Self { boundary } + } +} + +#[cfg(test)] +#[async_trait] +impl McpToolExecutor for BoundaryFailureExecutor { + async fn execute( + &self, + _action: ExecutableAction, + ) -> Result, auths_runtime::ToolExecutionFailure> { + Err(match self.boundary { + auths_runtime::ProviderBoundary::BeforeEntry => { + auths_runtime::ToolExecutionFailure::before_provider_entry( + "connection refused before any request byte was written", + ) + } + auths_runtime::ProviderBoundary::AfterEntry => { + auths_runtime::ToolExecutionFailure::after_provider_entry("provider call timed out") + } + }) + } +} + +/// Budget ledger that refuses every claim. +/// +/// Drives the runtime's budget gate — the refusing check furthest from the +/// verification verdict — without needing a budget-bearing canonical action. +#[cfg(test)] +struct ExhaustedBudgetLedger; + +#[cfg(test)] +impl BudgetLedger for ExhaustedBudgetLedger { + fn claim( + &self, + _action: auths_model::ActionId, + _requested: Option<&auths_model::BudgetCeiling>, + ) -> auths_runtime::BudgetClaim { + auths_runtime::BudgetClaim::Exhausted + } +} + #[derive(Default)] struct MemoryReceiptSink { decisions: Mutex>>, executions: Mutex>>, + decision_writes: AtomicUsize, + execution_writes: AtomicUsize, } impl MemoryReceiptSink { @@ -158,6 +215,50 @@ impl MemoryReceiptSink { ) } + /// Total `store_decision` / `store_execution` calls, including calls that + /// re-store an identical receipt identifier. + /// + /// [`Self::counts`] deduplicates by receipt identifier, which is exactly + /// what hid the pre-replay-check decision-receipt write: a replayed request + /// re-derives the same identifier, so the map size never grew. + fn write_calls(&self) -> (usize, usize) { + ( + self.decision_writes.load(Ordering::SeqCst), + self.execution_writes.load(Ordering::SeqCst), + ) + } + + /// Every stored decision receipt's decision class, in identifier order. + fn stored_decision_classes(&self) -> Vec { + self.decisions + .lock() + .expect("decision lock") + .values() + .map(|bytes| { + decode_attested_decision(bytes) + .expect("canonical attested decision receipt") + .receipt() + .decision() + }) + .collect() + } + + /// Every stored execution receipt's outcome, in identifier order. + #[cfg(test)] + fn stored_execution_outcomes(&self) -> Vec { + self.executions + .lock() + .expect("execution lock") + .values() + .map(|bytes| { + decode_attested_execution(bytes) + .expect("canonical attested execution receipt") + .receipt() + .outcome() + }) + .collect() + } + fn assert_canonical(&self) { for bytes in self.decisions.lock().expect("decision lock").values() { decode_attested_decision(bytes).expect("canonical attested decision receipt"); @@ -174,6 +275,7 @@ impl ReceiptSink for MemoryReceiptSink { id: auths_model::ReceiptId, bytes: Vec, ) -> Result<(), ReceiptStoreError> { + self.decision_writes.fetch_add(1, Ordering::SeqCst); self.decisions .lock() .map_err(|_| ReceiptStoreError)? @@ -186,6 +288,7 @@ impl ReceiptSink for MemoryReceiptSink { id: auths_model::ReceiptId, bytes: Vec, ) -> Result<(), ReceiptStoreError> { + self.execution_writes.fetch_add(1, Ordering::SeqCst); self.executions .lock() .map_err(|_| ReceiptStoreError)? @@ -431,6 +534,19 @@ impl DemoRuntimeSession { } } + /// Total `store_decision` / `store_execution` calls made in this session, + /// counting repeated writes of the same receipt identifier. + #[must_use] + pub fn receipt_write_calls(&self) -> (usize, usize) { + self.receipts.write_calls() + } + + /// Decision class of every decision receipt persisted in this session. + #[must_use] + pub fn stored_decision_classes(&self) -> Vec { + self.receipts.stored_decision_classes() + } + /// Submits the exact same proof-carrying action to this session. /// /// The first call executes once. Every later call is rejected by the @@ -668,9 +784,34 @@ fn demo_service_with_challenge( Arc, Arc, ) { + let executor = Arc::new(StaticReportExecutor::new(challenge)); + let (service, receipts) = demo_service_with_executor( + context, + channel_policy, + local_endpoint, + challenge, + executor.clone(), + Arc::new(NoBudgetLedger), + ); + (service, executor, receipts) +} + +/// Builds the real authorization service around a caller-supplied executor and +/// budget ledger. +/// +/// Every other demo constructor funnels through this so a test that needs a +/// pathological provider or an exhausted budget observes the identical runtime +/// wiring. +fn demo_service_with_executor( + context: TrustedContext, + channel_policy: ChannelBindingPolicy, + local_endpoint: Option<[u8; 32]>, + challenge: ChallengeNonce, + executor: Arc, + budgets: Arc, +) -> (Arc, Arc) { let kernel = AuthsKernel::new(context, demo_principal_methods(), demo_signature_suites()).unwrap(); - let executor = Arc::new(StaticReportExecutor::new(challenge)); let receipts = Arc::new(MemoryReceiptSink::default()); let service = McpAuthorizationService::new( McpServiceConfig::new( @@ -687,18 +828,18 @@ fn demo_service_with_challenge( Arc::new(FixedClock(DEMO_NOW)), Arc::new(FixedChallengeSource(challenge)), Arc::new(InMemoryChallengeLedger::new(64).unwrap()), - Arc::new(NoBudgetLedger), + budgets, ), McpExecutionDependencies::new( receipts.clone(), Arc::new(DemoReceiptAttestor::new()), Arc::new(kernel), - executor.clone(), + executor, ), ), ) .unwrap(); - (Arc::new(service), executor, receipts) + (Arc::new(service), receipts) } fn demo_principal_methods() -> Vec> { @@ -1111,3 +1252,220 @@ mod tests { assert_iroh_target_conformance().await; } } + +/// End-to-end evidence for contract §5A.3 and §5A.4. +/// +/// These drive the real [`McpAuthorizationService`] — the same kernel, replay +/// ledger, receipt attestor, and receipt sink as the shipped demos — because +/// both defects are properties of the *orchestration order* and of the +/// *signed artifact*, neither of which a unit test on a helper can observe. +#[cfg(test)] +mod signed_evidence_tests { + use super::{ + BoundaryFailureExecutor, ChannelBindingPolicy, DEMO_CHALLENGE, ExhaustedBudgetLedger, + McpToolExecutor, MemoryReceiptSink, PeerObservation, Permission, ProofExchangeService, + build_fixture, demo_service_with_executor, + }; + use auths_proof_exchange_model::{ + ActionResponse, ActionSubmission, ExchangeOutcome, RefusalKind, + }; + use auths_receipts::{DecisionClass, ExecutionOutcome}; + use auths_runtime::{NoBudgetLedger, ProviderBoundary}; + use std::sync::Arc; + + async fn submit_once( + executor: Arc, + ) -> (ActionResponse, Arc) { + let fixture = build_fixture(DEMO_CHALLENGE, None); + let (service, receipts) = demo_service_with_executor( + fixture.context, + ChannelBindingPolicy::None, + None, + DEMO_CHALLENGE, + executor, + Arc::new(NoBudgetLedger), + ); + let challenge = service + .issue_challenge(&PeerObservation::Unauthenticated) + .await + .expect("challenge"); + let request = + ActionSubmission::new(fixture.body, fixture.proof, &challenge).expect("submission"); + let response = service + .handle_action(&PeerObservation::Unauthenticated, &challenge, request) + .await; + receipts.assert_canonical(); + (response, receipts) + } + + /// §5A.3. A provider timeout is the canonical unknown-effect failure: the + /// exact command may already have applied. The runtime must still leave + /// durable signed evidence, and that evidence must not assert non-effect. + /// + /// Before the third `ExecutionOutcome` variant existed the runtime had only + /// two ways to answer and chose the least-bad one — mint nothing — so the + /// timeout produced no execution receipt at all and the assertion below on + /// receipt presence failed. + #[tokio::test] + async fn a_provider_timeout_signs_a_receipt_that_does_not_claim_the_effect_failed() { + let (response, receipts) = submit_once(Arc::new(BoundaryFailureExecutor::new( + ProviderBoundary::AfterEntry, + ))) + .await; + + let outcomes = receipts.stored_execution_outcomes(); + assert_eq!( + outcomes.len(), + 1, + "a possibly-applied effect must leave durable signed evidence, not silence" + ); + assert_ne!( + outcomes[0], + ExecutionOutcome::Failed, + "the signed receipt asserts the effect did not happen when it may have" + ); + assert_eq!(outcomes[0], ExecutionOutcome::Indeterminate); + + assert!( + matches!(response.outcome(), ExchangeOutcome::Indeterminate { .. }), + "a refusal is read by every caller as 'not applied'; got {:?}", + response.outcome() + ); + } + + /// The other half of §5A.3: a failure the adapter *proved* happened before + /// provider entry is still entitled to the non-effect assertion. The third + /// variant must not swallow the definite answer. + #[tokio::test] + async fn a_proven_pre_entry_failure_still_signs_a_non_effect_receipt() { + let (response, receipts) = submit_once(Arc::new(BoundaryFailureExecutor::new( + ProviderBoundary::BeforeEntry, + ))) + .await; + + assert_eq!( + receipts.stored_execution_outcomes(), + vec![ExecutionOutcome::Failed] + ); + assert!(matches!( + response.outcome(), + ExchangeOutcome::Refused { .. } + )); + } + + /// §5A.4. A replayed request is refused by the consumed-challenge gate. + /// No receipt asserting `Authorized` may be written for it, and the write + /// must not even be attempted — the sink counts calls, not distinct + /// identifiers, because a replay re-derives the same identifier and the + /// deduplicating map is exactly what hid this. + #[tokio::test] + async fn a_replayed_request_writes_no_authorization_receipt() { + let session = super::DemoRuntimeSession::new(*DEMO_CHALLENGE.as_bytes()).await; + let first = session.execute().await; + assert!(matches!( + first.response.outcome(), + ExchangeOutcome::Completed { .. } + )); + let writes_after_first = session.receipt_write_calls(); + + let replay = session.execute().await; + assert!(matches!( + replay.response.outcome(), + ExchangeOutcome::Refused { + kind: RefusalKind::ConsumedChallenge, + .. + } + )); + + assert_eq!( + session.receipt_write_calls(), + writes_after_first, + "the refused replay still wrote into the receipt sink" + ); + assert_eq!( + session.stored_decision_classes(), + vec![DecisionClass::Authorized], + "exactly one authorization was granted, so exactly one may be attested" + ); + } + + /// §5A.4 generalized — "audit the whole function, not just the replay + /// check". The budget gate is the *other* refusing check that ran after the + /// decision-receipt write. A budget-exhausted request must leave no + /// authorization receipt either. + #[tokio::test] + async fn a_budget_refused_request_writes_no_authorization_receipt() { + let fixture = build_fixture(DEMO_CHALLENGE, None); + let (service, receipts) = demo_service_with_executor( + fixture.context, + ChannelBindingPolicy::None, + None, + DEMO_CHALLENGE, + Arc::new(BoundaryFailureExecutor::new(ProviderBoundary::AfterEntry)), + Arc::new(ExhaustedBudgetLedger), + ); + let challenge = service + .issue_challenge(&PeerObservation::Unauthenticated) + .await + .expect("challenge"); + let request = + ActionSubmission::new(fixture.body, fixture.proof, &challenge).expect("submission"); + let response = service + .handle_action(&PeerObservation::Unauthenticated, &challenge, request) + .await; + + assert!(matches!( + response.outcome(), + ExchangeOutcome::Refused { .. } + )); + receipts.assert_canonical(); + assert_eq!( + receipts.write_calls(), + (0, 0), + "a request the runtime refused still wrote signed receipts" + ); + assert!(receipts.stored_decision_classes().is_empty()); + } + + /// A denied verification still records its honest `Denied` receipt. The + /// reordering must not silence the audit trail it was meant to make + /// truthful. + #[tokio::test] + async fn a_denied_verification_still_records_a_denied_receipt() { + let wrong = Permission::new( + auths_model::CapabilityId::parse("tools/call").expect("capability"), + auths_model::ResourceId::parse("mcp://reports/tools/delete_report").expect("resource"), + ); + let fixture = build_fixture(DEMO_CHALLENGE, Some(wrong)); + let (service, receipts) = demo_service_with_executor( + fixture.context, + ChannelBindingPolicy::None, + None, + DEMO_CHALLENGE, + Arc::new(BoundaryFailureExecutor::new(ProviderBoundary::AfterEntry)), + Arc::new(NoBudgetLedger), + ); + let challenge = service + .issue_challenge(&PeerObservation::Unauthenticated) + .await + .expect("challenge"); + let request = + ActionSubmission::new(fixture.body, fixture.proof, &challenge).expect("submission"); + let response = service + .handle_action(&PeerObservation::Unauthenticated, &challenge, request) + .await; + + assert!(matches!( + response.outcome(), + ExchangeOutcome::Refused { + kind: RefusalKind::AuthsVerdict, + .. + } + )); + receipts.assert_canonical(); + assert_eq!( + receipts.stored_decision_classes(), + vec![DecisionClass::Denied] + ); + } +} diff --git a/docs/target-state/v1-api-contract.md b/docs/target-state/v1-api-contract.md index 86722844..b3d1bfae 100644 --- a/docs/target-state/v1-api-contract.md +++ b/docs/target-state/v1-api-contract.md @@ -248,9 +248,13 @@ indeterminate member. So for a provider timeout, the reference runtime signs a d asserting **Failed** for an effect that may have applied — and §5's error-model fixes cannot repair it, because the evidence artifact itself has no way to say "possible". -**Disposition:** adding a third variant changes signed bytes and is therefore a **protocol change**, -out of scope under §10 without separate review. Flagged, scoped, not auto-fixed. This is the highest -priority item for the review that follows this wave. +**Disposition:** **DONE.** Superseded by §10A decision 11.8, which authorizes the protocol change. +`ExecutionOutcome` gained `Indeterminate` (wire tag `2`) and `ExchangeOutcome` gained +`Indeterminate { verdict, message }` (outcome code `2`). Any provider failure the adapter cannot +place before provider entry now mints a signed `Indeterminate` execution receipt instead of minting +nothing, and the exchange response is no longer projected as a refusal. Both tags are **additive**: +`cargo xtask wire` reports 516 byte-stable golden vectors and `cargo xtask product-fixtures` reports +stable, so **no canonical fixture required regeneration** and none was performed. ### 5A.4 A signed "Authorized" receipt is written before the replay check runs @@ -258,7 +262,10 @@ priority item for the review that follows this wave. replay check. Consequences: audit records assert authorization for requests that are then refused, and an attacker gets unbounded write amplification into the receipt sink. -**Disposition:** in scope. Reorder so the receipt is written only after every check that can refuse. +**Disposition:** **DONE.** The `Authorized` decision receipt is now written after the last refusing +gate — replay claim *and* budget claim, both of which ran after the write. The pure +action-identifier check was hoisted above the replay claim so a verified action the runtime cannot +lease no longer consumes the caller's challenge. ## 6. Public surface @@ -521,7 +528,7 @@ These change the contract's content and cannot be defaulted by an implementer. (b) remove it from `demos/open-production-reference` and label it a non-production sandbox; (c) keep it, and publicly scope the security claims to exclude the reference deployment. Option (c) is not recommended — the deployment README is what operators will follow. -8. **`ExecutionOutcome` third variant (§5A.3).** Adding `Indeterminate` changes signed receipt bytes. - That is a protocol change requiring review, but without it no honest receipt can be written for a - provider timeout. Recommended: accept the protocol change before 1.0, since after 1.0 it becomes - permanently harder. +8. ~~**`ExecutionOutcome` third variant (§5A.3).**~~ **CLOSED** by §10A decision 11.8 and implemented. + `Indeterminate` is wire tag `2` on the execution receipt and outcome code `2` on the exchange + response. Both assignments are additive, so no existing signed bytes changed and no canonical + fixture was regenerated. diff --git a/exchange/crates/auths-proof-exchange-codec/src/lib.rs b/exchange/crates/auths-proof-exchange-codec/src/lib.rs index 617ac778..4f42a043 100644 --- a/exchange/crates/auths-proof-exchange-codec/src/lib.rs +++ b/exchange/crates/auths-proof-exchange-codec/src/lib.rs @@ -291,6 +291,12 @@ pub fn encode_response(message: &ActionResponse) -> Vec { verdict, message, } => (1, &[][..], Some(*kind), verdict.as_ref(), message.as_str()), + // Decision 11.8 (contract §10A / §5A.3): additive outcome code. A + // possibly-applied effect carries no refusal kind, because it is not a + // refusal, and no result, because none was observed. + ExchangeOutcome::Indeterminate { verdict, message } => { + (2, &[][..], None, verdict.as_ref(), message.as_str()) + } }; encoder @@ -413,6 +419,14 @@ pub fn decode_response(input: &[u8]) -> Result { }; ExchangeOutcome::refused(kind, verdict, message)? } + 2 if result.is_empty() && refusal.is_none() => { + let verdict = match decision { + Some(decision) => Some(VerdictSummary::new(decision, reasons)?), + None if reasons.is_empty() => None, + None => return Err(CodecError::Malformed), + }; + ExchangeOutcome::indeterminate(verdict, message)? + } _ => return Err(CodecError::Malformed), }; let response = ActionResponse::new( @@ -621,6 +635,67 @@ mod tests { assert_eq!(decode_response(&encoded).unwrap(), response); } + /// Decision 11.8 (contract §10A / §5A.3). An unknown-effect response must + /// survive the wire as itself. If it decoded as a refusal the caller would + /// retry a possibly-applied effect, which is the exact failure the third + /// member exists to prevent. + #[test] + fn an_indeterminate_response_round_trips_and_never_decodes_as_a_refusal() { + let verdict = + VerdictSummary::new(VerdictDecision::Authorized, vec!["authorized".into()]).unwrap(); + let response = ActionResponse::new( + None, + ExchangeOutcome::indeterminate( + Some(verdict), + "effect possible, reconcile before retry: provider call timed out", + ) + .unwrap(), + ExchangeMetrics::new(12, 0), + ); + let encoded = encode_response(&response); + let decoded = decode_response(&encoded).unwrap(); + assert_eq!(decoded, response); + assert!(matches!( + decoded.outcome(), + ExchangeOutcome::Indeterminate { .. } + )); + + // Same shape without a verdict, so the optional field is exercised. + let bare = ActionResponse::new( + None, + ExchangeOutcome::indeterminate(None, "unknown effect").unwrap(), + ExchangeMetrics::new(0, 0), + ); + assert_eq!(decode_response(&encode_response(&bare)).unwrap(), bare); + } + + /// An indeterminate response carries no refusal kind and no result. A + /// hand-built encoding that smuggles either in must fail closed rather than + /// decode into a member whose fields the encoder never wrote. + #[test] + fn an_indeterminate_response_rejects_a_refusal_kind_or_a_result() { + let refused = ActionResponse::new( + None, + ExchangeOutcome::refused(RefusalKind::ApplicationPolicy, None, "refused").unwrap(), + ExchangeMetrics::new(0, 0), + ); + let mut smuggled = encode_response(&refused); + // Flip only the outcome code 1 -> 2, leaving the refusal kind in place. + let baseline = encode_response(&ActionResponse::new( + None, + ExchangeOutcome::indeterminate(None, "refused").unwrap(), + ExchangeMetrics::new(0, 0), + )); + let index = smuggled + .iter() + .zip(&baseline) + .position(|(left, right)| left != right) + .expect("outcome code differs"); + assert_eq!(smuggled[index], 1); + smuggled[index] = 2; + assert!(decode_response(&smuggled).is_err()); + } + #[test] fn capabilities_round_trip_without_downgrade_aliases() { let capabilities = ExchangeCapabilities::new( diff --git a/exchange/crates/auths-proof-exchange-model/src/lib.rs b/exchange/crates/auths-proof-exchange-model/src/lib.rs index 7edccaae..90b24140 100644 --- a/exchange/crates/auths-proof-exchange-model/src/lib.rs +++ b/exchange/crates/auths-proof-exchange-model/src/lib.rs @@ -541,6 +541,15 @@ pub enum RefusalKind { ConsumedChallenge, } +/// What the responder is able to assert about the requested effect. +/// +/// The three members are mutually exclusive and exhaustive over the effect +/// axis. `Completed` asserts the effect applied; `Refused` asserts it did +/// **not**; `Indeterminate` asserts the responder cannot prove either. +/// +/// The distinction matters at the wire, not just in prose: every caller reads a +/// refusal as "nothing happened" and retries. A responder that projected a +/// possibly-applied effect as `Refused` was inviting a duplicate side effect. #[derive(Clone, Debug, Eq, PartialEq)] pub enum ExchangeOutcome { Completed { @@ -551,6 +560,13 @@ pub enum ExchangeOutcome { verdict: Option, message: String, }, + /// The exact requested effect may or may not have been applied. The caller + /// must reconcile with the provider before retrying; it must not treat this + /// as a refusal. + Indeterminate { + verdict: Option, + message: String, + }, } impl ExchangeOutcome { @@ -590,6 +606,29 @@ impl ExchangeOutcome { message, }) } + + /// Constructs an unknown-effect result with a bounded, display-safe message. + /// + /// Use this — never [`Self::refused`] — when the responder cannot prove the + /// exact effect did not apply. + /// + /// # Errors + /// + /// Returns [`ModelError::InvalidMessage`] when the message is empty, + /// oversized, or contains control characters. + pub fn indeterminate( + verdict: Option, + message: impl Into, + ) -> Result { + let message = message.into(); + if message.is_empty() + || message.len() > MAX_MESSAGE_BYTES + || message.bytes().any(|byte| byte.is_ascii_control()) + { + return Err(ModelError::InvalidMessage); + } + Ok(Self::Indeterminate { verdict, message }) + } } #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] diff --git a/exchange/spec/v1/protocol.md b/exchange/spec/v1/protocol.md index b898f64a..717a901f 100644 --- a/exchange/spec/v1/protocol.md +++ b/exchange/spec/v1/protocol.md @@ -201,10 +201,21 @@ pub struct ActionResponse { - application refusal after an Auths verdict; - malformed or oversized exchange input; - expired, unknown, or consumed challenge; -- transport-policy rejection. - -It must not manufacture `Authorized`, `Denied`, or `Indeterminate` when the -Auths verifier did not run. When a verifier did run, the application may +- transport-policy rejection; +- an **indeterminate** result, where the responder cannot prove whether the + exact requested effect was applied. + +The three members partition the effect axis and are wire-tagged `0` +(`Completed`), `1` (`Refused`), and `2` (`Indeterminate`). `Completed` asserts +the effect applied; `Refused` asserts it did not; `Indeterminate` asserts the +responder can prove neither. A responder must not project a possibly-applied +effect as `Refused`: every caller reads a refusal as "nothing happened" and +retries, so doing so invites a duplicate side effect. `Indeterminate` carries +no refusal kind — it is not a refusal — and no result, because none was +observed. A caller receiving it must reconcile with the provider before retry. + +`ExchangeOutcome` must not manufacture `Authorized`, `Denied`, or +`Indeterminate` verdicts when the Auths verifier did not run. When a verifier did run, the application may return a safe projection of its stable decision and reason codes. The submission repeats the exact challenge, Auths protocol major, profile ID, diff --git a/product/receipts/auths-receipts/src/lib.rs b/product/receipts/auths-receipts/src/lib.rs index 7d5c1ab9..c7b52113 100644 --- a/product/receipts/auths-receipts/src/lib.rs +++ b/product/receipts/auths-receipts/src/lib.rs @@ -295,12 +295,35 @@ impl DecisionReceipt { } /// Execution outcome recorded separately from authority validity. +/// +/// The three variants are the receipt projection of the error model's effect +/// axis (`auths_errors::EffectState`), and the mapping is total: +/// +/// | `ExecutionOutcome` | `EffectState` | The receipt asserts | +/// | --- | --- | --- | +/// | [`Self::Succeeded`] | `applied` | the exact effect happened | +/// | [`Self::Failed`] | `not-applied` | the exact effect did **not** happen | +/// | [`Self::Indeterminate`] | `possible` | the enforcement point cannot prove either | +/// +/// `Failed` is an assertion of non-effect, not a description of an error. An +/// enforcement point that cannot place a failure before provider entry must +/// record [`Self::Indeterminate`]; recording `Failed` there signs a false +/// non-effect proof. The name matches [`DecisionClass::Indeterminate`], which +/// this crate already uses for the same "a required fact is unavailable" +/// meaning, rather than mirroring the error model's `Possible`, so one receipt +/// does not carry two vocabularies for one epistemic state. +/// +/// `Indeterminate` is not a terminal answer. It is a durable, signed +/// instruction to reconcile with the provider before retrying. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ExecutionOutcome { - /// Command completed successfully. + /// Command completed successfully; the exact effect was applied. Succeeded, - /// Authorized command failed during execution. + /// Authorized command provably failed before the effect could apply. Failed, + /// The exact effect may or may not have been applied and the enforcement + /// point cannot prove which. Reconcile before retrying. + Indeterminate, } /// Canonical execution record. @@ -633,6 +656,9 @@ pub fn encode_execution(receipt: &ExecutionReceipt) -> Result, ReceiptEr .u8(match receipt.outcome { ExecutionOutcome::Succeeded => 0, ExecutionOutcome::Failed => 1, + // Decision 11.8 (contract §10A / §5A.3): additive tag. Existing + // `Succeeded`/`Failed` receipts keep their exact signed bytes. + ExecutionOutcome::Indeterminate => 2, }) .map_err(|_| ReceiptError::Malformed)?; key(&mut encoder, 5)?; @@ -900,6 +926,7 @@ pub fn decode_execution(input: &[u8]) -> Result let outcome = match decoder.u8().map_err(|_| ReceiptError::Malformed)? { 0 => ExecutionOutcome::Succeeded, 1 => ExecutionOutcome::Failed, + 2 => ExecutionOutcome::Indeterminate, _ => return Err(ReceiptError::Malformed), }; key_decode(&mut decoder, 5)?; @@ -1704,6 +1731,77 @@ mod tests { ); } + /// Decision 11.8 (contract §10A / §5A.3). The third outcome must be a + /// first-class signed value: it round-trips, it is canonical, and it binds + /// a distinct receipt identifier from the same receipt recorded as + /// `Failed`. Without the last property an auditor could not tell a proven + /// non-effect from an unknown one. + #[test] + fn the_indeterminate_outcome_is_a_distinct_canonical_signed_value() { + let decision = decision_receipt_id(&receipt()).unwrap(); + let of = |outcome| { + ExecutionReceipt::new( + decision, + Digest::new([6; 32]), + Digest::new([7; 32]), + outcome, + None, + Timestamp::new(11), + ) + }; + let unknown = of(ExecutionOutcome::Indeterminate); + let encoded = encode_execution(&unknown).unwrap(); + assert_eq!(decode_execution(&encoded).unwrap(), unknown); + let id = execution_receipt_id(&unknown).unwrap(); + assert_eq!(verify_execution_bytes(&encoded, id).unwrap(), unknown); + + for other in [ExecutionOutcome::Succeeded, ExecutionOutcome::Failed] { + assert_ne!(encode_execution(&of(other)).unwrap(), encoded); + assert_ne!(execution_receipt_id(&of(other)).unwrap(), id); + } + } + + /// The three outcomes occupy wire tags 0, 1, and 2 and nothing else. This + /// pins both halves: the additive tag assignment (so existing `Succeeded` + /// and `Failed` receipts keep byte-identical signed bytes), and the + /// fail-closed rejection of any fourth tag a future or hostile encoder + /// might emit. + #[test] + fn execution_outcome_wire_tags_are_exactly_zero_one_two() { + let decision = decision_receipt_id(&receipt()).unwrap(); + let build = |outcome| { + encode_execution(&ExecutionReceipt::new( + decision, + Digest::new([6; 32]), + Digest::new([7; 32]), + outcome, + None, + Timestamp::new(11), + )) + .unwrap() + }; + let succeeded = build(ExecutionOutcome::Succeeded); + // Key 4 is the outcome; the encodings differ in exactly that one byte. + let tag_index = succeeded + .iter() + .zip(build(ExecutionOutcome::Failed)) + .position(|(left, right)| *left != right) + .expect("outcome byte"); + assert_eq!(succeeded[tag_index], 0); + assert_eq!(build(ExecutionOutcome::Failed)[tag_index], 1); + assert_eq!(build(ExecutionOutcome::Indeterminate)[tag_index], 2); + + for unassigned in [3_u8, 4, 255] { + let mut hostile = succeeded.clone(); + hostile[tag_index] = unassigned; + assert_eq!( + decode_execution(&hostile), + Err(ReceiptError::Malformed), + "wire tag {unassigned} must fail closed" + ); + } + } + #[test] fn application_execution_receipts_bind_replay_plan_command_and_result() { let plan = Digest::new([4; 32]); diff --git a/product/runtime/auths-runtime/src/lib.rs b/product/runtime/auths-runtime/src/lib.rs index bfacee8c..51f75818 100644 --- a/product/runtime/auths-runtime/src/lib.rs +++ b/product/runtime/auths-runtime/src/lib.rs @@ -458,6 +458,21 @@ const fn may_sign_non_effect_receipt(failure: &ToolExecutionFailure) -> bool { matches!(failure.effect(), EffectState::NotApplied) } +/// Selects the signed execution outcome a failed execution is entitled to +/// record (decision 11.8, contract §10A / §5A.3). +/// +/// This is the single place the adapter's proven provider boundary becomes a +/// durable assertion, and it is total: `Failed` for a proven pre-entry failure, +/// `Indeterminate` for everything else. `Succeeded` is unreachable here because +/// the executor returned an error. +const fn receipt_outcome_for(failure: &ToolExecutionFailure) -> ReceiptExecutionOutcome { + if may_sign_non_effect_receipt(failure) { + ReceiptExecutionOutcome::Failed + } else { + ReceiptExecutionOutcome::Indeterminate + } +} + /// Prefix that marks a refusal whose exact effect may already have been applied. pub const OUTCOME_UNKNOWN_PREFIX: &str = "effect possible, reconcile before retry: "; @@ -946,17 +961,18 @@ impl ProofExchangeService for McpAuthorizationService { verification_micros, ); } - let Some(decision_receipt_id) = self.record_decision( - request.proof(), - &canonical, - &request_context, - &VerificationOutcome::Authorized(Box::new(verified.clone())), - now, - ) else { + // Contract §5A.4. Every refusing gate runs BEFORE the authorization + // receipt is signed. A durable receipt asserting `Authorized` for a + // request the runtime then refuses is a false audit record, and writing + // one per attempt hands an attacker unbounded write amplification into + // the receipt sink. The pure action-identifier check is hoisted above + // the replay claim so a verified action the runtime cannot lease does + // not consume the caller's challenge either. + let Some(action_id) = verified.action_ids().first().copied() else { return Self::refusal( - RefusalKind::ApplicationPolicy, + RefusalKind::AuthsVerdict, None, - "receipt store unavailable", + "verified action has no action identifier", verification_micros, ); }; @@ -985,14 +1001,6 @@ impl ProofExchangeService for McpAuthorizationService { ); } } - let Some(action_id) = verified.action_ids().first().copied() else { - return Self::refusal( - RefusalKind::AuthsVerdict, - None, - "verified action has no action identifier", - verification_micros, - ); - }; match self .budgets .claim(action_id, verified.canonical_action().requested_budget()) @@ -1015,6 +1023,22 @@ impl ProofExchangeService for McpAuthorizationService { ); } } + // Last refusing gate has passed. Only now is `Authorized` a claim this + // runtime can stand behind, so only now is it signed. + let Some(decision_receipt_id) = self.record_decision( + request.proof(), + &canonical, + &request_context, + &VerificationOutcome::Authorized(Box::new(verified.clone())), + now, + ) else { + return Self::refusal( + RefusalKind::ApplicationPolicy, + None, + "receipt store unavailable", + verification_micros, + ); + }; let lease = ExecutionLease { challenge: challenge.challenge(), action: action_id, @@ -1046,34 +1070,71 @@ impl ProofExchangeService for McpAuthorizationService { }, micros(execution_started.elapsed()), ); - // A signed execution receipt has exactly two outcomes, - // `Succeeded` and `Failed` (auths_receipts::ExecutionOutcome). - // `Failed` asserts the exact effect did not happen. When the - // adapter cannot place the failure before provider entry, that - // assertion is unprovable, so no execution receipt is minted at - // all: the caller is told the outcome is unknown and must - // reconcile rather than being handed a false non-effect proof. - if !applied_is_possible { - let _ = self.record_execution( - decision_receipt_id, - lease_digest, - &verified, - ReceiptExecutionOutcome::Failed, + // Decision 11.8 (contract §10A / §5A.3). The provider boundary + // the adapter proved is projected directly onto the signed + // receipt: `Failed` asserts the exact effect did not happen and + // is reserved for a proven pre-entry failure; everything the + // adapter cannot place on the near side of provider entry is + // recorded as `Indeterminate`. Both are durable evidence — an + // unknown effect is exactly the case an auditor most needs + // signed, so silence is not an option either. + let stored = self.record_execution( + decision_receipt_id, + lease_digest, + &verified, + receipt_outcome_for(&failure), + None, + ); + let verdict = Some(verdict_summary(&VerificationOutcome::Authorized(Box::new( + verified, + )))); + if !stored { + // Losing the receipt does not make the effect definite. + // Only a proven pre-entry failure may still be refused. + let unstored = if applied_is_possible { + ExchangeOutcome::indeterminate( + verdict, + "effect possible, reconcile before retry: receipt store unavailable", + ) + } else { + ExchangeOutcome::refused( + RefusalKind::ApplicationPolicy, + verdict, + "receipt store unavailable", + ) + } + .expect("static runtime message is bounded"); + return ActionResponse::new( None, + unstored, + ExchangeMetrics::new(verification_micros, 0), ); } - let message = if applied_is_possible { - alloc_unknown_message(failure.summary()) + // A refusal is read by every caller as "nothing happened", so a + // possibly-applied effect must never be projected as one. + let outcome = if applied_is_possible { + ExchangeOutcome::indeterminate( + verdict, + alloc_unknown_message(failure.summary()), + ) } else { - failure.summary().to_owned() - }; - return Self::refusal( - RefusalKind::ApplicationPolicy, - Some(verdict_summary(&VerificationOutcome::Authorized(Box::new( - verified, - )))), - &message, - verification_micros, + ExchangeOutcome::refused( + RefusalKind::ApplicationPolicy, + verdict, + failure.summary(), + ) + } + // INVARIANT: `ToolExecutionFailure` normalizes every adapter + // summary through `bounded_summary`, which guarantees a + // non-empty, control-character-free string of at most + // `MAX_FAILURE_SUMMARY_BYTES`, and `OUTCOME_UNKNOWN_PREFIX` is + // printable ASCII. Proved by + // `an_adapter_summary_can_never_panic_the_outcome_projection`. + .expect("bounded adapter summary is a valid exchange message"); + return ActionResponse::new( + None, + outcome, + ExchangeMetrics::new(verification_micros, 0), ); } }; @@ -1083,6 +1144,12 @@ impl ProofExchangeService for McpAuthorizationService { OperationalReasonCode::None, micros(execution_started.elapsed()), ); + // The two failures below happen AFTER the provider applied the effect. + // Projecting either as a refusal would tell the caller nothing happened + // and invite a duplicate side effect, so both are unknown-effect + // results even though this runtime knows the effect applied: there is + // no "applied but undeliverable" member, and `Indeterminate` is the + // only projection that does not assert a falsehood. if !self.record_execution( decision_receipt_id, lease_digest, @@ -1090,24 +1157,26 @@ impl ProofExchangeService for McpAuthorizationService { ReceiptExecutionOutcome::Succeeded, Some(&result), ) { - return Self::refusal( - RefusalKind::ApplicationPolicy, + return ActionResponse::new( None, - "receipt store unavailable", - verification_micros, + ExchangeOutcome::indeterminate( + None, + "effect applied; receipt store unavailable, reconcile before retry", + ) + .expect("static runtime message is bounded"), + ExchangeMetrics::new(verification_micros, 0), ); } let execution_micros = micros(execution_started.elapsed()); let request_id = request_id(challenge.challenge(), request.body(), request.proof()); let outcome = ExchangeOutcome::completed(result).unwrap_or_else(|_| { - ExchangeOutcome::refused( - RefusalKind::ApplicationPolicy, + ExchangeOutcome::indeterminate( Some(verdict_summary(&VerificationOutcome::Authorized(Box::new( verified, )))), - "tool result exceeds exchange limit", + "effect applied; tool result exceeds exchange limit, reconcile before retry", ) - .expect("static refusal is bounded") + .expect("static runtime message is bounded") }); ActionResponse::new( Some(request_id), @@ -1457,7 +1526,8 @@ mod channel_policy_tests { mod provider_boundary_tests { use super::{ EffectState, MAX_FAILURE_SUMMARY_BYTES, OUTCOME_UNKNOWN_PREFIX, ProviderBoundary, - ToolExecutionFailure, alloc_unknown_message, may_sign_non_effect_receipt, + ReceiptExecutionOutcome, ToolExecutionFailure, alloc_unknown_message, + may_sign_non_effect_receipt, receipt_outcome_for, }; use auths_proof_exchange_model::{ExchangeOutcome, RefusalKind}; @@ -1508,9 +1578,10 @@ mod provider_boundary_tests { /// The adapter-supplied summary used to flow straight into /// `ExchangeOutcome::refused(..).expect("static runtime refusal is bounded")`, /// so an empty, oversized, or control-character summary panicked the - /// authorization service. + /// authorization service. The unknown-effect branch now carries the same + /// obligation through `ExchangeOutcome::indeterminate`. #[test] - fn an_adapter_summary_can_never_panic_the_refusal_projection() { + fn an_adapter_summary_can_never_panic_the_outcome_projection() { for raw in [ String::new(), "\u{0}\u{7}\n\r\t".to_owned(), @@ -1520,16 +1591,52 @@ mod provider_boundary_tests { ToolExecutionFailure::before_provider_entry(&raw), ToolExecutionFailure::after_provider_entry(&raw), ] { - let message = if may_sign_non_effect_receipt(&failure) { - failure.summary().to_owned() - } else { + let possible = !may_sign_non_effect_receipt(&failure); + let message = if possible { alloc_unknown_message(failure.summary()) + } else { + failure.summary().to_owned() }; assert!(!message.is_empty()); assert!(message.len() <= OUTCOME_UNKNOWN_PREFIX.len() + MAX_FAILURE_SUMMARY_BYTES); assert!(!message.bytes().any(|byte| byte.is_ascii_control())); - assert!( - ExchangeOutcome::refused(RefusalKind::ApplicationPolicy, None, message).is_ok() + let projected = if possible { + ExchangeOutcome::indeterminate(None, message) + } else { + ExchangeOutcome::refused(RefusalKind::ApplicationPolicy, None, message) + }; + assert!(projected.is_ok()); + } + } + } + + /// The production mapping from proven provider boundary to signed receipt + /// outcome, exercised directly. A timeout must never mint `Failed`, and a + /// proven pre-entry failure must never be downgraded to `Indeterminate` — + /// the third variant is not a place to hide a definite answer. + #[test] + fn the_provider_boundary_selects_the_signed_receipt_outcome() { + assert_eq!( + receipt_outcome_for(&ToolExecutionFailure::after_provider_entry( + "provider call timed out" + )), + ReceiptExecutionOutcome::Indeterminate + ); + assert_eq!( + receipt_outcome_for(&ToolExecutionFailure::before_provider_entry( + "connection refused before any request byte" + )), + ReceiptExecutionOutcome::Failed + ); + // No failure of any wording may be signed as a success. + for summary in ["", "ok", "succeeded", "\u{0}"] { + for failure in [ + ToolExecutionFailure::before_provider_entry(summary), + ToolExecutionFailure::after_provider_entry(summary), + ] { + assert_ne!( + receipt_outcome_for(&failure), + ReceiptExecutionOutcome::Succeeded ); } } From d82d57f9d60efd1c1b34cf1e98631f3edc9d10d3 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sat, 15 Aug 2026 14:51:14 +0100 Subject: [PATCH 05/61] product: rebuild auths-node on the verified kernel (UNSIGNED) UNSIGNED: auths-sign requires an interactive passphrase unavailable here. auths-node was a second, independent authorization system. Its Cargo.toml depended on no kernel crate, it hand-rolled narrowing over 4 dimensions where the kernel checks 11, and `create()` minted a ROOT authority (`parent: None`) whose subject was `digest(request.identity())` -- caller- supplied bytes with no authentication anywhere in the request path. ACCEPTANCE CRITERION, RED FIRST. tests/kernel_differential.rs feeds every `(proof, canonical action, trusted context)` triple in the canonical corpus (`core/fixtures/v1`, via `auths_testkit::corpus()`) to auths-node's public decision path and to `auths_verifier::verify`, and asserts the decisions are identical -- including the exact `DenialReason` and the exact `Requirement`, not just the three-valued verdict. Nothing is translated: both sides get the same bytes, the same context, the same registries, and the same clock. First run: "auths-node disagreed with the kernel on 103 of 103 canonical corpus inputs", every one of them `core.malformed-input` against a real kernel decision. The node could not parse the protocol it claimed to enforce. Final run: 103 of 103 agree. WHAT CHANGED * src/sandbox.rs deleted. src/kernel.rs replaces it. The decision is `auths_verifier::verify` and nothing else; the node supplies context, clock, stateful replay budget, effect, and receipt. * `create()` and `delegate()` now refuse with `core.unauthenticated-principal`. This is a finding, not a design: `ProductionRequest.identity` is unauthenticated `Vec`, `api.rs` performs no client authentication, and the reference ingress requests no client certificate. There is no authentication at that call site to require instead, so the node refuses to mint rather than mint from a self-asserted identity. Authority in V1 originates from a trust anchor's signature and arrives inside the proof. * `[verification] trusted_context_path` is now a mandatory config section. A node that cannot state its trust anchors cannot decide anything. * src/bin/auths-sandbox-request.rs deleted: it encoded the hand-rolled scope that no longer exists. * The replay claim is keyed on (proof digest, action digest) and allows one effect, strictly stronger than the previous `max_uses` scope field. * Receipt disclosure returns one failure for both unknown and unauthorized so the endpoint is not an existence oracle. WIRE CODES. All 8 unregistered codes are gone (`authority.denied`, `authority.indeterminate`, `profile.disabled`, `workflow.unknown`, `receipt.unknown`, `receipt.disclosure-denied`, `provider.outcome-unknown`, `verification.rejected`). Three codes were added to auths-errors and regenerated through `cargo xtask error-registry --update`: `core.authorization-denied`, `core.authorization-indeterminate`, `core.unauthenticated-principal`. `every_wire_code_is_registered` and `every_wire_code_carries_a_registered_effect` hold it. BEHAVIOUR CHANGES, NAMED. (1) `RuntimeFailure::Indeterminate` claimed `EffectState::Possible` with `Reconcile`; the authorization variant that replaces it claims `NotApplied` with `Backoff`, because an authorization decision is reached strictly before provider entry. The post-entry variant `ProviderOutcomeUnknown` keeps `Possible`/`Reconcile` unchanged. (2) `ReceiptSummary.outcome: String = "succeeded"` became `effect: EffectState`, the one Rust-owned vocabulary. No canonical fixture was regenerated. `cargo xtask wire` reports 516 golden vector files byte-stable and `cargo xtask product-fixtures` reports stable. `product/fixtures/v1/errors/manifest.json` is the generated projection of the error registry and moved only through the sanctioned update path. --- Cargo.lock | 15 + architecture/dependency-graph.dot | 15 + architecture/dependency-graph.json | 180 ++++ .../python/python/auths/_error_registry.py | 69 ++ .../src/generated/error-registry.ts | 69 ++ compliance.toml | 24 +- demos/open-production-reference/Dockerfile | 1 - .../config/local.toml | 6 + .../config/production.example.toml | 6 + .../deploy/kubernetes/base/config-map.yaml | 2 + .../overlays/aws-kms/config-map-patch.yaml | 2 + docs/reference/error-codes.md | 3 + product/errors/auths-errors/src/lib.rs | 39 + product/errors/v1/registry.json | 69 ++ product/fixtures/v1/errors/manifest.json | 75 ++ product/runtime/auths-node/Cargo.toml | 15 + product/runtime/auths-node/src/api.rs | 15 +- .../src/bin/auths-sandbox-request.rs | 56 -- product/runtime/auths-node/src/config.rs | 54 ++ product/runtime/auths-node/src/kernel.rs | 913 ++++++++++++++++++ product/runtime/auths-node/src/lib.rs | 4 +- product/runtime/auths-node/src/main.rs | 40 +- product/runtime/auths-node/src/profiles.rs | 150 ++- product/runtime/auths-node/src/sandbox.rs | 801 --------------- .../runtime/auths-node/src/sandbox_store.rs | 10 +- .../auths-node/tests/kernel_differential.rs | 232 +++++ 26 files changed, 1957 insertions(+), 908 deletions(-) delete mode 100644 product/runtime/auths-node/src/bin/auths-sandbox-request.rs create mode 100644 product/runtime/auths-node/src/kernel.rs delete mode 100644 product/runtime/auths-node/src/sandbox.rs create mode 100644 product/runtime/auths-node/tests/kernel_differential.rs diff --git a/Cargo.lock b/Cargo.lock index b6acb7c9..1b68c0e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -839,9 +839,24 @@ dependencies = [ name = "auths-node" version = "1.0.0-rc.1" dependencies = [ + "auths-codec", + "auths-did-keri", + "auths-did-key", + "auths-did-web", + "auths-errors", + "auths-hsm-attested", + "auths-model", "auths-operations", "auths-operations-otel", + "auths-ports", "auths-production-client", + "auths-raw-key", + "auths-registries", + "auths-signature", + "auths-spiffe-x509", + "auths-testkit", + "auths-verifier", + "auths-webauthn", "axum", "base64ct", "ed25519-dalek 2.2.0", diff --git a/architecture/dependency-graph.dot b/architecture/dependency-graph.dot index 7a869e73..57155116 100644 --- a/architecture/dependency-graph.dot +++ b/architecture/dependency-graph.dot @@ -288,9 +288,24 @@ digraph auths_architecture { "auths-live-service" -> "auths-proof-wasm" [label="normal"]; "auths-mcp-demo" -> "auths-apps-testkit" [label="normal"]; "auths-mcp-demo" -> "auths-proof-exchange-model" [label="normal"]; + "auths-node" -> "auths-codec" [label="normal"]; + "auths-node" -> "auths-did-keri" [label="normal"]; + "auths-node" -> "auths-did-key" [label="normal"]; + "auths-node" -> "auths-did-web" [label="dev"]; + "auths-node" -> "auths-errors" [label="normal"]; + "auths-node" -> "auths-hsm-attested" [label="dev"]; + "auths-node" -> "auths-model" [label="normal"]; "auths-node" -> "auths-operations" [label="normal"]; "auths-node" -> "auths-operations-otel" [label="normal"]; + "auths-node" -> "auths-ports" [label="normal"]; "auths-node" -> "auths-production-client" [label="normal"]; + "auths-node" -> "auths-raw-key" [label="normal"]; + "auths-node" -> "auths-registries" [label="normal"]; + "auths-node" -> "auths-signature" [label="normal"]; + "auths-node" -> "auths-spiffe-x509" [label="dev"]; + "auths-node" -> "auths-testkit" [label="dev"]; + "auths-node" -> "auths-verifier" [label="normal"]; + "auths-node" -> "auths-webauthn" [label="dev"]; "auths-opentofu" -> "auths-bounded-policy" [label="normal"]; "auths-opentofu" -> "auths-lifecycle" [label="normal"]; "auths-opentofu" -> "auths-model" [label="normal"]; diff --git a/architecture/dependency-graph.json b/architecture/dependency-graph.json index e16ddb82..f4864322 100644 --- a/architecture/dependency-graph.json +++ b/architecture/dependency-graph.json @@ -5053,6 +5053,90 @@ "pkcs8" ] }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-codec", + "target_layer": "core", + "scope": "internal", + "kind": "normal", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-did-keri", + "target_layer": "core", + "scope": "internal", + "kind": "normal", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-did-key", + "target_layer": "core", + "scope": "internal", + "kind": "normal", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-did-web", + "target_layer": "core", + "scope": "internal", + "kind": "dev", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-errors", + "target_layer": "product", + "scope": "internal", + "kind": "normal", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-hsm-attested", + "target_layer": "core", + "scope": "internal", + "kind": "dev", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-model", + "target_layer": "core", + "scope": "internal", + "kind": "normal", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, { "source": "auths-node", "source_layer": "product", @@ -5077,6 +5161,18 @@ "default_features": true, "features": [] }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-ports", + "target_layer": "core", + "scope": "internal", + "kind": "normal", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, { "source": "auths-node", "source_layer": "product", @@ -5089,6 +5185,90 @@ "default_features": true, "features": [] }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-raw-key", + "target_layer": "core", + "scope": "internal", + "kind": "normal", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-registries", + "target_layer": "core", + "scope": "internal", + "kind": "normal", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-signature", + "target_layer": "core", + "scope": "internal", + "kind": "normal", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-spiffe-x509", + "target_layer": "core", + "scope": "internal", + "kind": "dev", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-testkit", + "target_layer": "core", + "scope": "internal", + "kind": "dev", + "target_condition": null, + "optional": false, + "default_features": true, + "features": [] + }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-verifier", + "target_layer": "core", + "scope": "internal", + "kind": "normal", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-webauthn", + "target_layer": "core", + "scope": "internal", + "kind": "dev", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, { "source": "auths-node", "source_layer": "product", diff --git a/bindings/python/python/auths/_error_registry.py b/bindings/python/python/auths/_error_registry.py index 245a7271..c5bf599e 100644 --- a/bindings/python/python/auths/_error_registry.py +++ b/bindings/python/python/auths/_error_registry.py @@ -328,6 +328,75 @@ "explanation": "Auths rejected an impossible internal state before an effect.", "fixtureId": "core-internal-invariant" }, + { + "code": "core.authorization-denied", + "family": "input", + "owner": "core", + "ownerVersion": 1, + "operation": "verify", + "stages": [ + "authorization" + ], + "outcomes": [ + { + "retry": "never", + "effect": "not-applied" + } + ], + "recommendedAction": "satisfy-condition", + "allowsExecutionReference": false, + "allowsDecisionReference": false, + "allowsReceiptReference": false, + "title": "Authorization denied", + "explanation": "Available facts prove the supplied proof does not authorize the exact action.", + "fixtureId": "core-authorization-denied" + }, + { + "code": "core.authorization-indeterminate", + "family": "state", + "owner": "core", + "ownerVersion": 1, + "operation": "verify", + "stages": [ + "authorization" + ], + "outcomes": [ + { + "retry": "conditional", + "effect": "not-applied" + } + ], + "recommendedAction": "satisfy-condition", + "allowsExecutionReference": false, + "allowsDecisionReference": false, + "allowsReceiptReference": false, + "title": "Authorization indeterminate", + "explanation": "A required authorization fact was unavailable, so no decision was reached before any effect.", + "fixtureId": "core-authorization-indeterminate" + }, + { + "code": "core.unauthenticated-principal", + "family": "input", + "owner": "core", + "ownerVersion": 1, + "operation": "create", + "stages": [ + "authentication" + ], + "outcomes": [ + { + "retry": "never", + "effect": "not-applied" + } + ], + "recommendedAction": "correct-input", + "allowsExecutionReference": false, + "allowsDecisionReference": false, + "allowsReceiptReference": false, + "title": "Unauthenticated principal", + "explanation": "The request asserts a principal the runtime cannot authenticate, so no authority is issued.", + "fixtureId": "core-unauthenticated-principal" + }, { "code": "mcp.invalid-handler-output", "family": "profile", diff --git a/bindings/typescript/src/generated/error-registry.ts b/bindings/typescript/src/generated/error-registry.ts index ba281b61..dc77563d 100644 --- a/bindings/typescript/src/generated/error-registry.ts +++ b/bindings/typescript/src/generated/error-registry.ts @@ -323,6 +323,75 @@ export const ERROR_REGISTRY = { "explanation": "Auths rejected an impossible internal state before an effect.", "fixtureId": "core-internal-invariant" }, + { + "code": "core.authorization-denied", + "family": "input", + "owner": "core", + "ownerVersion": 1, + "operation": "verify", + "stages": [ + "authorization" + ], + "outcomes": [ + { + "retry": "never", + "effect": "not-applied" + } + ], + "recommendedAction": "satisfy-condition", + "allowsExecutionReference": false, + "allowsDecisionReference": false, + "allowsReceiptReference": false, + "title": "Authorization denied", + "explanation": "Available facts prove the supplied proof does not authorize the exact action.", + "fixtureId": "core-authorization-denied" + }, + { + "code": "core.authorization-indeterminate", + "family": "state", + "owner": "core", + "ownerVersion": 1, + "operation": "verify", + "stages": [ + "authorization" + ], + "outcomes": [ + { + "retry": "conditional", + "effect": "not-applied" + } + ], + "recommendedAction": "satisfy-condition", + "allowsExecutionReference": false, + "allowsDecisionReference": false, + "allowsReceiptReference": false, + "title": "Authorization indeterminate", + "explanation": "A required authorization fact was unavailable, so no decision was reached before any effect.", + "fixtureId": "core-authorization-indeterminate" + }, + { + "code": "core.unauthenticated-principal", + "family": "input", + "owner": "core", + "ownerVersion": 1, + "operation": "create", + "stages": [ + "authentication" + ], + "outcomes": [ + { + "retry": "never", + "effect": "not-applied" + } + ], + "recommendedAction": "correct-input", + "allowsExecutionReference": false, + "allowsDecisionReference": false, + "allowsReceiptReference": false, + "title": "Unauthenticated principal", + "explanation": "The request asserts a principal the runtime cannot authenticate, so no authority is issued.", + "fixtureId": "core-unauthenticated-principal" + }, { "code": "mcp.invalid-handler-output", "family": "profile", diff --git a/compliance.toml b/compliance.toml index 059f818e..f10ee7b7 100644 --- a/compliance.toml +++ b/compliance.toml @@ -1320,26 +1320,32 @@ independent-semantic-implementation = [ kind = "cargo" layer = "product" path = "product/runtime/auths-node" -core_apis = [] -protocol_versions = ["auths.production-client/1"] -wire_objects = ["ProductionRequest", "ProductionResponse", "SandboxAuthority", "SandboxReceipt"] -fixture_suites = ["product/fixtures/v1/production-client"] -principal_families = ["registry-provided"] +core_apis = ["auths-codec", "auths-did-keri", "auths-did-key", "auths-did-web", "auths-hsm-attested", "auths-model", "auths-ports", "auths-raw-key", "auths-registries", "auths-signature", "auths-spiffe-x509", "auths-testkit", "auths-verifier", "auths-webauthn"] +protocol_versions = ["auths-proof/v1", "auths.production-client/1"] +wire_objects = ["CanonicalAction", "ProductionRequest", "ProductionResponse", "VerifierContext"] +fixture_suites = ["core/fixtures/v1", "product/fixtures/v1/production-client"] +principal_families = ["did-keri-v1", "did-key-v1", "raw-key-v1"] signature_families = ["ed25519-v1", "p256-sha256-v1"] profiles = ["auths.github.issue-address/1", "auths.opentofu.saved-plan-apply/1", "auths.postgresql.bounded-update/1"] transports = ["https"] -configuration_inputs = ["custody", "lifecycle-store", "otlp", "profile-registry", "request-limits"] +configuration_inputs = ["custody", "lifecycle-store", "otlp", "profile-registry", "request-limits", "trusted-context"] security_state = ["opaque-recovery-reference", "receipt-disclosure", "shared-lifecycle-store"] [packages.auths-node.claims] +core-api-consumer = [ + "product/runtime/auths-node/tests/kernel_differential.rs#node_and_kernel_agree_on_every_canonical_corpus_input", + "product/runtime/auths-node/src/kernel.rs#the_node_refuses_to_mint_authority_from_a_self_asserted_identity", +] runtime-enforcement-boundary = [ "product/runtime/auths-node/src/api.rs#route_and_envelope_must_agree", - "product/runtime/auths-node/src/sandbox.rs#exact_action_and_replay_budget_are_enforced", + "product/runtime/auths-node/src/kernel.rs#exact_action_and_replay_budget_are_enforced", + "product/runtime/auths-node/tests/kernel_differential.rs#node_and_kernel_agree_on_every_canonical_corpus_input", ] operational-diagnostics = ["product/runtime/auths-node/src/api.rs#metrics_expose_only_the_frozen_operational_vocabulary"] stateful-replay-budget-component = [ - "product/runtime/auths-node/src/sandbox.rs#exact_action_and_replay_budget_are_enforced", - "product/runtime/auths-node/src/sandbox.rs#recovery_is_committed_once_and_replays_the_same_receipt", + "product/runtime/auths-node/src/kernel.rs#exact_action_and_replay_budget_are_enforced", + "product/runtime/auths-node/src/kernel.rs#a_denied_request_consumes_no_budget_and_leaves_no_receipt", + "product/runtime/auths-node/src/kernel.rs#recovery_is_committed_once_and_replays_the_same_receipt", ] [packages.auths-runtime] diff --git a/demos/open-production-reference/Dockerfile b/demos/open-production-reference/Dockerfile index 65ddb193..c0260792 100644 --- a/demos/open-production-reference/Dockerfile +++ b/demos/open-production-reference/Dockerfile @@ -5,7 +5,6 @@ RUN cargo build --locked --release -p auths-node FROM gcr.io/distroless/cc-debian12:nonroot@sha256:adcd20c7b4c988b73cbfbddb26d2eee574571e6d7c9ffea29b3821e0690efb77 COPY --from=builder /workspace/target/release/auths-node /usr/local/bin/auths-node -COPY --from=builder /workspace/target/release/auths-sandbox-request /usr/local/bin/auths-sandbox-request USER 65532:65532 EXPOSE 8080 ENTRYPOINT ["/usr/local/bin/auths-node"] diff --git a/demos/open-production-reference/config/local.toml b/demos/open-production-reference/config/local.toml index 8471afd9..e31a3b09 100644 --- a/demos/open-production-reference/config/local.toml +++ b/demos/open-production-reference/config/local.toml @@ -21,6 +21,12 @@ seed_env = "AUTHS_LOCAL_SEED" otlp_endpoint = "http://otel-collector:4317" service_name = "auths-node" +[verification] +# Canonical TrustedContext bytes (auths_codec::encode_verifier_context). +# Carries the trust anchors, accepted registries, status snapshots, assurance +# policy, and verifier limits every authorization decision is made against. +trusted_context_path = "/run/config/trusted-context.cbor" + [profiles] opentofu_saved_plan_apply = true postgresql_bounded_update = true diff --git a/demos/open-production-reference/config/production.example.toml b/demos/open-production-reference/config/production.example.toml index ff3ec603..eceb5e65 100644 --- a/demos/open-production-reference/config/production.example.toml +++ b/demos/open-production-reference/config/production.example.toml @@ -23,6 +23,12 @@ account = "000000000000" otlp_endpoint = "https://otel.internal.example" service_name = "auths-node" +[verification] +# Canonical TrustedContext bytes (auths_codec::encode_verifier_context). +# Carries the trust anchors, accepted registries, status snapshots, assurance +# policy, and verifier limits every authorization decision is made against. +trusted_context_path = "/run/secrets/trusted-context.cbor" + [profiles] opentofu_saved_plan_apply = true postgresql_bounded_update = true diff --git a/demos/open-production-reference/deploy/kubernetes/base/config-map.yaml b/demos/open-production-reference/deploy/kubernetes/base/config-map.yaml index b8c619a6..770cdeb0 100644 --- a/demos/open-production-reference/deploy/kubernetes/base/config-map.yaml +++ b/demos/open-production-reference/deploy/kubernetes/base/config-map.yaml @@ -26,6 +26,8 @@ data: [telemetry] otlp_endpoint = "http://otel-collector.observability.svc:4317" service_name = "auths-node" + [verification] + trusted_context_path = "/run/config/trusted-context.cbor" [profiles] opentofu_saved_plan_apply = true postgresql_bounded_update = true diff --git a/demos/open-production-reference/deploy/kubernetes/overlays/aws-kms/config-map-patch.yaml b/demos/open-production-reference/deploy/kubernetes/overlays/aws-kms/config-map-patch.yaml index 93f153b6..72058f8a 100644 --- a/demos/open-production-reference/deploy/kubernetes/overlays/aws-kms/config-map-patch.yaml +++ b/demos/open-production-reference/deploy/kubernetes/overlays/aws-kms/config-map-patch.yaml @@ -25,6 +25,8 @@ data: [telemetry] otlp_endpoint = "http://otel-collector.observability.svc:4317" service_name = "auths-node" + [verification] + trusted_context_path = "/run/config/trusted-context.cbor" [profiles] opentofu_saved_plan_apply = true postgresql_bounded_update = true diff --git a/docs/reference/error-codes.md b/docs/reference/error-codes.md index 091a8377..2154c00e 100644 --- a/docs/reference/error-codes.md +++ b/docs/reference/error-codes.md @@ -18,6 +18,9 @@ Every row is generated from the Rust-owned registry. `possible` effects are neve | `core.observation-inconclusive` | `resume` | possible / unknown | `ResumeAndReconcile` | Available evidence cannot prove effect or non-effect for the exact request. | | `core.workflow-terminal` | `resume` | notapplied / never | `InspectReceipt` | The workflow has already reached an immutable terminal state. | | `core.internal-invariant` | `execute` | notapplied / never | `ContactSupport` | Auths rejected an impossible internal state before an effect. | +| `core.authorization-denied` | `verify` | notapplied / never | `SatisfyCondition` | Available facts prove the supplied proof does not authorize the exact action. | +| `core.authorization-indeterminate` | `verify` | notapplied / conditional | `SatisfyCondition` | A required authorization fact was unavailable, so no decision was reached before any effect. | +| `core.unauthenticated-principal` | `create` | notapplied / never | `CorrectInput` | The request asserts a principal the runtime cannot authenticate, so no authority is issued. | | `mcp.invalid-handler-output` | `execute` | possible / unknown | `ResumeAndReconcile` | The invoked handler returned an invalid or oversized bounded result. | | `mcp.handler-failed` | `execute` | possible / unknown | `ResumeAndReconcile` | The invoked handler failed without conclusive no-effect evidence. | | `mcp.handler-timeout` | `execute` | possible / unknown | `ResumeAndReconcile` | The invoked handler did not produce conclusive effect evidence before its deadline. | diff --git a/product/errors/auths-errors/src/lib.rs b/product/errors/auths-errors/src/lib.rs index 206c50e3..9e8a63bc 100644 --- a/product/errors/auths-errors/src/lib.rs +++ b/product/errors/auths-errors/src/lib.rs @@ -536,6 +536,45 @@ const CORE_ERRORS: &[ErrorDefinition] = &[ "Auths rejected an impossible internal state before an effect.", "core-internal-invariant", ), + definition( + "core.authorization-denied", + ErrorFamily::Input, + "core", + "verify", + &["authorization"], + NOT_APPLIED_NEVER, + RecommendedAction::SatisfyCondition, + false, + "Authorization denied", + "Available facts prove the supplied proof does not authorize the exact action.", + "core-authorization-denied", + ), + definition( + "core.authorization-indeterminate", + ErrorFamily::State, + "core", + "verify", + &["authorization"], + NOT_APPLIED_CONDITIONAL, + RecommendedAction::SatisfyCondition, + false, + "Authorization indeterminate", + "A required authorization fact was unavailable, so no decision was reached before any effect.", + "core-authorization-indeterminate", + ), + definition( + "core.unauthenticated-principal", + ErrorFamily::Input, + "core", + "create", + &["authentication"], + NOT_APPLIED_NEVER, + RecommendedAction::CorrectInput, + false, + "Unauthenticated principal", + "The request asserts a principal the runtime cannot authenticate, so no authority is issued.", + "core-unauthenticated-principal", + ), ]; const MCP_ERRORS: &[ErrorDefinition] = &[ diff --git a/product/errors/v1/registry.json b/product/errors/v1/registry.json index ebb26b68..c0ddb71c 100644 --- a/product/errors/v1/registry.json +++ b/product/errors/v1/registry.json @@ -323,6 +323,75 @@ "explanation": "Auths rejected an impossible internal state before an effect.", "fixtureId": "core-internal-invariant" }, + { + "code": "core.authorization-denied", + "family": "input", + "owner": "core", + "ownerVersion": 1, + "operation": "verify", + "stages": [ + "authorization" + ], + "outcomes": [ + { + "retry": "never", + "effect": "not-applied" + } + ], + "recommendedAction": "satisfy-condition", + "allowsExecutionReference": false, + "allowsDecisionReference": false, + "allowsReceiptReference": false, + "title": "Authorization denied", + "explanation": "Available facts prove the supplied proof does not authorize the exact action.", + "fixtureId": "core-authorization-denied" + }, + { + "code": "core.authorization-indeterminate", + "family": "state", + "owner": "core", + "ownerVersion": 1, + "operation": "verify", + "stages": [ + "authorization" + ], + "outcomes": [ + { + "retry": "conditional", + "effect": "not-applied" + } + ], + "recommendedAction": "satisfy-condition", + "allowsExecutionReference": false, + "allowsDecisionReference": false, + "allowsReceiptReference": false, + "title": "Authorization indeterminate", + "explanation": "A required authorization fact was unavailable, so no decision was reached before any effect.", + "fixtureId": "core-authorization-indeterminate" + }, + { + "code": "core.unauthenticated-principal", + "family": "input", + "owner": "core", + "ownerVersion": 1, + "operation": "create", + "stages": [ + "authentication" + ], + "outcomes": [ + { + "retry": "never", + "effect": "not-applied" + } + ], + "recommendedAction": "correct-input", + "allowsExecutionReference": false, + "allowsDecisionReference": false, + "allowsReceiptReference": false, + "title": "Unauthenticated principal", + "explanation": "The request asserts a principal the runtime cannot authenticate, so no authority is issued.", + "fixtureId": "core-unauthenticated-principal" + }, { "code": "mcp.invalid-handler-output", "family": "profile", diff --git a/product/fixtures/v1/errors/manifest.json b/product/fixtures/v1/errors/manifest.json index 1e3ae559..b40485b9 100644 --- a/product/fixtures/v1/errors/manifest.json +++ b/product/fixtures/v1/errors/manifest.json @@ -351,6 +351,81 @@ "unknown" ] }, + { + "schema": "auths.error/1", + "family": "input", + "code": "core.authorization-denied", + "operation": "verify", + "stage": "authorization", + "summary": "Authorization denied", + "correlationId": "fixture:core-authorization-denied", + "retry": "never", + "effect": "not-applied", + "entered": { + "approval": false, + "signer": false, + "state": false, + "credential": false, + "provider": false + }, + "recommendedAction": "satisfy-condition", + "executionReference": null, + "decisionReference": null, + "receiptReference": null, + "causes": [ + "unknown" + ] + }, + { + "schema": "auths.error/1", + "family": "state", + "code": "core.authorization-indeterminate", + "operation": "verify", + "stage": "authorization", + "summary": "Authorization indeterminate", + "correlationId": "fixture:core-authorization-indeterminate", + "retry": "conditional", + "effect": "not-applied", + "entered": { + "approval": false, + "signer": false, + "state": false, + "credential": false, + "provider": false + }, + "recommendedAction": "satisfy-condition", + "executionReference": null, + "decisionReference": null, + "receiptReference": null, + "causes": [ + "unknown" + ] + }, + { + "schema": "auths.error/1", + "family": "input", + "code": "core.unauthenticated-principal", + "operation": "create", + "stage": "authentication", + "summary": "Unauthenticated principal", + "correlationId": "fixture:core-unauthenticated-principal", + "retry": "never", + "effect": "not-applied", + "entered": { + "approval": false, + "signer": false, + "state": false, + "credential": false, + "provider": false + }, + "recommendedAction": "correct-input", + "executionReference": null, + "decisionReference": null, + "receiptReference": null, + "causes": [ + "unknown" + ] + }, { "schema": "auths.error/1", "family": "profile", diff --git a/product/runtime/auths-node/Cargo.toml b/product/runtime/auths-node/Cargo.toml index 009f9aaf..75e10ddb 100644 --- a/product/runtime/auths-node/Cargo.toml +++ b/product/runtime/auths-node/Cargo.toml @@ -8,9 +8,19 @@ description = "Hardened open reference node for the Auths production contract" publish = false [dependencies] +auths-codec.workspace = true +auths-did-key.workspace = true +auths-did-keri.workspace = true +auths-errors.workspace = true +auths-model.workspace = true auths-operations.workspace = true auths-operations-otel.workspace = true +auths-ports.workspace = true auths-production-client.workspace = true +auths-raw-key.workspace = true +auths-registries.workspace = true +auths-signature.workspace = true +auths-verifier.workspace = true axum.workspace = true base64ct.workspace = true ed25519-dalek.workspace = true @@ -29,6 +39,11 @@ toml.workspace = true tower-http = { workspace = true, features = ["limit", "request-id", "timeout", "trace"] } [dev-dependencies] +auths-did-web.workspace = true +auths-hsm-attested.workspace = true +auths-spiffe-x509.workspace = true +auths-testkit.workspace = true +auths-webauthn.workspace = true tempfile.workspace = true tower.workspace = true diff --git a/product/runtime/auths-node/src/api.rs b/product/runtime/auths-node/src/api.rs index c2e2df64..c4314bd3 100644 --- a/product/runtime/auths-node/src/api.rs +++ b/product/runtime/auths-node/src/api.rs @@ -417,10 +417,14 @@ fn valid_receipt_id(value: &str) -> bool { fn status_for(error: RuntimeFailure) -> StatusCode { match error { - RuntimeFailure::Denied | RuntimeFailure::DisclosureDenied => StatusCode::FORBIDDEN, - RuntimeFailure::UnknownWorkflow | RuntimeFailure::UnknownReceipt => StatusCode::NOT_FOUND, + RuntimeFailure::AuthorizationDenied(_) + | RuntimeFailure::DisclosureDenied + | RuntimeFailure::ReplayBudgetExhausted + | RuntimeFailure::UnauthenticatedPrincipal => StatusCode::FORBIDDEN, + RuntimeFailure::UnknownReference => StatusCode::NOT_FOUND, RuntimeFailure::Malformed | RuntimeFailure::ProfileDisabled => StatusCode::BAD_REQUEST, - RuntimeFailure::Indeterminate | RuntimeFailure::Unavailable => { + RuntimeFailure::StateConflict => StatusCode::CONFLICT, + RuntimeFailure::AuthorizationIndeterminate(_) | RuntimeFailure::Unavailable => { StatusCode::SERVICE_UNAVAILABLE } // Deliberately not 503: proxies and clients treat 503 as a safe @@ -485,7 +489,7 @@ mod tests { Ok(ReceiptSummary { receipt_id: receipt_id.into(), profile: QualifiedProfile::GitHubIssueAddress.as_str().into(), - outcome: "succeeded".into(), + effect: auths_operations::EffectState::Applied, completed_at: 1, disclosure: "summary", }) @@ -533,6 +537,9 @@ seed_env = "AUTHS_LOCAL_SEED" otlp_endpoint = "http://otel:4317" service_name = "auths-node" +[verification] +trusted_context_path = "/run/config/trusted-context.cbor" + [profiles] opentofu_saved_plan_apply = true postgresql_bounded_update = true diff --git a/product/runtime/auths-node/src/bin/auths-sandbox-request.rs b/product/runtime/auths-node/src/bin/auths-sandbox-request.rs deleted file mode 100644 index e49c35d4..00000000 --- a/product/runtime/auths-node/src/bin/auths-sandbox-request.rs +++ /dev/null @@ -1,56 +0,0 @@ -use auths_node::encode_sandbox_authority_request; -use base64ct::{Base64UrlUnpadded, Encoding as _}; -use std::{ - env, fs, - process::ExitCode, - time::{SystemTime, UNIX_EPOCH}, -}; - -fn main() -> ExitCode { - let mut arguments = env::args().skip(1); - let Some(action_path) = arguments.next() else { - eprintln!("usage: auths-sandbox-request [lifetime-seconds]"); - return ExitCode::from(1); - }; - let lifetime = arguments - .next() - .as_deref() - .unwrap_or("600") - .parse::() - .ok() - .filter(|value| (1..=86_400).contains(value)); - if arguments.next().is_some() || lifetime.is_none() { - eprintln!("auths-sandbox-request: invalid arguments"); - return ExitCode::from(1); - } - let Ok(action) = fs::read(action_path) else { - eprintln!("auths-sandbox-request: action is unavailable"); - return ExitCode::from(1); - }; - if action.is_empty() || action.len() > 1024 * 1024 { - eprintln!("auths-sandbox-request: action is outside bounds"); - return ExitCode::from(1); - } - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_or(0, |duration| duration.as_secs()); - let Some(expires_at) = now.checked_add(lifetime.unwrap()) else { - eprintln!("auths-sandbox-request: expiry overflow"); - return ExitCode::from(1); - }; - let Ok(request) = encode_sandbox_authority_request(expires_at, 2, 2, &[&action]) else { - eprintln!("auths-sandbox-request: request could not be encoded"); - return ExitCode::from(1); - }; - let Ok(attenuation) = encode_sandbox_authority_request(expires_at, 1, 1, &[&action]) else { - eprintln!("auths-sandbox-request: attenuation could not be encoded"); - return ExitCode::from(1); - }; - println!( - "{{\"request\":\"{}\",\"attenuation\":\"{}\",\"action\":\"{}\"}}", - Base64UrlUnpadded::encode_string(&request), - Base64UrlUnpadded::encode_string(&attenuation), - Base64UrlUnpadded::encode_string(&action) - ); - ExitCode::SUCCESS -} diff --git a/product/runtime/auths-node/src/config.rs b/product/runtime/auths-node/src/config.rs index dc0de0e1..cb59c124 100644 --- a/product/runtime/auths-node/src/config.rs +++ b/product/runtime/auths-node/src/config.rs @@ -25,9 +25,23 @@ pub struct NodeConfig { lifecycle: LifecycleConfig, custody: CustodyConfig, telemetry: TelemetryConfig, + verification: VerificationConfig, profiles: ProfilesConfig, } +/// The deployment's trust decision, stated as bytes rather than as code. +/// +/// `trusted_context_path` names a file holding one canonical +/// `TrustedContext` (`auths_codec::encode_verifier_context`). It carries the +/// trust anchors, accepted registries, status snapshots, assurance policy, and +/// verifier limits every authorization decision is made against. A node without +/// one cannot decide anything, which is why this section is mandatory. +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct VerificationConfig { + trusted_context_path: String, +} + #[derive(Clone, Debug, Deserialize)] #[serde(deny_unknown_fields)] struct LifecycleConfig { @@ -178,6 +192,9 @@ impl NodeConfig { { return Err(StartupError::InvalidPath); } + if !Path::new(&self.verification.trusted_context_path).is_absolute() { + return Err(StartupError::InvalidPath); + } if !self.telemetry.otlp_endpoint.starts_with("http://") && !self.telemetry.otlp_endpoint.starts_with("https://") { @@ -300,6 +317,11 @@ impl NodeConfig { } } + #[must_use] + pub fn trusted_context_path(&self) -> &Path { + Path::new(&self.verification.trusted_context_path) + } + #[must_use] pub fn lifecycle_url_env(&self) -> &str { &self.lifecycle.url_env @@ -449,6 +471,9 @@ maximum_records = 4096 otlp_endpoint = "http://otel:4317" service_name = "auths-node" +[verification] +trusted_context_path = "/run/config/trusted-context.cbor" + [profiles] opentofu_saved_plan_apply = true postgresql_bounded_update = true @@ -458,6 +483,35 @@ sandbox_providers = {sandbox} ) } + /// A node cannot decide anything without the deployment's trusted context, + /// so a configuration that does not name one must not start. + #[test] + fn a_node_without_a_trusted_context_refuses_to_start() { + let complete = source( + "local", + "[custody]\nkind = \"software-fixture\"\nseed_env = \"AUTHS_LOCAL_SEED\"", + true, + false, + ); + let without = complete.replace( + "[verification]\ntrusted_context_path = \"/run/config/trusted-context.cbor\"\n\n", + "", + ); + assert_ne!(without, complete, "the fixture must contain the section"); + assert_eq!( + NodeConfig::parse(&without).unwrap_err(), + StartupError::MalformedConfig + ); + let relative = complete.replace( + "\"/run/config/trusted-context.cbor\"", + "\"trusted-context.cbor\"", + ); + assert_eq!( + NodeConfig::parse(&relative).unwrap_err(), + StartupError::InvalidPath + ); + } + #[test] fn local_fixture_is_explicit_and_redacted() { let config = NodeConfig::parse(&source( diff --git a/product/runtime/auths-node/src/kernel.rs b/product/runtime/auths-node/src/kernel.rs new file mode 100644 index 00000000..bfbc38df --- /dev/null +++ b/product/runtime/auths-node/src/kernel.rs @@ -0,0 +1,913 @@ +//! The node's authorization path. +//! +//! Every authorization decision this node makes is the verified kernel's +//! decision. `auths-node` decodes bytes, supplies the trusted context and the +//! clock, claims stateful replay budget, applies the effect, and signs a +//! receipt. It never decides. The eleven attenuation dimensions declared in +//! `core/crates/auths-algebra-kernel/src/generated.rs` are checked exactly once, +//! inside `auths_verifier::verify`, and `tests/kernel_differential.rs` holds +//! that equality against the canonical corpus. +//! +//! The node issues no authority. `/v1/authority/create` and +//! `/v1/authority/delegate` refuse: see [`KernelRuntime::create`]. + +use crate::{ + api::NodeRuntime, + profiles::{ReceiptSummary, RuntimeFailure, WorkflowProjection}, + sandbox_store::{ + MemorySandboxStore, PendingEffect, PostgresSandboxStore, SandboxStore, StoredReceipt, + }, +}; +use auths_model::{CanonicalAction, Timestamp, TrustedContext}; +use auths_operations::EffectState; +use auths_ports::{PrincipalMethod, SignatureSuite}; +use auths_production_client::{ + ClientOutcomeKind, NextCall, ProductVerb, ProductionRequest, ProductionResponse, + QualifiedProfile, RecoveryReference, +}; +use auths_registries::ImmutableRegistries; +use auths_verifier::{VerificationFailure, VerificationOutcome, VerifiedAction}; +use base64ct::{Base64UrlUnpadded, Encoding as _}; +use ed25519_dalek::{Signer as _, SigningKey}; +use minicbor::Encoder; +use sha2::{Digest as _, Sha256}; +use std::{ + collections::BTreeSet, + sync::Arc, + time::{SystemTime, UNIX_EPOCH}, +}; + +const RECEIPT_DOMAIN: &[u8] = b"AUTHS-NODE-RECEIPT\x00\x01"; +const RECOVERY_DOMAIN: &[u8] = b"AUTHS-NODE-RECOVERY\x00\x01"; +const DISCLOSURE_DOMAIN: &[u8] = b"AUTHS-NODE-DISCLOSURE\x00\x01"; +const CLAIM_DOMAIN: &[u8] = b"AUTHS-NODE-EFFECT-CLAIM\x00\x01"; + +/// Marker prefix inside a canonical action body that makes one effect +/// deliberately recoverable, used by the open-production reference to exercise +/// the outcome-unknown path. +const RECOVERABLE_BODY_MARKER: &[u8] = b"AUTHS-NODE-RECOVER"; + +/// A stateful (proof, action) pair may produce at most this many effects. +/// +/// The kernel proves the budget *ceiling* is not widened anywhere in the chain. +/// Stateful consumption is the node's obligation and is not something the pure +/// verifier can do. One effect per exact authorized pair is the fail-closed +/// choice: the caller who wants a second effect must present a second action. +const MAXIMUM_EFFECTS_PER_AUTHORIZED_PAIR: u32 = 1; + +/// Source of the evaluation instant handed to the kernel. +/// +/// This is a real dependency, not a test seam: an authorization decision is a +/// function of time, and a node that cannot be told what time it is cannot be +/// differentially compared against the kernel on fixed corpus inputs. +pub trait NodeClock: Send + Sync { + fn now_unix_seconds(&self) -> u64; +} + +/// Wall-clock time, used by every deployed node. +pub struct SystemNodeClock; + +impl NodeClock for SystemNodeClock { + fn now_unix_seconds(&self) -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_or(0, |duration| duration.as_secs()) + } +} + +/// Immutable verification inputs the node holds on behalf of the kernel. +/// +/// The node owns these rather than delegating to `auths-kernel-runtime` because +/// it needs the same registries for both the complete decision +/// ([`auths_verifier::verify`]) and the staged proof check behind +/// `/v1/authority/verify`, and `AuthsKernel` exposes no registry accessor. +pub struct NodeKernel { + context_template: TrustedContext, + principal_methods: Vec>, + signature_suites: Vec>, +} + +impl NodeKernel { + /// Builds the node's immutable verification inputs. + /// + /// # Errors + /// + /// Returns malformed when either executable registry is empty. + pub fn new( + context_template: TrustedContext, + principal_methods: Vec>, + signature_suites: Vec>, + ) -> Result { + if principal_methods.is_empty() || signature_suites.is_empty() { + return Err(RuntimeFailure::Malformed); + } + Ok(Self { + context_template, + principal_methods, + signature_suites, + }) + } + + fn with_registries(&self, call: impl FnOnce(&ImmutableRegistries<'_>) -> T) -> Option { + let methods: Vec<&dyn PrincipalMethod> = self + .principal_methods + .iter() + .map(|method| method.as_ref() as &dyn PrincipalMethod) + .collect(); + let suites: Vec<&dyn SignatureSuite> = self + .signature_suites + .iter() + .map(|suite| suite.as_ref() as &dyn SignatureSuite) + .collect(); + ImmutableRegistries::new(&methods, &suites) + .ok() + .map(|registries| call(®istries)) + } +} + +/// A node whose authorization decisions are the kernel's decisions. +pub struct KernelRuntime { + kernel: NodeKernel, + signing: SigningKey, + profiles: BTreeSet, + store: Arc, + clock: Arc, +} + +impl KernelRuntime { + /// Builds a node on the supplied kernel with in-memory effect state. + /// + /// # Errors + /// + /// Returns malformed for a zero receipt seed or an empty profile set. + pub fn new( + kernel: NodeKernel, + seed: [u8; 32], + profiles: BTreeSet, + ) -> Result { + Self::with_store( + kernel, + seed, + profiles, + Arc::new(MemorySandboxStore::default()), + Arc::new(SystemNodeClock), + ) + } + + /// Builds a node on the supplied kernel with `PostgreSQL` effect state. + /// + /// # Errors + /// + /// Returns malformed for a zero receipt seed or an empty profile set. + pub fn with_postgres( + kernel: NodeKernel, + seed: [u8; 32], + profiles: BTreeSet, + store: PostgresSandboxStore, + ) -> Result { + Self::with_store( + kernel, + seed, + profiles, + Arc::new(store), + Arc::new(SystemNodeClock), + ) + } + + /// Builds a node with an explicit clock. + /// + /// # Errors + /// + /// Returns malformed for a zero receipt seed or an empty profile set. + pub fn with_clock( + kernel: NodeKernel, + seed: [u8; 32], + profiles: BTreeSet, + clock: Arc, + ) -> Result { + Self::with_store( + kernel, + seed, + profiles, + Arc::new(MemorySandboxStore::default()), + clock, + ) + } + + pub(crate) fn with_store( + kernel: NodeKernel, + seed: [u8; 32], + profiles: BTreeSet, + store: Arc, + clock: Arc, + ) -> Result { + if seed == [0; 32] || profiles.is_empty() { + return Err(RuntimeFailure::Malformed); + } + Ok(Self { + kernel, + signing: SigningKey::from_bytes(&seed), + profiles, + store, + clock, + }) + } + + /// Refuses to issue authority. + /// + /// A `ProductionRequest` carries `identity` as unauthenticated caller-supplied + /// bytes (`auths-production-client/src/lib.rs`: `identity: Vec`), the HTTP + /// surface performs no client authentication (`api.rs::production_call` decodes + /// the body and nothing else), and the reference ingress terminates TLS without + /// client certificates. There is therefore no authenticated principal at this + /// call site. Minting authority for a self-asserted identity would hand any + /// caller a root, so the node refuses instead. + /// + /// Authority in target V1 originates from a trust anchor's signature and + /// reaches the node inside the proof. Nothing about that requires a node. + /// + /// # Errors + /// + /// Always returns [`RuntimeFailure::UnauthenticatedPrincipal`]. + #[allow(clippy::unused_self, reason = "the refusal is a property of the node")] + fn create(&self, _request: &ProductionRequest) -> Result { + Err(RuntimeFailure::UnauthenticatedPrincipal) + } + + /// Refuses to delegate authority, for the reason given on [`Self::create`]. + /// + /// Delegation narrows an authority the caller already holds; proving the + /// caller holds it requires authenticating the caller, which this call site + /// cannot do. + #[allow(clippy::unused_self, reason = "the refusal is a property of the node")] + fn delegate(&self, _request: &ProductionRequest) -> Result { + Err(RuntimeFailure::UnauthenticatedPrincipal) + } + + /// Decides one request with the kernel and returns the sealed action. + /// + /// This is the node's whole decision path. It contains no attenuation, + /// expiry, budget, audience, or permission logic of its own. + /// + /// # Errors + /// + /// Returns the kernel's denial or indeterminate class, or malformed when the + /// supplied bytes are not a proof and a canonical action. + pub fn authorize( + &self, + proof: &[u8], + action_bytes: &[u8], + ) -> Result { + let action = self.decode_action(action_bytes)?; + let context = self.request_context()?; + let outcome = self + .kernel + .with_registries(|registries| { + auths_verifier::verify(proof, &action, &context, registries) + }) + .ok_or(RuntimeFailure::Unavailable)?; + match outcome { + VerificationOutcome::Authorized(verified) => Ok(*verified), + VerificationOutcome::Denied(reason) => Err(RuntimeFailure::AuthorizationDenied(reason)), + VerificationOutcome::Indeterminate(requirement) => { + Err(RuntimeFailure::AuthorizationIndeterminate(requirement)) + } + } + } + + /// Derives the per-request trusted context. + /// + /// Trust anchors, registries, status snapshots, policies, and limits come + /// from the deployment's immutable template. Only the evaluation instant is + /// per-request, because the production request contract carries no audience + /// or challenge field; see the note on `verify_proof`. + fn request_context(&self) -> Result { + let template = &self.kernel.context_template; + template + .for_request( + template.expected_audience().clone(), + template.expected_challenge(), + Timestamp::new(self.clock.now_unix_seconds()), + ) + .map_err(|_| RuntimeFailure::Unavailable) + } + + fn decode_action(&self, bytes: &[u8]) -> Result { + auths_codec::decode_canonical_action(bytes, self.kernel.context_template.limits()) + .map_err(|_| RuntimeFailure::Malformed) + } + + fn execute(&self, request: &ProductionRequest) -> Result { + self.require_profile(request.profile())?; + let proof = request.authority().ok_or(RuntimeFailure::Malformed)?; + let action_bytes = request.body().ok_or(RuntimeFailure::Malformed)?; + let verified = self.authorize(proof, action_bytes)?; + let claim = effect_claim(proof, action_bytes); + let recoverable = verified + .canonical_action() + .body() + .starts_with(RECOVERABLE_BODY_MARKER); + self.store + .claim_use(claim, MAXIMUM_EFFECTS_PER_AUTHORIZED_PAIR)?; + if recoverable { + let reference = Self::recovery_reference(claim)?; + self.store.put_pending( + reference.as_str(), + &PendingEffect { + profile: request.profile(), + authority: claim, + action: action_bytes.to_vec(), + created_at: self.clock.now_unix_seconds(), + }, + )?; + return ProductionResponse::new( + ClientOutcomeKind::Recoverable, + Some(RuntimeFailure::ProviderOutcomeUnknown.code().to_owned()), + NextCall::Resume, + Some(reference), + None, + None, + ) + .map_err(|_| RuntimeFailure::Malformed); + } + let (receipt, value) = self.complete_effect( + request.profile(), + claim, + action_bytes, + self.clock.now_unix_seconds(), + )?; + completed_response(receipt, value) + } + + fn resume(&self, request: &ProductionRequest) -> Result { + let reference = request + .recovery_reference() + .ok_or(RuntimeFailure::Malformed)?; + let Some(pending) = self.store.pending(reference.as_str())? else { + let completed = self + .store + .recovered(reference.as_str())? + .ok_or(RuntimeFailure::UnknownReference)?; + if completed.profile != request.profile() { + return Err(RuntimeFailure::UnknownReference); + } + return completed_response(completed.bytes, completed.value); + }; + if pending.profile != request.profile() { + return Err(RuntimeFailure::UnknownReference); + } + let (receipt_id, receipt) = self.build_effect( + pending.profile, + pending.authority, + &pending.action, + self.clock.now_unix_seconds(), + )?; + let completed = + self.store + .finish_pending(reference.as_str(), &pending, &receipt_id, &receipt)?; + completed_response(completed.bytes, completed.value) + } + + fn complete_effect( + &self, + profile: QualifiedProfile, + claim: [u8; 32], + action: &[u8], + completed_at: u64, + ) -> Result<(Vec, Vec), RuntimeFailure> { + let (receipt_id, receipt) = self.build_effect(profile, claim, action, completed_at)?; + self.store.put_receipt(&receipt_id, &receipt)?; + Ok((receipt.bytes, receipt.value)) + } + + fn build_effect( + &self, + profile: QualifiedProfile, + claim: [u8; 32], + action: &[u8], + completed_at: u64, + ) -> Result<(String, StoredReceipt), RuntimeFailure> { + let value = effect_value(profile, action); + let payload = + encode_receipt_payload(profile, claim, digest(action), digest(&value), completed_at)?; + let receipt = self.sign(RECEIPT_DOMAIN, &payload)?; + let receipt_id = hex::encode(digest(&receipt)); + Ok(( + receipt_id, + StoredReceipt { + profile, + completed_at, + bytes: receipt, + value, + }, + )) + } + + /// Answers `/v1/authority/verify` with the kernel's own staged result. + /// + /// The verify request shape carries no action (`ProductVerb::Verify` forbids + /// the `authority` field, leaving one body), so this endpoint can only make + /// the claim the kernel can make without one: the proof decodes canonically, + /// its reference graph resolves, and every principal controls its keys. It + /// deliberately does not claim any action is authorized. + fn verify_proof(&self, proof: &[u8]) -> Result<(), RuntimeFailure> { + let context = self.request_context()?; + self.kernel + .with_registries(|registries| { + auths_verifier::decode_proof(proof, &context) + .and_then(|decoded| auths_verifier::resolve_proof(decoded, &context)) + .and_then(|resolved| { + auths_verifier::verify_principal_control(resolved, &context, registries) + }) + .map(|_| ()) + }) + .ok_or(RuntimeFailure::Unavailable)? + .map_err(runtime_failure) + } + + fn recovery_reference(claim: [u8; 32]) -> Result { + let mut nonce = [0; 32]; + getrandom::fill(&mut nonce).map_err(|_| RuntimeFailure::Unavailable)?; + let mut hasher = Sha256::new(); + hasher.update(RECOVERY_DOMAIN); + hasher.update(claim); + hasher.update(nonce); + RecoveryReference::parse(&Base64UrlUnpadded::encode_string(&hasher.finalize())) + .map_err(|_| RuntimeFailure::Malformed) + } + + fn sign(&self, domain: &[u8], payload: &[u8]) -> Result, RuntimeFailure> { + let preimage = preimage(domain, payload); + let signature = self.signing.sign(&preimage).to_bytes(); + encode_envelope(payload, &signature) + } + + fn require_profile(&self, profile: QualifiedProfile) -> Result<(), RuntimeFailure> { + if self.profiles.contains(&profile) { + Ok(()) + } else { + Err(RuntimeFailure::ProfileDisabled) + } + } +} + +fn runtime_failure(failure: VerificationFailure) -> RuntimeFailure { + match failure { + VerificationFailure::Denied(reason) => RuntimeFailure::AuthorizationDenied(reason), + VerificationFailure::Indeterminate(requirement) => { + RuntimeFailure::AuthorizationIndeterminate(requirement) + } + } +} + +impl NodeRuntime for KernelRuntime { + fn handle(&self, request: ProductionRequest) -> Result { + match request.verb() { + ProductVerb::Create => self.create(&request), + ProductVerb::Delegate => self.delegate(&request), + ProductVerb::Execute => self.execute(&request), + ProductVerb::Resume => self.resume(&request), + ProductVerb::Verify => { + self.verify_proof(request.body().ok_or(RuntimeFailure::Malformed)?)?; + ProductionResponse::new( + ClientOutcomeKind::Verified, + None, + NextCall::Never, + None, + None, + None, + ) + .map_err(|_| RuntimeFailure::Malformed) + } + } + } + + fn status(&self, reference: &RecoveryReference) -> Result { + if let Some(pending) = self.store.pending(reference.as_str())? { + return Ok(WorkflowProjection { + reference: reference.as_str().to_owned(), + profile: pending.profile.as_str().into(), + state: "outcome-unknown".into(), + effect: EffectState::Possible, + retry: NextCall::Resume, + updated_at: pending.created_at, + receipt_id: None, + }); + } + let completed = self + .store + .recovered(reference.as_str())? + .ok_or(RuntimeFailure::UnknownReference)?; + Ok(WorkflowProjection { + reference: reference.as_str().to_owned(), + profile: completed.profile.as_str().into(), + state: "committed".into(), + effect: EffectState::Applied, + retry: NextCall::Never, + updated_at: completed.completed_at, + receipt_id: Some(hex::encode(digest(&completed.bytes))), + }) + } + + fn receipt_summary(&self, receipt_id: &str) -> Result { + let receipt = self + .store + .receipt(receipt_id)? + .ok_or(RuntimeFailure::UnknownReference)?; + Ok(ReceiptSummary { + receipt_id: receipt_id.into(), + profile: receipt.profile.as_str().into(), + effect: EffectState::Applied, + completed_at: receipt.completed_at, + disclosure: "summary", + }) + } + + /// Discloses one receipt to a caller holding the node's disclosure + /// authorization. + /// + /// An unknown receipt and an unauthorized disclosure return the same + /// failure. Distinguishing them would turn this endpoint into an existence + /// oracle for receipts the caller is not entitled to read. + fn disclose_receipt( + &self, + receipt_id: &str, + authorization: &[u8], + ) -> Result, RuntimeFailure> { + let expected = self.sign(DISCLOSURE_DOMAIN, receipt_id.as_bytes())?; + if authorization != expected { + return Err(RuntimeFailure::DisclosureDenied); + } + self.store + .receipt(receipt_id)? + .map(|receipt| receipt.bytes.clone()) + .ok_or(RuntimeFailure::DisclosureDenied) + } + + fn ready(&self) -> bool { + self.store.ready() + } +} + +/// Binds one stateful effect claim to the exact proof and the exact action. +fn effect_claim(proof: &[u8], action: &[u8]) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(CLAIM_DOMAIN); + hasher.update(digest(proof)); + hasher.update(digest(action)); + hasher.finalize().into() +} + +fn completed_response( + receipt: Vec, + value: Vec, +) -> Result { + ProductionResponse::new( + ClientOutcomeKind::Completed, + None, + NextCall::Never, + None, + Some(value), + Some(receipt), + ) + .map_err(|_| RuntimeFailure::Malformed) +} + +fn encode_receipt_payload( + profile: QualifiedProfile, + claim: [u8; 32], + action: [u8; 32], + result: [u8; 32], + completed_at: u64, +) -> Result, RuntimeFailure> { + let mut encoder = Encoder::new(Vec::new()); + encoder + .array(6) + .and_then(|encoder| encoder.u16(1)) + .and_then(|encoder| encoder.str(profile.as_str())) + .and_then(|encoder| encoder.bytes(&claim)) + .and_then(|encoder| encoder.bytes(&action)) + .and_then(|encoder| encoder.bytes(&result)) + .and_then(|encoder| encoder.u64(completed_at)) + .map_err(|_| RuntimeFailure::Malformed)?; + Ok(encoder.into_writer()) +} + +fn encode_envelope(payload: &[u8], signature: &[u8; 64]) -> Result, RuntimeFailure> { + let mut encoder = Encoder::new(Vec::new()); + encoder + .array(3) + .and_then(|encoder| encoder.u16(1)) + .and_then(|encoder| encoder.bytes(payload)) + .and_then(|encoder| encoder.bytes(signature)) + .map_err(|_| RuntimeFailure::Malformed)?; + Ok(encoder.into_writer()) +} + +fn preimage(domain: &[u8], payload: &[u8]) -> Vec { + let mut value = Vec::with_capacity(domain.len() + 8 + payload.len()); + value.extend_from_slice(domain); + value.extend_from_slice( + &u64::try_from(payload.len()) + .unwrap_or(u64::MAX) + .to_be_bytes(), + ); + value.extend_from_slice(payload); + value +} + +fn effect_value(profile: QualifiedProfile, action: &[u8]) -> Vec { + let mut hasher = Sha256::new(); + hasher.update(b"AUTHS-NODE-EFFECT\x00\x01"); + hasher.update(profile.as_str().as_bytes()); + hasher.update(action); + hasher.finalize().to_vec() +} + +fn digest(value: &[u8]) -> [u8; 32] { + Sha256::digest(value).into() +} + +#[cfg(test)] +mod tests { + use super::*; + use auths_testkit::CorpusFixture; + + struct FrozenClock(u64); + + impl NodeClock for FrozenClock { + fn now_unix_seconds(&self) -> u64 { + self.0 + } + } + + fn corpus_methods() -> Vec> { + let (spiffe_trust, spiffe_status) = auths_testkit::spiffe_corpus_context(); + vec![ + Box::new(auths_raw_key::RawKeyMethod::new().unwrap()), + Box::new(auths_did_key::DidKeyMethod::new().unwrap()), + Box::new(auths_did_keri::DidKeriMethod::new().unwrap()), + Box::new( + auths_did_web::DidWebMethod::new(auths_testkit::did_web_corpus_trust_records()) + .unwrap(), + ), + Box::new( + auths_webauthn::WebAuthnMethod::new(auths_testkit::webauthn_corpus_credentials()) + .unwrap(), + ), + Box::new( + auths_hsm_attested::HsmAttestedMethod::new(auths_testkit::hsm_corpus_records()) + .unwrap(), + ), + Box::new( + auths_spiffe_x509::SpiffeX509Method::new(spiffe_trust, spiffe_status).unwrap(), + ), + ] + } + + fn corpus_suites() -> Vec> { + vec![ + Box::new(auths_signature::Ed25519Suite::new().unwrap()), + Box::new(auths_signature::P256Sha256Suite::new().unwrap()), + ] + } + + fn fixture(name: &str) -> CorpusFixture { + auths_testkit::corpus() + .into_iter() + .find(|value| value.name() == name) + .unwrap_or_else(|| panic!("corpus fixture {name}")) + } + + fn runtime_for(fixture: &CorpusFixture) -> KernelRuntime { + let context = auths_codec::decode_verifier_context(fixture.context_bytes()).unwrap(); + let evaluation_time = context.evaluation_time().get(); + KernelRuntime::with_clock( + NodeKernel::new(context, corpus_methods(), corpus_suites()).unwrap(), + [7; 32], + [QualifiedProfile::GitHubIssueAddress].into_iter().collect(), + Arc::new(FrozenClock(evaluation_time)), + ) + .unwrap() + } + + fn execute(fixture: &CorpusFixture, identity: &[u8]) -> ProductionRequest { + ProductionRequest::new( + ProductVerb::Execute, + QualifiedProfile::GitHubIssueAddress, + identity.to_vec(), + Some(fixture.proof_bytes().to_vec()), + Some(auths_codec::encode_canonical_action(fixture.canonical_action()).unwrap()), + None, + ) + .unwrap() + } + + /// `create` previously built `Authority { parent: None, subject: + /// digest(request.identity()) }` and signed it. `parent: None` is a root, + /// and `request.identity()` is unauthenticated caller-supplied bytes, so any + /// caller could mint a root over any subject they named. + /// + /// There is no authentication at this call site to require instead: the + /// production request carries no credential, `api.rs` performs no client + /// authentication, and the reference ingress does not request client + /// certificates. The node therefore refuses. + #[test] + fn the_node_refuses_to_mint_authority_from_a_self_asserted_identity() { + let runtime = runtime_for(&fixture("raw-key-chain")); + for verb in [ProductVerb::Create, ProductVerb::Delegate] { + let authority = (verb == ProductVerb::Delegate).then(|| vec![1]); + let request = ProductionRequest::new( + verb, + QualifiedProfile::GitHubIssueAddress, + b"i-am-whoever-i-say-i-am".to_vec(), + authority, + Some(vec![2]), + None, + ) + .unwrap(); + assert_eq!( + runtime.handle(request), + Err(RuntimeFailure::UnauthenticatedPrincipal), + "{verb:?} issued authority for an unauthenticated identity" + ); + } + } + + /// The decision must be a function of the proof, the action, the context, + /// and the clock alone. The self-asserted identity must not move it. + #[test] + fn the_self_asserted_identity_never_changes_the_decision() { + let authorized = fixture("raw-key-chain"); + for identity in [b"alice".as_slice(), b"root", b"\x00\xff"] { + let runtime = runtime_for(&authorized); + assert_eq!( + runtime + .handle(execute(&authorized, identity)) + .unwrap() + .kind(), + ClientOutcomeKind::Completed + ); + } + let denied = fixture("permission-widening"); + for identity in [b"alice".as_slice(), b"root"] { + let runtime = runtime_for(&denied); + assert_eq!( + runtime.handle(execute(&denied, identity)), + Err(RuntimeFailure::AuthorizationDenied( + auths_model::DenialReason::DelegationExpanded + )) + ); + } + } + + /// The kernel proves the budget ceiling never widens; consuming it is + /// stateful and belongs to the node. One authorized pair, one effect. + #[test] + fn exact_action_and_replay_budget_are_enforced() { + let authorized = fixture("raw-key-chain"); + let runtime = runtime_for(&authorized); + assert_eq!( + runtime + .handle(execute(&authorized, b"caller")) + .unwrap() + .kind(), + ClientOutcomeKind::Completed + ); + assert_eq!( + runtime.handle(execute(&authorized, b"caller")), + Err(RuntimeFailure::ReplayBudgetExhausted) + ); + // A different action under the same proof is the kernel's question, and + // the kernel answers it before any budget is claimed. + let other = fixture("byte-distinct-action"); + let mismatched = ProductionRequest::new( + ProductVerb::Execute, + QualifiedProfile::GitHubIssueAddress, + b"caller".to_vec(), + Some(authorized.proof_bytes().to_vec()), + Some(auths_codec::encode_canonical_action(other.canonical_action()).unwrap()), + None, + ) + .unwrap(); + assert_eq!( + runtime.handle(mismatched), + Err(RuntimeFailure::AuthorizationDenied( + auths_model::DenialReason::ActionBodyMismatch + )) + ); + } + + /// Nothing is claimed and no effect is applied before the kernel authorizes. + #[test] + fn a_denied_request_consumes_no_budget_and_leaves_no_receipt() { + let denied = fixture("permission-widening"); + let runtime = runtime_for(&denied); + for _ in 0..3 { + assert_eq!( + runtime.handle(execute(&denied, b"caller")), + Err(RuntimeFailure::AuthorizationDenied( + auths_model::DenialReason::DelegationExpanded + )), + "a denial consumed stateful budget and changed the answer" + ); + } + } + + /// Recovery commits exactly once and every later resume replays the same + /// signed receipt. The subject here is the durable commit, not the + /// authorization decision, so the pending effect is seeded directly. + #[test] + fn recovery_is_committed_once_and_replays_the_same_receipt() { + let runtime = runtime_for(&fixture("raw-key-chain")); + let claim = effect_claim(b"proof", b"action"); + let reference = KernelRuntime::recovery_reference(claim).unwrap(); + runtime + .store + .put_pending( + reference.as_str(), + &PendingEffect { + profile: QualifiedProfile::GitHubIssueAddress, + authority: claim, + action: b"action".to_vec(), + created_at: 1, + }, + ) + .unwrap(); + let resume = || { + ProductionRequest::new( + ProductVerb::Resume, + QualifiedProfile::GitHubIssueAddress, + b"caller".to_vec(), + None, + None, + Some(reference.clone()), + ) + .unwrap() + }; + let first = runtime.handle(resume()).unwrap(); + let replay = runtime.handle(resume()).unwrap(); + assert_eq!(first.kind(), ClientOutcomeKind::Completed); + assert_eq!(first.receipt(), replay.receipt()); + assert_eq!(first.value(), replay.value()); + let status = runtime.status(&reference).unwrap(); + assert_eq!(status.state, "committed"); + assert_eq!(status.effect, EffectState::Applied); + assert!(status.receipt_id.is_some()); + } + + /// An unknown receipt and an unauthorized disclosure must be + /// indistinguishable, or the endpoint is an existence oracle. + #[test] + fn disclosure_never_reveals_whether_an_unreadable_receipt_exists() { + let authorized = fixture("raw-key-chain"); + let runtime = runtime_for(&authorized); + runtime.handle(execute(&authorized, b"caller")).unwrap(); + let known = runtime + .store + .receipt(&hex::encode([0_u8; 32])) + .unwrap() + .map_or_else(|| "0".repeat(64), |_| unreachable!()); + assert_eq!( + runtime.disclose_receipt(&known, b"forged"), + Err(RuntimeFailure::DisclosureDenied) + ); + assert_eq!( + runtime.disclose_receipt(&"f".repeat(64), b"forged"), + Err(RuntimeFailure::DisclosureDenied) + ); + } + + /// `/v1/authority/verify` may only claim what the kernel can establish + /// without an action, and must never answer for a proof the kernel rejects. + #[test] + fn verify_reports_the_kernels_staged_result() { + let authorized = fixture("raw-key-chain"); + let runtime = runtime_for(&authorized); + let request = |body: Vec| { + ProductionRequest::new( + ProductVerb::Verify, + QualifiedProfile::GitHubIssueAddress, + b"caller".to_vec(), + None, + Some(body), + None, + ) + .unwrap() + }; + assert_eq!( + runtime + .handle(request(authorized.proof_bytes().to_vec())) + .unwrap() + .kind(), + ClientOutcomeKind::Verified + ); + let malformed = fixture("trailing-bytes"); + assert_eq!( + runtime.handle(request(malformed.proof_bytes().to_vec())), + Err(RuntimeFailure::AuthorizationDenied( + auths_model::DenialReason::MalformedProof + )) + ); + } +} diff --git a/product/runtime/auths-node/src/lib.rs b/product/runtime/auths-node/src/lib.rs index ebfdca13..a591907c 100644 --- a/product/runtime/auths-node/src/lib.rs +++ b/product/runtime/auths-node/src/lib.rs @@ -2,13 +2,13 @@ pub mod api; pub mod config; +pub mod kernel; pub mod profiles; -pub mod sandbox; mod sandbox_store; pub mod shutdown; pub use api::{NodeRuntime, app}; pub use config::{DoctorReport, NodeConfig, StartupError}; +pub use kernel::{KernelRuntime, NodeClock, NodeKernel, SystemNodeClock}; pub use profiles::{ClosedProfileRegistry, RuntimeFailure}; -pub use sandbox::{SandboxRuntime, encode_sandbox_authority_request}; pub use sandbox_store::PostgresSandboxStore; diff --git a/product/runtime/auths-node/src/main.rs b/product/runtime/auths-node/src/main.rs index a0d7e0ce..b966f437 100644 --- a/product/runtime/auths-node/src/main.rs +++ b/product/runtime/auths-node/src/main.rs @@ -1,9 +1,12 @@ #![forbid(unsafe_code)] -use auths_node::{NodeConfig, NodeRuntime, PostgresSandboxStore, SandboxRuntime, app, shutdown}; +use auths_node::{ + KernelRuntime, NodeConfig, NodeKernel, NodeRuntime, PostgresSandboxStore, app, shutdown, +}; +use auths_ports::{PrincipalMethod, SignatureSuite}; use base64ct::{Base64UrlUnpadded, Encoding as _}; use std::{ - env, + env, fs, path::Path, process::ExitCode, sync::{Arc, atomic::AtomicBool}, @@ -20,6 +23,35 @@ async fn main() -> ExitCode { } } +/// Builds the deployment's verification inputs from configuration. +/// +/// The trusted context is supplied as canonical bytes rather than assembled +/// from TOML: it is the deployment's complete trust decision, and re-encoding it +/// from a friendlier format would put a second, unverified encoder between the +/// operator and the verifier. +/// +/// The registered principal methods are the three that need no external trust +/// material. `did:web`, `WebAuthn`, HSM attestation, and SPIFFE each require +/// deployment-supplied trust records that this configuration does not yet carry; +/// a proof relying on one of them is answered `core.authorization-indeterminate` +/// rather than accepted, which is the fail-closed direction. +fn kernel(config: &NodeConfig) -> Result> { + let bytes = fs::read(config.trusted_context_path()) + .map_err(|_| "the trusted context is unavailable")?; + let context = auths_codec::decode_verifier_context(&bytes) + .map_err(|_| "the trusted context is not canonical")?; + let methods: Vec> = vec![ + Box::new(auths_raw_key::RawKeyMethod::new()?), + Box::new(auths_did_key::DidKeyMethod::new()?), + Box::new(auths_did_keri::DidKeriMethod::new()?), + ]; + let suites: Vec> = vec![ + Box::new(auths_signature::Ed25519Suite::new()?), + Box::new(auths_signature::P256Sha256Suite::new()?), + ]; + Ok(NodeKernel::new(context, methods, suites)?) +} + async fn run() -> Result<(), Box> { let mut arguments = env::args().skip(1); let config_path = arguments @@ -43,6 +75,7 @@ async fn run() -> Result<(), Box> { let mut seed_bytes = [0; 32]; Base64UrlUnpadded::decode(&seed, &mut seed_bytes) .map_err(|_| "local fixture custody seed is malformed")?; + let kernel = kernel(&config)?; let connection = env::var(config.lifecycle_url_env()) .map_err(|_| "PostgreSQL lifecycle connection is unavailable")?; let lifecycle_ca_pem = config.lifecycle_ca_pem().to_owned(); @@ -57,7 +90,8 @@ async fn run() -> Result<(), Box> { ) }) .await??; - let runtime = Arc::new(SandboxRuntime::with_postgres( + let runtime = Arc::new(KernelRuntime::with_postgres( + kernel, seed_bytes, config.enabled_profiles(), store, diff --git a/product/runtime/auths-node/src/profiles.rs b/product/runtime/auths-node/src/profiles.rs index 7e7573f1..f362fc47 100644 --- a/product/runtime/auths-node/src/profiles.rs +++ b/product/runtime/auths-node/src/profiles.rs @@ -1,3 +1,4 @@ +use auths_model::{DenialReason, Requirement}; use auths_operations::EffectState; use auths_production_client::{ ClientOutcomeKind, NextCall, ProductVerb, ProductionRequest, ProductionResponse, @@ -20,10 +21,29 @@ pub enum ProviderBoundary { AfterEntry, } +/// Every failure this node can put on the wire. +/// +/// Each variant projects to one code that exists in +/// `product/errors/v1/registry.json`; `every_wire_code_is_registered` holds +/// that. The two authorization variants carry the kernel's exact reason so the +/// node's decision can be compared to the kernel's without translation, while +/// the wire stays at the coarse registered code. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum RuntimeFailure { - Denied, - Indeterminate, + /// Available facts prove the proof does not authorize the exact action. + AuthorizationDenied(DenialReason), + /// A required authorization fact was unavailable, before any effect. + AuthorizationIndeterminate(Requirement), + /// The request asserts a principal the node cannot authenticate. + UnauthenticatedPrincipal, + /// The exact authorized pair has already produced every effect it may. + /// + /// The kernel proves the budget ceiling never widens. Consuming it is + /// stateful and therefore the node's obligation, not the verifier's, so + /// this denial is the node's own and never carries a kernel reason. + ReplayBudgetExhausted, + /// A concurrent operation changed the exact workflow state. + StateConflict, /// A bounded operation failed **before provider entry**. /// /// This is the only runtime failure that may claim a safe blind retry. A @@ -33,8 +53,8 @@ pub enum RuntimeFailure { ProviderOutcomeUnknown, Malformed, ProfileDisabled, - UnknownWorkflow, - UnknownReceipt, + /// A workflow or receipt reference does not name state this caller may read. + UnknownReference, DisclosureDenied, } @@ -51,23 +71,31 @@ impl RuntimeFailure { #[must_use] pub const fn code(self) -> &'static str { match self { - Self::Denied => "authority.denied", - Self::Indeterminate => "authority.indeterminate", + // A denied disclosure is a denied authorization: the caller did not + // present the authorization this receipt requires. + Self::AuthorizationDenied(_) | Self::DisclosureDenied | Self::ReplayBudgetExhausted => { + "core.authorization-denied" + } + Self::AuthorizationIndeterminate(_) => "core.authorization-indeterminate", + Self::UnauthenticatedPrincipal => "core.unauthenticated-principal", + Self::StateConflict => "core.runtime-conflict", Self::Unavailable => "core.runtime-unavailable", Self::ProviderOutcomeUnknown => "core.outcome-unknown", Self::Malformed => "core.malformed-input", - Self::ProfileDisabled => "profile.disabled", - Self::UnknownWorkflow => "workflow.unknown", - Self::UnknownReceipt => "receipt.unknown", - Self::DisclosureDenied => "receipt.disclosure-denied", + // A profile this deployment did not enable is a configuration fact. + Self::ProfileDisabled => "core.invalid-configuration", + Self::UnknownReference => "core.forged-execution-reference", } } #[must_use] pub const fn retry(self) -> NextCall { match self { - Self::Unavailable => NextCall::Backoff, - Self::Indeterminate | Self::ProviderOutcomeUnknown => NextCall::Reconcile, + // Nothing was applied and the blocking condition may clear. + Self::AuthorizationIndeterminate(_) | Self::Unavailable | Self::StateConflict => { + NextCall::Backoff + } + Self::ProviderOutcomeUnknown => NextCall::Reconcile, _ => NextCall::Never, } } @@ -76,15 +104,19 @@ impl RuntimeFailure { #[must_use] pub const fn effect(self) -> EffectState { match self { - // Authority verification and provider entry both end here without - // conclusive evidence, so neither may assert non-effect. - Self::Indeterminate | Self::ProviderOutcomeUnknown => EffectState::Possible, - Self::Denied + // Provider entry is the only place this node loses the proof of + // non-effect. Every authorization outcome, including an + // indeterminate one, is decided strictly before any effect. + Self::ProviderOutcomeUnknown => EffectState::Possible, + Self::AuthorizationDenied(_) + | Self::AuthorizationIndeterminate(_) + | Self::UnauthenticatedPrincipal + | Self::ReplayBudgetExhausted + | Self::StateConflict | Self::Unavailable | Self::Malformed | Self::ProfileDisabled - | Self::UnknownWorkflow - | Self::UnknownReceipt + | Self::UnknownReference | Self::DisclosureDenied => EffectState::NotApplied, } } @@ -116,7 +148,9 @@ pub struct WorkflowProjection { pub struct ReceiptSummary { pub receipt_id: String, pub profile: String, - pub outcome: String, + /// Rust-owned effect axis. Never a locally invented word: this field + /// previously carried the free `String` `"succeeded"`. + pub effect: EffectState, pub completed_at: u64, pub disclosure: &'static str, } @@ -324,13 +358,15 @@ fn completed_authority(value: Vec) -> Result ProductionResponse { let kind = match error { - RuntimeFailure::Denied + RuntimeFailure::AuthorizationDenied(_) + | RuntimeFailure::UnauthenticatedPrincipal + | RuntimeFailure::ReplayBudgetExhausted | RuntimeFailure::Malformed | RuntimeFailure::ProfileDisabled - | RuntimeFailure::UnknownWorkflow - | RuntimeFailure::UnknownReceipt + | RuntimeFailure::UnknownReference | RuntimeFailure::DisclosureDenied => ClientOutcomeKind::Denied, - RuntimeFailure::Indeterminate + RuntimeFailure::AuthorizationIndeterminate(_) + | RuntimeFailure::StateConflict | RuntimeFailure::Unavailable | RuntimeFailure::ProviderOutcomeUnknown => ClientOutcomeKind::Indeterminate, }; @@ -354,14 +390,16 @@ mod tests { use super::*; const EVERY_FAILURE: &[RuntimeFailure] = &[ - RuntimeFailure::Denied, - RuntimeFailure::Indeterminate, + RuntimeFailure::AuthorizationDenied(DenialReason::UntrustedRoot), + RuntimeFailure::AuthorizationIndeterminate(Requirement::ExternalFactUnavailable), + RuntimeFailure::UnauthenticatedPrincipal, + RuntimeFailure::ReplayBudgetExhausted, + RuntimeFailure::StateConflict, RuntimeFailure::Unavailable, RuntimeFailure::ProviderOutcomeUnknown, RuntimeFailure::Malformed, RuntimeFailure::ProfileDisabled, - RuntimeFailure::UnknownWorkflow, - RuntimeFailure::UnknownReceipt, + RuntimeFailure::UnknownReference, RuntimeFailure::DisclosureDenied, ]; @@ -405,6 +443,64 @@ mod tests { } } + /// Every code this node can put on the wire must exist in the product error + /// registry. Eight of the ten it previously emitted did not: + /// `authority.denied`, `authority.indeterminate`, `profile.disabled`, + /// `workflow.unknown`, `receipt.unknown`, `receipt.disclosure-denied`, + /// `provider.outcome-unknown`, and `verification.rejected`. + #[test] + fn every_wire_code_is_registered() { + for failure in EVERY_FAILURE.iter().copied() { + let code = failure.code(); + assert!( + auths_errors::registry().any(|definition| definition.code == code), + "{failure:?} puts the unregistered code {code:?} on the wire" + ); + } + } + + /// A registry definition states which retry and effect pairs the code is + /// allowed to carry. The node's own projection must be one of them. + #[test] + fn every_wire_code_carries_a_registered_effect() { + for failure in EVERY_FAILURE.iter().copied() { + let code = failure.code(); + let definition = auths_errors::registry() + .find(|definition| definition.code == code) + .unwrap_or_else(|| panic!("{code} is registered")); + let effect = match failure.effect() { + EffectState::NotApplied => auths_errors::EffectState::NotApplied, + EffectState::Possible => auths_errors::EffectState::Possible, + EffectState::Applied => auths_errors::EffectState::Applied, + }; + assert!( + definition + .outcomes + .iter() + .any(|outcome| outcome.effect == effect), + "{failure:?} claims an effect {code} does not allow" + ); + } + } + + /// The receipt summary must speak the same Rust-owned effect vocabulary as + /// the workflow projection. It previously carried the invented + /// `outcome: "succeeded"`. + #[test] + fn the_receipt_summary_speaks_only_the_rust_owned_effect_vocabulary() { + let summary = ReceiptSummary { + receipt_id: "a".repeat(64), + profile: "auths.github.issue-address/1".into(), + effect: EffectState::Applied, + completed_at: 1, + disclosure: "summary", + }; + let value: serde_json::Value = + serde_json::from_str(&serde_json::to_string(&summary).unwrap()).unwrap(); + assert_eq!(value["effect"].as_str(), Some("applied")); + assert!(value.get("outcome").is_none()); + } + /// The public status projection must speak the one Rust-owned effect /// vocabulary. It previously carried a free `String` and shipped the locally /// invented words `"unknown"` and `"succeeded"`. diff --git a/product/runtime/auths-node/src/sandbox.rs b/product/runtime/auths-node/src/sandbox.rs deleted file mode 100644 index f4a91852..00000000 --- a/product/runtime/auths-node/src/sandbox.rs +++ /dev/null @@ -1,801 +0,0 @@ -use crate::{ - api::NodeRuntime, - profiles::{ReceiptSummary, RuntimeFailure, WorkflowProjection}, - sandbox_store::{ - MemorySandboxStore, PendingEffect, PostgresSandboxStore, SandboxStore, StoredReceipt, - }, -}; -use auths_operations::EffectState; -use auths_production_client::{ - ClientOutcomeKind, NextCall, ProductVerb, ProductionRequest, ProductionResponse, - QualifiedProfile, RecoveryReference, decode_delegation_body, -}; -use base64ct::{Base64UrlUnpadded, Encoding as _}; -use ed25519_dalek::{Signature, Signer as _, SigningKey, Verifier as _, VerifyingKey}; -use minicbor::{Decoder, Encoder}; -use sha2::{Digest as _, Sha256}; -use std::{ - collections::BTreeSet, - sync::Arc, - time::{SystemTime, UNIX_EPOCH}, -}; - -const AUTHORITY_DOMAIN: &[u8] = b"AUTHS-SANDBOX-AUTHORITY\x00\x01"; -const RECEIPT_DOMAIN: &[u8] = b"AUTHS-SANDBOX-RECEIPT\x00\x01"; -const RECOVERY_DOMAIN: &[u8] = b"AUTHS-SANDBOX-RECOVERY\x00\x01"; -const DISCLOSURE_DOMAIN: &[u8] = b"AUTHS-SANDBOX-DISCLOSURE\x00\x01"; -const MAX_ACTIONS: usize = 64; - -#[derive(Clone, Debug, Eq, PartialEq)] -struct Scope { - expires_at: u64, - remaining_depth: u16, - max_uses: u32, - action_digests: Vec<[u8; 32]>, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -struct Authority { - profile: QualifiedProfile, - subject: [u8; 32], - parent: Option<[u8; 32]>, - scope: Scope, -} - -pub struct SandboxRuntime { - signing: SigningKey, - verifying: VerifyingKey, - profiles: BTreeSet, - store: Arc, -} - -impl SandboxRuntime { - /// Creates an isolated deterministic runtime with in-memory state. - /// - /// # Errors - /// - /// Returns malformed for a zero seed or an empty profile set. - pub fn new( - seed: [u8; 32], - profiles: BTreeSet, - ) -> Result { - Self::with_store(seed, profiles, Arc::new(MemorySandboxStore::default())) - } - - pub(crate) fn with_store( - seed: [u8; 32], - profiles: BTreeSet, - store: Arc, - ) -> Result { - if seed == [0; 32] || profiles.is_empty() { - return Err(RuntimeFailure::Malformed); - } - let signing = SigningKey::from_bytes(&seed); - let verifying = signing.verifying_key(); - Ok(Self { - signing, - verifying, - profiles, - store, - }) - } - - /// Creates an isolated deterministic runtime with `PostgreSQL` state. - /// - /// # Errors - /// - /// Returns malformed for a zero seed or an empty profile set. - pub fn with_postgres( - seed: [u8; 32], - profiles: BTreeSet, - store: PostgresSandboxStore, - ) -> Result { - Self::with_store(seed, profiles, Arc::new(store)) - } - - fn create(&self, request: &ProductionRequest) -> Result { - self.require_profile(request.profile())?; - let scope = decode_scope(request.body().ok_or(RuntimeFailure::Malformed)?)?; - if scope.expires_at <= now() { - return Err(RuntimeFailure::Denied); - } - let authority = Authority { - profile: request.profile(), - subject: digest(request.identity()), - parent: None, - scope, - }; - authority_response(self.sign(AUTHORITY_DOMAIN, &encode_authority_payload(&authority)?)?) - } - - fn delegate(&self, request: &ProductionRequest) -> Result { - self.require_profile(request.profile())?; - let parent_bytes = request.authority().ok_or(RuntimeFailure::Malformed)?; - let parent = self.verify_authority(parent_bytes)?; - if parent.profile != request.profile() || parent.subject != digest(request.identity()) { - return Err(RuntimeFailure::Denied); - } - let (subject, attenuation) = - decode_delegation_body(request.body().ok_or(RuntimeFailure::Malformed)?) - .map_err(|_| RuntimeFailure::Malformed)?; - let scope = decode_scope(&attenuation)?; - if parent.scope.remaining_depth == 0 - || scope.remaining_depth >= parent.scope.remaining_depth - || scope.expires_at > parent.scope.expires_at - || scope.max_uses > parent.scope.max_uses - || !scope - .action_digests - .iter() - .all(|digest| parent.scope.action_digests.contains(digest)) - { - return Err(RuntimeFailure::Denied); - } - let authority = Authority { - profile: request.profile(), - subject: digest(&subject), - parent: Some(digest(parent_bytes)), - scope, - }; - authority_response(self.sign(AUTHORITY_DOMAIN, &encode_authority_payload(&authority)?)?) - } - - fn execute(&self, request: &ProductionRequest) -> Result { - self.require_profile(request.profile())?; - let authority_bytes = request.authority().ok_or(RuntimeFailure::Malformed)?; - let authority = self.verify_authority(authority_bytes)?; - let action = request.body().ok_or(RuntimeFailure::Malformed)?; - let action_digest = digest(action); - if authority.profile != request.profile() - || authority.subject != digest(request.identity()) - || authority.scope.expires_at <= now() - || !authority.scope.action_digests.contains(&action_digest) - { - return Err(RuntimeFailure::Denied); - } - let authority_digest = digest(authority_bytes); - self.store - .claim_use(authority_digest, authority.scope.max_uses)?; - if action.starts_with(b"AUTHS-SANDBOX-RECOVER") { - let reference = recovery_reference(authority_digest, action_digest)?; - self.store.put_pending( - reference.as_str(), - &PendingEffect { - profile: request.profile(), - authority: authority_digest, - action: action.to_vec(), - created_at: now(), - }, - )?; - return ProductionResponse::new( - ClientOutcomeKind::Recoverable, - Some("provider.outcome-unknown".into()), - NextCall::Resume, - Some(reference), - None, - None, - ) - .map_err(|_| RuntimeFailure::Malformed); - } - let (receipt, value) = - self.complete_effect(request.profile(), authority_digest, action, now())?; - completed_response(receipt, value) - } - - fn resume(&self, request: &ProductionRequest) -> Result { - let reference = request - .recovery_reference() - .ok_or(RuntimeFailure::Malformed)?; - let Some(pending) = self.store.pending(reference.as_str())? else { - let completed = self - .store - .recovered(reference.as_str())? - .ok_or(RuntimeFailure::UnknownWorkflow)?; - if completed.profile != request.profile() { - return Err(RuntimeFailure::Denied); - } - return completed_response(completed.bytes, completed.value); - }; - if pending.profile != request.profile() { - return Err(RuntimeFailure::Denied); - } - let (receipt_id, receipt) = - self.build_effect(pending.profile, pending.authority, &pending.action, now())?; - let completed = - self.store - .finish_pending(reference.as_str(), &pending, &receipt_id, &receipt)?; - completed_response(completed.bytes, completed.value) - } - - fn complete_effect( - &self, - profile: QualifiedProfile, - authority: [u8; 32], - action: &[u8], - completed_at: u64, - ) -> Result<(Vec, Vec), RuntimeFailure> { - let (receipt_id, receipt) = self.build_effect(profile, authority, action, completed_at)?; - self.store.put_receipt(&receipt_id, &receipt)?; - Ok((receipt.bytes, receipt.value)) - } - - fn build_effect( - &self, - profile: QualifiedProfile, - authority: [u8; 32], - action: &[u8], - completed_at: u64, - ) -> Result<(String, StoredReceipt), RuntimeFailure> { - let value = effect_value(profile, action); - let payload = encode_receipt_payload( - profile, - authority, - digest(action), - digest(&value), - completed_at, - )?; - let receipt = self.sign(RECEIPT_DOMAIN, &payload)?; - let receipt_id = hex::encode(digest(&receipt)); - Ok(( - receipt_id, - StoredReceipt { - profile, - completed_at, - bytes: receipt, - value, - }, - )) - } - - fn verify_authority(&self, bytes: &[u8]) -> Result { - let payload = self.verify_envelope(AUTHORITY_DOMAIN, bytes)?; - decode_authority_payload(&payload) - } - - fn sign(&self, domain: &[u8], payload: &[u8]) -> Result, RuntimeFailure> { - let preimage = preimage(domain, payload); - let signature = self.signing.sign(&preimage).to_bytes(); - encode_envelope(payload, &signature) - } - - fn verify_envelope(&self, domain: &[u8], bytes: &[u8]) -> Result, RuntimeFailure> { - let (payload, signature) = decode_envelope(bytes)?; - self.verifying - .verify(&preimage(domain, &payload), &signature) - .map_err(|_| RuntimeFailure::Denied)?; - Ok(payload) - } - - fn require_profile(&self, profile: QualifiedProfile) -> Result<(), RuntimeFailure> { - if self.profiles.contains(&profile) { - Ok(()) - } else { - Err(RuntimeFailure::ProfileDisabled) - } - } -} - -impl NodeRuntime for SandboxRuntime { - fn handle(&self, request: ProductionRequest) -> Result { - match request.verb() { - ProductVerb::Create => self.create(&request), - ProductVerb::Delegate => self.delegate(&request), - ProductVerb::Execute => self.execute(&request), - ProductVerb::Resume => self.resume(&request), - ProductVerb::Verify => { - let bytes = request.body().ok_or(RuntimeFailure::Malformed)?; - let valid = self.verify_authority(bytes).is_ok() - || self.verify_envelope(RECEIPT_DOMAIN, bytes).is_ok(); - if !valid { - return ProductionResponse::new( - ClientOutcomeKind::Rejected, - Some("verification.rejected".into()), - NextCall::Never, - None, - None, - None, - ) - .map_err(|_| RuntimeFailure::Malformed); - } - ProductionResponse::new( - ClientOutcomeKind::Verified, - None, - NextCall::Never, - None, - None, - None, - ) - .map_err(|_| RuntimeFailure::Malformed) - } - } - } - - fn status(&self, reference: &RecoveryReference) -> Result { - if let Some(pending) = self.store.pending(reference.as_str())? { - return Ok(WorkflowProjection { - reference: reference.as_str().to_owned(), - profile: pending.profile.as_str().into(), - state: "outcome-unknown".into(), - effect: EffectState::Possible, - retry: NextCall::Resume, - updated_at: pending.created_at, - receipt_id: None, - }); - } - let completed = self - .store - .recovered(reference.as_str())? - .ok_or(RuntimeFailure::UnknownWorkflow)?; - Ok(WorkflowProjection { - reference: reference.as_str().to_owned(), - profile: completed.profile.as_str().into(), - state: "committed".into(), - effect: EffectState::Applied, - retry: NextCall::Never, - updated_at: completed.completed_at, - receipt_id: Some(hex::encode(digest(&completed.bytes))), - }) - } - - fn receipt_summary(&self, receipt_id: &str) -> Result { - let receipt = self - .store - .receipt(receipt_id)? - .ok_or(RuntimeFailure::UnknownReceipt)?; - Ok(ReceiptSummary { - receipt_id: receipt_id.into(), - profile: receipt.profile.as_str().into(), - outcome: "succeeded".into(), - completed_at: receipt.completed_at, - disclosure: "summary", - }) - } - - fn disclose_receipt( - &self, - receipt_id: &str, - authorization: &[u8], - ) -> Result, RuntimeFailure> { - let expected = self.sign(DISCLOSURE_DOMAIN, receipt_id.as_bytes())?; - if authorization != expected { - return Err(RuntimeFailure::DisclosureDenied); - } - self.store - .receipt(receipt_id)? - .map(|receipt| receipt.bytes.clone()) - .ok_or(RuntimeFailure::UnknownReceipt) - } - - fn ready(&self) -> bool { - self.store.ready() - } -} - -/// Encodes one bounded sandbox authority request. -/// -/// # Errors -/// -/// Returns malformed for empty, duplicate, excessive, or zero-use actions. -pub fn encode_sandbox_authority_request( - expires_at: u64, - remaining_depth: u16, - max_uses: u32, - actions: &[&[u8]], -) -> Result, RuntimeFailure> { - if actions.is_empty() || actions.len() > MAX_ACTIONS || max_uses == 0 { - return Err(RuntimeFailure::Malformed); - } - let mut action_digests = actions - .iter() - .map(|action| digest(action)) - .collect::>(); - action_digests.sort_unstable(); - action_digests.dedup(); - if action_digests.len() != actions.len() { - return Err(RuntimeFailure::Malformed); - } - encode_scope(&Scope { - expires_at, - remaining_depth, - max_uses, - action_digests, - }) -} - -fn authority_response(value: Vec) -> Result { - ProductionResponse::new( - ClientOutcomeKind::Completed, - None, - NextCall::Never, - None, - Some(value), - Some(b"auths-sandbox-authority-v1".to_vec()), - ) - .map_err(|_| RuntimeFailure::Malformed) -} - -fn completed_response( - receipt: Vec, - value: Vec, -) -> Result { - ProductionResponse::new( - ClientOutcomeKind::Completed, - None, - NextCall::Never, - None, - Some(value), - Some(receipt), - ) - .map_err(|_| RuntimeFailure::Malformed) -} - -fn encode_scope(scope: &Scope) -> Result, RuntimeFailure> { - let mut encoder = Encoder::new(Vec::new()); - encoder - .array(5) - .and_then(|encoder| encoder.u16(1)) - .and_then(|encoder| encoder.u64(scope.expires_at)) - .and_then(|encoder| encoder.u16(scope.remaining_depth)) - .and_then(|encoder| encoder.u32(scope.max_uses)) - .and_then(|encoder| { - encoder.array(u64::try_from(scope.action_digests.len()).unwrap_or(u64::MAX)) - }) - .map_err(|_| RuntimeFailure::Malformed)?; - for digest in &scope.action_digests { - encoder - .bytes(digest) - .map_err(|_| RuntimeFailure::Malformed)?; - } - Ok(encoder.into_writer()) -} - -fn decode_scope(bytes: &[u8]) -> Result { - let mut decoder = Decoder::new(bytes); - if decoder.array().ok().flatten() != Some(5) || decoder.u16().ok() != Some(1) { - return Err(RuntimeFailure::Malformed); - } - let expires_at = decoder.u64().map_err(|_| RuntimeFailure::Malformed)?; - let remaining_depth = decoder.u16().map_err(|_| RuntimeFailure::Malformed)?; - let max_uses = decoder.u32().map_err(|_| RuntimeFailure::Malformed)?; - let count = decoder - .array() - .map_err(|_| RuntimeFailure::Malformed)? - .ok_or(RuntimeFailure::Malformed)?; - if count == 0 || count > u64::try_from(MAX_ACTIONS).unwrap_or(u64::MAX) || max_uses == 0 { - return Err(RuntimeFailure::Malformed); - } - let mut action_digests = - Vec::with_capacity(usize::try_from(count).map_err(|_| RuntimeFailure::Malformed)?); - for _ in 0..count { - let value: [u8; 32] = decoder - .bytes() - .map_err(|_| RuntimeFailure::Malformed)? - .try_into() - .map_err(|_| RuntimeFailure::Malformed)?; - action_digests.push(value); - } - let value = Scope { - expires_at, - remaining_depth, - max_uses, - action_digests, - }; - if decoder.position() != bytes.len() - || value - .action_digests - .windows(2) - .any(|pair| pair[0] >= pair[1]) - || encode_scope(&value)? != bytes - { - return Err(RuntimeFailure::Malformed); - } - Ok(value) -} - -fn encode_authority_payload(authority: &Authority) -> Result, RuntimeFailure> { - let scope = encode_scope(&authority.scope)?; - let mut encoder = Encoder::new(Vec::new()); - encoder - .array(6) - .and_then(|encoder| encoder.u16(1)) - .and_then(|encoder| encoder.str(authority.profile.as_str())) - .and_then(|encoder| encoder.bytes(&authority.subject)) - .map_err(|_| RuntimeFailure::Malformed)?; - match authority.parent { - Some(parent) => encoder.bytes(&parent), - None => encoder.null(), - } - .map_err(|_| RuntimeFailure::Malformed)?; - encoder - .bytes(&scope) - .map_err(|_| RuntimeFailure::Malformed)?; - encoder - .str("sandbox-only") - .map_err(|_| RuntimeFailure::Malformed)?; - Ok(encoder.into_writer()) -} - -fn decode_authority_payload(bytes: &[u8]) -> Result { - let mut decoder = Decoder::new(bytes); - if decoder.array().ok().flatten() != Some(6) || decoder.u16().ok() != Some(1) { - return Err(RuntimeFailure::Malformed); - } - let profile = QualifiedProfile::parse(decoder.str().map_err(|_| RuntimeFailure::Malformed)?) - .map_err(|_| RuntimeFailure::Malformed)?; - let subject = decoder - .bytes() - .map_err(|_| RuntimeFailure::Malformed)? - .try_into() - .map_err(|_| RuntimeFailure::Malformed)?; - let parent = if decoder.datatype().map_err(|_| RuntimeFailure::Malformed)? - == minicbor::data::Type::Null - { - decoder.null().map_err(|_| RuntimeFailure::Malformed)?; - None - } else { - Some( - decoder - .bytes() - .map_err(|_| RuntimeFailure::Malformed)? - .try_into() - .map_err(|_| RuntimeFailure::Malformed)?, - ) - }; - let scope = decode_scope(decoder.bytes().map_err(|_| RuntimeFailure::Malformed)?)?; - if decoder.str().map_err(|_| RuntimeFailure::Malformed)? != "sandbox-only" { - return Err(RuntimeFailure::Malformed); - } - let value = Authority { - profile, - subject, - parent, - scope, - }; - if decoder.position() != bytes.len() || encode_authority_payload(&value)? != bytes { - return Err(RuntimeFailure::Malformed); - } - Ok(value) -} - -fn encode_receipt_payload( - profile: QualifiedProfile, - authority: [u8; 32], - action: [u8; 32], - result: [u8; 32], - completed_at: u64, -) -> Result, RuntimeFailure> { - let mut encoder = Encoder::new(Vec::new()); - encoder - .array(6) - .and_then(|encoder| encoder.u16(1)) - .and_then(|encoder| encoder.str(profile.as_str())) - .and_then(|encoder| encoder.bytes(&authority)) - .and_then(|encoder| encoder.bytes(&action)) - .and_then(|encoder| encoder.bytes(&result)) - .and_then(|encoder| encoder.u64(completed_at)) - .map_err(|_| RuntimeFailure::Malformed)?; - Ok(encoder.into_writer()) -} - -fn encode_envelope(payload: &[u8], signature: &[u8; 64]) -> Result, RuntimeFailure> { - let mut encoder = Encoder::new(Vec::new()); - encoder - .array(3) - .and_then(|encoder| encoder.u16(1)) - .and_then(|encoder| encoder.bytes(payload)) - .and_then(|encoder| encoder.bytes(signature)) - .map_err(|_| RuntimeFailure::Malformed)?; - Ok(encoder.into_writer()) -} - -fn decode_envelope(bytes: &[u8]) -> Result<(Vec, Signature), RuntimeFailure> { - let mut decoder = Decoder::new(bytes); - if decoder.array().ok().flatten() != Some(3) || decoder.u16().ok() != Some(1) { - return Err(RuntimeFailure::Malformed); - } - let payload = decoder - .bytes() - .map_err(|_| RuntimeFailure::Malformed)? - .to_vec(); - let signature = Signature::from_slice(decoder.bytes().map_err(|_| RuntimeFailure::Malformed)?) - .map_err(|_| RuntimeFailure::Malformed)?; - if decoder.position() != bytes.len() - || encode_envelope(&payload, &signature.to_bytes())? != bytes - { - return Err(RuntimeFailure::Malformed); - } - Ok((payload, signature)) -} - -fn preimage(domain: &[u8], payload: &[u8]) -> Vec { - let mut value = Vec::with_capacity(domain.len() + 8 + payload.len()); - value.extend_from_slice(domain); - value.extend_from_slice( - &u64::try_from(payload.len()) - .unwrap_or(u64::MAX) - .to_be_bytes(), - ); - value.extend_from_slice(payload); - value -} - -fn recovery_reference( - authority: [u8; 32], - action: [u8; 32], -) -> Result { - let mut nonce = [0; 32]; - getrandom::fill(&mut nonce).map_err(|_| RuntimeFailure::Unavailable)?; - let mut hasher = Sha256::new(); - hasher.update(RECOVERY_DOMAIN); - hasher.update(authority); - hasher.update(action); - hasher.update(nonce); - RecoveryReference::parse(&Base64UrlUnpadded::encode_string(&hasher.finalize())) - .map_err(|_| RuntimeFailure::Malformed) -} - -fn effect_value(profile: QualifiedProfile, action: &[u8]) -> Vec { - let mut hasher = Sha256::new(); - hasher.update(b"AUTHS-SANDBOX-EFFECT\x00\x01"); - hasher.update(profile.as_str().as_bytes()); - hasher.update(action); - hasher.finalize().to_vec() -} - -fn digest(value: &[u8]) -> [u8; 32] { - Sha256::digest(value).into() -} - -fn now() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_or(0, |duration| duration.as_secs()) -} - -#[cfg(test)] -mod tests { - use super::*; - use auths_production_client::{ProductionRequest, encode_delegation_body}; - - fn runtime() -> SandboxRuntime { - SandboxRuntime::new( - [7; 32], - [QualifiedProfile::GitHubIssueAddress].into_iter().collect(), - ) - .unwrap() - } - - #[test] - fn delegation_can_only_narrow_actions_depth_expiry_and_uses() { - let runtime = runtime(); - let identity = b"human".to_vec(); - let action_a = b"edit issue"; - let action_b = b"delete repository"; - let parent_scope = - encode_sandbox_authority_request(now() + 60, 2, 2, &[action_a, action_b]).unwrap(); - let parent = runtime - .handle( - ProductionRequest::new( - ProductVerb::Create, - QualifiedProfile::GitHubIssueAddress, - identity.clone(), - None, - Some(parent_scope), - None, - ) - .unwrap(), - ) - .unwrap() - .value() - .unwrap() - .to_vec(); - let child_scope = encode_sandbox_authority_request(now() + 30, 1, 1, &[action_a]).unwrap(); - let child = runtime - .handle( - ProductionRequest::new( - ProductVerb::Delegate, - QualifiedProfile::GitHubIssueAddress, - identity, - Some(parent), - Some(encode_delegation_body(b"agent", &child_scope).unwrap()), - None, - ) - .unwrap(), - ) - .unwrap(); - assert!(child.value().is_some()); - } - - #[test] - fn exact_action_and_replay_budget_are_enforced() { - let runtime = runtime(); - let action = b"edit issue"; - let authority = runtime - .handle( - ProductionRequest::new( - ProductVerb::Create, - QualifiedProfile::GitHubIssueAddress, - b"agent".to_vec(), - None, - Some(encode_sandbox_authority_request(now() + 60, 0, 1, &[action]).unwrap()), - None, - ) - .unwrap(), - ) - .unwrap() - .value() - .unwrap() - .to_vec(); - let request = || { - ProductionRequest::new( - ProductVerb::Execute, - QualifiedProfile::GitHubIssueAddress, - b"agent".to_vec(), - Some(authority.clone()), - Some(action.to_vec()), - None, - ) - .unwrap() - }; - assert_eq!( - runtime.handle(request()).unwrap().kind(), - ClientOutcomeKind::Completed - ); - assert_eq!(runtime.handle(request()), Err(RuntimeFailure::Denied)); - } - - #[test] - fn recovery_is_committed_once_and_replays_the_same_receipt() { - let runtime = runtime(); - let action = b"AUTHS-SANDBOX-RECOVER edit issue"; - let authority = runtime - .handle( - ProductionRequest::new( - ProductVerb::Create, - QualifiedProfile::GitHubIssueAddress, - b"agent".to_vec(), - None, - Some(encode_sandbox_authority_request(now() + 60, 0, 1, &[action]).unwrap()), - None, - ) - .unwrap(), - ) - .unwrap() - .value() - .unwrap() - .to_vec(); - let unknown = runtime - .handle( - ProductionRequest::new( - ProductVerb::Execute, - QualifiedProfile::GitHubIssueAddress, - b"agent".to_vec(), - Some(authority), - Some(action.to_vec()), - None, - ) - .unwrap(), - ) - .unwrap(); - let reference = unknown.recovery_reference().unwrap().clone(); - let resume = || { - ProductionRequest::new( - ProductVerb::Resume, - QualifiedProfile::GitHubIssueAddress, - b"agent".to_vec(), - None, - None, - Some(reference.clone()), - ) - .unwrap() - }; - let first = runtime.handle(resume()).unwrap(); - let replay = runtime.handle(resume()).unwrap(); - assert_eq!(first.kind(), ClientOutcomeKind::Completed); - assert_eq!(first.receipt(), replay.receipt()); - assert_eq!(first.value(), replay.value()); - let status = runtime.status(&reference).unwrap(); - assert_eq!(status.state, "committed"); - assert_eq!(status.effect, EffectState::Applied); - assert!(status.receipt_id.is_some()); - } -} diff --git a/product/runtime/auths-node/src/sandbox_store.rs b/product/runtime/auths-node/src/sandbox_store.rs index b9069e12..e9cde1ad 100644 --- a/product/runtime/auths-node/src/sandbox_store.rs +++ b/product/runtime/auths-node/src/sandbox_store.rs @@ -62,7 +62,7 @@ impl SandboxStore for MemorySandboxStore { let mut state = self.state.lock().map_err(|_| RuntimeFailure::Unavailable)?; let uses = state.uses.entry(authority).or_default(); if *uses >= maximum { - return Err(RuntimeFailure::Denied); + return Err(RuntimeFailure::ReplayBudgetExhausted); } *uses += 1; Ok(()) @@ -132,7 +132,7 @@ impl SandboxStore for MemorySandboxStore { return Ok(existing.clone()); } if state.pending.get(reference) != Some(expected) { - return Err(RuntimeFailure::UnknownWorkflow); + return Err(RuntimeFailure::UnknownReference); } state.pending.remove(reference); state.receipts.insert(receipt_id.into(), receipt.clone()); @@ -218,7 +218,7 @@ impl SandboxStore for PostgresSandboxStore { &[&&authority[..], &maximum], ) .map_err(|_| RuntimeFailure::Unavailable)?; - affected.map_or(Err(RuntimeFailure::Denied), |_| Ok(())) + affected.map_or(Err(RuntimeFailure::ReplayBudgetExhausted), |_| Ok(())) } fn put_pending(&self, reference: &str, pending: &PendingEffect) -> Result<(), RuntimeFailure> { @@ -367,14 +367,14 @@ impl SandboxStore for PostgresSandboxStore { .map_err(|_| RuntimeFailure::Unavailable)? .map(decode_receipt) .transpose()? - .ok_or(RuntimeFailure::UnknownWorkflow)?; + .ok_or(RuntimeFailure::UnknownReference)?; transaction .commit() .map_err(|_| RuntimeFailure::Unavailable)?; return Ok(existing); }; if &pending != expected { - return Err(RuntimeFailure::Denied); + return Err(RuntimeFailure::StateConflict); } enforce_capacity(&mut transaction, self.maximum_records)?; transaction diff --git a/product/runtime/auths-node/tests/kernel_differential.rs b/product/runtime/auths-node/tests/kernel_differential.rs new file mode 100644 index 00000000..d3fac75f --- /dev/null +++ b/product/runtime/auths-node/tests/kernel_differential.rs @@ -0,0 +1,232 @@ +//! Differential test: `auths-node` and the verified kernel must reach the same +//! authorization decision on identical inputs. +//! +//! The inputs are the canonical target V1 corpus in `core/fixtures/v1`, reached +//! through `auths_testkit::corpus()`. Every case supplies one exact +//! `(proof, canonical action, trusted context)` triple and the normative +//! decision. The reference side is `auths_verifier::verify` called directly. +//! The subject side is `auths-node`'s public decision path reached through +//! `NodeRuntime::handle`. Nothing is translated between the two sides: both +//! receive the same proof bytes, the same canonical action, the same trusted +//! context, and the same principal-method and signature-suite registries. +//! +//! The corpus exercises every one of the eleven generated attenuation +//! dimensions declared in `core/crates/auths-algebra-kernel/src/generated.rs`. +//! `dimension_coverage_is_complete` fails if a dimension loses its case. + +use auths_model::{CanonicalAction, DenialReason, Requirement, TrustedContext}; +use auths_node::{KernelRuntime, NodeClock, NodeKernel, NodeRuntime, RuntimeFailure}; +use auths_ports::{PrincipalMethod, SignatureSuite}; +use auths_production_client::{ + ClientOutcomeKind, ProductVerb, ProductionRequest, QualifiedProfile, +}; +use auths_registries::ImmutableRegistries; +use auths_testkit::{CorpusFixture, Expected}; +use auths_verifier::VerificationOutcome; +use std::collections::BTreeSet; + +/// The closed decision both sides must agree on. +#[derive(Debug, Eq, PartialEq)] +enum Decision { + Authorized, + Denied(DenialReason), + Indeterminate(Requirement), + /// Any answer that is not an authorization decision at all. + NotAnAuthorizationAnswer(String), +} + +/// One corpus case and the dimension it pins, for the coverage assertion. +/// +/// The names are the eleven `AttenuationProjection` methods. +const DIMENSION_CASES: &[(&str, &str)] = &[ + ("root_preserved", "untrusted-root"), + ("depth_decreases", "depth-widening"), + ("profile_attenuates", "unsupported-action-profile"), + ("permissions_attenuate", "permission-widening"), + ("validity_attenuates", "validity-widening"), + ("audiences_attenuate", "audience-widening"), + ("action_constraint_attenuates", "action-constraint-mismatch"), + ("budget_attenuates", "budget-widening"), + ("status_attenuates", "revoked-grant-status"), + ("assurance_attenuates", "assurance-policy-change"), + ("extensions_attenuate", "critical-extension-attenuation"), +]; + +fn methods() -> Vec> { + let (spiffe_trust, spiffe_status) = auths_testkit::spiffe_corpus_context(); + vec![ + Box::new(auths_raw_key::RawKeyMethod::new().expect("raw key method")), + Box::new(auths_did_key::DidKeyMethod::new().expect("did:key method")), + Box::new(auths_did_keri::DidKeriMethod::new().expect("did:keri method")), + Box::new( + auths_did_web::DidWebMethod::new(auths_testkit::did_web_corpus_trust_records()) + .expect("did:web method"), + ), + Box::new( + auths_webauthn::WebAuthnMethod::new(auths_testkit::webauthn_corpus_credentials()) + .expect("webauthn method"), + ), + Box::new( + auths_hsm_attested::HsmAttestedMethod::new(auths_testkit::hsm_corpus_records()) + .expect("hsm method"), + ), + Box::new( + auths_spiffe_x509::SpiffeX509Method::new(spiffe_trust, spiffe_status) + .expect("spiffe method"), + ), + ] +} + +fn suites() -> Vec> { + vec![ + Box::new(auths_signature::Ed25519Suite::new().expect("ed25519 suite")), + Box::new(auths_signature::P256Sha256Suite::new().expect("p256 suite")), + ] +} + +/// Reference decision: the kernel verifier, called directly. +fn kernel_decision(proof: &[u8], action: &CanonicalAction, context: &TrustedContext) -> Decision { + let owned_methods = methods(); + let owned_suites = suites(); + let borrowed: Vec<&dyn PrincipalMethod> = owned_methods + .iter() + .map(|method| method.as_ref() as &dyn PrincipalMethod) + .collect(); + let borrowed_suites: Vec<&dyn SignatureSuite> = owned_suites + .iter() + .map(|suite| suite.as_ref() as &dyn SignatureSuite) + .collect(); + let registries = + ImmutableRegistries::new(&borrowed, &borrowed_suites).expect("corpus registries"); + match auths_verifier::verify(proof, action, context, ®istries) { + VerificationOutcome::Authorized(_) => Decision::Authorized, + VerificationOutcome::Denied(reason) => Decision::Denied(reason), + VerificationOutcome::Indeterminate(requirement) => Decision::Indeterminate(requirement), + } +} + +/// Subject decision: `auths-node`'s public decision path on the same triple. +/// +/// The request identity is deliberately a self-asserted string with no relation +/// to any principal in the proof. If it influenced the decision at all this +/// differential would diverge. +fn node_decision(context: &TrustedContext, proof: &[u8], action_bytes: &[u8]) -> Decision { + let runtime = node_runtime(context); + let request = ProductionRequest::new( + ProductVerb::Execute, + QualifiedProfile::GitHubIssueAddress, + b"self-asserted-caller".to_vec(), + Some(proof.to_vec()), + Some(action_bytes.to_vec()), + None, + ) + .expect("execute request shape"); + match runtime.handle(request) { + Ok(response) if response.kind() == ClientOutcomeKind::Completed => Decision::Authorized, + Ok(response) => Decision::NotAnAuthorizationAnswer(format!("{:?}", response.kind())), + Err(RuntimeFailure::AuthorizationDenied(reason)) => Decision::Denied(reason), + Err(RuntimeFailure::AuthorizationIndeterminate(requirement)) => { + Decision::Indeterminate(requirement) + } + Err(other) => Decision::NotAnAuthorizationAnswer(other.code().to_owned()), + } +} + +/// A clock frozen at the trusted context's own evaluation instant. +/// +/// Time is one of the inputs. Handing the node a different instant than the +/// reference verifier would make this a comparison of two different questions. +struct FrozenClock(u64); + +impl NodeClock for FrozenClock { + fn now_unix_seconds(&self) -> u64 { + self.0 + } +} + +fn node_runtime(context: &TrustedContext) -> KernelRuntime { + KernelRuntime::with_clock( + NodeKernel::new(context.clone(), methods(), suites()).expect("node kernel"), + [7; 32], + [QualifiedProfile::GitHubIssueAddress].into_iter().collect(), + std::sync::Arc::new(FrozenClock(context.evaluation_time().get())), + ) + .expect("node runtime") +} + +fn fixture_context(fixture: &CorpusFixture) -> TrustedContext { + auths_codec::decode_verifier_context(fixture.context_bytes()).expect("corpus context") +} + +fn fixture_action_bytes(fixture: &CorpusFixture) -> Vec { + auths_codec::encode_canonical_action(fixture.canonical_action()).expect("corpus action bytes") +} + +/// The corpus expectation and the reference verifier must agree before the +/// reference is used to judge `auths-node`. Without this the differential could +/// be green against a drifted reference. +#[test] +fn reference_side_matches_the_normative_corpus() { + for fixture in auths_testkit::corpus() { + let context = fixture_context(&fixture); + let actual = kernel_decision(fixture.proof_bytes(), fixture.canonical_action(), &context); + let expected = match fixture.expected() { + Expected::Authorized => Decision::Authorized, + Expected::Denied(reason) => Decision::Denied(reason), + Expected::Indeterminate(requirement) => Decision::Indeterminate(requirement), + }; + assert_eq!( + actual, + expected, + "reference verifier drifted from the corpus on {}", + fixture.name() + ); + } +} + +#[test] +fn node_and_kernel_agree_on_every_canonical_corpus_input() { + let mut disagreements = Vec::new(); + let mut compared = 0_usize; + for fixture in auths_testkit::corpus() { + let context = fixture_context(&fixture); + let action_bytes = fixture_action_bytes(&fixture); + let kernel = kernel_decision(fixture.proof_bytes(), fixture.canonical_action(), &context); + let node = node_decision(&context, fixture.proof_bytes(), &action_bytes); + compared += 1; + if node != kernel { + disagreements.push(format!( + "{}: kernel={kernel:?} node={node:?}", + fixture.name() + )); + } + } + assert!( + disagreements.is_empty(), + "auths-node disagreed with the kernel on {} of {compared} canonical corpus inputs:\n{}", + disagreements.len(), + disagreements.join("\n") + ); +} + +#[test] +fn dimension_coverage_is_complete() { + let names: BTreeSet<&str> = auths_testkit::corpus() + .iter() + .map(CorpusFixture::name) + .collect(); + let missing: Vec<&str> = DIMENSION_CASES + .iter() + .filter(|(_, case)| !names.contains(case)) + .map(|(dimension, _)| *dimension) + .collect(); + assert!( + missing.is_empty(), + "these attenuation dimensions lost their corpus case: {missing:?}" + ); + assert_eq!( + DIMENSION_CASES.len(), + 11, + "the generated kernel declares eleven attenuation dimensions" + ); +} From a85f6b7f8e3f6ed1a863f551425b8303d92768fe Mon Sep 17 00:00:00 2001 From: bordumb Date: Sat, 15 Aug 2026 15:00:20 +0100 Subject: [PATCH 06/61] formal: repoint the assurance inventory at the renamed root theorem Wave 1b renamed rich_projection_accepts_iff_scope_depth_checks to rich_projection_accepts_iff_root_and_scope_depth_checks, because the projection now also checks root preservation. It updated the internal rewrite sites but not the two places that cite the theorem by name, so AssuranceAudit.lean failed with "assurance inventory names missing declaration". That is the same class the wave was sent to fix: evidence naming a proof that does not exist. Here the gate caught it, which is the gate working. - formal/Auths/Theorems.lean: inventory entry repointed - formal/assurance-manifest-v1.toml: AP-FORMAL-RICH-045 lean_declaration repointed and claim_text widened to state root preservation - semantic_source_closure_sha256 values resynchronized (108 compiled claims); the Lean sources genuinely moved when the wave added 11 rootedness theorems Assurance audit now PASSES over 108 compiled statements with transitive axioms reviewed. NOT FIXED, environment: `cargo xtask formal` still reports the production translation source closure as drifted. Updating that pin requires re-running the pinned charon + aeneas translation (formal/translation-toolchain.lock pins aeneas @ 3a8586fa), and neither binary is installed on this host. The pin asserts "this Lean was produced from this source", so bumping it without translating would fabricate that claim. Left red deliberately; hosted CI is the place it clears. UNSIGNED: auths-sign needs an interactive passphrase. Co-Authored-By: Claude Opus 5 --- formal/Auths/Theorems.lean | 2 +- formal/assurance-manifest-v1.toml | 226 +++++++++++++++--------------- 2 files changed, 114 insertions(+), 114 deletions(-) diff --git a/formal/Auths/Theorems.lean b/formal/Auths/Theorems.lean index 238336dc..dba01116 100644 --- a/formal/Auths/Theorems.lean +++ b/formal/Auths/Theorems.lean @@ -56,7 +56,7 @@ def theoremInventory : List Lean.Name := `Auths.Rich.finite_delegation_chain, `Auths.Rich.chain_transitive_attenuation, `Auths.Rich.authorized_action_covered, - `Auths.Rich.rich_projection_accepts_iff_scope_depth_checks, + `Auths.Rich.rich_projection_accepts_iff_root_and_scope_depth_checks, `Auths.Rich.apply_grant_success_iff_linked_and_projection, `Auths.Rich.apply_grant_success_iff_delegates, `Auths.Rich.apply_grant_success_unique, diff --git a/formal/assurance-manifest-v1.toml b/formal/assurance-manifest-v1.toml index 0c64129f..95be48fe 100644 --- a/formal/assurance-manifest-v1.toml +++ b/formal/assurance-manifest-v1.toml @@ -46,7 +46,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -98,7 +98,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -150,7 +150,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -202,7 +202,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -254,7 +254,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -303,7 +303,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -356,7 +356,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -409,7 +409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -462,7 +462,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -514,7 +514,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -567,7 +567,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -620,7 +620,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -673,7 +673,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -726,7 +726,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -775,7 +775,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -824,7 +824,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -873,7 +873,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -922,7 +922,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -971,7 +971,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1020,7 +1020,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1072,7 +1072,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1121,7 +1121,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1173,7 +1173,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1225,7 +1225,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1277,7 +1277,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1329,7 +1329,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1381,7 +1381,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1434,7 +1434,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1487,7 +1487,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1540,7 +1540,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1592,7 +1592,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1645,7 +1645,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1697,7 +1697,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1749,7 +1749,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1802,7 +1802,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1854,7 +1854,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1907,7 +1907,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1959,7 +1959,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2011,7 +2011,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2063,7 +2063,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2115,7 +2115,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2167,7 +2167,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2219,7 +2219,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2271,7 +2271,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2324,7 +2324,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2340,10 +2340,10 @@ artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] claim_id = "AP-FORMAL-RICH-045" -claim_text = "Lean proves the rich authority property: rich projection accepts iff scope depth checks." +claim_text = "Lean proves the rich authority property: rich projection accepts iff root preservation and scope depth checks." claim_status = "proved" -lean_declaration = "Auths.Rich.rich_projection_accepts_iff_scope_depth_checks" -lean_statement_sha256 = "c0d2f92e3212b8abf6f85ca9d15272169e44c1c5df58964254e51e54c619a729" +lean_declaration = "Auths.Rich.rich_projection_accepts_iff_root_and_scope_depth_checks" +lean_statement_sha256 = "a1e6551c5196120585c745f72b806cb4c977454495e1901ecaba1e5908613930" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -2377,7 +2377,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2430,7 +2430,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2483,7 +2483,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2536,7 +2536,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2589,7 +2589,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2642,7 +2642,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2695,7 +2695,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2748,7 +2748,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2800,7 +2800,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2862,7 +2862,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The exact Aeneas translation of the production pre-signing scope evaluator over validated, bounded Rust model views, including ordered diagnostics." residual_assumptions = [ "Lean's kernel, the pinned Rust/Lean/Aeneas/Charon toolchain, the reviewed Aeneas external models, the listed foundational axioms, and the theorem's representation-validity premises are trusted.", @@ -2900,7 +2900,7 @@ claim_id = "AP-FORMAL-RICH-055" claim_text = "The mechanically translated production terminal-coverage evaluator returns exactly the ordered rich target-V1 decision, including linkage, set membership, interval containment, action constraints, and requested-budget coverage." claim_status = "proved" lean_declaration = "Auths.Refinement.translated_coverage_refines_rich_spec" -lean_statement_sha256 = "634baf398f6c0e43bb6550386d45e21e0d18acb8e16feefa6d077dd4340ee211" +lean_statement_sha256 = "31a91e9677792773c02ab0ccdd84945dd126172f3141fda745e116c80f9fa1df" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [ "auths_authority::evaluate_action_coverage_view", @@ -2944,7 +2944,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Validated target-V1 authority and action views through the production terminal-coverage decision, including its first-failure diagnostic order." residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget result is stateless per action, assurance uses exact identifier equality, and deterministic diagnostic precedence is not a side-channel noninterference claim. Codec validation and cryptography remain outside this claim."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2975,7 +2975,7 @@ claim_id = "AP-FORMAL-RICH-056" claim_text = "The mechanically translated production delegation evaluator returns exactly the rich target-V1 linkage and attenuation decision, including the unique accepted transition fields." claim_status = "proved" lean_declaration = "Auths.Refinement.translated_delegation_refines_rich_spec" -lean_statement_sha256 = "b2f8cb734d50810214996ced009817ed145c783f3ab6632af6718b19db0c603a" +lean_statement_sha256 = "14f3b7aa4e01346ba18dab4a3b7c7d3347c25d58ce0925c51b40a10fab821397" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [ "auths_authority::evaluate_grant_view", @@ -3022,7 +3022,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Validated target-V1 parent-authority and grant views through the production delegation outcome and accepted transition." residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget relation is stateless per action, assurance retention is exact identifier equality rather than a lattice, and deterministic diagnostic precedence is not a side-channel noninterference claim. Root preservation and assurance retention occur in the caller around the proved returned transition; codec validation and cryptography remain outside this claim."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3087,7 +3087,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3136,7 +3136,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3185,7 +3185,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3234,7 +3234,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3283,7 +3283,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3332,7 +3332,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3381,7 +3381,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3430,7 +3430,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3482,7 +3482,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3531,7 +3531,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3584,7 +3584,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3637,7 +3637,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3690,7 +3690,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3743,7 +3743,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3796,7 +3796,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3848,7 +3848,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3900,7 +3900,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3952,7 +3952,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4005,7 +4005,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4058,7 +4058,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4107,7 +4107,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4156,7 +4156,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4205,7 +4205,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4254,7 +4254,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4303,7 +4303,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4352,7 +4352,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4401,7 +4401,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4450,7 +4450,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4499,7 +4499,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4548,7 +4548,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4597,7 +4597,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::model::LifecycleState::is_terminal` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4646,7 +4646,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::transition_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4698,7 +4698,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::exclusive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4747,7 +4747,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::additive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4800,7 +4800,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::replay_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4849,7 +4849,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4898,7 +4898,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4947,7 +4947,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4996,7 +4996,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5045,7 +5045,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5094,7 +5094,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5143,7 +5143,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5196,7 +5196,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5249,7 +5249,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "Exactly the two-input threshold helper and the declared three-valued truth order." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5305,7 +5305,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "A two-value swap only; arbitrary authorization-plan permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5354,7 +5354,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The two-value canonicalCode helper; arbitrary plan diagnostic permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5403,7 +5403,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "A definitional model equality; it does not prove that the shipping evaluator visits leaves once." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5455,7 +5455,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "A definitional model equality; no asymptotic or shipping evaluator cost bound is claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5507,7 +5507,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5560,7 +5560,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5616,7 +5616,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "2e714c4857a06293208aba05846554f2dd81ca5c0cd1cbb995b085d2bed5f34f" +semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" From ac5b96871ea3b784e4f99ad0eddb00836241d610 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sat, 15 Aug 2026 21:03:28 +0100 Subject: [PATCH 07/61] core: make terminal budget coverage the kernel's own answer (UNSIGNED) optional_budget_covers returned true for (bounded ceiling, absent request), so EffectiveAuthority coverage returned Authorized for an action that declares no bound on what it may spend. The full verifier denied that case only because Wave 1's registry-driven guard in validate_budget_constraints runs before `authorizes`; the kernel's own answer was never reached. Correct behavior rested on statement order, not on the algebra. BEHAVIOR CHANGE, stated explicitly: a bounded ceiling no longer vacuously covers an absent request. An ABSENT ceiling still covers everything; an absent REQUEST under a present ceiling is now denied. This matches the Rust verifier guard, Go bindings/independent/go/auths/semantic.go budgetCovers, and TypeScript bindings/independent/typescript/semantic-verifier.ts budgetCovers. Two artifacts that pinned the old answer are inverted for that reason, not weakened: - auths-model assert!(optional_budget_covers(Some(&zero), None)) -> ! - the "optional-budget-no-request" mutation oracle now requires canonical == false; its mutant is a computed value, so the oracle still fails both if coverage returns to fail-open and if it over-denies (verified by temporarily reverting the kernel: the matrix reported "required semantic mutation survived"). The Wave 1 verifier guard is KEPT. It is not a duplicate: it alone resolves the ceiling's algebra against the accepted registries and can return Indeterminate(UnsupportedBudgetAlgebra), and it alone meters that work. A new test proves the kernel authorizes exactly the case the guard must call indeterminate. Three new tests drive the kernel with the guard bypassed so a kernel regression cannot hide behind it. KNOWN RED, left for the Lean lane: the Lean-generated vector budget-cover-no-request (formal/Auths/VectorExport.lean:142) still expects true, so lean_rich_authority_vectors_refine_shipping_rust_predicates fails. Auths.Rich.budgetCovers (formal/Auths/Rich/Semantics.lean:28) is the definition that must change. A scratch Lean probe confirmed Auths.Rich.budget_coverage_monotone (Rich/Theorems.lean:138) stays TRUE under the corrected relation and needs only a proof-script repair. Co-Authored-By: Claude Opus 5 --- core/crates/auths-authority/src/lib.rs | 88 ++++++++++++++- .../crates/auths-formal-refinement/src/lib.rs | 11 +- core/crates/auths-model/src/lib.rs | 47 +++++++- core/crates/auths-verifier/src/lib.rs | 100 +++++++++++++++++- formal/refinement-mutations-v1.json | 4 +- 5 files changed, 242 insertions(+), 8 deletions(-) diff --git a/core/crates/auths-authority/src/lib.rs b/core/crates/auths-authority/src/lib.rs index 434c8496..586a144f 100644 --- a/core/crates/auths-authority/src/lib.rs +++ b/core/crates/auths-authority/src/lib.rs @@ -472,6 +472,10 @@ mod tests { } fn anchor() -> TrustAnchor { + anchor_with_budget(None) + } + + fn anchor_with_budget(budget_ceiling: Option) -> TrustAnchor { TrustAnchor::new( TrustAnchorId::parse("root").expect("anchor id"), PrincipalId::parse("did:key:root").expect("root"), @@ -481,7 +485,7 @@ mod tests { vec![ResourceId::parse("cluster://").expect("namespace")], audiences(), ValidityWindow::new(Timestamp::new(0), Timestamp::new(u64::MAX)).expect("validity"), - None, + budget_ceiling, 2, AssurancePolicyId::parse("assurance-v1").expect("assurance"), StatusPolicy::ExpiryOnly, @@ -653,6 +657,88 @@ mod tests { ); } + fn numeric_budget(value: u64) -> BudgetCeiling { + BudgetCeiling::new( + auths_model::BudgetAlgebraId::parse("numeric-ceiling-v1").expect("algebra"), + value, + ) + } + + /// A bounded ceiling must not cover an action that declares no budget. + /// + /// This reaches terminal coverage directly, so nothing above the kernel can + /// supply the denial: the full verifier's `validate_budget_constraints` + /// guard runs earlier in `auths-verifier` and is bypassed here on purpose. + /// An action with no requested budget under a bounded ceiling has no bound + /// on what it may spend, so the kernel itself must deny it. + #[test] + fn terminal_coverage_denies_an_absent_request_under_a_bounded_ceiling() { + let anchor = anchor_with_budget(Some(numeric_budget(10))); + let authority = EffectiveAuthority::from_anchor(&anchor); + let actor = PrincipalId::parse("did:key:root").expect("root"); + let selected = profile("profile-a"); + let permission = auths_model::Permission::new( + CapabilityId::parse("deploy").expect("capability"), + ResourceId::parse("cluster://production").expect("resource"), + ); + let audience = Audience::parse("cluster://production").expect("audience"); + let action = ActionAuthorityView { + profile: &selected, + canonical_body_digest: auths_model::Digest::new([0; 32]), + permission: &permission, + requested_budget: None, + audience: &audience, + validity: anchor.validity(), + actor: &actor, + terminal_grant: None, + }; + assert_eq!( + evaluate_action_coverage_view(authority_state_view(&authority), action), + CoverageDecision::Denied(DenialReason::BudgetCeilingExceeded), + "a bounded ceiling must not authorize an unbounded (absent) request" + ); + } + + /// The same fact through the public `EffectiveAuthority::authorizes` entry + /// point, which is what every embedder that is not the full verifier calls. + #[test] + fn authorizes_denies_an_absent_request_under_a_bounded_ceiling() { + let anchor = anchor_with_budget(Some(numeric_budget(10))); + let authority = EffectiveAuthority::from_anchor(&anchor); + let actor = PrincipalId::parse("did:key:root").expect("root"); + let selected = profile("profile-a"); + let permission = auths_model::Permission::new( + CapabilityId::parse("deploy").expect("capability"), + ResourceId::parse("cluster://production").expect("resource"), + ); + let audience = Audience::parse("cluster://production").expect("audience"); + let action = ActionAuthorityView { + profile: &selected, + canonical_body_digest: auths_model::Digest::new([0; 32]), + permission: &permission, + requested_budget: None, + audience: &audience, + validity: anchor.validity(), + actor: &actor, + terminal_grant: None, + }; + // A bounded request inside the ceiling is still authorized: the denial + // above is specific to the absent request, not a blanket budget denial. + let requested = numeric_budget(5); + let bounded = ActionAuthorityView { + requested_budget: Some(&requested), + ..action + }; + assert_eq!( + evaluate_action_coverage_view(authority_state_view(&authority), bounded), + CoverageDecision::Authorized + ); + assert_eq!( + evaluate_action_coverage_view(authority_state_view(&authority), action), + CoverageDecision::Denied(DenialReason::BudgetCeilingExceeded) + ); + } + #[test] fn every_edge_of_a_rooted_chain_reports_root_preservation() { // Guards the other direction: a check that denied everything would diff --git a/core/crates/auths-formal-refinement/src/lib.rs b/core/crates/auths-formal-refinement/src/lib.rs index 0ac7eb5a..1b066669 100644 --- a/core/crates/auths-formal-refinement/src/lib.rs +++ b/core/crates/auths-formal-refinement/src/lib.rs @@ -410,10 +410,17 @@ mod refinement { Some(!canonical && mutant) } "optional-budget-no-request" => { + // Behavior change: a bounded ceiling no longer vacuously covers + // an action that declares no budget. The mutation this oracle + // must kill is therefore the restored vacuous arm — the answer + // the *unbounded* ceiling gives — applied to a bounded ceiling. + // Requiring `!canonical && mutant` keeps the oracle live in both + // directions: it fails if coverage returns to fail-open, and it + // also fails if coverage collapses to denying everything. let ceiling = budget("numeric-v1", 10); let canonical = optional_budget_covers(Some(&ceiling), None); - let mutant = optional_budget_attenuates(None, Some(&ceiling)); - Some(canonical && !mutant) + let mutant = optional_budget_covers(None, None); + Some(!canonical && mutant) } _ => None, } diff --git a/core/crates/auths-model/src/lib.rs b/core/crates/auths-model/src/lib.rs index ca53fd26..24478e1b 100644 --- a/core/crates/auths-model/src/lib.rs +++ b/core/crates/auths-model/src/lib.rs @@ -925,13 +925,22 @@ pub fn optional_budget_attenuates( } /// Applies target-V1 terminal coverage to an optional requested budget. +/// +/// An absent ceiling is the unbounded top scope and covers every request. A +/// bounded ceiling requires a bounded request in the same algebra: an action +/// that declares no budget states no bound on what it may spend, so there is +/// nothing for the ceiling to bound and the action is **not** covered. +/// +/// This is the whole answer. It does not depend on any earlier check a caller +/// may or may not run first. #[must_use] pub fn optional_budget_covers( ceiling: Option<&BudgetCeiling>, requested: Option<&BudgetCeiling>, ) -> bool { match (ceiling, requested) { - (_, None) | (None, Some(_)) => true, + (None, _) => true, + (Some(_), None) => false, (Some(ceiling), Some(requested)) => ceiling.covers(requested), } } @@ -4853,7 +4862,41 @@ mod tests { assert!(optional_budget_attenuates(Some(&maximum), None)); assert!(!optional_budget_attenuates(None, Some(&maximum))); assert!(optional_budget_covers(None, Some(&maximum))); - assert!(optional_budget_covers(Some(&zero), None)); + assert!(!optional_budget_covers(Some(&zero), None)); + } + + /// Terminal coverage is decided by the algebra, not by check ordering. + /// + /// The full verifier also refuses an absent request under a bounded ceiling + /// (`auths-verifier`'s `validate_budget_constraints`), but that guard runs + /// before the authority kernel is consulted. This test calls the kernel + /// predicate directly so a regression here cannot be masked by the order in + /// which the verifier happens to run its statements. + #[test] + fn a_bounded_ceiling_never_covers_an_absent_request() { + let bounded = numeric_budget(10); + let requested = numeric_budget(5); + + // An absent ceiling is the unbounded top scope: it covers everything. + assert!(optional_budget_covers(None, None)); + assert!(optional_budget_covers(None, Some(&requested))); + + // A present ceiling compares against a present request. + assert!(optional_budget_covers(Some(&bounded), Some(&requested))); + assert!(!optional_budget_covers( + Some(&bounded), + Some(&numeric_budget(11)) + )); + + // An absent request under a present ceiling states no bound at all, so + // there is nothing the ceiling can bound. It is not vacuously covered. + for ceiling in [numeric_budget(0), bounded, numeric_budget(u64::MAX)] { + assert!( + !optional_budget_covers(Some(&ceiling), None), + "bounded ceiling {} must not cover an absent request", + ceiling.value() + ); + } } #[test] diff --git a/core/crates/auths-verifier/src/lib.rs b/core/crates/auths-verifier/src/lib.rs index 56dcfcea..d0216e6a 100644 --- a/core/crates/auths-verifier/src/lib.rs +++ b/core/crates/auths-verifier/src/lib.rs @@ -2655,6 +2655,7 @@ mod tests { bytes: Vec, canonical: CanonicalAction, context: TrustedContext, + action: SignedAction, } fn target_fixture(mutate_signature: bool) -> Fixture { @@ -2755,7 +2756,7 @@ mod tests { let bundle = ProofBundle::new( BundleHeader::v1(), Vec::new(), - vec![signed_action], + vec![signed_action.clone()], plan, vec![evidence], vec![ControlBinding::new(statement, vec![evidence_identifier]).unwrap()], @@ -2862,6 +2863,7 @@ mod tests { bytes: encode_bundle(&bundle).unwrap(), canonical, context, + action: signed_action, } } @@ -2934,6 +2936,102 @@ mod tests { ); } + /// The denial above must not depend on statement order inside + /// [`verify_authority_and_assurance`]. + /// + /// `validate_budget_constraints` runs at the top of that function, before + /// `EffectiveAuthority::authorizes`. It is deliberately kept — it is the + /// only thing that resolves the ceiling's algebra against the accepted + /// registries (yielding `Indeterminate(UnsupportedBudgetAlgebra)` for an + /// algebra the verifier does not implement) and the only thing that meters + /// the comparison's work. But it must never be the *sole* reason a bounded + /// ceiling denies an absent request, or a kernel regression would be + /// invisible here. This drives the authority kernel with that guard + /// bypassed entirely. + #[test] + fn the_authority_kernel_denies_an_absent_request_without_the_verifier_guard() { + let fixture = target_fixture_with_budget(false, Some(numeric_ceiling(10_000)), None); + let anchor = fixture + .context + .trust_anchors() + .first() + .expect("fixture anchor"); + let authority = EffectiveAuthority::from_anchor(anchor); + assert_eq!( + authority.authorizes(fixture.action.envelope()), + Err(DenialReason::BudgetCeilingExceeded), + "the kernel alone must deny; the Wave 1 guard is defense in depth" + ); + + // The same kernel, same guard-free path, still authorizes a request + // that is inside the ceiling: this is not a blanket budget denial. + let inside = target_fixture_with_budget( + false, + Some(numeric_ceiling(10_000)), + Some(numeric_ceiling(10_000)), + ); + let inside_anchor = inside + .context + .trust_anchors() + .first() + .expect("fixture anchor"); + assert_eq!( + EffectiveAuthority::from_anchor(inside_anchor).authorizes(inside.action.envelope()), + Ok(()) + ); + } + + /// Evidence that `validate_budget_constraints` is not a duplicate of the + /// kernel and must be kept. + /// + /// The kernel compares algebra *identifiers* bytewise and then applies the + /// numeric `<=` unconditionally. It has no notion of "this verifier does + /// not implement that algebra". Only the verifier guard resolves the + /// ceiling's algebra against the accepted registries, so only it can fail + /// closed with `Indeterminate(UnsupportedBudgetAlgebra)`. Delete the guard + /// and this case silently becomes a numeric comparison the verifier is not + /// entitled to make. + #[test] + fn only_the_verifier_guard_can_reject_an_unimplemented_budget_algebra() { + let unknown = BudgetCeiling::new( + auths_model::BudgetAlgebraId::parse("credits-v1").unwrap(), + 10_000, + ); + let inside = BudgetCeiling::new( + auths_model::BudgetAlgebraId::parse("credits-v1").unwrap(), + 5_000, + ); + let fixture = target_fixture_with_budget(false, Some(unknown), Some(inside)); + + // The kernel alone authorizes: same algebra id, 5_000 <= 10_000. + let anchor = fixture + .context + .trust_anchors() + .first() + .expect("fixture anchor"); + assert_eq!( + EffectiveAuthority::from_anchor(anchor).authorizes(fixture.action.envelope()), + Ok(()) + ); + + // The full verifier refuses to decide, because it cannot evaluate that + // algebra. This is the capability the guard uniquely provides. + let method = RawKeyMethod::new().unwrap(); + let suite = Ed25519Suite::new().unwrap(); + let methods: [&dyn auths_ports::PrincipalMethod; 1] = [&method]; + let suites: [&dyn auths_ports::SignatureSuite; 1] = [&suite]; + let registries = ImmutableRegistries::new(&methods, &suites).unwrap(); + assert_eq!( + verify( + &fixture.bytes, + &fixture.canonical, + &fixture.context, + ®istries, + ), + VerificationOutcome::Indeterminate(Requirement::UnsupportedBudgetAlgebra) + ); + } + /// An unbounded authority is still the top scope: it does not require the /// action to declare a budget. #[test] diff --git a/formal/refinement-mutations-v1.json b/formal/refinement-mutations-v1.json index a0af78a9..eef725d0 100644 --- a/formal/refinement-mutations-v1.json +++ b/formal/refinement-mutations-v1.json @@ -73,8 +73,8 @@ }, { "id": "optional-budget-no-request", - "operator": "Treat a missing action budget request as an unbounded child authority.", - "witness": "No requested spend is covered by every authority ceiling." + "operator": "Treat a missing action budget request as vacuously covered by a bounded ceiling.", + "witness": "An absent request states no bound, so a bounded ceiling does not cover it." }, { "id": "status-age-direction", From f82cb0844bb36c48160ef309d10747ebda6d4a26 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sat, 15 Aug 2026 21:32:28 +0100 Subject: [PATCH 08/61] formal: run the 31 ungated Kani harnesses and fix what they proved (UNSIGNED) xtask/src/formal.rs ran Kani on auths-algebra-kernel and auths-model only. The 31 harnesses in auths-lifecycle, auths-bounded-policy and auths-stripe were executed by no gate at all. Baseline: all 31 pass. The value was never in the pass/fail column - it is that nothing was checking whether the harnesses could fail. Three could not, proven by mutation with the pre-fix harness as the control: * replay_code: reclassifying every Conflict as Absent left exact_replay_never_becomes_absent_or_conflict SUCCESSFUL. Zero kani::any() in the body. * transition_payment_mandate: letting a still-unknown reconcile silently free the capability slot left unknown_never_releases_capability SUCCESSFUL. One concrete transition, zero symbolic input. * merchant cancel/capture arbitrary_state() sampled 16 and 10 of the 18 MerchantReservationState variants. A cancel committing straight out of AuthorizationReleasedByCapture left the harness SUCCESSFUL. credits_never_reduce_incremental_term_liability asserted after.saturating_sub(before) == after.saturating_sub(before) - reflexive, so no change to any code could ever have reddened it. All rewritten to quantify over their real domains and to call production code rather than transcribe its arithmetic; term_liability_delta and source_basis_point_ceiling are extracted for that purpose and used by the evaluators themselves. Each rewrite re-verified RED under the break and GREEN after restore. The gate now runs every package carrying harnesses, and kani_harness_inventory fails when a #[kani::proof] appears outside the gated roots - so this class cannot silently return. Measured: 31 harnesses, ~281s, of which ~235s is basis_points_floor_never_exceeds_denominator alone (symbolic 64-bit division; kissat was tried and was slower). Its input domain is left unbounded. The nine kani_harnesses citations naming auths_bounded_policy::kernel::proof_configuration_match_total - a symbol that exists nowhere - are corrected through the generator to the real harness, proofs::configuration_match_is_eligible_only_when_every_gate_matches. Co-Authored-By: Claude Opus 5 --- .../fixtures/v1/bounded-policy/manifest.json | 2 +- .../fixtures/v1/bounded-policy/registry.toml | 18 +-- .../src/connect/transfer/evaluator.rs | 41 +++-- .../auths-stripe/src/mandate/execution.rs | 88 ++++++++-- .../src/merchant/cancel/execution.rs | 44 ++++- .../src/merchant/capture/execution.rs | 87 ++++++++-- .../src/subscription/modify/evaluator.rs | 55 +++++-- product/runtime/auths-lifecycle/src/kernel.rs | 19 ++- xtask/src/fixtures.rs | 18 +-- xtask/src/formal.rs | 151 +++++++++++++++++- 10 files changed, 441 insertions(+), 82 deletions(-) diff --git a/product/fixtures/v1/bounded-policy/manifest.json b/product/fixtures/v1/bounded-policy/manifest.json index 372412e2..23ce98b5 100644 --- a/product/fixtures/v1/bounded-policy/manifest.json +++ b/product/fixtures/v1/bounded-policy/manifest.json @@ -1 +1 @@ -{"schema":"auths.product.bounded-policy-conformance-manifest/1","contract":"auths.product.bounded-policy-contract/1","generator":"cargo xtask product-fixtures --update","registry":"registry.toml","registry_bytes":15838,"registry_sha256":"fa40ced3e1b2bdca36e071b170af7faa586168aa7d9efd4570ab0203b9f4fc89","migration_status":"reference-only","domain_oracles":["github","kubernetes","opentofu","postgresql","radicle","records-api","stripe"]} \ No newline at end of file +{"schema":"auths.product.bounded-policy-conformance-manifest/1","contract":"auths.product.bounded-policy-contract/1","generator":"cargo xtask product-fixtures --update","registry":"registry.toml","registry_bytes":16171,"registry_sha256":"7349b5f42c17f05f44ec910fbebe34ebd0b817dc705ee9474ccddcf1a623a5a5","migration_status":"reference-only","domain_oracles":["github","kubernetes","opentofu","postgresql","radicle","records-api","stripe"]} \ No newline at end of file diff --git a/product/fixtures/v1/bounded-policy/registry.toml b/product/fixtures/v1/bounded-policy/registry.toml index 453968c0..ed49c0cc 100644 --- a/product/fixtures/v1/bounded-policy/registry.toml +++ b/product/fixtures/v1/bounded-policy/registry.toml @@ -63,7 +63,7 @@ hard_limit_source = "product/integrations/auths-records-api/src/policy.rs" fixture_manifest = "product/fixtures/v1/records-api/manifest.json" mutation_corpus = "product/fixtures/v1/records-api" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-records-api/src/decision.rs" reference_evaluator = "auths_records_api::evaluate_create" migration_status = "reference-only" @@ -93,7 +93,7 @@ hard_limit_source = "product/integrations/auths-records-api/src/policy.rs" fixture_manifest = "product/fixtures/v1/records-api/manifest.json" mutation_corpus = "product/fixtures/v1/records-api" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-records-api/src/decision.rs" reference_evaluator = "auths_records_api::evaluate_read" migration_status = "reference-only" @@ -123,7 +123,7 @@ hard_limit_source = "product/integrations/auths-github/src/profile.rs" fixture_manifest = "product/fixtures/v1/github/manifest.json" mutation_corpus = "product/fixtures/v1/github" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-github/src/containment.rs" reference_evaluator = "auths_github::containment::evaluate" migration_status = "reference-only" @@ -153,7 +153,7 @@ hard_limit_source = "product/integrations/auths-github/src/profile.rs" fixture_manifest = "product/fixtures/v1/github/manifest.json" mutation_corpus = "product/fixtures/v1/github" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-github/src/containment.rs" reference_evaluator = "auths_github::containment::evaluate" migration_status = "reference-only" @@ -183,7 +183,7 @@ hard_limit_source = "product/integrations/auths-kubernetes/src/profile.rs" fixture_manifest = "product/fixtures/v1/kubernetes/manifest.json" mutation_corpus = "product/fixtures/v1/kubernetes" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-kubernetes/src/decision.rs" reference_evaluator = "auths_kubernetes::evaluate" migration_status = "reference-only" @@ -213,7 +213,7 @@ hard_limit_source = "product/integrations/auths-opentofu/src/profile.rs" fixture_manifest = "product/fixtures/v1/opentofu/manifest.json" mutation_corpus = "product/fixtures/v1/opentofu" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-opentofu/src/decision.rs" reference_evaluator = "auths_opentofu::evaluate" migration_status = "reference-only" @@ -243,7 +243,7 @@ hard_limit_source = "product/integrations/auths-postgresql/src/schema.rs" fixture_manifest = "product/fixtures/v1/postgresql/manifest.json" mutation_corpus = "product/fixtures/v1/postgresql" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-postgresql/src/decision.rs" reference_evaluator = "auths_postgresql::evaluate" migration_status = "reference-only" @@ -273,7 +273,7 @@ hard_limit_source = "product/integrations/auths-radicle/src/profile.rs" fixture_manifest = "product/fixtures/v1/radicle/manifest.json" mutation_corpus = "product/fixtures/v1/radicle" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-radicle/src/containment.rs" reference_evaluator = "auths_radicle::containment::evaluate" migration_status = "reference-only" @@ -303,7 +303,7 @@ hard_limit_source = "product/integrations/auths-stripe/src/bounded.rs" fixture_manifest = "product/fixtures/v1/stripe/manifest.json" mutation_corpus = "product/fixtures/v1/stripe" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-stripe/src/bounded.rs" reference_evaluator = "auths_stripe::evaluate_bounded_refund" migration_status = "reference-only" diff --git a/product/integrations/auths-stripe/src/connect/transfer/evaluator.rs b/product/integrations/auths-stripe/src/connect/transfer/evaluator.rs index 1ec433e0..64a57196 100644 --- a/product/integrations/auths-stripe/src/connect/transfer/evaluator.rs +++ b/product/integrations/auths-stripe/src/connect/transfer/evaluator.rs @@ -148,6 +148,20 @@ fn held(snapshot: &ConnectTransferAggregateSnapshot, id: &str) -> u64 { .unwrap_or_default() } +/// Returns the source-relative transfer ceiling in minor units. +/// +/// Returns `None` when the basis-point multiplication would overflow, so an +/// overflow denies rather than wrapping into a larger ceiling. +/// +/// The division floors deliberately: a fractional basis point of headroom is +/// never granted, so the ceiling can only round toward denying. +#[must_use] +pub fn source_basis_point_ceiling(source_amount_minor: u64, basis_points: u16) -> Option { + source_amount_minor + .checked_mul(u64::from(basis_points)) + .map(|product| product / 10_000) +} + /// Evaluates an exact source-funded transfer with checked arithmetic. #[must_use] #[allow( @@ -333,18 +347,16 @@ pub fn evaluate_connect_transfer( "exact amount exceeds the inclusive per-transfer ceiling", ); } - let Some(source_product) = context - .evidence - .source_charge_amount_minor - .checked_mul(u64::from(context.policy.source_basis_points())) - else { + let Some(source_ceiling) = source_basis_point_ceiling( + context.evidence.source_charge_amount_minor, + context.policy.source_basis_points(), + ) else { return ConnectTransferDecision::denied( ConnectTransferDecisionCode::ConnectArithmeticFailure, ConnectTransferDecisionStage::Source, "source-relative multiplication overflowed", ); }; - let source_ceiling = source_product / 10_000; let Some(source_committed_net) = context .evidence .source_committed_transfer_minor @@ -486,14 +498,23 @@ pub fn evaluate_connect_transfer( #[cfg(kani)] mod proofs { + use super::source_basis_point_ceiling; + + // MEASURED COST: ~235s on the default solver, ~279s on kissat, against + // <0.3s for every other harness in this crate. The 64x64 multiply plus the + // symbolic 64-bit division are what bit-blasting cannot do cheaply. The + // claim is deliberately left unbounded over the full u64 amount space; the + // gate pays the cost rather than shrinking the domain. See + // `run_kani_harnesses` in xtask/src/formal.rs. #[kani::proof] fn basis_points_floor_never_exceeds_denominator() { let amount: u64 = kani::any(); let basis_points: u16 = kani::any(); - if basis_points <= 10_000 - && let Some(product) = amount.checked_mul(u64::from(basis_points)) - { - assert!(product / 10_000 <= amount); + kani::assume(basis_points <= 10_000); + // Proves the production ceiling function itself, not a transcription of + // its arithmetic: a change at the call site is now falsifiable here. + if let Some(ceiling) = source_basis_point_ceiling(amount, basis_points) { + assert!(ceiling <= amount); } } } diff --git a/product/integrations/auths-stripe/src/mandate/execution.rs b/product/integrations/auths-stripe/src/mandate/execution.rs index 9bf4d676..da396199 100644 --- a/product/integrations/auths-stripe/src/mandate/execution.rs +++ b/product/integrations/auths-stripe/src/mandate/execution.rs @@ -283,20 +283,20 @@ mod tests { mod kani_proofs { use super::*; - #[kani::proof] - fn unknown_never_releases_capability() { - let state = transition_payment_mandate( - PaymentMandateCapabilityState::Attempting, - PaymentMandateTransition::OutcomeBecameUnknown, - ) - .unwrap(); - assert_eq!(state, PaymentMandateCapabilityState::OutcomeUnknown); - assert!(state.consumes_slot()); + fn any_state() -> PaymentMandateCapabilityState { + match kani::any::() % 7 { + 0 => PaymentMandateCapabilityState::Reserved, + 1 => PaymentMandateCapabilityState::Claimed, + 2 => PaymentMandateCapabilityState::Attempting, + 3 => PaymentMandateCapabilityState::Committed, + 4 => PaymentMandateCapabilityState::Released, + 5 => PaymentMandateCapabilityState::OutcomeUnknown, + _ => PaymentMandateCapabilityState::CustomerActionRequired, + } } - #[kani::proof] - fn only_success_commits_attempting_capability() { - let event = match kani::any::() % 9 { + fn any_event() -> PaymentMandateTransition { + match kani::any::() % 9 { 0 => PaymentMandateTransition::Claim, 1 => PaymentMandateTransition::BeginAttempt, 2 => PaymentMandateTransition::ProviderSucceeded, @@ -306,10 +306,66 @@ mod kani_proofs { 6 => PaymentMandateTransition::ReconcileSucceeded, 7 => PaymentMandateTransition::ReconcileReleased, _ => PaymentMandateTransition::ReconcileStillUnknown, - }; - let next = transition_payment_mandate(PaymentMandateCapabilityState::Attempting, event); - if next == Some(PaymentMandateCapabilityState::Committed) { - assert_eq!(event, PaymentMandateTransition::ProviderSucceeded); + } + } + + #[kani::proof] + fn unknown_never_releases_capability() { + let state = any_state(); + let event = any_event(); + + // Recording ambiguity never frees the capability slot, from any state. + if let Some(next) = + transition_payment_mandate(state, PaymentMandateTransition::OutcomeBecameUnknown) + { + assert_eq!(next, PaymentMandateCapabilityState::OutcomeUnknown); + assert!(next.consumes_slot()); + } + + // An ambiguous capability leaves the slot-consuming set only on an + // explicit reconciled-release fact; nothing else may free it. + if let Some(next) = + transition_payment_mandate(PaymentMandateCapabilityState::OutcomeUnknown, event) + && !next.consumes_slot() + { + assert_eq!(event, PaymentMandateTransition::ReconcileReleased); + } + + // Whole-kernel form: no state ever stops consuming its slot without a + // definite non-effect fact. + if let Some(next) = transition_payment_mandate(state, event) + && !next.consumes_slot() + { + assert!(matches!( + event, + PaymentMandateTransition::KnownFailureReleased + | PaymentMandateTransition::ReconcileReleased + )); + } + } + + #[kani::proof] + fn only_success_commits_attempting_capability() { + let state = any_state(); + let event = any_event(); + // Commitment is reachable only from an in-flight attempt via provider + // success, or from an ambiguous/pending capability via a reconciled + // success fact. Quantifying over the state as well as the event stops + // a new commit edge from any other state passing silently. + if transition_payment_mandate(state, event) + == Some(PaymentMandateCapabilityState::Committed) + { + assert!(matches!( + (state, event), + ( + PaymentMandateCapabilityState::Attempting, + PaymentMandateTransition::ProviderSucceeded + ) | ( + PaymentMandateCapabilityState::OutcomeUnknown + | PaymentMandateCapabilityState::CustomerActionRequired, + PaymentMandateTransition::ReconcileSucceeded + ) + )); } } } diff --git a/product/integrations/auths-stripe/src/merchant/cancel/execution.rs b/product/integrations/auths-stripe/src/merchant/cancel/execution.rs index 4707326b..6cdf7ebe 100644 --- a/product/integrations/auths-stripe/src/merchant/cancel/execution.rs +++ b/product/integrations/auths-stripe/src/merchant/cancel/execution.rs @@ -451,8 +451,40 @@ mod tests { mod kani_proofs { use super::*; + // Compile-time completeness guard: adding a `MerchantReservationState` + // variant fails to compile here, which forces `arbitrary_state` to be + // extended instead of silently under-sampling the state space. Omitted + // states are exactly how an illegal edge stays invisible to a harness whose + // name claims to quantify over all of them. + const fn state_index(state: MerchantReservationState) -> u8 { + use MerchantReservationState as S; + match state { + S::Reserved => 0, + S::Claimed => 1, + S::Attempting => 2, + S::ProviderAccepted => 3, + S::Committed => 4, + S::Authorized => 5, + S::Released => 6, + S::OutcomeUnknown => 7, + S::ReconciledCommitted => 8, + S::ReconciledAuthorized => 9, + S::CaptureCommitted => 10, + S::ReconciledCaptureCommitted => 11, + S::CancelCommitted => 12, + S::ReconciledCancelCommitted => 13, + S::CancelCaptureConflict => 14, + S::AuthorizationReleasedByCapture => 15, + S::AuthorizationReleasedByCancel => 16, + S::ReconciledReleased => 17, + } + } + + // Every `MerchantReservationState` variant must appear here. A generator + // that omits variants silently exempts those states from every harness + // below, which is how an illegal edge out of an omitted state would survive. fn arbitrary_state() -> MerchantReservationState { - match kani::any::() % 16 { + let state = match kani::any::() % 18 { 0 => MerchantReservationState::Reserved, 1 => MerchantReservationState::Claimed, 2 => MerchantReservationState::Attempting, @@ -468,8 +500,16 @@ mod kani_proofs { 12 => MerchantReservationState::CancelCommitted, 13 => MerchantReservationState::ReconciledCancelCommitted, 14 => MerchantReservationState::CancelCaptureConflict, + 15 => MerchantReservationState::AuthorizationReleasedByCapture, + 16 => MerchantReservationState::AuthorizationReleasedByCancel, _ => MerchantReservationState::ReconciledReleased, - } + }; + // Tripwire, not a constraint: `state_index` is always in range, so this + // never prunes the state space. Its purpose is the exhaustive match in + // `state_index`, which fails to compile when a variant is added and so + // forces an author to extend the generator above. + assert!(state_index(state) < 18); + state } #[kani::proof] diff --git a/product/integrations/auths-stripe/src/merchant/capture/execution.rs b/product/integrations/auths-stripe/src/merchant/capture/execution.rs index 3f166337..a3a9819f 100644 --- a/product/integrations/auths-stripe/src/merchant/capture/execution.rs +++ b/product/integrations/auths-stripe/src/merchant/capture/execution.rs @@ -567,20 +567,70 @@ mod tests { mod kani_proofs { use super::*; - #[kani::proof] - fn capture_commit_requires_provider_acceptance() { - let state = match kani::any::() % 10 { + // Compile-time completeness guard: adding a `MerchantReservationState` + // variant fails to compile here, which forces `arbitrary_state` to be + // extended instead of silently under-sampling the state space. Omitted + // states are exactly how an illegal edge stays invisible to a harness whose + // name claims to quantify over all of them. + const fn state_index(state: MerchantReservationState) -> u8 { + use MerchantReservationState as S; + match state { + S::Reserved => 0, + S::Claimed => 1, + S::Attempting => 2, + S::ProviderAccepted => 3, + S::Committed => 4, + S::Authorized => 5, + S::Released => 6, + S::OutcomeUnknown => 7, + S::ReconciledCommitted => 8, + S::ReconciledAuthorized => 9, + S::CaptureCommitted => 10, + S::ReconciledCaptureCommitted => 11, + S::CancelCommitted => 12, + S::ReconciledCancelCommitted => 13, + S::CancelCaptureConflict => 14, + S::AuthorizationReleasedByCapture => 15, + S::AuthorizationReleasedByCancel => 16, + S::ReconciledReleased => 17, + } + } + + // Every `MerchantReservationState` variant must appear here. A generator + // that omits variants silently exempts those states from every harness + // below, which is how an illegal edge out of an omitted state would survive. + fn arbitrary_state() -> MerchantReservationState { + let state = match kani::any::() % 18 { 0 => MerchantReservationState::Reserved, 1 => MerchantReservationState::Claimed, 2 => MerchantReservationState::Attempting, 3 => MerchantReservationState::ProviderAccepted, - 4 => MerchantReservationState::CaptureCommitted, - 5 => MerchantReservationState::OutcomeUnknown, + 4 => MerchantReservationState::Committed, + 5 => MerchantReservationState::Authorized, 6 => MerchantReservationState::Released, - 7 => MerchantReservationState::ReconciledCaptureCommitted, - 8 => MerchantReservationState::ReconciledReleased, - _ => MerchantReservationState::Authorized, + 7 => MerchantReservationState::OutcomeUnknown, + 8 => MerchantReservationState::ReconciledCommitted, + 9 => MerchantReservationState::ReconciledAuthorized, + 10 => MerchantReservationState::CaptureCommitted, + 11 => MerchantReservationState::ReconciledCaptureCommitted, + 12 => MerchantReservationState::CancelCommitted, + 13 => MerchantReservationState::ReconciledCancelCommitted, + 14 => MerchantReservationState::CancelCaptureConflict, + 15 => MerchantReservationState::AuthorizationReleasedByCapture, + 16 => MerchantReservationState::AuthorizationReleasedByCancel, + _ => MerchantReservationState::ReconciledReleased, }; + // Tripwire, not a constraint: `state_index` is always in range, so this + // never prunes the state space. Its purpose is the exhaustive match in + // `state_index`, which fails to compile when a variant is added and so + // forces an author to extend the generator above. + assert!(state_index(state) < 18); + state + } + + #[kani::proof] + fn capture_commit_requires_provider_acceptance() { + let state = arbitrary_state(); let next = transition_payment_capture(state, PaymentCaptureTransition::CaptureCommitted); if next.is_some() { assert_eq!(state, MerchantReservationState::ProviderAccepted); @@ -590,14 +640,17 @@ mod kani_proofs { #[kani::proof] fn provider_acceptance_never_commits_settlement() { - let state = if kani::any::() { - MerchantReservationState::Attempting - } else { - MerchantReservationState::ProviderAccepted - }; - assert_eq!( - transition_payment_capture(state, PaymentCaptureTransition::ProviderAccepted), - Some(MerchantReservationState::ProviderAccepted) - ); + let state = arbitrary_state(); + // Provider acceptance is a pre-settlement fact. Over the whole state + // space it may only park the reservation at `ProviderAccepted`; it must + // never itself produce a committed or reconciled-committed settlement. + let next = transition_payment_capture(state, PaymentCaptureTransition::ProviderAccepted); + if let Some(next) = next { + assert_eq!(next, MerchantReservationState::ProviderAccepted); + assert!(matches!( + state, + MerchantReservationState::Attempting | MerchantReservationState::ProviderAccepted + )); + } } } diff --git a/product/integrations/auths-stripe/src/subscription/modify/evaluator.rs b/product/integrations/auths-stripe/src/subscription/modify/evaluator.rs index 4800f3a9..79bb22b3 100644 --- a/product/integrations/auths-stripe/src/subscription/modify/evaluator.rs +++ b/product/integrations/auths-stripe/src/subscription/modify/evaluator.rs @@ -144,6 +144,24 @@ impl SubscriptionModifyDecision { } } +/// Splits a term-liability change into the amount that must be newly reserved +/// and the amount the modification supersedes. +/// +/// This is the whole liability arithmetic of a modification, isolated so it is +/// callable from a bounded proof harness with the same bytes production runs. +/// +/// The two sides are deliberately disjoint: an upgrade reserves and releases +/// nothing, a downgrade releases and reserves nothing. Netting them (or letting +/// a credit reduce `incremental`) would let a modification widen spend beyond +/// the reserved ceiling. +#[must_use] +pub const fn term_liability_delta(before_term: u64, after_term: u64) -> (u64, u64) { + ( + after_term.saturating_sub(before_term), + before_term.saturating_sub(after_term), + ) +} + pub struct SubscriptionModifyEvaluationContext<'a> { pub action: &'a StripeExactSubscriptionModifyV1, pub policy: &'a StripeBoundedSubscriptionPolicyV1, @@ -558,8 +576,7 @@ pub fn evaluate_subscription_modify( "after term liability overflowed", ); }; - let incremental = after_term.saturating_sub(before_term); - let superseded = before_term.saturating_sub(after_term); + let (incremental, superseded) = term_liability_delta(before_term, after_term); if incremental != action.incremental_term_liability_minor() { return SubscriptionModifyDecision::denied( SubscriptionModifyDecisionCode::PreviewMismatch, @@ -748,23 +765,43 @@ mod tests { #[cfg(kani)] mod proofs { + use super::term_liability_delta; + #[kani::proof] fn credits_never_reduce_incremental_term_liability() { let before = kani::any::(); let after = kani::any::(); - let credit = kani::any::(); - let incremental = after.saturating_sub(before); - let with_any_credit = after.saturating_sub(before); - assert_eq!(incremental, with_any_credit); - let _ = credit; + let (incremental, _) = term_liability_delta(before, after); + + // `term_liability_delta` takes no credit input at all, so no credit can + // enter the reserved amount. The falsifiable content of that claim is + // that `incremental` is exactly the un-netted upgrade amount: it is + // positive whenever the term grows, and equals the full growth. + if after > before { + assert!(incremental > 0); + assert_eq!( + u128::from(incremental), + u128::from(after) - u128::from(before) + ); + } else { + assert_eq!(incremental, 0); + } + // The reserved amount never exceeds the new term liability, so it can + // never demand more capacity than the modification actually creates. + assert!(incremental <= after); } #[kani::proof] fn downgrade_release_is_disjoint_from_upgrade_reservation() { let before = kani::any::(); let after = kani::any::(); - let reserve = after.saturating_sub(before); - let release = before.saturating_sub(after); + let (reserve, release) = term_liability_delta(before, after); assert!(reserve == 0 || release == 0); + // Conservation: the two sides reconstruct the original terms exactly, + // so neither side can silently absorb liability. + assert_eq!( + u128::from(before) + u128::from(reserve), + u128::from(after) + u128::from(release) + ); } } diff --git a/product/runtime/auths-lifecycle/src/kernel.rs b/product/runtime/auths-lifecycle/src/kernel.rs index 6bbe1e9a..f1e46d41 100644 --- a/product/runtime/auths-lifecycle/src/kernel.rs +++ b/product/runtime/auths-lifecycle/src/kernel.rs @@ -354,7 +354,24 @@ mod proofs { #[kani::proof] fn exact_replay_never_becomes_absent_or_conflict() { - assert_eq!(replay_code(true, true), ReplayCode::ExactReplay); + let record_exists: bool = kani::any(); + let commitments_equal: bool = kani::any(); + let code = replay_code(record_exists, commitments_equal); + // Total specification: each classification holds on exactly its own + // inputs. Stating all three as iff makes any reassignment between them + // falsifiable, which a single concrete point cannot do. + assert_eq!( + code == ReplayCode::ExactReplay, + record_exists && commitments_equal + ); + // Absence must never absorb a conflicting prior record: claiming Absent + // for an existing record would let a second identity be created for the + // same workflow. + assert_eq!(code == ReplayCode::Absent, !record_exists); + assert_eq!( + code == ReplayCode::Conflict, + record_exists && !commitments_equal + ); } #[kani::proof] diff --git a/xtask/src/fixtures.rs b/xtask/src/fixtures.rs index caa13b1a..94b6f9ec 100644 --- a/xtask/src/fixtures.rs +++ b/xtask/src/fixtures.rs @@ -616,7 +616,7 @@ hard_limit_source = "product/integrations/auths-records-api/src/policy.rs" fixture_manifest = "product/fixtures/v1/records-api/manifest.json" mutation_corpus = "product/fixtures/v1/records-api" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-records-api/src/decision.rs" reference_evaluator = "auths_records_api::evaluate_create" migration_status = "reference-only" @@ -646,7 +646,7 @@ hard_limit_source = "product/integrations/auths-records-api/src/policy.rs" fixture_manifest = "product/fixtures/v1/records-api/manifest.json" mutation_corpus = "product/fixtures/v1/records-api" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-records-api/src/decision.rs" reference_evaluator = "auths_records_api::evaluate_read" migration_status = "reference-only" @@ -676,7 +676,7 @@ hard_limit_source = "product/integrations/auths-github/src/profile.rs" fixture_manifest = "product/fixtures/v1/github/manifest.json" mutation_corpus = "product/fixtures/v1/github" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-github/src/containment.rs" reference_evaluator = "auths_github::containment::evaluate" migration_status = "reference-only" @@ -706,7 +706,7 @@ hard_limit_source = "product/integrations/auths-github/src/profile.rs" fixture_manifest = "product/fixtures/v1/github/manifest.json" mutation_corpus = "product/fixtures/v1/github" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-github/src/containment.rs" reference_evaluator = "auths_github::containment::evaluate" migration_status = "reference-only" @@ -736,7 +736,7 @@ hard_limit_source = "product/integrations/auths-kubernetes/src/profile.rs" fixture_manifest = "product/fixtures/v1/kubernetes/manifest.json" mutation_corpus = "product/fixtures/v1/kubernetes" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-kubernetes/src/decision.rs" reference_evaluator = "auths_kubernetes::evaluate" migration_status = "reference-only" @@ -766,7 +766,7 @@ hard_limit_source = "product/integrations/auths-opentofu/src/profile.rs" fixture_manifest = "product/fixtures/v1/opentofu/manifest.json" mutation_corpus = "product/fixtures/v1/opentofu" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-opentofu/src/decision.rs" reference_evaluator = "auths_opentofu::evaluate" migration_status = "reference-only" @@ -796,7 +796,7 @@ hard_limit_source = "product/integrations/auths-postgresql/src/schema.rs" fixture_manifest = "product/fixtures/v1/postgresql/manifest.json" mutation_corpus = "product/fixtures/v1/postgresql" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-postgresql/src/decision.rs" reference_evaluator = "auths_postgresql::evaluate" migration_status = "reference-only" @@ -826,7 +826,7 @@ hard_limit_source = "product/integrations/auths-radicle/src/profile.rs" fixture_manifest = "product/fixtures/v1/radicle/manifest.json" mutation_corpus = "product/fixtures/v1/radicle" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-radicle/src/containment.rs" reference_evaluator = "auths_radicle::containment::evaluate" migration_status = "reference-only" @@ -856,7 +856,7 @@ hard_limit_source = "product/integrations/auths-stripe/src/bounded.rs" fixture_manifest = "product/fixtures/v1/stripe/manifest.json" mutation_corpus = "product/fixtures/v1/stripe" fuzz_target = "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs" -kani_harnesses = "auths_bounded_policy::kernel::proof_configuration_match_total" +kani_harnesses = "auths_bounded_policy::kernel::proofs::configuration_match_is_eligible_only_when_every_gate_matches" property_tests = "product/integrations/auths-stripe/src/bounded.rs" reference_evaluator = "auths_stripe::evaluate_bounded_refund" migration_status = "reference-only" diff --git a/xtask/src/formal.rs b/xtask/src/formal.rs index 192b4aba..1a40bcc5 100644 --- a/xtask/src/formal.rs +++ b/xtask/src/formal.rs @@ -329,6 +329,147 @@ pub(crate) fn ci_formal_translation() -> Result<(), String> { run_formal_semantic_checks(&formal_root, false, false) } +/// One workspace package whose `#[kani::proof]` harnesses the formal gate runs. +struct KaniHarnessPackage { + /// Cargo package name passed to `cargo kani -p`. + package: &'static str, + /// Workspace-relative source root that must contain every harness the + /// package owns. + source_root: &'static str, +} + +/// Complete inventory of packages carrying Kani harnesses. +/// +/// `kani_harness_inventory` fails the gate when any `#[kani::proof]` appears +/// outside these roots. Before this list existed the gate ran only the two core +/// packages, so 31 product harnesses were never executed by anything — which is +/// the structural reason single-point harnesses could wear universally +/// quantified names undetected. Adding a harness in a new package must extend +/// this list, not silently skip the gate. +const KANI_HARNESS_PACKAGES: &[KaniHarnessPackage] = &[ + KaniHarnessPackage { + package: "auths-algebra-kernel", + source_root: "core/crates/auths-algebra-kernel", + }, + KaniHarnessPackage { + package: "auths-model", + source_root: "core/crates/auths-model", + }, + KaniHarnessPackage { + package: "auths-lifecycle", + source_root: "product/runtime/auths-lifecycle", + }, + KaniHarnessPackage { + package: "auths-bounded-policy", + source_root: "product/policy/auths-bounded-policy", + }, + KaniHarnessPackage { + package: "auths-stripe", + source_root: "product/integrations/auths-stripe", + }, +]; + +/// Runs the complete harness set for every package in `KANI_HARNESS_PACKAGES`. +/// +/// Nothing is filtered. Kani 0.67 offers only an inclusion filter +/// (`--harness`), so excluding one slow harness would mean hand-listing every +/// other harness name here — a list that goes stale exactly the way the phantom +/// `kani_harnesses` citations did. The complete set is run instead. +/// +/// MEASURED COST of the 31 product harnesses this list newly gates: ~281s of +/// solving, of which ~235s is the single harness +/// `connect::transfer::evaluator::proofs::basis_points_floor_never_exceeds_denominator` +/// (symbolic 64-bit division by 10_000; kissat was tried and was slower at +/// ~279s). Every other harness is under 0.3s. If this gate needs to get faster, +/// make that division cheaper to reason about — do not shrink its input domain. +fn run_kani_harnesses() -> Result<(), String> { + for package in KANI_HARNESS_PACKAGES.iter().map(|entry| entry.package) { + command_in( + "cargo", + // `-j` requires terse output; both are needed to keep the wall + // clock down while still running the complete set. + &["kani", "-p", package, "-j", "--output-format=terse"], + &root(), + None, + )?; + } + println!("Kani bounded harnesses: PASS (complete set)"); + Ok(()) +} + +/// Fails when a `#[kani::proof]` exists that no gated package would run. +/// +/// This runs even under `--skip-kani`: skipping execution is a local +/// convenience, but an unrunnable harness is a permanent evidence gap and must +/// be reported either way. +fn kani_harness_inventory() -> Result<(), String> { + let root = root(); + let mut orphans = Vec::new(); + let mut total = 0_usize; + let mut sources = Vec::new(); + collect_rust_sources(&root, &root, &mut sources)?; + sources.sort(); + for relative in sources { + let text = fs::read_to_string(root.join(&relative)) + .map_err(|error| format!("could not read {}: {error}", relative.display()))?; + let count = text + .lines() + .filter(|line| line.trim_start().starts_with("#[kani::proof]")) + .count(); + if count == 0 { + continue; + } + total += count; + let display = relative.to_string_lossy().replace('\\', "/"); + if !KANI_HARNESS_PACKAGES + .iter() + .any(|entry| display.starts_with(&format!("{}/", entry.source_root))) + { + orphans.push(format!("{display} ({count} harnesses)")); + } + } + if !orphans.is_empty() { + return Err(format!( + "Kani harnesses exist that no gated package runs; add the owning package to \ + KANI_HARNESS_PACKAGES in xtask/src/formal.rs: {}", + orphans.join(", ") + )); + } + println!("Kani harness inventory: PASS ({total} harnesses, all gated)"); + Ok(()) +} + +/// Collects workspace-relative paths of every `.rs` file outside build output. +fn collect_rust_sources( + root: &Path, + directory: &Path, + found: &mut Vec, +) -> Result<(), String> { + let entries = fs::read_dir(directory) + .map_err(|error| format!("could not read {}: {error}", directory.display()))?; + for entry in entries { + let entry = entry.map_err(|error| format!("could not read a directory entry: {error}"))?; + let path = entry.path(); + let name = entry.file_name(); + let name = name.to_string_lossy(); + if name.starts_with('.') || name == "target" || name == "node_modules" { + continue; + } + let kind = entry + .file_type() + .map_err(|error| format!("could not stat {}: {error}", path.display()))?; + if kind.is_dir() { + collect_rust_sources(root, &path, found)?; + } else if kind.is_file() && path.extension().is_some_and(|value| value == "rs") { + let relative = path + .strip_prefix(root) + .map_err(|error| format!("path escaped the workspace root: {error}"))?; + found.push(relative.to_path_buf()); + } + } + Ok(()) +} + pub(crate) fn prepare_formal( require_kani: bool, update: bool, @@ -355,17 +496,11 @@ pub(crate) fn run_formal_semantic_checks( synchronize_lean_vectors(formal_root, update)?; cargo(&["test", "-p", "auths-formal-refinement"])?; + kani_harness_inventory()?; if skip_kani { println!("Kani bounded harnesses: SKIPPED (--skip-kani)"); } else { - command_in( - "cargo", - &["kani", "-p", "auths-algebra-kernel"], - &root(), - None, - )?; - command_in("cargo", &["kani", "-p", "auths-model"], &root(), None)?; - println!("Kani bounded harnesses: PASS"); + run_kani_harnesses()?; } println!("Lean theorems: PASS"); println!("Generated semantic vectors: byte-stable"); From e21fb2cbcb3f038e332efcd7f7cfe42b1e68edc4 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sat, 15 Aug 2026 22:42:47 +0100 Subject: [PATCH 09/61] formal: make attenuation dimension 11 expressible, then prove it (UNSIGNED) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit formal/Auths/Rich/Semantics.lean read `extensionsAttenuate := true` — the same shape as the `root_preserved: true` fixed in 1a88ba3. The root cause was one level deeper: `Auths.Rich.Grant` had no extensions field, so `true` was the only value the model COULD write. The eleven-dimension contract was proved over ten dimensions and reported eleven. THE MODEL CAN NOW EXPRESS IT - Rich/Types.lean: Vocabulary gains ExtensionIdCarrier/ExtensionBodyCarrier; new CriticalExtension and CriticalExtensions carriers. CriticalExtensions carries `CriticalExtensions::new`'s own obligations as constructor fields — duplicate-id-freedom and length <= HARD_MAX_EXTENSIONS (32) — so an inhabitant is exactly a value the Rust constructor would have accepted. - Grant gains `extensions`; AuthorityScope gains `extensions : Option ...`, mirroring `EffectiveAuthority.extensions`: None at a fresh trust anchor, Some from the first accepted edge onward. - The entries are an ordered sequence and NOT a FiniteSet, deliberately. `critical_extensions_equal` compares the two canonical vectors POSITIONALLY. A set model would identify [a,b] with [b,a] and so report attenuation on a pair the shipping kernel denies — that is the model being weaker than the code, which is the defect class this wave exists to remove. Rationale is in the type's docstring. THE DECISION IS REAL - `extensionsLe` replaces the literal. structuralScopeLe and grantScopeChecks gain the conjunct; acceptedScope pins the set; evaluateAuthorScope gains an `.extensions` diagnostic in the Rust dimension order (last, after Assurance); delegationProjection.extensionsAttenuate is now a computed decision. - New theorems (Rich/Theorems.lean, mirrored in Attenuation.lean): extensions_refl/trans/antisymm, extensions_le_pinned_iff, extensions_le_false_of_dropped, extensions_le_false_of_altered, extensions_le_refuses_a_dropped_singleton (non-vacuity: the carrier is not a subsingleton), extensions_dimension_is_exact, extensions_dimension_false_of_altered_set, altered_extensions_deny_every_projection, delegate_preserves_pinned_extensions, chain_preserves_pinned_extensions. The chain theorem is the property the mechanism exists for: an extension attached anywhere in a chain survives every later delegation. MUTATION TESTED, TWICE Reverting `extensionsAttenuate := true` and running `lake build` turns four theorems RED and nothing else: extensions_dimension_is_exact (goal left: extensionsLe ...) extensions_dimension_false_of_altered_set (goal left: False) altered_extensions_deny_every_projection rich_projection_accepts_iff_root_and_scope_depth_checks (tauto failed) delegate_preserves_pinned_extensions and chain_preserves_pinned_extensions stayed GREEN under that mutation — correctly, because they constrain the delegation relation, not the projection. A second mutation dropping the extensionsLe conjunct from grantScopeChecks turns exactly those red, plus accepted_scope_le and rich_projection_accepts_iff_root_and_scope_depth_checks. Both mutations restored; the tree builds clean. REFINEMENT NOW COVERS ELEVEN DIMENSIONS translated_delegation_refines_rich_spec no longer wraps the rich decision in `extensionAwareDelegationDecision`. That wrapper existed only because the eleventh dimension lived on the Rust side of the bridge; it is deleted. richAuthorScopeDecision states the extension condition as the rich relation instead of raw translated key equality. BUDGET LANE REPAIR (ac5b968) - Auths.Rich.budgetCovers corrected to match the fixed kernel: an absent ceiling covers everything, an absent request under a present ceiling does not. BEHAVIOR CHANGE inherited from ac5b968, stated there. - budget_coverage_monotone re-proved (still true; proof script only). - core/formal-vectors/v1/rich-authority.json regenerated by `lake exe auths-vector-export`, not by hand. budget-cover-no-request now decides false and lean_rich_authority_vectors_refine_shipping_rust_predicates is GREEN. WEAKENED, DISCLOSED optional_budget_covers_spec and translated_coverage_refines_rich_spec gained `TranslatedBudgetCoverageCurrent`, which excludes exactly (present ceiling, absent request). The pinned Aeneas translation of optional_budget_covers still carries the pre-correction `| none => ok true`; charon/aeneas are not installed on this host, and hand-editing the generated body would fabricate the claim the translation pin asserts. The excluded class is pinned by a new theorem, translated_budget_coverage_gap_is_the_absent_request, which stops being provable the moment the translation is regenerated — that is what forces the hypothesis to be deleted then. The three refinement theorems also gained CriticalExtensionsCanonical obligations: Rust constructor postconditions, the same class as the existing StringBounded/ValidityWindowValid invariants. CITATIONS formal/Auths/Theorems.lean inventory and formal/assurance-manifest-v1.toml both updated. `lake env lean Auths/AssuranceAudit.lean` PASSES over 121 compiled statements; axioms are exactly Classical.choice, Quot.sound, propext. KNOWN RED, unchanged and not fixed here: `cargo xtask formal` still reports the production translation source closure as drifted (missing charon/aeneas). Co-Authored-By: Claude Opus 5 --- core/formal-vectors/v1/rich-authority.json | 2 +- formal/Auths/Attenuation.lean | 68 ++ formal/Auths/Refinement/Production.lean | 305 ++++-- formal/Auths/Rich/Semantics.lean | 61 +- formal/Auths/Rich/Theorems.lean | 201 +++- formal/Auths/Rich/Types.lean | 124 +++ formal/Auths/Theorems.lean | 13 + formal/Auths/VectorExport.lean | 4 + formal/assurance-manifest-v1.toml | 1104 ++++++++++++++++---- 9 files changed, 1574 insertions(+), 308 deletions(-) diff --git a/core/formal-vectors/v1/rich-authority.json b/core/formal-vectors/v1/rich-authority.json index 7e7d2f6e..c9fdd115 100644 --- a/core/formal-vectors/v1/rich-authority.json +++ b/core/formal-vectors/v1/rich-authority.json @@ -1 +1 @@ -{"schema":"auths-proof-rich-authority-vectors/v1","cases":[{"id":"window-inner","kind":"window","args":[10,20,11,19],"child":[],"parent":[],"expected":true},{"id":"window-inclusive-boundary","kind":"window","args":[10,20,10,20],"child":[],"parent":[],"expected":true},{"id":"window-expanded-start","kind":"window","args":[10,20,9,19],"child":[],"parent":[],"expected":false},{"id":"finite-set-subset","kind":"finite-set-subset","args":[],"child":[1],"parent":[1,2],"expected":true},{"id":"finite-set-widening","kind":"finite-set-subset","args":[],"child":[1,2],"parent":[1],"expected":false},{"id":"finite-set-member","kind":"finite-set-member","args":[2],"child":[],"parent":[1,2],"expected":true},{"id":"finite-set-missing","kind":"finite-set-member","args":[3],"child":[],"parent":[1,2],"expected":false},{"id":"budget-lower","kind":"budget","args":[1,5,1,10],"child":[],"parent":[],"expected":true},{"id":"budget-higher","kind":"budget","args":[1,11,1,10],"child":[],"parent":[],"expected":false},{"id":"budget-algebra-mismatch","kind":"budget","args":[1,5,2,10],"child":[],"parent":[],"expected":false},{"id":"budget-unbounded-parent","kind":"optional-budget","args":[1,0],"child":[],"parent":[],"expected":true},{"id":"budget-unbounded-child","kind":"optional-budget","args":[0,1],"child":[],"parent":[],"expected":false},{"id":"budget-cover-no-request","kind":"budget-covers","args":[1,1,10,0,0,0],"child":[],"parent":[],"expected":true},{"id":"budget-cover-within-ceiling","kind":"budget-covers","args":[1,1,10,1,1,5],"child":[],"parent":[],"expected":true},{"id":"budget-cover-over-ceiling","kind":"budget-covers","args":[1,1,10,1,1,11],"child":[],"parent":[],"expected":false},{"id":"budget-cover-unbounded-ceiling","kind":"budget-covers","args":[0,0,0,1,1,500],"child":[],"parent":[],"expected":true},{"id":"status-fresher","kind":"status","args":[1,5,1,10],"child":[],"parent":[],"expected":true},{"id":"status-weaker","kind":"status","args":[1,11,1,10],"child":[],"parent":[],"expected":false},{"id":"status-method-mismatch","kind":"status","args":[1,5,2,10],"child":[],"parent":[],"expected":false},{"id":"action-exact-match","kind":"action-allows-exact","args":[1,1],"child":[],"parent":[],"expected":true},{"id":"action-exact-mismatch","kind":"action-allows-exact","args":[1,2],"child":[],"parent":[],"expected":false},{"id":"action-set-attenuation","kind":"action-set-attenuation","args":[],"child":[1],"parent":[1,2],"expected":true},{"id":"action-set-widening","kind":"action-set-attenuation","args":[],"child":[1,2],"parent":[1],"expected":false},{"id":"action-singleton-exact","kind":"action-singleton-exact-attenuation","args":[1],"child":[1],"parent":[],"expected":true},{"id":"action-singleton-exact-mismatch","kind":"action-singleton-exact-attenuation","args":[2],"child":[1],"parent":[],"expected":false},{"id":"action-multiple-exact","kind":"action-singleton-exact-attenuation","args":[1],"child":[1,2],"parent":[],"expected":false}]} +{"schema":"auths-proof-rich-authority-vectors/v1","cases":[{"id":"window-inner","kind":"window","args":[10,20,11,19],"child":[],"parent":[],"expected":true},{"id":"window-inclusive-boundary","kind":"window","args":[10,20,10,20],"child":[],"parent":[],"expected":true},{"id":"window-expanded-start","kind":"window","args":[10,20,9,19],"child":[],"parent":[],"expected":false},{"id":"finite-set-subset","kind":"finite-set-subset","args":[],"child":[1],"parent":[1,2],"expected":true},{"id":"finite-set-widening","kind":"finite-set-subset","args":[],"child":[1,2],"parent":[1],"expected":false},{"id":"finite-set-member","kind":"finite-set-member","args":[2],"child":[],"parent":[1,2],"expected":true},{"id":"finite-set-missing","kind":"finite-set-member","args":[3],"child":[],"parent":[1,2],"expected":false},{"id":"budget-lower","kind":"budget","args":[1,5,1,10],"child":[],"parent":[],"expected":true},{"id":"budget-higher","kind":"budget","args":[1,11,1,10],"child":[],"parent":[],"expected":false},{"id":"budget-algebra-mismatch","kind":"budget","args":[1,5,2,10],"child":[],"parent":[],"expected":false},{"id":"budget-unbounded-parent","kind":"optional-budget","args":[1,0],"child":[],"parent":[],"expected":true},{"id":"budget-unbounded-child","kind":"optional-budget","args":[0,1],"child":[],"parent":[],"expected":false},{"id":"budget-cover-no-request","kind":"budget-covers","args":[1,1,10,0,0,0],"child":[],"parent":[],"expected":false},{"id":"budget-cover-within-ceiling","kind":"budget-covers","args":[1,1,10,1,1,5],"child":[],"parent":[],"expected":true},{"id":"budget-cover-over-ceiling","kind":"budget-covers","args":[1,1,10,1,1,11],"child":[],"parent":[],"expected":false},{"id":"budget-cover-unbounded-ceiling","kind":"budget-covers","args":[0,0,0,1,1,500],"child":[],"parent":[],"expected":true},{"id":"status-fresher","kind":"status","args":[1,5,1,10],"child":[],"parent":[],"expected":true},{"id":"status-weaker","kind":"status","args":[1,11,1,10],"child":[],"parent":[],"expected":false},{"id":"status-method-mismatch","kind":"status","args":[1,5,2,10],"child":[],"parent":[],"expected":false},{"id":"action-exact-match","kind":"action-allows-exact","args":[1,1],"child":[],"parent":[],"expected":true},{"id":"action-exact-mismatch","kind":"action-allows-exact","args":[1,2],"child":[],"parent":[],"expected":false},{"id":"action-set-attenuation","kind":"action-set-attenuation","args":[],"child":[1],"parent":[1,2],"expected":true},{"id":"action-set-widening","kind":"action-set-attenuation","args":[],"child":[1,2],"parent":[1],"expected":false},{"id":"action-singleton-exact","kind":"action-singleton-exact-attenuation","args":[1],"child":[1],"parent":[],"expected":true},{"id":"action-singleton-exact-mismatch","kind":"action-singleton-exact-attenuation","args":[2],"child":[1],"parent":[],"expected":false},{"id":"action-multiple-exact","kind":"action-singleton-exact-attenuation","args":[1],"child":[1,2],"parent":[],"expected":false}]} diff --git a/formal/Auths/Attenuation.lean b/formal/Auths/Attenuation.lean index 81e596ed..80c73a12 100644 --- a/formal/Auths/Attenuation.lean +++ b/formal/Auths/Attenuation.lean @@ -62,4 +62,72 @@ theorem attenuation_root_dimension_is_exact {v : Vocabulary} rootPreserved parent grant := root_dimension_is_exact parent grant +/-! +## The critical-extension dimension + +`extensionsAttenuate` was the last literal in the projection. The model could +not express it at all: `Grant` had no `extensions` field, so `true` was the +only writable value and the eleven-dimension contract was proved over ten. +The statements below give the dimension the same treatment the trust root +received. +-/ + +/-- +Acceptance of the generated attenuation contract implies the parent's pinned +critical-extension set survived the edge exactly. +-/ +theorem attenuation_requires_critical_extensions {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) + (accepted : + Auths.Generated.attenuationAccepts + (delegationProjection parent grant) = true) : + extensionsLe (some grant.extensions) parent.scope.extensions := + ((rich_projection_accepts_iff_root_and_scope_depth_checks + parent grant).1 accepted).2.2.2.2.2.2.2.2.2.2.2 + +/-- +The contrapositive, stated for every input: a stripped or altered critical +extension denies the whole projection no matter what the other ten dimensions +report. +-/ +theorem attenuation_denied_when_extensions_altered {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) + (broken : ¬ extensionsLe (some grant.extensions) parent.scope.extensions) : + Auths.Generated.attenuationAccepts + (delegationProjection parent grant) = false := + altered_extensions_deny_every_projection parent grant broken + +/-- +The dimension is falsifiable. Any grant that alters a pinned critical-extension +set drives it to `false`, so no implementation that returns a literal `true` +can satisfy this theorem. +-/ +theorem attenuation_extension_dimension_is_not_a_literal {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) + (pinned : CriticalExtensions v) + (pinnedBy : parent.scope.extensions = some pinned) + (altered : grant.extensions ≠ pinned) : + (delegationProjection parent grant).extensionsAttenuate = false := + extensions_dimension_false_of_altered_set parent grant pinned pinnedBy altered + +/-- The dimension reports the semantic relation exactly, in both directions. -/ +theorem attenuation_extension_dimension_is_exact {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) : + (delegationProjection parent grant).extensionsAttenuate = true ↔ + extensionsLe (some grant.extensions) parent.scope.extensions := + extensions_dimension_is_exact parent grant + +/-- +A critical extension attached anywhere in a chain survives every later +delegation. This is the property the whole mechanism exists for: an unaware +verifier must not be able to have the constraint removed from under it. +-/ +theorem attenuation_chain_cannot_strip_a_critical_extension {v : Vocabulary} + {start : ChainState v} {rest : List (ChainState v)} + (chain : DelegationChain start rest) + (pinned : CriticalExtensions v) + (pinnedBy : start.scope.extensions = some pinned) : + ∀ state ∈ rest, state.scope.extensions = some pinned := + chain_preserves_pinned_extensions chain pinned pinnedBy + end Auths.Attenuation diff --git a/formal/Auths/Refinement/Production.lean b/formal/Auths/Refinement/Production.lean index 2b3fb3e6..5264a3d4 100644 --- a/formal/Auths/Refinement/Production.lean +++ b/formal/Auths/Refinement/Production.lean @@ -1140,6 +1140,8 @@ abbrev productionVocabulary : Auths.Rich.Vocabulary where StatusMethodCarrier := List Std.U8 AssuranceCarrier := List Std.U8 GrantIdCarrier := List Std.U8 + ExtensionIdCarrier := List Std.U8 + ExtensionBodyCarrier := List Std.U8 principalDecidableEq := inferInstance profileDecidableEq := inferInstance permissionDecidableEq := inferInstance @@ -1149,9 +1151,140 @@ abbrev productionVocabulary : Auths.Rich.Vocabulary where statusMethodDecidableEq := inferInstance assuranceDecidableEq := inferInstance grantIdDecidableEq := inferInstance + extensionIdDecidableEq := inferInstance + extensionBodyDecidableEq := inferInstance abbrev ProductionVocabulary := productionVocabulary +/-! +### Critical extensions + +`criticalExtensionKey` already reads one translated extension as its canonical +`(identifier bytes, payload bytes)` pair. The rich carrier is that same pair +in named fields, so the representation map is injective and the model's +positional equality is exactly `critical_extensions_equal`. +-/ + +def richCriticalExtensionOfKey (key : List Std.U8 × List Std.U8) : + Auths.Rich.CriticalExtension ProductionVocabulary where + id := ⟨key.1⟩ + body := ⟨key.2⟩ + +theorem richCriticalExtensionOfKey_injective : + Function.Injective richCriticalExtensionOfKey := by + rintro ⟨leftId, leftBody⟩ ⟨rightId, rightBody⟩ equality + simpa [richCriticalExtensionOfKey, Prod.ext_iff] using equality + +def richCriticalExtension (extension : auths_model.CriticalExtension) : + Auths.Rich.CriticalExtension ProductionVocabulary := + richCriticalExtensionOfKey (criticalExtensionKey extension) + +/-- +The representation invariants `CriticalExtensions::new` establishes. + +The Rust constructor rejects a repeated identifier with +`ModelError::DuplicateExtension` and rejects more than `HARD_MAX_EXTENSIONS` +entries, so every value the shipping code can hold satisfies both. The Aeneas +translation erases the constructor, leaving a bare `Vec`, so the obligations are +carried here — the same pattern as `ValidityWindowValid` and +`SelectedProfileValid`. +-/ +structure CriticalExtensionsCanonical + (extensions : auths_model.CriticalExtensions) : Prop where + distinctIds : + (extensions.val.map fun extension => stringBytes extension.id).Nodup + size : extensions.val.length ≤ Auths.Rich.hardMaxExtensions + +theorem richCriticalExtension_entries + (extensions : auths_model.CriticalExtensions) : + extensions.val.map richCriticalExtension = + (criticalExtensionsKey extensions).map richCriticalExtensionOfKey := by + simp [criticalExtensionsKey, richCriticalExtension, List.map_map, + Function.comp_def] + +def richCriticalExtensions + (extensions : auths_model.CriticalExtensions) + (canonical : CriticalExtensionsCanonical extensions) : + Auths.Rich.CriticalExtensions ProductionVocabulary where + entries := extensions.val.map richCriticalExtension + distinctIds := by + have nodup : + (extensions.val.map fun extension => stringBytes extension.id).Pairwise + (· ≠ ·) := canonical.distinctIds + rw [List.pairwise_map] at nodup + rw [List.pairwise_map] + refine nodup.imp ?_ + intro left right different equality + exact different (by + simpa [richCriticalExtension, richCriticalExtensionOfKey, + criticalExtensionKey] using equality) + bounded := by + simpa using canonical.size + +/-- +Rich equality of two translated extension sets is exactly key-list equality. + +No canonicalisation argument is needed in either direction: the rich carrier is +an ordered sequence precisely because `critical_extensions_equal` compares the +two canonical vectors positionally. +-/ +@[simp] theorem richCriticalExtensions_eq_iff + (child parent : auths_model.CriticalExtensions) + (childCanonical : CriticalExtensionsCanonical child) + (parentCanonical : CriticalExtensionsCanonical parent) : + richCriticalExtensions child childCanonical = + richCriticalExtensions parent parentCanonical ↔ + criticalExtensionsKey child = criticalExtensionsKey parent := by + rw [Auths.Rich.CriticalExtensions.ext_iff] + show child.val.map richCriticalExtension = + parent.val.map richCriticalExtension ↔ _ + rw [richCriticalExtension_entries, richCriticalExtension_entries] + exact (List.map_injective_iff.mpr richCriticalExtensionOfKey_injective).eq_iff + +def richOptionalCriticalExtensions + (extensions : Option auths_model.CriticalExtensions) + (canonical : ∀ value ∈ extensions, CriticalExtensionsCanonical value) : + Option (Auths.Rich.CriticalExtensions ProductionVocabulary) := + match extensions with + | none => none + | some value => + some (richCriticalExtensions value (canonical value rfl)) + +@[simp] theorem richOptionalCriticalExtensions_none + (canonical : ∀ value ∈ (none : Option auths_model.CriticalExtensions), + CriticalExtensionsCanonical value) : + richOptionalCriticalExtensions none canonical = none := rfl + +@[simp] theorem richOptionalCriticalExtensions_some + (value : auths_model.CriticalExtensions) + (canonical : ∀ candidate ∈ some value, + CriticalExtensionsCanonical candidate) : + richOptionalCriticalExtensions (some value) canonical = + some (richCriticalExtensions value (canonical value rfl)) := rfl + +/-- +The shipping kernel's optional extension gate is exactly the rich relation. + +This is what lets `Auths.Rich.evaluateGrant` own dimension 11 outright. Before +the rich model had an `extensions` field the delegation refinement had to wrap +the rich decision in `extensionAwareDelegationDecision`, because the eleventh +dimension lived only on the Rust side of the bridge. +-/ +theorem extensions_le_rich_iff + (child : auths_model.CriticalExtensions) + (parent : Option auths_model.CriticalExtensions) + (childCanonical : CriticalExtensionsCanonical child) + (parentCanonical : ∀ value ∈ parent, CriticalExtensionsCanonical value) : + Auths.Rich.extensionsLe + (some (richCriticalExtensions child childCanonical)) + (richOptionalCriticalExtensions parent parentCanonical) ↔ + OptionalCriticalExtensionsAttenuate child parent := by + cases parent with + | none => + simp [OptionalCriticalExtensionsAttenuate, Auths.Rich.extensionsLe] + | some parentExtensions => + simp [OptionalCriticalExtensionsAttenuate, Auths.Rich.extensionsLe] + def richDigest (digest : auths_model.Digest) : Auths.Rich.Digest ProductionVocabulary := ⟨digestKey digest⟩ @@ -1333,16 +1466,42 @@ def OptionalBudgetBounded · simpa [OptionalBudgetBounded, childEq] using childBounded · simpa [OptionalBudgetBounded, parentEq] using parentBounded +/-- +The one input class on which the pinned Aeneas translation of +`auths_model::optional_budget_covers` is known to be stale. + +The shipping Rust now answers `false` for a present ceiling with an absent +request — an action that declares no bound on what it may spend is exactly the +authority a ceiling exists to deny. The translation replayed here predates +that correction and still answers `true` on that one pair +(`formal/qualification/aeneas/generated/model/Funs.lean`, `optional_budget_covers`, +`| none => ok true`). + +Hand-editing the generated body would fabricate the claim the translation pin +asserts — "this Lean was produced from this source" — so the gap is carried as +an explicit hypothesis instead, exactly as `AuthorityStateAnchored` carries the +missing `root` field. `translated_budget_coverage_gap_is_the_absent_request` +below pins the gap precisely and stops compiling the moment the translation is +regenerated, which is what forces this hypothesis to be deleted then. +-/ +def TranslatedBudgetCoverageCurrent + (ceiling requested : Option auths_model.BudgetCeiling) : Prop := + ceiling = none ∨ requested ≠ none + @[step] theorem optional_budget_covers_spec (ceiling requested : Option auths_model.BudgetCeiling) (ceilingBounded : OptionalBudgetBounded ceiling) - (requestedBounded : OptionalBudgetBounded requested) : + (requestedBounded : OptionalBudgetBounded requested) + (translationCurrent : TranslatedBudgetCoverageCurrent ceiling requested) : auths_model.optional_budget_covers ceiling requested ⦃ result => result ↔ Auths.Rich.budgetCovers (ceiling.map richBudget) (requested.map richBudget) ⦄ := by cases ceilingEq : ceiling <;> cases requestedEq : requested <;> simp only [auths_model.optional_budget_covers] + case some.none => + exact absurd translationCurrent (by simp [TranslatedBudgetCoverageCurrent, + ceilingEq, requestedEq]) case some.some => unfold auths_model.BudgetCeiling.covers unfold auths_model.BudgetCeiling.attenuates @@ -1357,6 +1516,23 @@ def OptionalBudgetBounded simp [Auths.Rich.budgetCovers, WP.spec, WP.theta, WP.wp_return] +/-- +The translation gap is exactly one pair, and it is a fail-open one. + +For every bounded ceiling the pinned translation answers `true` where the +shipping semantics answer `false`. Stating it as a theorem rather than a +comment means the staleness is itself checked evidence: once the translation is +regenerated this stops being provable, and the hypothesis +`TranslatedBudgetCoverageCurrent` must be removed in the same change. +-/ +theorem translated_budget_coverage_gap_is_the_absent_request + (ceiling : auths_model.BudgetCeiling) : + auths_model.optional_budget_covers (some ceiling) none = ok true ∧ + ¬ Auths.Rich.budgetCovers (some (richBudget ceiling)) none := by + constructor + · rfl + · simp [Auths.Rich.budgetCovers] + def StatusPolicyValid (policy : auths_model.StatusPolicy) : Prop := match policy with | .ExpiryOnly => True @@ -1538,6 +1714,7 @@ structure ScopeAuthorityViewValid status : StatusPolicyValid view.status_policy assurance : StringBounded view.assurance_floor extensions : CriticalExtensionsBounded view.extensions + extensionsCanonical : CriticalExtensionsCanonical view.extensions structure SelectedProfileValid (selected : Option auths_model.ProfileRef) @@ -1605,6 +1782,8 @@ structure AuthorityStateViewValid assurance : StringBounded view.assurance_policy extensions : ∀ extensions ∈ view.extensions, CriticalExtensionsBounded extensions + extensionsCanonical : ∀ extensions ∈ view.extensions, + CriticalExtensionsCanonical extensions structure GrantAuthorityViewValid (view : auths_model.GrantAuthorityView) : Prop where @@ -1618,6 +1797,7 @@ structure GrantAuthorityViewValid status : StatusPolicyValid view.status_policy assurance : StringBounded view.assurance_floor extensions : CriticalExtensionsBounded view.extensions + extensionsCanonical : CriticalExtensionsCanonical view.extensions structure ActionAuthorityViewValid (view : auths_model.ActionAuthorityView) : Prop where @@ -1663,6 +1843,8 @@ def richAuthorityState budget := view.budget_ceiling.map richBudget status := richStatus view.status_policy valid.status assurance := richAssurance view.assurance_policy + extensions := + richOptionalCriticalExtensions view.extensions valid.extensionsCanonical } remainingDepth := view.remaining_depth.val lastGrant := view.last_grant.map richGrantId @@ -1683,6 +1865,7 @@ def richGrant parent := view.parent.map richGrantId status := richStatus view.status_policy valid.status assurance := richAssurance view.assurance_floor + extensions := richCriticalExtensions view.extensions valid.extensionsCanonical def richAction (view : auths_model.ActionAuthorityView) @@ -1726,19 +1909,6 @@ def productionDelegationOutcome | .denied .delegationExpanded => .Denied auths_model.DenialReason.DelegationExpanded -def extensionAwareDelegationDecision - (parent : Option auths_model.CriticalExtensions) - (grant : auths_model.CriticalExtensions) - (decision : Auths.Rich.DelegationDecision ProductionVocabulary) : - Auths.Rich.DelegationDecision ProductionVocabulary := - match decision with - | .denied .brokenGrantChain => decision - | _ => - if OptionalCriticalExtensionsAttenuate grant parent then - decision - else - .denied .delegationExpanded - def productionCoverageDecision (decision : Auths.Rich.CoverageDecision) : auths_authority.CoverageDecision := @@ -1939,8 +2109,11 @@ def richAuthorScopeDecision (richStatus parent.status_policy parentValid.status) then if richAssurance child.assurance_floor = richAssurance parent.assurance_floor then - if criticalExtensionsKey child.extensions = - criticalExtensionsKey parent.extensions then + if Auths.Rich.extensionsLe + (some (richCriticalExtensions child.extensions + childValid.extensionsCanonical)) + (some (richCriticalExtensions parent.extensions + parentValid.extensionsCanonical)) then .Accepted else .Denied .Extensions else .Denied .Assurance @@ -1968,10 +2141,12 @@ theorem translated_rust_refines_rich_spec parent child parentValid childValid ⦄ := by rcases parentValid with ⟨parentProfile, parentPermissions, parentWindow, parentAudiences, - parentBudget, parentStatus, parentAssurance, parentExtensions⟩ + parentBudget, parentStatus, parentAssurance, parentExtensions, + parentExtensionsCanonical⟩ rcases childValid with ⟨childProfile, childPermissions, childWindow, childAudiences, - childBudget, childStatus, childAssurance, childExtensions⟩ + childBudget, childStatus, childAssurance, childExtensions, + childExtensionsCanonical⟩ unfold auths_authority.evaluate_author_scope_view unfold richAuthorScopeDecision step with profile_ref_equal_spec as ⟨profileAccepted, profileIff⟩ @@ -2038,6 +2213,15 @@ theorem translated_rust_refines_rich_spec simpa using assuranceIff.mp assuranceCondition step with critical_extensions_equal_spec as ⟨extensionsAccepted, extensionsIff⟩ + have extensionsRich : + Auths.Rich.extensionsLe + (some (richCriticalExtensions child.extensions + childExtensionsCanonical)) + (some (richCriticalExtensions parent.extensions + parentExtensionsCanonical)) ↔ + criticalExtensionsKey child.extensions = + criticalExtensionsKey parent.extensions := by + simp [Auths.Rich.extensionsLe] split <;> rename_i extensionsCondition · have extensionsSemantic : criticalExtensionsKey child.extensions = @@ -2109,8 +2293,13 @@ theorem translated_rust_refines_rich_spec /-- The mechanically translated terminal-coverage evaluator returns exactly the ordered rich coverage decision. Permission and audience checks are proved as -membership, and a missing requested budget is correctly treated as no spend -rather than as an unbounded child authority. +membership. + +A bounded ceiling with an absent requested budget is **excluded** by +`budgetTranslationCurrent`: on that one pair the pinned translation still +returns the pre-correction answer, so the statement would be false rather than +weak if it claimed that case. See `TranslatedBudgetCoverageCurrent` and +`translated_budget_coverage_gap_is_the_absent_request`. -/ theorem translated_coverage_refines_rich_spec (root : Auths.Rich.Principal ProductionVocabulary) @@ -2118,7 +2307,10 @@ theorem translated_coverage_refines_rich_spec (action : auths_model.ActionAuthorityView) (authorityValid : AuthorityStateViewValid authority) (actionValid : ActionAuthorityViewValid action) - (anchored : AuthorityStateAnchored root authority) : + (anchored : AuthorityStateAnchored root authority) + (budgetTranslationCurrent : + TranslatedBudgetCoverageCurrent + authority.budget_ceiling action.requested_budget) : auths_authority.evaluate_action_coverage_view authority action ⦃ result => result = productionCoverageDecision @@ -2128,7 +2320,8 @@ theorem translated_coverage_refines_rich_spec rcases authorityValid with ⟨authoritySubject, allowedProfiles, selectedProfile, authorityPermissions, authorityWindow, authorityAudiences, - authorityBudget, authorityStatus, authorityAssurance⟩ + authorityBudget, authorityStatus, authorityAssurance, + authorityExtensions, authorityExtensionsCanonical⟩ rcases actionValid with ⟨actionActor, actionProfile, actionPermission, requestedBudget, actionAudience, actionWindow⟩ @@ -2271,6 +2464,14 @@ theorem translated_coverage_refines_rich_spec The mechanically translated delegation evaluator returns the same ordered rich delegation decision and the accepted production transition is the exact field projection of the rich accepted next state. + +All eleven attenuation dimensions are now decided by `Auths.Rich.evaluateGrant` +itself. This statement previously wrapped the rich decision in +`extensionAwareDelegationDecision`, a post-filter that re-applied the critical +extension gate outside the model — necessarily, because `Auths.Rich.Grant` had +no `extensions` field and `delegationProjection.extensionsAttenuate` was the +literal `true`. Removing the wrapper is what makes this a refinement of eleven +dimensions rather than of ten plus a patch. -/ theorem translated_delegation_refines_rich_spec (root : Auths.Rich.Principal ProductionVocabulary) @@ -2283,20 +2484,20 @@ theorem translated_delegation_refines_rich_spec auths_authority.evaluate_grant_view parent grantId grant ⦃ result => result.outcome = productionDelegationOutcome - (extensionAwareDelegationDecision parent.extensions grant.extensions - (Auths.Rich.evaluateGrant - (richAuthorityState root parent parentValid) - (richGrantId grantId) - (richGrant grant grantValid))) + (Auths.Rich.evaluateGrant + (richAuthorityState root parent parentValid) + (richGrantId grantId) + (richGrant grant grantValid)) grantId grant ⦄ := by rcases parentValid with ⟨parentSubject, allowedProfiles, selectedProfile, parentPermissions, parentWindow, parentAudiences, - parentBudget, parentStatus, parentAssurance, parentExtensions⟩ + parentBudget, parentStatus, parentAssurance, parentExtensions, + parentExtensionsCanonical⟩ rcases grantValid with ⟨grantIssuer, grantSubject, grantProfile, grantPermissions, grantWindow, grantAudiences, grantBudget, grantStatus, - grantAssurance, grantExtensions⟩ + grantAssurance, grantExtensions, grantExtensionsCanonical⟩ unfold auths_authority.evaluate_grant_view split <;> rename_i parentDepthCondition all_goals @@ -2331,8 +2532,7 @@ theorem translated_delegation_refines_rich_spec Auths.Rich.rootPreserved, Auths.Rich.rooted, AuthorityStateAnchored, Auths.Rich.scopeDepthChecks, Auths.Rich.grantScopeChecks, productionDelegationOutcome, expectedAcceptedTransition, - extensionAwareDelegationDecision, - OptionalCriticalExtensionsAttenuate, + extensions_le_rich_iff, OptionalCriticalExtensionsAttenuate, richAuthorityState, richGrant] · have failedScope : ¬(grant.remaining_depth.val < parent.remaining_depth.val ∧ @@ -2384,8 +2584,8 @@ theorem translated_delegation_refines_rich_spec apply scopeIff.mpr simp_all simp_all - have scopeOrExtensions : - (¬Auths.Rich.scopeDepthChecks + have scopeSemantic : + ¬Auths.Rich.scopeDepthChecks (richAuthorityState root parent { subject := parentSubject @@ -2398,6 +2598,7 @@ theorem translated_delegation_refines_rich_spec status := parentStatus assurance := parentAssurance extensions := parentExtensions + extensionsCanonical := parentExtensionsCanonical }) (richGrant grant { @@ -2411,22 +2612,14 @@ theorem translated_delegation_refines_rich_spec status := grantStatus assurance := grantAssurance extensions := grantExtensions - }) ∧ OptionalCriticalExtensionsAttenuate - grant.extensions parent.extensions) ∨ - ¬OptionalCriticalExtensionsAttenuate - grant.extensions parent.extensions := by - by_cases extensionsSemantic : - OptionalCriticalExtensionsAttenuate - grant.extensions parent.extensions - · left - refine ⟨?_, extensionsSemantic⟩ - intro checks - apply failedScope - have richChecks := checks.2 - simpa [Auths.Rich.scopeDepthChecks, - Auths.Rich.grantScopeChecks, richAuthorityState, - richGrant] using And.intro richChecks extensionsSemantic - · exact Or.inr extensionsSemantic + extensionsCanonical := grantExtensionsCanonical + }) := by + intro checks + apply failedScope + have richChecks := checks.2 + simpa [Auths.Rich.scopeDepthChecks, + Auths.Rich.grantScopeChecks, richAuthorityState, + richGrant, extensions_le_rich_iff] using richChecks have linkedSemantic : Auths.Rich.linked (richAuthorityState root parent @@ -2441,6 +2634,7 @@ theorem translated_delegation_refines_rich_spec status := parentStatus assurance := parentAssurance extensions := parentExtensions + extensionsCanonical := parentExtensionsCanonical }) (richGrant grant { @@ -2454,27 +2648,20 @@ theorem translated_delegation_refines_rich_spec status := grantStatus assurance := grantAssurance extensions := grantExtensions + extensionsCanonical := grantExtensionsCanonical }) := by simp_all [Auths.Rich.linked, Auths.Rich.rootPreserved, Auths.Rich.rooted, AuthorityStateAnchored, richAuthorityState, richGrant] - rcases scopeOrExtensions with - ⟨scopeSemantic, extensionsSemantic⟩ | extensionsSemantic - · simp [Auths.Rich.evaluateGrant, linkedSemantic, - scopeSemantic, productionDelegationOutcome, - extensionAwareDelegationDecision, extensionsSemantic] - · simp only [Auths.Rich.evaluateGrant, linkedSemantic, if_pos] - split <;> simp [productionDelegationOutcome, - extensionAwareDelegationDecision, extensionsSemantic] + simp [Auths.Rich.evaluateGrant, linkedSemantic, + scopeSemantic, productionDelegationOutcome] · simp_all [Auths.Rich.evaluateGrant, Auths.Rich.linked, Auths.Rich.rootPreserved, Auths.Rich.rooted, AuthorityStateAnchored, productionDelegationOutcome, - extensionAwareDelegationDecision, richAuthorityState, richGrant] · simp_all [Auths.Rich.evaluateGrant, Auths.Rich.linked, Auths.Rich.rootPreserved, Auths.Rich.rooted, AuthorityStateAnchored, productionDelegationOutcome, - extensionAwareDelegationDecision, richAuthorityState, richGrant] end Auths.Refinement diff --git a/formal/Auths/Rich/Semantics.lean b/formal/Auths/Rich/Semantics.lean index 2628a22b..25a72d72 100644 --- a/formal/Auths/Rich/Semantics.lean +++ b/formal/Auths/Rich/Semantics.lean @@ -25,12 +25,21 @@ instance {v : Vocabulary} Decidable (budgetLe child parent) := by cases child <;> cases parent <;> simp [budgetLe] <;> infer_instance +/-- +Terminal budget coverage. + +An absent ceiling is the unbounded top scope, so it covers every request. An +absent *request* under a present ceiling is **not** covered: an action that +declares no bound on what it may spend is exactly the authority the ceiling +exists to deny. This mirrors `auths_model::optional_budget_covers`, the +`auths-verifier` guard, Go `budgetCovers`, and TypeScript `budgetCovers`. +-/ def budgetCovers {v : Vocabulary} (ceiling requested : Option (BudgetCeiling v)) : Prop := - match requested, ceiling with + match ceiling, requested with | none, _ => True - | some _, none => True - | some requested, some ceiling => + | some _, none => False + | some ceiling, some requested => requested.algebra = ceiling.algebra ∧ requested.value ≤ ceiling.value @@ -40,6 +49,39 @@ instance {v : Vocabulary} cases ceiling <;> cases requested <;> simp [budgetCovers] <;> infer_instance +/-- +The critical-extension delegation relation. + +This is the one dimension where delegation must **preserve**, not narrow. A +critical extension is a constraint an unaware verifier is forbidden to ignore +(the X.509 / JWT sense). If a delegate could drop one, the mechanism would be +worthless: attach a constraint at the root and the first delegation strips it. +Equality is the point, and it is what +`auths_model::critical_extensions_equal` computes. + +A parent that has not pinned a set yet (`none`, the state of +`EffectiveAuthority::from_anchor`) admits any set, matching +`match parent.extensions { Some(parent) => .., None => true }` in +`auths_authority::evaluate_grant_view`. A child that drops back to `none` +under a parent that has pinned one is rejected — that is precisely the +strip-the-constraint move. +-/ +def extensionsLe {v : Vocabulary} + (child parent : Option (CriticalExtensions v)) : Prop := + match child, parent with + | _, none => True + | none, some _ => False + | some child, some parent => child = parent + +instance {v : Vocabulary} + (child parent : Option (CriticalExtensions v)) : + Decidable (extensionsLe child parent) := + match child, parent with + | _, none => isTrue trivial + | none, some _ => isFalse fun absurdity => absurdity + | some child, some parent => + if equality : child = parent then isTrue equality else isFalse equality + def statusLe {v : Vocabulary} (child parent : StatusPolicy v) : Prop := match child, parent with @@ -127,7 +169,8 @@ def structuralScopeLe {v : Vocabulary} actionConstraintLe child.actionConstraint parent.actionConstraint ∧ budgetLe child.budget parent.budget ∧ statusLe child.status parent.status ∧ - child.assurance = parent.assurance + child.assurance = parent.assurance ∧ + extensionsLe child.extensions parent.extensions instance {v : Vocabulary} (child parent : AuthorityScope v) : Decidable (structuralScopeLe child parent) := by @@ -217,7 +260,8 @@ def grantScopeChecks {v : Vocabulary} actionConstraintLe grant.actionConstraint parent.actionConstraint ∧ budgetLe grant.budget parent.budget ∧ statusLe grant.status parent.status ∧ - grant.assurance = parent.assurance + grant.assurance = parent.assurance ∧ + extensionsLe (some grant.extensions) parent.extensions instance {v : Vocabulary} (parent : AuthorityScope v) (grant : Grant v) : Decidable (grantScopeChecks parent grant) := by @@ -270,6 +314,7 @@ def acceptedScope {v : Vocabulary} budget := grant.budget status := grant.status assurance := grant.assurance + extensions := some grant.extensions def acceptedNextState {v : Vocabulary} (parent : ChainState v) (grantId : GrantId v) (grant : Grant v) @@ -330,6 +375,7 @@ inductive AuthorDiagnostic where | delegationDepth | status | assurance + | extensions deriving DecidableEq, Repr inductive AuthorDecision where @@ -359,6 +405,8 @@ def evaluateAuthorScope {v : Vocabulary} .denied .status else if child.assurance ≠ parent.assurance then .denied .assurance + else if ¬ extensionsLe child.extensions parent.extensions then + .denied .extensions else .accepted @@ -429,6 +477,7 @@ def delegationProjection {v : Vocabulary} decide (statusLe grant.status parent.scope.status) assuranceAttenuates := decide (grant.assurance = parent.scope.assurance) - extensionsAttenuate := true + extensionsAttenuate := + decide (extensionsLe (some grant.extensions) parent.scope.extensions) end Auths.Rich diff --git a/formal/Auths/Rich/Theorems.lean b/formal/Auths/Rich/Theorems.lean index fcbf9735..2dfeb7e3 100644 --- a/formal/Auths/Rich/Theorems.lean +++ b/formal/Auths/Rich/Theorems.lean @@ -140,19 +140,92 @@ theorem budget_coverage_monotone {v : Vocabulary} (order : budgetLe child parent) (covered : budgetCovers child requested) : budgetCovers parent requested := by - cases requested with + cases parent with | none => simp [budgetCovers] - | some requested => - cases parent with - | none => simp [budgetCovers] - | some parent => - cases child with - | none => simp [budgetLe] at order - | some child => - simp only [budgetLe] at order + | some parent => + cases child with + | none => simp [budgetLe] at order + | some child => + simp only [budgetLe] at order + cases requested with + | none => simp only [budgetCovers] at covered + | some requested => simp only [budgetCovers] at covered ⊢ exact ⟨covered.1.trans order.1, covered.2.trans order.2⟩ +theorem extensions_refl {v : Vocabulary} + (extensions : Option (CriticalExtensions v)) : + extensionsLe extensions extensions := by + cases extensions <;> simp [extensionsLe] + +theorem extensions_trans {v : Vocabulary} + {a b c : Option (CriticalExtensions v)} + (hab : extensionsLe a b) (hbc : extensionsLe b c) : + extensionsLe a c := by + cases c with + | none => trivial + | some c => + cases b with + | none => simp [extensionsLe] at hbc + | some b => + cases a with + | none => simp [extensionsLe] at hab + | some a => + simp only [extensionsLe] at hab hbc ⊢ + exact hab.trans hbc + +theorem extensions_antisymm {v : Vocabulary} + {a b : Option (CriticalExtensions v)} + (hab : extensionsLe a b) (hba : extensionsLe b a) : + a = b := by + cases a with + | none => + cases b with + | none => rfl + | some b => simp [extensionsLe] at hab + | some a => + cases b with + | none => simp [extensionsLe] at hba + | some b => + simp only [extensionsLe] at hab + rw [hab] + +/-- +A parent that has pinned an extension set admits exactly that set. + +This is the falsifiable content of dimension 11: for every pinned set there is +a child set the relation refuses, so no constant can satisfy it. +-/ +@[simp] theorem extensions_le_pinned_iff {v : Vocabulary} + (child parent : CriticalExtensions v) : + extensionsLe (some child) (some parent) ↔ child = parent := + Iff.rfl + +/-- A child that drops a pinned set is refused, for every pinned set. -/ +theorem extensions_le_false_of_dropped {v : Vocabulary} + (parent : CriticalExtensions v) : + ¬ extensionsLe (none : Option (CriticalExtensions v)) (some parent) := by + simp [extensionsLe] + +/-- A child that alters a pinned set is refused, for every alteration. -/ +theorem extensions_le_false_of_altered {v : Vocabulary} + {child parent : CriticalExtensions v} + (altered : child ≠ parent) : + ¬ extensionsLe (some child) (some parent) := by + simpa [extensionsLe] using altered + +/-- +The class the two theorems above quantify over is inhabited, so neither is +vacuous: dropping the single extension of a one-element set is refused. +-/ +theorem extensions_le_refuses_a_dropped_singleton {v : Vocabulary} + (extension : CriticalExtension v) : + ¬ extensionsLe + (some (CriticalExtensions.empty v)) + (some (CriticalExtensions.singleton extension)) := + extensions_le_false_of_altered + (CriticalExtensions.empty_ne_singleton extension) + theorem status_refl {v : Vocabulary} (status : StatusPolicy v) : statusLe status status := by cases status <;> simp [statusLe] @@ -258,16 +331,16 @@ theorem structural_scope_le_refl {v : Vocabulary} (scope : AuthorityScope v) : structuralScopeLe scope scope := by simp [structuralScopeLe, profile_refl, window_contained_refl, - action_constraint_refl, budget_refl, status_refl] + action_constraint_refl, budget_refl, status_refl, extensions_refl] theorem structural_scope_le_trans {v : Vocabulary} {a b c : AuthorityScope v} (hab : structuralScopeLe a b) (hbc : structuralScopeLe b c) : structuralScopeLe a c := by rcases hab with ⟨profileAB, permissionAB, validityAB, audienceAB, - actionAB, budgetAB, statusAB, assuranceAB⟩ + actionAB, budgetAB, statusAB, assuranceAB, extensionsAB⟩ rcases hbc with ⟨profileBC, permissionBC, validityBC, audienceBC, - actionBC, budgetBC, statusBC, assuranceBC⟩ + actionBC, budgetBC, statusBC, assuranceBC, extensionsBC⟩ exact ⟨ profile_trans profileAB profileBC, Finset.Subset.trans permissionAB permissionBC, @@ -276,7 +349,8 @@ theorem structural_scope_le_trans {v : Vocabulary} action_constraint_trans actionAB actionBC, budget_trans budgetAB budgetBC, status_trans statusAB statusBC, - assuranceAB.trans assuranceBC + assuranceAB.trans assuranceBC, + extensions_trans extensionsAB extensionsBC ⟩ theorem scope_le_canonical_antisymmetry {v : Vocabulary} @@ -286,9 +360,9 @@ theorem scope_le_canonical_antisymmetry {v : Vocabulary} (hab : structuralScopeLe a b) (hba : structuralScopeLe b a) : a = b := by rcases hab with ⟨profileAB, permissionAB, validityAB, audienceAB, - actionAB, budgetAB, statusAB, assuranceAB⟩ + actionAB, budgetAB, statusAB, assuranceAB, extensionsAB⟩ rcases hba with ⟨profileBA, permissionBA, validityBA, audienceBA, - actionBA, budgetBA, statusBA, assuranceBA⟩ + actionBA, budgetBA, statusBA, assuranceBA, extensionsBA⟩ have profileEquality := profile_antisymm profileAB profileBA have permissionEquality := Finset.Subset.antisymm permissionAB permissionBA have validityEquality := window_contained_antisymm validityAB validityBA @@ -297,10 +371,11 @@ theorem scope_le_canonical_antisymmetry {v : Vocabulary} aActionCanonical bActionCanonical actionAB actionBA have budgetEquality := budget_antisymm budgetAB budgetBA have statusEquality := status_antisymm statusAB statusBA + have extensionsEquality := extensions_antisymm extensionsAB extensionsBA rcases a with ⟨aProfile, aPermissions, aValidity, aAudiences, aAction, - aBudget, aStatus, aAssurance⟩ + aBudget, aStatus, aAssurance, aExtensions⟩ rcases b with ⟨bProfile, bPermissions, bValidity, bAudiences, bAction, - bBudget, bStatus, bAssurance⟩ + bBudget, bStatus, bAssurance, bExtensions⟩ simp_all theorem action_coverage_downward_closed {v : Vocabulary} @@ -309,7 +384,8 @@ theorem action_coverage_downward_closed {v : Vocabulary} (covered : actionCovers child action) : actionCovers parent action := by rcases order with ⟨profileOrder, permissionOrder, validityOrder, - audienceOrder, actionOrder, budgetOrder, statusOrder, assuranceOrder⟩ + audienceOrder, actionOrder, budgetOrder, statusOrder, assuranceOrder, + extensionsOrder⟩ rcases covered with ⟨profileCovered, permissionCovered, validityCovered, audienceCovered, actionCovered, budgetCovered⟩ exact ⟨ @@ -326,7 +402,7 @@ theorem evidence_requirements_downward_closed {v : Vocabulary} (order : structuralScopeLe child parent) (satisfied : evidenceRequirementsSatisfied child facts) : evidenceRequirementsSatisfied parent facts := by - rcases order with ⟨_, _, _, _, _, _, statusOrder, assuranceOrder⟩ + rcases order with ⟨_, _, _, _, _, _, statusOrder, assuranceOrder, _⟩ rcases satisfied with ⟨statusSatisfiedByFacts, assuranceSatisfied⟩ exact ⟨ status_satisfaction_monotone statusOrder statusSatisfiedByFacts, @@ -386,7 +462,8 @@ theorem accepted_scope_le {v : Vocabulary} (checks : grantScopeChecks parent grant) : structuralScopeLe (acceptedScope parent grant checks) parent := by rcases checks with ⟨profileCheck, permissionCheck, validityCheck, - audienceCheck, actionCheck, budgetCheck, statusCheck, assuranceCheck⟩ + audienceCheck, actionCheck, budgetCheck, statusCheck, assuranceCheck, + extensionsCheck⟩ constructor · constructor · rfl @@ -395,7 +472,7 @@ theorem accepted_scope_le {v : Vocabulary} | some selectedProfile => simpa [acceptedScope, profileAllows, selected] using profileCheck · exact ⟨permissionCheck, validityCheck, audienceCheck, actionCheck, - budgetCheck, statusCheck, assuranceCheck⟩ + budgetCheck, statusCheck, assuranceCheck, extensionsCheck⟩ theorem delegate_implies_scope_le {v : Vocabulary} {parent child : ChainState v} {grantId : GrantId v} {grant : Grant v} @@ -531,6 +608,83 @@ theorem broken_root_denies_every_projection {v : Vocabulary} (delegationProjection parent grant) = false := by simp [Auths.Generated.attenuationAccepts, delegationProjection, broken] +/-! +### Critical-extension preservation + +`extensionsAttenuate` was a literal `true` until the model gained an +`extensions` field, so the eleven-dimension contract was proved over ten +dimensions and reported eleven — structurally the same defect as the old +`root_preserved: true`. The theorems below pin the dimension to the semantic +relation and exhibit the input classes on which it is `false`, so a literal +cannot satisfy them. +-/ + +/-- The generated extension dimension reports exactly the semantic relation. -/ +theorem extensions_dimension_is_exact {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) : + (delegationProjection parent grant).extensionsAttenuate = true ↔ + extensionsLe (some grant.extensions) parent.scope.extensions := by + simp [delegationProjection] + +/-- +Witness that the dimension is falsifiable, stated over all inputs: a grant that +alters a pinned critical-extension set drives it to `false`. +-/ +theorem extensions_dimension_false_of_altered_set {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) + (pinned : CriticalExtensions v) + (pinnedBy : parent.scope.extensions = some pinned) + (altered : grant.extensions ≠ pinned) : + (delegationProjection parent grant).extensionsAttenuate = false := by + simp [delegationProjection, extensionsLe, pinnedBy, altered] + +/-- +No other attenuation dimension can rescue a stripped critical extension: +acceptance is the conjunction, so the whole projection is rejected. +-/ +theorem altered_extensions_deny_every_projection {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) + (broken : ¬ extensionsLe (some grant.extensions) parent.scope.extensions) : + Auths.Generated.attenuationAccepts + (delegationProjection parent grant) = false := by + simp [Auths.Generated.attenuationAccepts, delegationProjection, broken] + +/-- Every accepted edge preserves a pinned critical-extension set exactly. -/ +theorem delegate_preserves_pinned_extensions {v : Vocabulary} + {parent child : ChainState v} {grantId : GrantId v} {grant : Grant v} + (accepted : delegates parent grantId grant child) + (pinned : CriticalExtensions v) + (pinnedBy : parent.scope.extensions = some pinned) : + child.scope.extensions = some pinned := by + rcases accepted with ⟨_, ⟨checks, rfl⟩⟩ + obtain ⟨_, _, _, _, _, _, _, _, preserved⟩ := checks.2.2 + rw [pinnedBy] at preserved + simp only [extensionsLe] at preserved + simp [acceptedNextState, acceptedScope, preserved] + +/-- +Once a chain has pinned a critical-extension set, every reachable state carries +that same set. This is the inductive statement the single-edge theorem does +not carry, and it is what makes "a delegate cannot strip a critical extension" +a claim about whole chains rather than about one hop. +-/ +theorem chain_preserves_pinned_extensions {v : Vocabulary} + {start : ChainState v} {rest : List (ChainState v)} + (chain : DelegationChain start rest) + (pinned : CriticalExtensions v) + (pinnedBy : start.scope.extensions = some pinned) : + ∀ state ∈ rest, state.scope.extensions = some pinned := by + induction chain with + | nil => simp + | cons parent child grantId grant rest edge tail inductionHypothesis => + intro state member + have childPinned : child.scope.extensions = some pinned := + delegate_preserves_pinned_extensions edge pinned pinnedBy + rcases List.mem_cons.1 member with head | inTail + · rw [head] + exact childPinned + · exact inductionHypothesis childPinned state inTail + theorem delegate_updates_subject_and_parent {v : Vocabulary} {parent child : ChainState v} {grantId : GrantId v} {grant : Grant v} (accepted : delegates parent grantId grant child) : @@ -594,6 +748,11 @@ The `rootPreserved` conjunct is not redundant: before the trust root became a computed dimension this theorem read `↔ scopeDepthChecks parent grant`, which is precisely the vacuity — the eleven-dimension contract was proved equivalent to ten dimensions. + +`scopeDepthChecks` now also carries `extensionsLe`. While `extensionsAttenuate` +was a literal `true` this equivalence held with `grantScopeChecks` silent about +critical extensions, so the same vacuity was present in the eleventh dimension +and invisible here. -/ theorem rich_projection_accepts_iff_root_and_scope_depth_checks {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : diff --git a/formal/Auths/Rich/Types.lean b/formal/Auths/Rich/Types.lean index 4ddcc150..bb1fce07 100644 --- a/formal/Auths/Rich/Types.lean +++ b/formal/Auths/Rich/Types.lean @@ -22,6 +22,8 @@ structure Vocabulary where StatusMethodCarrier : Type u AssuranceCarrier : Type u GrantIdCarrier : Type u + ExtensionIdCarrier : Type u + ExtensionBodyCarrier : Type u principalDecidableEq : DecidableEq PrincipalCarrier profileDecidableEq : DecidableEq ProfileCarrier permissionDecidableEq : DecidableEq PermissionCarrier @@ -31,6 +33,8 @@ structure Vocabulary where statusMethodDecidableEq : DecidableEq StatusMethodCarrier assuranceDecidableEq : DecidableEq AssuranceCarrier grantIdDecidableEq : DecidableEq GrantIdCarrier + extensionIdDecidableEq : DecidableEq ExtensionIdCarrier + extensionBodyDecidableEq : DecidableEq ExtensionBodyCarrier structure Principal (v : Vocabulary) where value : v.PrincipalCarrier @@ -59,6 +63,12 @@ structure AssurancePolicy (v : Vocabulary) where structure GrantId (v : Vocabulary) where value : v.GrantIdCarrier +structure ExtensionId (v : Vocabulary) where + value : v.ExtensionIdCarrier + +structure ExtensionBody (v : Vocabulary) where + value : v.ExtensionBodyCarrier + instance (v : Vocabulary) : DecidableEq (Principal v) := fun left right => match v.principalDecidableEq left.value right.value with @@ -113,6 +123,18 @@ instance (v : Vocabulary) : DecidableEq (GrantId v) := | isTrue equality => isTrue (by cases left; cases right; simp_all) | isFalse different => isFalse (by intro equality; exact different (by cases equality; rfl)) +instance (v : Vocabulary) : DecidableEq (ExtensionId v) := + fun left right => + match v.extensionIdDecidableEq left.value right.value with + | isTrue equality => isTrue (by cases left; cases right; simp_all) + | isFalse different => isFalse (by intro equality; exact different (by cases equality; rfl)) + +instance (v : Vocabulary) : DecidableEq (ExtensionBody v) := + fun left right => + match v.extensionBodyDecidableEq left.value right.value with + | isTrue equality => isTrue (by cases left; cases right; simp_all) + | isFalse different => isFalse (by intro equality; exact different (by cases equality; rfl)) + /-- The semantic finite-set carrier. Rust's sorted bounded vectors are connected to this extensional value by the production representation bridge. @@ -134,6 +156,98 @@ structure BudgetCeiling (v : Vocabulary) where algebra : BudgetAlgebra v value : Nat +/-- +One critical extension: an identifier and its opaque canonical payload. + +Mirrors `auths_model::CriticalExtension`. The payload is an opaque carrier +because the kernel never interprets it — a critical extension is precisely a +constraint an unaware verifier must not ignore, so the only thing the kernel +may do with it is compare it. +-/ +structure CriticalExtension (v : Vocabulary) where + id : ExtensionId v + body : ExtensionBody v + +instance (v : Vocabulary) : DecidableEq (CriticalExtension v) := + fun left right => + match decEq left.id right.id, decEq left.body right.body with + | isTrue idEquality, isTrue bodyEquality => + isTrue (by cases left; cases right; simp_all) + | isFalse different, _ => + isFalse (by intro equality; exact different (by cases equality; rfl)) + | _, isFalse different => + isFalse (by intro equality; exact different (by cases equality; rfl)) + +/-- Mirrors Rust `auths_model::HARD_MAX_EXTENSIONS`. -/ +def hardMaxExtensions : Nat := 32 + +/-- +A canonical critical-extension set. + +`CriticalExtensions::new` sorts its input, rejects a repeated identifier with +`ModelError::DuplicateExtension`, and rejects more than +`HARD_MAX_EXTENSIONS` entries. Both rejections are carried here as +constructor obligations, so a value of this type is exactly a value the Rust +constructor would have accepted. + +The entries are an ordered sequence rather than a `FiniteSet` deliberately. +`critical_extensions_equal` compares the two canonical vectors **positionally**; +a set-valued model would identify `[a, b]` with `[b, a]` and therefore report +attenuation on a pair the shipping kernel denies, which is the model being +weaker than the code. Duplicate-freedom by identifier is what makes the +sequence a faithful map from identifier to payload; the total order Rust sorts +by is a representation-level fact that the opaque carriers cannot state, and +none of the decisions below depend on it. +-/ +structure CriticalExtensions (v : Vocabulary) where + entries : List (CriticalExtension v) + distinctIds : entries.Pairwise fun left right => left.id ≠ right.id + bounded : entries.length ≤ hardMaxExtensions + +/-- Two extension sets are equal exactly when their canonical entries are. -/ +@[ext] theorem CriticalExtensions.ext {v : Vocabulary} + {left right : CriticalExtensions v} + (entries : left.entries = right.entries) : left = right := by + cases left + cases right + cases entries + rfl + +instance (v : Vocabulary) : DecidableEq (CriticalExtensions v) := + fun left right => + if entries : left.entries = right.entries then + isTrue (CriticalExtensions.ext entries) + else + isFalse fun equality => entries (by rw [equality]) + +/-- The empty set, the value `CriticalExtensions::empty` constructs. -/ +def CriticalExtensions.empty (v : Vocabulary) : CriticalExtensions v where + entries := [] + distinctIds := List.Pairwise.nil + bounded := by simp [hardMaxExtensions] + +/-- The one-element set, the smallest thing a delegate could try to drop. -/ +def CriticalExtensions.singleton {v : Vocabulary} + (extension : CriticalExtension v) : CriticalExtensions v where + entries := [extension] + distinctIds := by simp + bounded := by simp [hardMaxExtensions] + +/-- +The carrier is not a subsingleton. + +Every falsifiability theorem about critical extensions is universally +quantified over a differing pair, so it would be vacuous if +`CriticalExtensions v` had at most one inhabitant. It does not, for every +vocabulary that can name a single extension. +-/ +theorem CriticalExtensions.empty_ne_singleton {v : Vocabulary} + (extension : CriticalExtension v) : + CriticalExtensions.empty v ≠ CriticalExtensions.singleton extension := by + intro equality + have entries := congrArg CriticalExtensions.entries equality + simp [CriticalExtensions.empty, CriticalExtensions.singleton] at entries + inductive StatusPolicy (v : Vocabulary) where | expiryOnly | snapshotRequired (method : StatusMethod v) (maxAge : FreshnessLimit) @@ -162,6 +276,14 @@ structure AuthorityScope (v : Vocabulary) where budget : Option (BudgetCeiling v) status : StatusPolicy v assurance : AssurancePolicy v + /-- + The critical-extension set this authority has been pinned to, if any. + + `EffectiveAuthority::from_anchor` starts at `None`: a fresh trust anchor has + not yet fixed a set, so its first edge may declare one. Every accepted edge + stores `Some`, and from then on the set may only be preserved exactly. + -/ + extensions : Option (CriticalExtensions v) structure ChainState (v : Vocabulary) where root : Principal v @@ -191,6 +313,8 @@ structure Grant (v : Vocabulary) where parent : Option (GrantId v) status : StatusPolicy v assurance : AssurancePolicy v + /-- The complete canonical critical-extension set the grant declares. -/ + extensions : CriticalExtensions v structure Action (v : Vocabulary) where actor : Principal v diff --git a/formal/Auths/Theorems.lean b/formal/Auths/Theorems.lean index dba01116..4878fe29 100644 --- a/formal/Auths/Theorems.lean +++ b/formal/Auths/Theorems.lean @@ -28,6 +28,13 @@ def theoremInventory : List Lean.Name := `Auths.Rich.budget_trans, `Auths.Rich.budget_antisymm, `Auths.Rich.budget_coverage_monotone, + `Auths.Rich.extensions_refl, + `Auths.Rich.extensions_trans, + `Auths.Rich.extensions_antisymm, + `Auths.Rich.extensions_le_pinned_iff, + `Auths.Rich.extensions_le_false_of_dropped, + `Auths.Rich.extensions_le_false_of_altered, + `Auths.Rich.extensions_le_refuses_a_dropped_singleton, `Auths.Rich.status_refl, `Auths.Rich.status_trans, `Auths.Rich.status_antisymm, @@ -56,6 +63,11 @@ def theoremInventory : List Lean.Name := `Auths.Rich.finite_delegation_chain, `Auths.Rich.chain_transitive_attenuation, `Auths.Rich.authorized_action_covered, + `Auths.Rich.extensions_dimension_is_exact, + `Auths.Rich.extensions_dimension_false_of_altered_set, + `Auths.Rich.altered_extensions_deny_every_projection, + `Auths.Rich.delegate_preserves_pinned_extensions, + `Auths.Rich.chain_preserves_pinned_extensions, `Auths.Rich.rich_projection_accepts_iff_root_and_scope_depth_checks, `Auths.Rich.apply_grant_success_iff_linked_and_projection, `Auths.Rich.apply_grant_success_iff_delegates, @@ -68,6 +80,7 @@ def theoremInventory : List Lean.Name := `Auths.Refinement.translated_rust_refines_rich_spec, `Auths.Refinement.translated_coverage_refines_rich_spec, `Auths.Refinement.translated_delegation_refines_rich_spec, + `Auths.Refinement.translated_budget_coverage_gap_is_the_absent_request, `Auths.Product.configuration_match_refl, `Auths.Product.configuration_match_deterministic, `Auths.Product.checked_add_never_wraps, diff --git a/formal/Auths/VectorExport.lean b/formal/Auths/VectorExport.lean index e324382c..22c73f33 100644 --- a/formal/Auths/VectorExport.lean +++ b/formal/Auths/VectorExport.lean @@ -62,6 +62,8 @@ def natVocabulary : Rich.Vocabulary where StatusMethodCarrier := Nat AssuranceCarrier := Nat GrantIdCarrier := Nat + ExtensionIdCarrier := Nat + ExtensionBodyCarrier := Nat principalDecidableEq := inferInstance profileDecidableEq := inferInstance permissionDecidableEq := inferInstance @@ -71,6 +73,8 @@ def natVocabulary : Rich.Vocabulary where statusMethodDecidableEq := inferInstance assuranceDecidableEq := inferInstance grantIdDecidableEq := inferInstance + extensionIdDecidableEq := inferInstance + extensionBodyDecidableEq := inferInstance def natArrayCode (values : List Nat) : String := "[" ++ String.intercalate "," (values.map toString) ++ "]" diff --git a/formal/assurance-manifest-v1.toml b/formal/assurance-manifest-v1.toml index 95be48fe..c1d2ccfb 100644 --- a/formal/assurance-manifest-v1.toml +++ b/formal/assurance-manifest-v1.toml @@ -46,7 +46,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -98,7 +98,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -150,7 +150,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -202,7 +202,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -254,7 +254,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -303,7 +303,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -356,7 +356,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -409,7 +409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -462,7 +462,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -514,7 +514,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -567,7 +567,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -620,7 +620,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -673,7 +673,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -726,7 +726,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -775,7 +775,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -824,7 +824,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -873,7 +873,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -883,6 +883,352 @@ axioms = [] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +[[claims]] +claim_id = "AP-FORMAL-RICH-093" +claim_text = "Lean proves the rich authority property: extensions refl." +claim_status = "proved" +lean_declaration = "Auths.Rich.extensions_refl" +lean_statement_sha256 = "3800be2f50fce51d7c628d2483de6694ad58566d2b041f0529f5cd84e70cbf86" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = ["propext"] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-094" +claim_text = "Lean proves the rich authority property: extensions trans." +claim_status = "proved" +lean_declaration = "Auths.Rich.extensions_trans" +lean_statement_sha256 = "e05d4e12a80d7c0003933711f75a212208a827b787ad87787a5f5a7d5ccaab1b" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-095" +claim_text = "Lean proves the rich authority property: extensions antisymm." +claim_status = "proved" +lean_declaration = "Auths.Rich.extensions_antisymm" +lean_statement_sha256 = "812db27beb5a86265909c1950c0f45882e0c47f196776cdd03a325537310396d" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-096" +claim_text = "Lean proves the rich authority property: extensions le pinned iff." +claim_status = "proved" +lean_declaration = "Auths.Rich.extensions_le_pinned_iff" +lean_statement_sha256 = "418ef27b7822eed76553ff7210f04e96ac27312e0b8bfb6f6eee955af3cdf3b0" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-097" +claim_text = "Lean proves the rich authority property: extensions le false of dropped." +claim_status = "proved" +lean_declaration = "Auths.Rich.extensions_le_false_of_dropped" +lean_statement_sha256 = "50fa5e1a3b26acec511d4f6459c5de3d8e7212f221f72267169f94be8db5d4b5" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = ["propext"] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-098" +claim_text = "Lean proves the rich authority property: extensions le false of altered." +claim_status = "proved" +lean_declaration = "Auths.Rich.extensions_le_false_of_altered" +lean_statement_sha256 = "081d0a8e30177f84ec4917d36954711736c6ffa22b2558174d22910f3d24e76f" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-105" +claim_text = "Lean proves the rich authority property: extensions le refuses a dropped singleton." +claim_status = "proved" +lean_declaration = "Auths.Rich.extensions_le_refuses_a_dropped_singleton" +lean_statement_sha256 = "25078e7dc5c2efc1372598c5838b1ea6d09db9de2a27113afa24b4a30d83dce2" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + [[claims]] claim_id = "AP-FORMAL-RICH-017" claim_text = "Lean proves the rich authority property: status refl." @@ -922,7 +1268,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -971,7 +1317,108 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-019" +claim_text = "Lean proves the rich authority property: status antisymm." +claim_status = "proved" +lean_declaration = "Auths.Rich.status_antisymm" +lean_statement_sha256 = "a3badc32c1fcd22da760494c3c6eb256e637312f34fa7691591e7283f504867e" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-020" +claim_text = "Lean proves the rich authority property: status satisfaction monotone." +claim_status = "proved" +lean_declaration = "Auths.Rich.status_satisfaction_monotone" +lean_statement_sha256 = "7f5f3cfc2e90f4a2bdf90499b6b596a0711d27f3668d3d99bcf8c5a0a146e022" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -982,11 +1429,167 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-019" -claim_text = "Lean proves the rich authority property: status antisymm." +claim_id = "AP-FORMAL-RICH-021" +claim_text = "Lean proves the rich authority property: profile refl." +claim_status = "proved" +lean_declaration = "Auths.Rich.profile_refl" +lean_statement_sha256 = "c256b640094e0458a6cb336c78c358b8f35906d7838c86142f649c4d7c3cf9ea" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-022" +claim_text = "Lean proves the rich authority property: profile trans." +claim_status = "proved" +lean_declaration = "Auths.Rich.profile_trans" +lean_statement_sha256 = "bc702fae63c302ea1b30323b66eaa633f2c489dd14a84bda0802852888da4e5d" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-023" +claim_text = "Lean proves the rich authority property: profile antisymm." +claim_status = "proved" +lean_declaration = "Auths.Rich.profile_antisymm" +lean_statement_sha256 = "306251093aa948294a625b5fd0e2707c52d9d391f172185ca36949ee0bd022ba" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-024" +claim_text = "Lean proves the rich authority property: profile coverage monotone." claim_status = "proved" -lean_declaration = "Auths.Rich.status_antisymm" -lean_statement_sha256 = "a3badc32c1fcd22da760494c3c6eb256e637312f34fa7691591e7283f504867e" +lean_declaration = "Auths.Rich.profile_coverage_monotone" +lean_statement_sha256 = "fa6e1ec9008031df18e845bb6457030c5169bff159a30e5a30e4be9d7b445c16" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1020,7 +1623,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1034,11 +1637,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-020" -claim_text = "Lean proves the rich authority property: status satisfaction monotone." +claim_id = "AP-FORMAL-RICH-025" +claim_text = "Lean proves the rich authority property: structural scope le refl." claim_status = "proved" -lean_declaration = "Auths.Rich.status_satisfaction_monotone" -lean_statement_sha256 = "7f5f3cfc2e90f4a2bdf90499b6b596a0711d27f3668d3d99bcf8c5a0a146e022" +lean_declaration = "Auths.Rich.structural_scope_le_refl" +lean_statement_sha256 = "e61ced1d2049cd8c84935cdde0858c82d8feb3b040f90d03ced37a27f740d874" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1072,22 +1675,25 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = [] +axioms = [ + "propext", + "Quot.sound", +] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-021" -claim_text = "Lean proves the rich authority property: profile refl." +claim_id = "AP-FORMAL-RICH-026" +claim_text = "Lean proves the rich authority property: structural scope le trans." claim_status = "proved" -lean_declaration = "Auths.Rich.profile_refl" -lean_statement_sha256 = "c256b640094e0458a6cb336c78c358b8f35906d7838c86142f649c4d7c3cf9ea" +lean_declaration = "Auths.Rich.structural_scope_le_trans" +lean_statement_sha256 = "35da0ff881c83e4a3077b07e6f8adb8930d92a72b1327b9781d6a0054b63886e" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1121,12 +1727,13 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", + "Classical.choice", "Quot.sound", ] @@ -1135,11 +1742,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-022" -claim_text = "Lean proves the rich authority property: profile trans." +claim_id = "AP-FORMAL-RICH-027" +claim_text = "Lean proves the rich authority property: scope le canonical antisymmetry." claim_status = "proved" -lean_declaration = "Auths.Rich.profile_trans" -lean_statement_sha256 = "bc702fae63c302ea1b30323b66eaa633f2c489dd14a84bda0802852888da4e5d" +lean_declaration = "Auths.Rich.scope_le_canonical_antisymmetry" +lean_statement_sha256 = "3da1337f88245aa5d26623cede90981ab56d0a541a02d164bf5f685781ea9911" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1173,12 +1780,13 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", + "Classical.choice", "Quot.sound", ] @@ -1187,11 +1795,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-023" -claim_text = "Lean proves the rich authority property: profile antisymm." +claim_id = "AP-FORMAL-RICH-028" +claim_text = "Lean proves the rich authority property: action coverage downward closed." claim_status = "proved" -lean_declaration = "Auths.Rich.profile_antisymm" -lean_statement_sha256 = "306251093aa948294a625b5fd0e2707c52d9d391f172185ca36949ee0bd022ba" +lean_declaration = "Auths.Rich.action_coverage_downward_closed" +lean_statement_sha256 = "966cd95b7984d06ef0db93a5319c757c2510dc98965e6fe5234774804877a941" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1225,12 +1833,13 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", + "Classical.choice", "Quot.sound", ] @@ -1239,11 +1848,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-024" -claim_text = "Lean proves the rich authority property: profile coverage monotone." +claim_id = "AP-FORMAL-RICH-029" +claim_text = "Lean proves the rich authority property: evidence requirements downward closed." claim_status = "proved" -lean_declaration = "Auths.Rich.profile_coverage_monotone" -lean_statement_sha256 = "fa6e1ec9008031df18e845bb6457030c5169bff159a30e5a30e4be9d7b445c16" +lean_declaration = "Auths.Rich.evidence_requirements_downward_closed" +lean_statement_sha256 = "2beb990df92aeb12bc99e21b5d4547630d571c3467658116dcba648bae91589b" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1277,7 +1886,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1291,11 +1900,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-025" -claim_text = "Lean proves the rich authority property: structural scope le refl." +claim_id = "AP-FORMAL-RICH-030" +claim_text = "Lean proves the rich authority property: complete admission downward closed." claim_status = "proved" -lean_declaration = "Auths.Rich.structural_scope_le_refl" -lean_statement_sha256 = "e61ced1d2049cd8c84935cdde0858c82d8feb3b040f90d03ced37a27f740d874" +lean_declaration = "Auths.Rich.complete_admission_downward_closed" +lean_statement_sha256 = "6a8eb24b2e620903dc1682cb63c6dd3922b425e560441ca3db16127a8dab285b" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1329,12 +1938,13 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", + "Classical.choice", "Quot.sound", ] @@ -1343,11 +1953,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-026" -claim_text = "Lean proves the rich authority property: structural scope le trans." +claim_id = "AP-FORMAL-RICH-031" +claim_text = "Lean proves the rich authority property: semantic attenuation preorder refl." claim_status = "proved" -lean_declaration = "Auths.Rich.structural_scope_le_trans" -lean_statement_sha256 = "35da0ff881c83e4a3077b07e6f8adb8930d92a72b1327b9781d6a0054b63886e" +lean_declaration = "Auths.Rich.semantic_attenuation_preorder_refl" +lean_statement_sha256 = "8c15f5f719bec44ca71e44f53ccc6c900cf26c67f9f8b9e3f8ef7cb8e52618f6" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1381,13 +1991,12 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", - "Classical.choice", "Quot.sound", ] @@ -1396,11 +2005,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-027" -claim_text = "Lean proves the rich authority property: scope le canonical antisymmetry." +claim_id = "AP-FORMAL-RICH-032" +claim_text = "Lean proves the rich authority property: semantic attenuation preorder trans." claim_status = "proved" -lean_declaration = "Auths.Rich.scope_le_canonical_antisymmetry" -lean_statement_sha256 = "3da1337f88245aa5d26623cede90981ab56d0a541a02d164bf5f685781ea9911" +lean_declaration = "Auths.Rich.semantic_attenuation_preorder_trans" +lean_statement_sha256 = "eadb4979eaede05d4d8262f1d47d02f0d63c5a1e76bd296e22dceb46e789d79d" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1434,13 +2043,12 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", - "Classical.choice", "Quot.sound", ] @@ -1449,11 +2057,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-028" -claim_text = "Lean proves the rich authority property: action coverage downward closed." +claim_id = "AP-FORMAL-RICH-033" +claim_text = "Lean proves the rich authority property: structural scope le implies semantic attenuation." claim_status = "proved" -lean_declaration = "Auths.Rich.action_coverage_downward_closed" -lean_statement_sha256 = "966cd95b7984d06ef0db93a5319c757c2510dc98965e6fe5234774804877a941" +lean_declaration = "Auths.Rich.structural_scope_le_implies_semantic_attenuation" +lean_statement_sha256 = "763897035b5b8b85931035453db38a19135cd78f9e8071be632e99774849650c" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1487,7 +2095,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1502,11 +2110,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-029" -claim_text = "Lean proves the rich authority property: evidence requirements downward closed." +claim_id = "AP-FORMAL-RICH-034" +claim_text = "Lean proves the rich authority property: scope semantic equivalence." claim_status = "proved" -lean_declaration = "Auths.Rich.evidence_requirements_downward_closed" -lean_statement_sha256 = "2beb990df92aeb12bc99e21b5d4547630d571c3467658116dcba648bae91589b" +lean_declaration = "Auths.Rich.scope_semantic_equivalence" +lean_statement_sha256 = "f1ffad1790150c98ed431798970479dc669fe52189b171385ea33ab1650f872e" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1540,7 +2148,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1554,11 +2162,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-030" -claim_text = "Lean proves the rich authority property: complete admission downward closed." +claim_id = "AP-FORMAL-RICH-035" +claim_text = "Lean proves the rich authority property: structural scope le decides declared v1 relation." claim_status = "proved" -lean_declaration = "Auths.Rich.complete_admission_downward_closed" -lean_statement_sha256 = "6a8eb24b2e620903dc1682cb63c6dd3922b425e560441ca3db16127a8dab285b" +lean_declaration = "Auths.Rich.structural_scope_le_decides_declared_v1_relation" +lean_statement_sha256 = "b0370bdf959189b5146c84c956e48eba5b9ab855985c8b3724ec82bb1c684f3e" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1592,7 +2200,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1607,11 +2215,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-031" -claim_text = "Lean proves the rich authority property: semantic attenuation preorder refl." +claim_id = "AP-FORMAL-RICH-036" +claim_text = "Lean proves the rich authority property: delegate implies scope le." claim_status = "proved" -lean_declaration = "Auths.Rich.semantic_attenuation_preorder_refl" -lean_statement_sha256 = "8c15f5f719bec44ca71e44f53ccc6c900cf26c67f9f8b9e3f8ef7cb8e52618f6" +lean_declaration = "Auths.Rich.delegate_implies_scope_le" +lean_statement_sha256 = "de367444bfa8ef53140e157da7429ec957d0a7f6ccc17f80255ef3a93e3fe104" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1645,7 +2253,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1659,11 +2267,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-032" -claim_text = "Lean proves the rich authority property: semantic attenuation preorder trans." +claim_id = "AP-FORMAL-RICH-037" +claim_text = "Lean proves the rich authority property: delegate preserves root." claim_status = "proved" -lean_declaration = "Auths.Rich.semantic_attenuation_preorder_trans" -lean_statement_sha256 = "eadb4979eaede05d4d8262f1d47d02f0d63c5a1e76bd296e22dceb46e789d79d" +lean_declaration = "Auths.Rich.delegate_preserves_root" +lean_statement_sha256 = "60648d6a9993223e4261470f7d55b13fb2cc0407d28277d7b74abfcf8c468c06" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1697,7 +2305,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1711,11 +2319,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-033" -claim_text = "Lean proves the rich authority property: structural scope le implies semantic attenuation." +claim_id = "AP-FORMAL-RICH-038" +claim_text = "Lean proves the rich authority property: delegate updates subject and parent." claim_status = "proved" -lean_declaration = "Auths.Rich.structural_scope_le_implies_semantic_attenuation" -lean_statement_sha256 = "763897035b5b8b85931035453db38a19135cd78f9e8071be632e99774849650c" +lean_declaration = "Auths.Rich.delegate_updates_subject_and_parent" +lean_statement_sha256 = "0db5c5d208556a219fb461f7bc0909873bad9277dad0de06413ca8b01aec8e16" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1749,13 +2357,12 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", - "Classical.choice", "Quot.sound", ] @@ -1764,11 +2371,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-034" -claim_text = "Lean proves the rich authority property: scope semantic equivalence." +claim_id = "AP-FORMAL-RICH-039" +claim_text = "Lean proves the rich authority property: delegate strict depth." claim_status = "proved" -lean_declaration = "Auths.Rich.scope_semantic_equivalence" -lean_statement_sha256 = "f1ffad1790150c98ed431798970479dc669fe52189b171385ea33ab1650f872e" +lean_declaration = "Auths.Rich.delegate_strict_depth" +lean_statement_sha256 = "ded87036b3807b9f99d31cd220f2fa61a0b84d74d76bea27f7b2419ea6ba777a" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1802,7 +2409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1816,11 +2423,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-035" -claim_text = "Lean proves the rich authority property: structural scope le decides declared v1 relation." +claim_id = "AP-FORMAL-RICH-040" +claim_text = "Lean proves the rich authority property: delegate never widens." claim_status = "proved" -lean_declaration = "Auths.Rich.structural_scope_le_decides_declared_v1_relation" -lean_statement_sha256 = "b0370bdf959189b5146c84c956e48eba5b9ab855985c8b3724ec82bb1c684f3e" +lean_declaration = "Auths.Rich.delegate_never_widens" +lean_statement_sha256 = "de367444bfa8ef53140e157da7429ec957d0a7f6ccc17f80255ef3a93e3fe104" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1854,13 +2461,12 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", - "Classical.choice", "Quot.sound", ] @@ -1869,11 +2475,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-036" -claim_text = "Lean proves the rich authority property: delegate implies scope le." +claim_id = "AP-FORMAL-RICH-041" +claim_text = "Lean proves the rich authority property: remaining depth well founded." claim_status = "proved" -lean_declaration = "Auths.Rich.delegate_implies_scope_le" -lean_statement_sha256 = "de367444bfa8ef53140e157da7429ec957d0a7f6ccc17f80255ef3a93e3fe104" +lean_declaration = "Auths.Rich.remaining_depth_well_founded" +lean_statement_sha256 = "dcf9ed58cbeb7f0b6d9c7fa332d1b3f696f0114cee5ab770d2ad31b27d8698eb" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1907,7 +2513,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1921,11 +2527,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-037" -claim_text = "Lean proves the rich authority property: delegate preserves root." +claim_id = "AP-FORMAL-RICH-042" +claim_text = "Lean proves the rich authority property: finite delegation chain." claim_status = "proved" -lean_declaration = "Auths.Rich.delegate_preserves_root" -lean_statement_sha256 = "60648d6a9993223e4261470f7d55b13fb2cc0407d28277d7b74abfcf8c468c06" +lean_declaration = "Auths.Rich.finite_delegation_chain" +lean_statement_sha256 = "be71ae8cda95eacce7a2b8249bc41432bd1927b5739bcc7e44cd69227fe2bd5f" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1959,7 +2565,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1973,11 +2579,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-038" -claim_text = "Lean proves the rich authority property: delegate updates subject and parent." +claim_id = "AP-FORMAL-RICH-043" +claim_text = "Lean proves the rich authority property: chain transitive attenuation." claim_status = "proved" -lean_declaration = "Auths.Rich.delegate_updates_subject_and_parent" -lean_statement_sha256 = "0db5c5d208556a219fb461f7bc0909873bad9277dad0de06413ca8b01aec8e16" +lean_declaration = "Auths.Rich.chain_transitive_attenuation" +lean_statement_sha256 = "4b27f3c343e686e05192449049a211a9c1b680ebdc8192d50672e90fc79a0c00" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -2011,12 +2617,13 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", + "Classical.choice", "Quot.sound", ] @@ -2025,11 +2632,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-039" -claim_text = "Lean proves the rich authority property: delegate strict depth." +claim_id = "AP-FORMAL-RICH-044" +claim_text = "Lean proves the rich authority property: authorized action covered." claim_status = "proved" -lean_declaration = "Auths.Rich.delegate_strict_depth" -lean_statement_sha256 = "ded87036b3807b9f99d31cd220f2fa61a0b84d74d76bea27f7b2419ea6ba777a" +lean_declaration = "Auths.Rich.authorized_action_covered" +lean_statement_sha256 = "c39a96f75c9dc2d3d536b9749bbd7b27a5f54acda84f831b8eb05ea0187fa641" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -2063,12 +2670,13 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", + "Classical.choice", "Quot.sound", ] @@ -2077,11 +2685,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-040" -claim_text = "Lean proves the rich authority property: delegate never widens." +claim_id = "AP-FORMAL-RICH-099" +claim_text = "Lean proves the rich authority property: extensions dimension is exact." claim_status = "proved" -lean_declaration = "Auths.Rich.delegate_never_widens" -lean_statement_sha256 = "de367444bfa8ef53140e157da7429ec957d0a7f6ccc17f80255ef3a93e3fe104" +lean_declaration = "Auths.Rich.extensions_dimension_is_exact" +lean_statement_sha256 = "8954ec95cd557c36066dbab34caf5c84638f875323780f657196567ae9135b6c" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -2115,12 +2723,13 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", + "Classical.choice", "Quot.sound", ] @@ -2129,11 +2738,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-041" -claim_text = "Lean proves the rich authority property: remaining depth well founded." +claim_id = "AP-FORMAL-RICH-100" +claim_text = "Lean proves the rich authority property: extensions dimension false of altered set." claim_status = "proved" -lean_declaration = "Auths.Rich.remaining_depth_well_founded" -lean_statement_sha256 = "dcf9ed58cbeb7f0b6d9c7fa332d1b3f696f0114cee5ab770d2ad31b27d8698eb" +lean_declaration = "Auths.Rich.extensions_dimension_false_of_altered_set" +lean_statement_sha256 = "96f843f9c457e944659f96f87994aa6c1d2067d8d180d724a5e2e15f81f446f8" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -2167,12 +2776,13 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", + "Classical.choice", "Quot.sound", ] @@ -2181,11 +2791,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-042" -claim_text = "Lean proves the rich authority property: finite delegation chain." +claim_id = "AP-FORMAL-RICH-101" +claim_text = "Lean proves the rich authority property: altered extensions deny every projection." claim_status = "proved" -lean_declaration = "Auths.Rich.finite_delegation_chain" -lean_statement_sha256 = "be71ae8cda95eacce7a2b8249bc41432bd1927b5739bcc7e44cd69227fe2bd5f" +lean_declaration = "Auths.Rich.altered_extensions_deny_every_projection" +lean_statement_sha256 = "ee960b0abe0e40585a1c00b7bbccc19f72d2b6d996243e60a87ce927c596cc36" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -2219,12 +2829,13 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", + "Classical.choice", "Quot.sound", ] @@ -2233,11 +2844,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-043" -claim_text = "Lean proves the rich authority property: chain transitive attenuation." +claim_id = "AP-FORMAL-RICH-102" +claim_text = "Lean proves the rich authority property: delegate preserves pinned extensions." claim_status = "proved" -lean_declaration = "Auths.Rich.chain_transitive_attenuation" -lean_statement_sha256 = "4b27f3c343e686e05192449049a211a9c1b680ebdc8192d50672e90fc79a0c00" +lean_declaration = "Auths.Rich.delegate_preserves_pinned_extensions" +lean_statement_sha256 = "8da584848a272ae46a9e3e6e262e9d2eaead2b7b57f02b27c8963f0f6760e8d4" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -2271,13 +2882,12 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", - "Classical.choice", "Quot.sound", ] @@ -2286,11 +2896,11 @@ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" [[claims]] -claim_id = "AP-FORMAL-RICH-044" -claim_text = "Lean proves the rich authority property: authorized action covered." +claim_id = "AP-FORMAL-RICH-103" +claim_text = "Lean proves the rich authority property: chain preserves pinned extensions." claim_status = "proved" -lean_declaration = "Auths.Rich.authorized_action_covered" -lean_statement_sha256 = "c39a96f75c9dc2d3d536b9749bbd7b27a5f54acda84f831b8eb05ea0187fa641" +lean_declaration = "Auths.Rich.chain_preserves_pinned_extensions" +lean_statement_sha256 = "bff321e42864cf75665958de0721ddf808ae78207f1e090c86d71138540375fe" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -2324,13 +2934,12 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", - "Classical.choice", "Quot.sound", ] @@ -2377,7 +2986,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2430,7 +3039,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2483,7 +3092,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2536,7 +3145,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2589,7 +3198,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2642,7 +3251,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2695,7 +3304,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2748,7 +3357,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2800,7 +3409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2862,7 +3471,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The exact Aeneas translation of the production pre-signing scope evaluator over validated, bounded Rust model views, including ordered diagnostics." residual_assumptions = [ "Lean's kernel, the pinned Rust/Lean/Aeneas/Charon toolchain, the reviewed Aeneas external models, the listed foundational axioms, and the theorem's representation-validity premises are trusted.", @@ -2900,7 +3509,7 @@ claim_id = "AP-FORMAL-RICH-055" claim_text = "The mechanically translated production terminal-coverage evaluator returns exactly the ordered rich target-V1 decision, including linkage, set membership, interval containment, action constraints, and requested-budget coverage." claim_status = "proved" lean_declaration = "Auths.Refinement.translated_coverage_refines_rich_spec" -lean_statement_sha256 = "31a91e9677792773c02ab0ccdd84945dd126172f3141fda745e116c80f9fa1df" +lean_statement_sha256 = "ab27e2b4d47c49c1978a41c8c6414cd53ff1b1a500432b2adb88e6c4367b5140" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [ "auths_authority::evaluate_action_coverage_view", @@ -2944,7 +3553,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Validated target-V1 authority and action views through the production terminal-coverage decision, including its first-failure diagnostic order." residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget result is stateless per action, assurance uses exact identifier equality, and deterministic diagnostic precedence is not a side-channel noninterference claim. Codec validation and cryptography remain outside this claim."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2975,7 +3584,7 @@ claim_id = "AP-FORMAL-RICH-056" claim_text = "The mechanically translated production delegation evaluator returns exactly the rich target-V1 linkage and attenuation decision, including the unique accepted transition fields." claim_status = "proved" lean_declaration = "Auths.Refinement.translated_delegation_refines_rich_spec" -lean_statement_sha256 = "14f3b7aa4e01346ba18dab4a3b7c7d3347c25d58ce0925c51b40a10fab821397" +lean_statement_sha256 = "9687263cb09963abc3090c34d4d58560512b322659b0828628ddcb941c6af51e" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [ "auths_authority::evaluate_grant_view", @@ -3022,7 +3631,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Validated target-V1 parent-authority and grant views through the production delegation outcome and accepted transition." residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget relation is stateless per action, assurance retention is exact identifier equality rather than a lattice, and deterministic diagnostic precedence is not a side-channel noninterference claim. Root preservation and assurance retention occur in the caller around the proved returned transition; codec validation and cryptography remain outside this claim."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3048,6 +3657,59 @@ artifact = "core/formal-vectors/v1/rich-authority.json" kind = "mutation-matrix" artifact = "formal/refinement-mutations-v1.json" +[[claims]] +claim_id = "AP-FORMAL-RICH-104" +claim_text = "Lean records, as a checked theorem rather than a comment, that the pinned Aeneas translation of optional_budget_covers is stale on exactly one input class: a present ceiling with an absent request, where the translation answers true and the shipping Rust answers false. It stops being provable when the translation is regenerated." +claim_status = "proved" +lean_declaration = "Auths.Refinement.translated_budget_coverage_gap_is_the_absent_request" +lean_statement_sha256 = "3c0b39c13aef92097036b16c569166a15be4b6a8dc4e38f8eac70fb3ce677e58" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + [[claims]] claim_id = "AP-FORMAL-RICH-057" claim_text = "Lean proves the bounded product-policy property: configuration match refl." @@ -3087,7 +3749,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3136,7 +3798,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3185,7 +3847,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3234,7 +3896,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3283,7 +3945,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3332,7 +3994,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3381,7 +4043,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3430,7 +4092,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3482,7 +4144,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3531,7 +4193,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3584,7 +4246,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3637,7 +4299,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3690,7 +4352,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3743,7 +4405,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3796,7 +4458,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3848,7 +4510,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3900,7 +4562,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3952,7 +4614,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4005,7 +4667,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4058,7 +4720,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4107,7 +4769,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4156,7 +4818,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4205,7 +4867,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4254,7 +4916,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4303,7 +4965,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4352,7 +5014,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4401,7 +5063,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4450,7 +5112,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4499,7 +5161,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4548,7 +5210,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4597,7 +5259,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::model::LifecycleState::is_terminal` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4646,7 +5308,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::transition_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4698,7 +5360,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::exclusive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4747,7 +5409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::additive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4800,7 +5462,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::replay_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4849,7 +5511,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4898,7 +5560,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4947,7 +5609,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4996,7 +5658,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5045,7 +5707,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5094,7 +5756,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5143,7 +5805,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5196,7 +5858,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5249,7 +5911,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "Exactly the two-input threshold helper and the declared three-valued truth order." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5305,7 +5967,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "A two-value swap only; arbitrary authorization-plan permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5354,7 +6016,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The two-value canonicalCode helper; arbitrary plan diagnostic permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5403,7 +6065,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "A definitional model equality; it does not prove that the shipping evaluator visits leaves once." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5455,7 +6117,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "A definitional model equality; no asymptotic or shipping evaluator cost bound is claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5507,7 +6169,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5560,7 +6222,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5616,7 +6278,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "ffd54fce0512f0b674ae3b4c59712042fcfe7ba6bd48caecdf97a1e718c72f8e" +semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" From f34d37a0ff5b65c388f630906af93ad7a5157d27 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sat, 15 Aug 2026 23:12:33 +0100 Subject: [PATCH 10/61] test: make the effect axis a failing acceptance test in both bindings (UNSIGNED) The wave acceptance test, written before anything is fixed. Its failure is the specification the Transport and Surface lanes implement against. Derived from product/errors/v1/registry.json, not from any stated number: 48 stable codes; 9 carry effect 'possible'; 1 carries 'applied'; 38 are 'not-applied' only. Every code has exactly one outcome. effect 'possible' (9): core.observation-inconclusive, core.observation-pending, core.outcome-unknown, mcp.handler-failed, mcp.handler-timeout, mcp.invalid-handler-output, mcp.reconciliation-pending, plan.member-interrupted, plan.reconciliation-pending effect 'applied' (1): mcp.receipt-persist-failed Reads every value the way a real caller does: TypeScript through the subpaths in package.json "exports", Python through the modules in public-topology-v1.json. No internal module is imported. Currently red, deliberately, and NOT marked xfail: 8 of 11 node:test cases, 9 of 12 pytest cases, and 11 tsc errors in the contract project. Each check was mutation-tested and flips green when the axis is supplied, except EA-4b, which correctly stays red under a fabricated constant code. Test-only. No shipping source changed. --- .../python/tests/test_effect_axis_boundary.py | 426 ++++++++++++++++++ .../typescript/test/contract/effect-axis.ts | 86 ++++ .../integration/effect-axis-boundary.test.js | 340 ++++++++++++++ 3 files changed, 852 insertions(+) create mode 100644 bindings/python/tests/test_effect_axis_boundary.py create mode 100644 bindings/typescript/test/contract/effect-axis.ts create mode 100644 bindings/typescript/test/integration/effect-axis-boundary.test.js diff --git a/bindings/python/tests/test_effect_axis_boundary.py b/bindings/python/tests/test_effect_axis_boundary.py new file mode 100644 index 00000000..883af1e4 --- /dev/null +++ b/bindings/python/tests/test_effect_axis_boundary.py @@ -0,0 +1,426 @@ +"""WAVE ACCEPTANCE TEST -- the effect axis must survive every boundary. + +This module is the specification for the Transport and Surface lanes. It is +EXPECTED TO BE RED until they land. Every failure here is a finding, not a +flake, and none of these assertions may be weakened to make the suite green. + +Property under test (contract 4.1, 5.1, 5.2, 5.4, 5.5, 5.6): + + Rust classifies each of the 48 registry codes with an effect -- + not-applied | possible | applied. ``possible`` means WE DO NOT KNOW whether + the real-world effect happened. A caller who reads ``not-applied`` when the + truth is ``possible`` will blindly retry and may repeat a payment or a + database write. That distinction must arrive intact at the public Python + API, together with the stable code identity, the retry class, and the + recommended action. + +Every value read here is read the way a REAL CALLER reads it: through a module +declared in ``bindings/public-topology-v1.json``. No test in this module may +import a private ``auths._*`` module. +""" + +from __future__ import annotations + +import importlib +import json +from pathlib import Path +from typing import Any, Mapping + +import pytest + +RED = "EFFECT-AXIS ACCEPTANCE (expected red until the Transport and Surface lanes land)" + +_REPO_ROOT = Path(__file__).resolve().parents[3] +_REGISTRY_PATH = _REPO_ROOT / "product/errors/v1/registry.json" +_FIXTURES_PATH = _REPO_ROOT / "product/fixtures/v1/errors/manifest.json" +_TOPOLOGY_PATH = _REPO_ROOT / "bindings/public-topology-v1.json" + + +def _read(path: Path) -> Mapping[str, Any]: + return json.loads(path.read_text(encoding="utf-8")) + + +# The Rust-owned registry. Nothing here hardcodes a count or a code. +REGISTRY = _read(_REGISTRY_PATH) +# Rust-minted `auths.error/1` envelopes, one per code, from `ErrorEnvelope::parse`. +FIXTURES = _read(_FIXTURES_PATH) +TOPOLOGY = _read(_TOPOLOGY_PATH) + +DEFINITIONS = REGISTRY["definitions"] +BY_CODE = {definition["code"]: definition for definition in DEFINITIONS} +ENVELOPE_FOR = {fixture["code"]: fixture for fixture in FIXTURES["fixtures"]} + +# Derived, never assumed. +RUST_EFFECT_STATES = ("not-applied", "possible", "applied") +RUST_RETRY_CLASSES = ("never", "safe", "conditional", "unknown") + + +def _codes_with_effect(effect: str) -> list[str]: + return sorted( + definition["code"] + for definition in DEFINITIONS + if any(outcome["effect"] == effect for outcome in definition["outcomes"]) + ) + + +POSSIBLE_CODES = _codes_with_effect("possible") +APPLIED_CODES = _codes_with_effect("applied") + +PUBLIC_MODULES = tuple( + module for layer in TOPOLOGY["layers"] for module in layer["python"] +) + + +def _public(module: str) -> Any: + """Imports a module a real consumer can import, refusing private paths.""" + assert module in PUBLIC_MODULES, ( + f"{module} is not declared in bindings/public-topology-v1.json" + ) + return importlib.import_module(module) + + +def _effect_value(value: Any) -> Any: + """Reads an effect the way a caller does, tolerating str or str-Enum.""" + return getattr(value, "value", value) + + +# --------------------------------------------------------------------------- +# EA-0 Anti-vacuity guards. A check that iterates an empty set cannot fail. +# --------------------------------------------------------------------------- + + +def test_ea0_registry_fixtures_and_topology_are_non_empty_and_aligned() -> None: + assert REGISTRY["schema"] == "auths.error-registry/1" + assert DEFINITIONS, "registry has no definitions; every per-code test would be vacuous" + assert len(ENVELOPE_FOR) == len(DEFINITIONS), ( + "the Rust-minted fixture corpus does not cover every registry code" + ) + assert POSSIBLE_CODES, ( + "no code carries effect 'possible'; the safety-critical case would be untested" + ) + assert APPLIED_CODES, ( + "no code carries effect 'applied'; that arm of the axis would be untested" + ) + for definition in DEFINITIONS: + for outcome in definition["outcomes"]: + assert outcome["effect"] in RUST_EFFECT_STATES, ( + f"{definition['code']} declares effect {outcome['effect']!r}, " + f"outside the Rust-owned set" + ) + assert PUBLIC_MODULES, "public topology declares no Python entry points" + for module in PUBLIC_MODULES: + importlib.import_module(module) + + +# --------------------------------------------------------------------------- +# EA-1 Surface: every registry code reaches a public caller with all four +# fields of its recovery contract. +# --------------------------------------------------------------------------- + + +def test_ea1_every_registry_code_reaches_a_public_python_caller_with_the_full_axis() -> None: + auths = _public("auths") + assert hasattr(auths, "AuthsError"), f"{RED}: the product root publishes no AuthsError" + lost: list[str] = [] + for definition in DEFINITIONS: + code = definition["code"] + outcome = definition["outcomes"][0] + try: + error = auths.AuthsError.parse(ENVELOPE_FOR[code]) + except Exception as cause: # noqa: BLE001 - the failure is the finding + lost.append(f"{code}: public root refused the Rust-minted envelope ({cause})") + continue + if error.code != code: + lost.append(f"{code}: code identity became {error.code}") + effect = _effect_value(error.effect) + if effect != outcome["effect"]: + lost.append(f"{code}: effect became {effect}, Rust says {outcome['effect']}") + retry = _effect_value(error.retry) + if retry != outcome["retry"]: + lost.append(f"{code}: retry became {retry}, Rust says {outcome['retry']}") + action = _effect_value(error.recommended_action) + if action != definition["recommendedAction"]: + lost.append( + f"{code}: recommended_action became {action}, " + f"Rust says {definition['recommendedAction']}" + ) + if effect not in RUST_EFFECT_STATES: + lost.append(f"{code}: effect {effect!r} is outside the three Rust-owned states") + assert not lost, f"{RED}\n" + "\n".join(lost) + + +# --------------------------------------------------------------------------- +# EA-2 Fail-closed: an unrecognized code must become `possible`, never a +# fourth value and never `not-applied` (contract 4.1). +# --------------------------------------------------------------------------- + + +def test_ea2_an_unregistered_code_fails_closed_to_possible() -> None: + auths = _public("auths") + template = dict(ENVELOPE_FOR[POSSIBLE_CODES[0]]) + future = "mcp.code-minted-by-a-newer-rust" + assert future not in BY_CODE, "the unknown-code probe accidentally uses a registered code" + template["code"] = future + error = auths.AuthsError.parse(template) + assert error.code == future, f"{RED}: an unknown code lost its identity at the public surface" + effect = _effect_value(error.effect) + assert effect == "possible", ( + f"{RED}: an unknown code mapped to effect {effect!r}. Contract 4.1 requires 'possible'. " + f"A newer Rust code must never be silently swallowed or downgraded by an older binding." + ) + + +def test_ea2b_the_public_python_surface_admits_exactly_three_effect_states() -> None: + auths = _public("auths") + observed = { + _effect_value(auths.AuthsError.parse(ENVELOPE_FOR[definition["code"]]).effect) + for definition in DEFINITIONS + } + unknown_envelope = dict(ENVELOPE_FOR[POSSIBLE_CODES[0]]) + unknown_envelope["code"] = "plan.code-minted-by-a-newer-rust" + observed.add(_effect_value(auths.AuthsError.parse(unknown_envelope).effect)) + extra = sorted(value for value in observed if value not in RUST_EFFECT_STATES) + assert not extra, ( + f"{RED}: the public surface produced effect value(s) {extra} outside " + f"{list(RUST_EFFECT_STATES)}. EffectState has exactly three members." + ) + + +def test_ea2c_the_public_root_publishes_the_effect_axis_vocabulary() -> None: + auths = _public("auths") + exported = set(auths.__all__) + missing = sorted({"EffectState", "RetryClass", "RecommendedAction"} - exported) + assert not missing, ( + f"{RED}: the public root does not export {missing}. A caller cannot name the type " + f"of the value they must branch on." + ) + effect_state = auths.EffectState + members = sorted(member.value for member in effect_state) + assert members == sorted(RUST_EFFECT_STATES), ( + f"{RED}: auths.EffectState has members {members}, not {sorted(RUST_EFFECT_STATES)}. " + f"There are exactly three." + ) + retry_class = auths.RetryClass + retry_members = sorted(getattr(member, "value", member) for member in retry_class) + assert retry_members == sorted(RUST_RETRY_CLASSES), ( + f"{RED}: auths.RetryClass has members {retry_members}, not {sorted(RUST_RETRY_CLASSES)}. " + f"The root currently exports the NextCall set ('never'|'backoff'|'resume'|'reconcile') " + f"under the RetryClass name; they answer different questions and must never share an " + f"identifier." + ) + + +# --------------------------------------------------------------------------- +# EA-3 Transport: Rust -> pyo3 -> Python. An error crossing the pyo3 boundary +# must arrive as a structured envelope, not a bare ValueError string +# (contract 5.2). +# --------------------------------------------------------------------------- + + +def test_ea3_an_error_crossing_the_pyo3_boundary_arrives_structured() -> None: + identity = _public("auths.identity") + auths = _public("auths") + with pytest.raises(BaseException) as caught: # noqa: PT011 - the type is the finding + identity.decode_identity(b"\xff\xff\xff") + error = caught.value + assert isinstance(error, auths.AuthsError), ( + f"{RED}: the pyo3 boundary raised {type(error).__name__}({error.args!r}), not the public " + f"AuthsError. The bare exception carries no code identity, no effect state, no retry " + f"class, and no recommended action." + ) + assert error.code in BY_CODE, ( + f"{RED}: the pyo3 boundary reported code {error.code!r}, which is not in the registry" + ) + assert _effect_value(error.effect) in RUST_EFFECT_STATES, ( + f"{RED}: the pyo3 boundary reported effect {_effect_value(error.effect)!r}" + ) + assert error.retry is not None, f"{RED}: the pyo3 boundary reported no retry class" + assert error.recommended_action is not None, ( + f"{RED}: the pyo3 boundary reported no recommended action" + ) + + +def test_ea3b_every_pyo3_boundary_failure_on_a_public_entry_point_is_structured() -> None: + identity = _public("auths.identity") + verify = _public("auths.verify") + auths = _public("auths") + probes = ( + ("auths.identity.decode_identity", lambda: identity.decode_identity(b"\xff\xff\xff")), + ( + "auths.verify.decode_receipt", + lambda: verify.decode_receipt(b"\x01\x02\x03"), + ), + ) + flattened: list[str] = [] + for name, probe in probes: + try: + probe() + except BaseException as error: # noqa: BLE001 - the type is the finding + if not isinstance(error, auths.AuthsError): + flattened.append(f"{name}: raised {type(error).__name__}({error.args!r})") + else: + flattened.append(f"{name}: adversarial input did not fail; the probe proves nothing") + assert not flattened, ( + f"{RED}: the following published entry points lose the effect axis at the pyo3 " + f"boundary\n" + "\n".join(flattened) + ) + + +# --------------------------------------------------------------------------- +# EA-4 The execution path. This is the safety-critical one: a real failed +# execution, driven through published entry points, must tell the caller +# whether the effect may have happened (contract 5.1). +# --------------------------------------------------------------------------- + + +async def _drive_execution(tool: str, tools: Mapping[str, Any], request_id: str) -> Any: + development = _public("auths.integrations").development + mcp = _public("auths.profiles").mcp + provider = mcp.development_provider(tools=dict(tools)) + session = await development.create_auths(authority=mcp.allow_tools([tool])) + try: + return await session.execute( + action=mcp.call_tool(name=tool, arguments={}), + provider=provider, + request_id=request_id, + ) + finally: + await session.aclose() + + +async def _drive_denial(request_id: str) -> Any: + development = _public("auths.integrations").development + mcp = _public("auths.profiles").mcp + + async def allowed(**_: Any) -> Mapping[str, Any]: + return {"ok": True} + + provider = mcp.development_provider(tools={"allowed": allowed}) + session = await development.create_auths(authority=mcp.allow_tools(["allowed"])) + try: + return await session.execute( + action=mcp.call_tool(name="forbidden", arguments={}), + provider=provider, + request_id=request_id, + ) + finally: + await session.aclose() + + +def _shape(result: Any) -> str: + fields = sorted( + name for name in dir(result) if not name.startswith("_") and not callable(getattr(result, name)) + ) + return f"{type(result).__name__} {{ {', '.join(fields)} }}" + + +@pytest.mark.asyncio +async def test_ea4_a_provider_failure_tells_the_public_caller_the_effect_is_possible() -> None: + async def boom(**_: Any) -> Mapping[str, Any]: + raise RuntimeError("provider exploded after entry") + + result = await _drive_execution("boom", {"boom": boom}, "effect-axis-boom-000001") + shape = _shape(result) + code = getattr(result, "code", None) + assert isinstance(code, str), ( + f"{RED}: a provider failure surfaced as {shape} with no stable code identity. " + f"Rust classifies this as mcp.handler-failed, effect 'possible'." + ) + assert code in BY_CODE, ( + f"{RED}: a provider failure surfaced code {code!r}, which is not in the registry" + ) + effect = _effect_value(getattr(result, "effect", None)) + assert effect == "possible", ( + f"{RED}: a provider failure surfaced as {shape} with effect {effect!r}. The caller cannot " + f"tell that the real-world effect may have been applied, and may blindly retry." + ) + assert _effect_value(getattr(result, "retry", None)) == "unknown", ( + f"{RED}: a possible-effect failure did not report retry 'unknown'" + ) + assert _effect_value(getattr(result, "recommended_action", None)) == "resume-and-reconcile", ( + f"{RED}: a possible-effect failure did not recommend reconciliation" + ) + + +@pytest.mark.asyncio +async def test_ea4b_two_distinct_registry_codes_do_not_collapse_to_one_caller_shape() -> None: + async def boom(**_: Any) -> Mapping[str, Any]: + raise RuntimeError("provider exploded after entry") + + async def oversized(**_: Any) -> Mapping[str, Any]: + return {"blob": "x" * (2 * 1024 * 1024)} + + failed = await _drive_execution("boom", {"boom": boom}, "effect-axis-boom-000002") + invalid = await _drive_execution( + "oversized", {"oversized": oversized}, "effect-axis-oversized-000001" + ) + assert (failed.kind, getattr(failed, "code", None)) != ( + invalid.kind, + getattr(invalid, "code", None), + ), ( + f"{RED}: a handler that raised and a handler that produced invalid output both surfaced " + f"as {failed.kind} with code {getattr(failed, 'code', None)!r}. Rust distinguishes " + f"mcp.handler-failed from mcp.invalid-handler-output; the public path destroys that " + f"identity." + ) + + +@pytest.mark.asyncio +async def test_ea4c_a_denial_tells_the_public_caller_the_effect_is_not_applied() -> None: + denied = await _drive_denial("effect-axis-denied-000001") + assert denied.kind == "denied" + effect = _effect_value(getattr(denied, "effect", None)) + assert effect == "not-applied", ( + f"{RED}: a denial surfaced as {_shape(denied)} with effect {effect!r}. A caller cannot " + f"prove from the public result that nothing happened." + ) + + +# --------------------------------------------------------------------------- +# EA-5 Inventory gate: bindings mint no error codes (contract 5.4). Rather +# than listing the codes to check, fail when a code appears OUTSIDE the +# registry, so the whole class cannot return. +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_ea5_every_code_the_public_execution_path_emits_is_in_the_rust_registry() -> None: + async def boom(**_: Any) -> Mapping[str, Any]: + raise RuntimeError("provider exploded after entry") + + emitted: set[str] = set() + for result in ( + await _drive_execution("boom", {"boom": boom}, "effect-axis-inventory-000001"), + await _drive_denial("effect-axis-inventory-000002"), + ): + code = getattr(result, "code", None) + if isinstance(code, str): + emitted.add(code) + assert emitted, "no code was observed; this inventory gate would be vacuous" + unregistered = sorted(code for code in emitted if code not in BY_CODE) + assert not unregistered, ( + f"{RED}: the public execution path emitted code(s) {unregistered} that exist in no " + f"registry. All codes originate in product/errors/v1/registry.json " + f"({len(DEFINITIONS)} today)." + ) + + +# --------------------------------------------------------------------------- +# EA-6 The reported inventory, so the transcript carries the derived sets. +# --------------------------------------------------------------------------- + + +def test_ea6_the_derived_effect_inventory_is_reported(capsys: Any) -> None: + only_not_applied = [ + definition["code"] + for definition in DEFINITIONS + if all(outcome["effect"] == "not-applied" for outcome in definition["outcomes"]) + ] + with capsys.disabled(): + print(f"\nregistry: {len(DEFINITIONS)} stable codes") + print(f"effect 'possible' ({len(POSSIBLE_CODES)}): {', '.join(POSSIBLE_CODES)}") + print(f"effect 'applied' ({len(APPLIED_CODES)}): {', '.join(APPLIED_CODES)}") + print(f"effect 'not-applied' only ({len(only_not_applied)})") + assert ( + len(POSSIBLE_CODES) + len(APPLIED_CODES) + len(only_not_applied) == len(DEFINITIONS) + ), "the three effect partitions do not sum to the registry size" diff --git a/bindings/typescript/test/contract/effect-axis.ts b/bindings/typescript/test/contract/effect-axis.ts new file mode 100644 index 00000000..07702fc2 --- /dev/null +++ b/bindings/typescript/test/contract/effect-axis.ts @@ -0,0 +1,86 @@ +/** + * WAVE ACCEPTANCE TEST (compile-time half) — the frozen effect vocabulary. + * + * EXPECTED TO BE RED until the Surface lane lands. `npm run test:contract` + * type-checks this file; every error below is a finding. + * + * `EffectState` and `RetryClass` are erased at runtime, so their shape cannot + * be asserted by a node:test file. This is where contract 4.1 is enforced: + * + * EffectState = "not-applied" | "possible" | "applied" exactly three + * RetryClass = "never" | "safe" | "conditional" | "unknown" — "may I retry" + * NextCall = "never" | "backoff" | "resume" | "reconcile" — "what next" + * + * The last two are DIFFERENT QUESTIONS and must never share an identifier. + * Today `@auths-dev/sdk` exports the NextCall set under the name `RetryClass` + * (src/index.ts:47 re-exporting src/production-client.ts:12), and exports no + * `EffectState` at all. + */ + +import type { + AuthsErrorDetails, + EffectState, + NextCall, + RecommendedAction, + RetryClass, +} from "../../src/index.js"; + +// --- EffectState: exactly three members, no fourth ------------------------ + +const everyEffectState: readonly EffectState[] = ["not-applied", "possible", "applied"]; +void everyEffectState; + +// @ts-expect-error "unknown" is not a fourth effect state; unrecognized codes map to "possible" +const fourthEffectState: EffectState = "unknown"; +void fourthEffectState; + +// @ts-expect-error the workflow-local vocabulary (none|possible|occurred) is deleted +const workflowEffectState: EffectState = "occurred"; +void workflowEffectState; + +// A total switch proves the union is closed at exactly three. +export function describeEffect(effect: EffectState): string { + switch (effect) { + case "not-applied": return "the real-world effect did not happen"; + case "possible": return "we do not know whether the real-world effect happened"; + case "applied": return "the real-world effect happened"; + default: { + const exhaustive: never = effect; + return exhaustive; + } + } +} + +// --- RetryClass: the "may I retry" question ------------------------------- + +const everyRetryClass: readonly RetryClass[] = ["never", "safe", "conditional", "unknown"]; +void everyRetryClass; + +// @ts-expect-error "backoff" belongs to NextCall, not to the retry question +const nextCallAsRetry: RetryClass = "backoff"; +void nextCallAsRetry; + +// --- NextCall: the "what should I call next" question --------------------- + +const everyNextCall: readonly NextCall[] = ["never", "backoff", "resume", "reconcile"]; +void everyNextCall; + +// @ts-expect-error "safe" belongs to RetryClass, not to the next-call question +const retryAsNextCall: NextCall = "safe"; +void retryAsNextCall; + +// --- The axis is reachable and typed on the public error details ---------- + +export function readAxis(details: AuthsErrorDetails): { + readonly code: string; + readonly effect: EffectState; + readonly retry: RetryClass; + readonly recommendedAction: RecommendedAction; +} { + return { + code: details.code, + effect: details.effect, + retry: details.retry, + recommendedAction: details.recommendedAction, + }; +} diff --git a/bindings/typescript/test/integration/effect-axis-boundary.test.js b/bindings/typescript/test/integration/effect-axis-boundary.test.js new file mode 100644 index 00000000..62e9cf37 --- /dev/null +++ b/bindings/typescript/test/integration/effect-axis-boundary.test.js @@ -0,0 +1,340 @@ +/** + * WAVE ACCEPTANCE TEST — the effect axis must survive every boundary. + * + * This file is the specification for the Transport and Surface lanes. It is + * EXPECTED TO BE RED until they land. Every failure here is a finding, not a + * flake, and none of these assertions may be weakened to make the suite green. + * + * Property under test (contract 4.1, 5.1, 5.2, 5.4, 5.5, 5.6): + * + * Rust classifies each of the 48 registry codes with an effect — + * not-applied | possible | applied. `possible` means WE DO NOT KNOW whether + * the real-world effect happened. A caller who reads `not-applied` when the + * truth is `possible` will blindly retry and may repeat a payment or a + * database write. That distinction must arrive intact at the public + * TypeScript API, together with the stable code identity, the retry class, + * and the recommended action. + * + * Every value read here is read the way a REAL CALLER reads it: through a + * subpath declared in bindings/public-topology-v1.json and published in + * package.json "exports". No test in this file may import an internal module. + */ + +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import test from "node:test"; + +const RED = "EFFECT-AXIS ACCEPTANCE (expected red until the Transport and Surface lanes land)"; + +const repoRoot = new URL("../../../../", import.meta.url); +const packageRoot = new URL("../../", import.meta.url); + +const readJson = async (url) => JSON.parse(await readFile(url, "utf8")); + +/** The Rust-owned registry. Nothing in this file hardcodes a count or a code. */ +const registry = await readJson(new URL("product/errors/v1/registry.json", repoRoot)); +/** Rust-minted `auths.error/1` envelopes, one per code, produced by `ErrorEnvelope::parse`. */ +const fixtures = await readJson(new URL("product/fixtures/v1/errors/manifest.json", repoRoot)); +const topology = await readJson(new URL("bindings/public-topology-v1.json", repoRoot)); +const manifest = await readJson(new URL("package.json", packageRoot)); + +const definitions = registry.definitions; +const byCode = new Map(definitions.map((definition) => [definition.code, definition])); +const envelopeFor = new Map(fixtures.fixtures.map((fixture) => [fixture.code, fixture])); + +/** Derived, never assumed. */ +const RUST_EFFECT_STATES = Object.freeze(["not-applied", "possible", "applied"]); +const codesWithEffect = (effect) => + definitions + .filter((definition) => definition.outcomes.some((outcome) => outcome.effect === effect)) + .map((definition) => definition.code) + .sort(); +const POSSIBLE_CODES = codesWithEffect("possible"); +const APPLIED_CODES = codesWithEffect("applied"); + +/** + * Resolves a published subpath to its built module, proving the import is one + * a real consumer can write. A subpath that is not in "exports" is not public. + */ +const exportKey = (subpath) => + subpath === manifest.name ? "." : `.${subpath.slice(manifest.name.length)}`; + +const importPublic = async (subpath) => { + const entry = manifest.exports[exportKey(subpath)]; + assert.ok(entry, `${subpath} is not a published entry point of ${manifest.name}`); + return import(new URL(entry.import, packageRoot).href); +}; + +// --------------------------------------------------------------------------- +// Anti-vacuity guards. A check that iterates an empty set cannot fail. +// --------------------------------------------------------------------------- + +test("EA-0 the registry, its fixtures, and the declared topology are all non-empty and aligned", () => { + assert.equal(registry.schema, "auths.error-registry/1"); + assert.ok(definitions.length > 0, "registry has no definitions; every per-code test below would be vacuous"); + assert.equal(envelopeFor.size, definitions.length, + "the Rust-minted fixture corpus does not cover every registry code"); + assert.ok(POSSIBLE_CODES.length > 0, + "no code carries effect 'possible'; the safety-critical case would be untested"); + assert.ok(APPLIED_CODES.length > 0, + "no code carries effect 'applied'; that arm of the axis would be untested"); + for (const definition of definitions) { + for (const outcome of definition.outcomes) { + assert.ok(RUST_EFFECT_STATES.includes(outcome.effect), + `${definition.code} declares effect '${outcome.effect}', outside the Rust-owned set`); + } + } + const declared = topology.layers.flatMap((layer) => layer.typescript); + assert.ok(declared.length > 0, "public topology declares no TypeScript entry points"); + for (const subpath of declared) { + assert.ok(manifest.exports[exportKey(subpath)], + `${subpath} is declared in public-topology-v1.json but is not published in package.json exports`); + } +}); + +// --------------------------------------------------------------------------- +// EA-1 Surface: every registry code reaches a public caller with all four +// fields of its recovery contract. +// --------------------------------------------------------------------------- + +test("EA-1 every registry code reaches a public TypeScript caller with code, effect, retry, and action", async () => { + const sdk = await importPublic("@auths-dev/sdk"); + assert.ok(sdk.AuthsError, `${RED}: the product root publishes no AuthsError`); + const lost = []; + for (const definition of definitions) { + const envelope = envelopeFor.get(definition.code); + const outcome = definition.outcomes[0]; + let error; + try { + error = sdk.AuthsError.parse(envelope); + } catch (cause) { + lost.push(`${definition.code}: public root refused the Rust-minted envelope (${cause})`); + continue; + } + if (error.code !== definition.code) lost.push(`${definition.code}: code identity became ${error.code}`); + if (error.effect !== outcome.effect) lost.push(`${definition.code}: effect became ${error.effect}, Rust says ${outcome.effect}`); + if (error.retry !== outcome.retry) lost.push(`${definition.code}: retry became ${error.retry}, Rust says ${outcome.retry}`); + if (error.recommendedAction !== definition.recommendedAction) { + lost.push(`${definition.code}: recommendedAction became ${error.recommendedAction}, Rust says ${definition.recommendedAction}`); + } + if (!RUST_EFFECT_STATES.includes(error.effect)) { + lost.push(`${definition.code}: effect '${error.effect}' is outside the three Rust-owned states`); + } + } + assert.deepEqual(lost, [], `${RED}\n${lost.join("\n")}`); +}); + +// --------------------------------------------------------------------------- +// EA-2 Fail-closed: an unrecognized code must become `possible`, never a +// fourth value and never `not-applied` (contract 4.1). +// --------------------------------------------------------------------------- + +test("EA-2 an unregistered code fails closed to effect 'possible' at the public TypeScript surface", async () => { + const sdk = await importPublic("@auths-dev/sdk"); + const template = envelopeFor.get(POSSIBLE_CODES[0]); + const future = "mcp.code-minted-by-a-newer-rust"; + assert.equal(byCode.has(future), false, "the unknown-code probe accidentally uses a registered code"); + const error = sdk.AuthsError.parse({ ...template, code: future }); + assert.equal(error.code, future, `${RED}: an unknown code lost its identity at the public surface`); + assert.equal(error.effect, "possible", + `${RED}: an unknown code mapped to effect '${error.effect}'. Contract 4.1 requires 'possible'. ` + + `A newer Rust code must never be silently swallowed or downgraded by an older binding.`); + assert.ok(RUST_EFFECT_STATES.includes(error.effect), + `${RED}: '${error.effect}' is a fourth effect state. There are exactly three.`); +}); + +test("EA-2b the public TypeScript surface admits no effect value outside the three Rust-owned states", async () => { + const sdk = await importPublic("@auths-dev/sdk"); + const observed = new Set(); + for (const definition of definitions) { + observed.add(sdk.AuthsError.parse(envelopeFor.get(definition.code)).effect); + } + observed.add(sdk.AuthsError.parse({ + ...envelopeFor.get(POSSIBLE_CODES[0]), + code: "plan.code-minted-by-a-newer-rust", + }).effect); + const extra = [...observed].filter((value) => !RUST_EFFECT_STATES.includes(value)).sort(); + assert.deepEqual(extra, [], + `${RED}: the public surface produced effect value(s) ${JSON.stringify(extra)} outside ` + + `${JSON.stringify(RUST_EFFECT_STATES)}. EffectState has exactly three members.`); +}); + +// --------------------------------------------------------------------------- +// EA-3 Transport: Rust -> WASM -> TypeScript. An error crossing the WASM +// boundary must arrive as a structured envelope, not a flattened string +// (contract 5.2; bindings/wasm/auths-proof-wasm/src/lib.rs:4928). +// --------------------------------------------------------------------------- + +test("EA-3 an error crossing the WASM boundary arrives structured, not flattened to a string", async () => { + const identity = await importPublic("@auths-dev/sdk/identity"); + const sdk = await importPublic("@auths-dev/sdk"); + const client = await identity.loadIdentity(); + let thrown; + let threw = false; + try { + client.decodePublicIdentity(new Uint8Array([0xff, 0xff, 0xff])); + } catch (error) { + threw = true; + thrown = error; + } + assert.ok(threw, "the WASM boundary probe did not fail; pick adversarial input that does"); + assert.notEqual(typeof thrown, "string", + `${RED}: the WASM boundary threw a bare JavaScript string ${JSON.stringify(String(thrown))}. ` + + `js_error at bindings/wasm/auths-proof-wasm/src/lib.rs:4928 flattens every error to ` + + `JsValue::from_str, destroying code identity, effect state, retry class, and recommended action.`); + assert.ok(thrown instanceof Error, `${RED}: the WASM boundary threw a non-Error value`); + assert.ok(sdk.AuthsError && thrown instanceof sdk.AuthsError, + `${RED}: the WASM boundary threw ${thrown?.constructor?.name}, not the public AuthsError`); + assert.ok(byCode.has(thrown.code), + `${RED}: the WASM boundary reported code ${JSON.stringify(thrown.code)}, which is not in the registry`); + assert.ok(RUST_EFFECT_STATES.includes(thrown.effect), + `${RED}: the WASM boundary reported effect ${JSON.stringify(thrown.effect)}`); + assert.ok(typeof thrown.retry === "string", `${RED}: the WASM boundary reported no retry class`); + assert.ok(typeof thrown.recommendedAction === "string", + `${RED}: the WASM boundary reported no recommended action`); +}); + +test("EA-3b every WASM-boundary failure on a published entry point is a structured Auths error", async () => { + const identity = await importPublic("@auths-dev/sdk/identity"); + const client = await identity.loadIdentity(); + const probes = [ + ["identity.decodePublicIdentity", () => client.decodePublicIdentity(new Uint8Array([0xff, 0xff, 0xff]))], + ["identity.decodeSignedMessage", () => client.decodeSignedMessage(new Uint8Array([0x01, 0x02, 0x03]))], + ]; + const flattened = []; + for (const [name, probe] of probes) { + try { + probe(); + flattened.push(`${name}: adversarial input did not fail; the probe proves nothing`); + } catch (error) { + if (typeof error === "string") { + flattened.push(`${name}: threw the bare string ${JSON.stringify(error)}`); + } else if (!(error instanceof Error)) { + flattened.push(`${name}: threw a ${typeof error}, not an Error`); + } else if (typeof error.code !== "string" || typeof error.effect !== "string") { + flattened.push(`${name}: threw ${error.constructor.name} with code=${error.code} effect=${error.effect}`); + } + } + } + assert.deepEqual(flattened, [], + `${RED}: the following published entry points lose the effect axis at the WASM boundary\n` + + flattened.join("\n")); +}); + +// --------------------------------------------------------------------------- +// EA-4 The execution path. This is the safety-critical one: a real failed +// execution, driven through published entry points, must tell the caller +// whether the effect may have happened (contract 5.1). +// --------------------------------------------------------------------------- + +const driveExecution = async (toolName, tools) => { + const { development } = await importPublic("@auths-dev/sdk/integrations"); + const { mcp } = await importPublic("@auths-dev/sdk/profiles"); + const provider = mcp.developmentProvider({ tools }); + const auths = await development.createAuths({ authority: mcp.allowTools([toolName]) }); + try { + return await auths.execute({ + action: mcp.callTool({ name: toolName, arguments: {} }), + provider, + requestId: `effect-axis-${toolName}-000001`, + }); + } finally { + await auths.close(); + await provider.close(); + } +}; + +test("EA-4 a provider failure tells the public caller the effect is 'possible'", async () => { + const result = await driveExecution("boom", { + async boom() { throw new Error("provider exploded after entry"); }, + }); + const shape = `${result.kind} { ${Object.keys(result).join(", ")} }`; + assert.ok(typeof result.code === "string", + `${RED}: a provider failure surfaced as ${shape} with no stable code identity. ` + + `Rust classifies this as mcp.handler-failed, effect 'possible'.`); + assert.ok(byCode.has(result.code), + `${RED}: a provider failure surfaced code ${JSON.stringify(result.code)}, not in the registry`); + assert.equal(result.effect, "possible", + `${RED}: a provider failure surfaced as ${shape} with effect ${JSON.stringify(result.effect)}. ` + + `The caller cannot tell that the real-world effect may have been applied, and may blindly retry.`); + assert.equal(result.retry, "unknown", `${RED}: a possible-effect failure did not report retry 'unknown'`); + assert.equal(result.recommendedAction, "resume-and-reconcile", + `${RED}: a possible-effect failure did not recommend reconciliation`); +}); + +test("EA-4b two distinct registry codes do not collapse to one caller-visible shape", async () => { + const failed = await driveExecution("boom", { + async boom() { throw new Error("provider exploded after entry"); }, + }); + const invalidOutput = await driveExecution("oversized", { + async oversized() { return { blob: "x".repeat(2 * 1024 * 1024) }; }, + }); + assert.notDeepEqual( + { kind: failed.kind, code: failed.code }, + { kind: invalidOutput.kind, code: invalidOutput.code }, + `${RED}: a handler that threw and a handler that produced invalid output both surfaced as ` + + `${failed.kind} with code ${JSON.stringify(failed.code)}. Rust distinguishes mcp.handler-failed ` + + `from mcp.invalid-handler-output; the public path destroys that identity.`); +}); + +const driveDenial = async (requestId) => { + const { development } = await importPublic("@auths-dev/sdk/integrations"); + const { mcp } = await importPublic("@auths-dev/sdk/profiles"); + const provider = mcp.developmentProvider({ tools: { async allowed() { return { ok: true }; } } }); + const auths = await development.createAuths({ authority: mcp.allowTools(["allowed"]) }); + try { + return await auths.execute({ + action: mcp.callTool({ name: "forbidden", arguments: {} }), + provider, + requestId, + }); + } finally { + await auths.close(); + await provider.close(); + } +}; + +test("EA-4c a denial tells the public caller the effect is 'not-applied'", async () => { + const denied = await driveDenial("effect-axis-denied-000001"); + assert.equal(denied.kind, "denied"); + assert.equal(denied.effect, "not-applied", + `${RED}: a denial surfaced as ${denied.kind} { ${Object.keys(denied).join(", ")} } with effect ` + + `${JSON.stringify(denied.effect)}. A caller cannot prove from the public result that nothing happened.`); +}); + +// --------------------------------------------------------------------------- +// EA-5 Inventory gate: bindings mint no error codes (contract 5.4). Rather +// than listing the codes to check, fail when a code appears OUTSIDE the +// registry, so the whole class cannot return. +// --------------------------------------------------------------------------- + +test("EA-5 every code the public execution path emits originates in the Rust registry", async () => { + const emitted = new Set(); + const record = (result) => { + if (typeof result?.code === "string") emitted.add(result.code); + }; + record(await driveExecution("boom", { async boom() { throw new Error("provider exploded"); } })); + record(await driveDenial("effect-axis-inventory-000001")); + assert.ok(emitted.size > 0, "no code was observed; this inventory gate would be vacuous"); + const unregistered = [...emitted].filter((code) => !byCode.has(code)).sort(); + assert.deepEqual(unregistered, [], + `${RED}: the public execution path emitted code(s) ${JSON.stringify(unregistered)} that exist in no ` + + `registry. All codes originate in product/errors/v1/registry.json (${definitions.length} today).`); +}); + +// --------------------------------------------------------------------------- +// EA-6 The reported inventory. Not an assertion about behaviour: this prints +// the derived possible/applied sets so the transcript carries them. +// --------------------------------------------------------------------------- + +test("EA-6 the derived effect inventory is reported", () => { + const lines = [ + `registry: ${definitions.length} stable codes`, + `effect 'possible' (${POSSIBLE_CODES.length}): ${POSSIBLE_CODES.join(", ")}`, + `effect 'applied' (${APPLIED_CODES.length}): ${APPLIED_CODES.join(", ")}`, + ]; + for (const line of lines) console.log(`# ${line}`); + assert.equal(POSSIBLE_CODES.length + APPLIED_CODES.length + + definitions.filter((d) => d.outcomes.every((o) => o.effect === "not-applied")).length, + definitions.length, "the three effect partitions do not sum to the registry size"); +}); From 29b7babfe14c45c6d700c5590f40757acbf238ea Mon Sep 17 00:00:00 2001 From: bordumb Date: Sat, 15 Aug 2026 23:50:32 +0100 Subject: [PATCH 11/61] gate: detect mirrored and homonymous public exports (UNSIGNED) The TypeScript root shipped two complete unrelated SDKs: 19 of 41 root symbols were a Production* mirror of the other 14, sharing zero methods and drawing `code` from disjoint spaces. tools/public-api.mjs did not notice, because it byte-compares the exports map against public-topology-v1.json and snapshots symbol names -- it proves the surface has not CHANGED, and cannot notice the surface was wrong to begin with. The enumeration already walked every export via the TypeScript compiler API, so the data was there. Two rules now run over it behind `--shape`: mirror one entry point exporting both `X` and `X` of the same kind homonym one name exported from two entry points resolving to DIFFERENT declarations (a shared declaration is a legitimate re-export) Current state: 15 violations. 13 mirrors, of which 7 are the Production* pair set the bindings wave is removing; the other 6 were previously unreported -- ./verify Receipt/Verified*Receipt, ./identity SignatureSuiteAdapter/Descriptor*, ./integrations DevelopmentAuthsOptions/Recoverable*, and two ./testkit ConformanceReport twins. Plus 2 homonyms: `Receipt` at root and ./verify, and `development` at ./integrations and ./testkit. Exceptions go in api/public-api-allowances.json with a written reason. A stale allowance fails as loudly as a violation, so exemptions cannot accumulate. NOT YET WIRED into `npm test`: the bindings wave is actively changing this surface, and a red gate mid-flight invites an agent to weaken it. Wire it after the wave lands and the count is zero. Verified: rule fires (15), allowance suppresses (0), stale allowance fails. Co-Authored-By: Claude Opus 5 --- bindings/python/src/application.rs | 826 ------------------ bindings/python/src/domains.rs | 109 --- bindings/python/src/http.rs | 719 --------------- .../typescript/api/public-api-allowances.json | 16 + bindings/typescript/tools/public-api.mjs | 93 ++ 5 files changed, 109 insertions(+), 1654 deletions(-) delete mode 100644 bindings/python/src/application.rs delete mode 100644 bindings/python/src/domains.rs delete mode 100644 bindings/python/src/http.rs create mode 100644 bindings/typescript/api/public-api-allowances.json diff --git a/bindings/python/src/application.rs b/bindings/python/src/application.rs deleted file mode 100644 index 3869aa95..00000000 --- a/bindings/python/src/application.rs +++ /dev/null @@ -1,826 +0,0 @@ -#![allow(clippy::needless_pass_by_value, clippy::too_many_arguments)] - -use crate::authoring::{ - PyPrincipal, PySignedObject, PyTrustedContext, PyUnsignedObject, SignedObject, UnsignedObject, - value_error, -}; -use crate::receipts::{PyReceiptPreparation, prepare_decision}; -use crate::result::{NativeVerificationResult, native_result, verify_sealed}; -use auths_author::{ - ProfilePlanCommitment, ProfilePlanMember, WorkflowProofBuilder, address_evidence, - prepare_profile_action, -}; -use auths_model::{ - Audience, BudgetAlgebraId, BudgetCeiling, CanonicalAction, CapabilityId, EvidenceTypeId, - MediaType, Permission, ProfileId, ProfileRef, ResourceId, -}; -use pyo3::{ - exceptions::{PyRuntimeError, PyTypeError, PyValueError}, - prelude::*, - types::PyBytes, -}; -use std::collections::HashSet; - -#[derive(Clone)] -#[pyclass( - name = "ApplicationAction", - frozen, - module = "auths._native", - skip_from_py_object -)] -pub struct PyApplicationAction { - canonical: CanonicalAction, - resource_namespace: ResourceId, - audience: Audience, -} - -#[pymethods] -impl PyApplicationAction { - #[getter] - fn profile_id(&self) -> &str { - self.canonical.profile().id().as_str() - } - - #[getter] - fn profile_version(&self) -> u16 { - self.canonical.profile().version() - } - - #[getter] - fn media_type(&self) -> &str { - self.canonical.media_type().as_str() - } - - #[getter] - fn body<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> { - PyBytes::new(py, self.canonical.body()) - } - - #[getter] - fn permission(&self) -> (String, String) { - ( - self.canonical.permission().capability().as_str().to_owned(), - self.canonical.permission().resource().as_str().to_owned(), - ) - } - - #[getter] - fn resource_namespace(&self) -> &str { - self.resource_namespace.as_str() - } - - #[getter] - fn audience(&self) -> &str { - self.audience.as_str() - } - - #[getter] - fn budget(&self) -> Option<(String, u64)> { - self.canonical - .requested_budget() - .map(|value| (value.algebra().as_str().to_owned(), value.value())) - } -} - -#[pyclass( - name = "ApplicationActionPreparation", - frozen, - module = "auths._native", - skip_from_py_object -)] -pub struct PyApplicationActionPreparation { - action: PyApplicationAction, - envelope: auths_model::ActionEnvelope, -} - -#[pymethods] -impl PyApplicationActionPreparation { - #[getter] - fn unsigned(&self) -> PyUnsignedObject { - PyUnsignedObject { - inner: UnsignedObject::Action(self.envelope.clone()), - } - } -} - -#[pyclass( - name = "ApplicationCommand", - module = "auths._native", - skip_from_py_object -)] -pub struct PyApplicationCommand { - action: Option, - receipt_artifacts: Option, - authority_commitment: [u8; 32], - context_commitment: [u8; 32], -} - -#[pymethods] -#[allow(clippy::unused_self)] -impl PyApplicationCommand { - #[getter] - fn action_commitment<'py>(&self, py: Python<'py>) -> PyResult> { - Ok(PyBytes::new(py, &self.action_commitment_bytes()?)) - } - - #[getter] - fn authority_commitment<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> { - PyBytes::new(py, &self.authority_commitment) - } - - #[getter] - fn context_commitment<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> { - PyBytes::new(py, &self.context_commitment) - } - - #[getter] - fn profile_id(&self) -> PyResult<&str> { - Ok(self.action()?.canonical.profile().id().as_str()) - } - - #[getter] - fn profile_version(&self) -> PyResult { - Ok(self.action()?.canonical.profile().version()) - } - - fn __repr__(&self) -> &'static str { - if self.action.is_some() { - "ApplicationCommand()" - } else { - "ApplicationCommand()" - } - } - - fn __copy__(&self) -> PyResult<()> { - Err(command_error()) - } - fn __deepcopy__(&self, _memo: &Bound<'_, PyAny>) -> PyResult<()> { - Err(command_error()) - } - fn __reduce__(&self) -> PyResult<()> { - Err(command_error()) - } - fn __reduce_ex__(&self, _protocol: i32) -> PyResult<()> { - Err(command_error()) - } -} - -impl PyApplicationCommand { - fn action(&self) -> PyResult<&PyApplicationAction> { - self.action - .as_ref() - .ok_or_else(|| PyRuntimeError::new_err("application command has already been consumed")) - } - - fn action_commitment_bytes(&self) -> PyResult<[u8; 32]> { - application_action_commitment(self.action()?) - } -} - -#[pyclass( - name = "ApplicationPlanCommand", - module = "auths._native", - skip_from_py_object -)] -pub struct PyApplicationPlanCommand { - actions: Option>, - receipt_artifacts: Option>, - commitment: [u8; 32], - receipt_bindings: Vec<([u8; 32], [u8; 32], [u8; 32])>, -} - -#[derive(Clone)] -struct ReceiptArtifacts { - proof: Vec, - canonical_action: Vec, - trusted_context: Vec, -} - -#[pymethods] -#[allow(clippy::unused_self)] -impl PyApplicationPlanCommand { - #[getter] - fn count(&self) -> PyResult { - Ok(self.actions()?.len()) - } - - #[getter] - fn plan_commitment<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> { - PyBytes::new(py, &self.commitment) - } - - #[getter] - fn receipt_bindings(&self) -> Vec<(Vec, Vec, Vec)> { - self.receipt_bindings - .iter() - .map(|(action, authority, context)| { - (action.to_vec(), authority.to_vec(), context.to_vec()) - }) - .collect() - } - - fn __repr__(&self) -> &'static str { - if self.actions.is_some() { - "ApplicationPlanCommand()" - } else { - "ApplicationPlanCommand()" - } - } - - fn __copy__(&self) -> PyResult<()> { - Err(plan_command_error()) - } - fn __deepcopy__(&self, _memo: &Bound<'_, PyAny>) -> PyResult<()> { - Err(plan_command_error()) - } - fn __reduce__(&self) -> PyResult<()> { - Err(plan_command_error()) - } -} - -impl PyApplicationPlanCommand { - fn actions(&self) -> PyResult<&[PyApplicationAction]> { - self.actions.as_deref().ok_or_else(|| { - PyRuntimeError::new_err("application plan command has already been consumed") - }) - } -} - -#[pyclass(name = "NativeApplicationPlan", frozen, module = "auths._native")] -pub struct PyNativeApplicationPlan { - commitment: [u8; 32], - members: Vec<[u8; 32]>, -} - -#[pymethods] -impl PyNativeApplicationPlan { - #[getter] - fn commitment<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> { - PyBytes::new(py, &self.commitment) - } - - #[getter] - fn members(&self) -> Vec> { - self.members.iter().map(|value| value.to_vec()).collect() - } -} - -#[pyclass(name = "ApplicationGatewayCall", frozen, module = "auths._native")] -pub struct PyApplicationGatewayCall { - profile_id: String, - profile_version: u16, - media_type: String, - body: Vec, - permission: (String, String), - resource_namespace: String, - audience: String, - budget: Option<(String, u64)>, -} - -#[pymethods] -impl PyApplicationGatewayCall { - #[getter] - fn profile_id(&self) -> &str { - &self.profile_id - } - #[getter] - fn profile_version(&self) -> u16 { - self.profile_version - } - #[getter] - fn media_type(&self) -> &str { - &self.media_type - } - #[getter] - fn body<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> { - PyBytes::new(py, &self.body) - } - #[getter] - fn permission(&self) -> (String, String) { - self.permission.clone() - } - #[getter] - fn resource_namespace(&self) -> &str { - &self.resource_namespace - } - #[getter] - fn audience(&self) -> &str { - &self.audience - } - #[getter] - fn budget(&self) -> Option<(String, u64)> { - self.budget.clone() - } -} - -#[pyfunction] -fn application_action( - profile_id: &str, - profile_version: u16, - media_type: &str, - body: &[u8], - capability: &str, - resource: &str, - budget: Option<(String, u64)>, - resource_namespace: &str, - audience: &str, -) -> PyResult { - let canonical = CanonicalAction::new( - ProfileRef::new( - ProfileId::parse(profile_id).map_err(value_error)?, - profile_version, - ) - .map_err(value_error)?, - MediaType::parse(media_type).map_err(value_error)?, - body.to_vec(), - Permission::new( - CapabilityId::parse(capability).map_err(value_error)?, - ResourceId::parse(resource).map_err(value_error)?, - ), - budget - .map(|(algebra, value)| { - Ok::(BudgetCeiling::new( - BudgetAlgebraId::parse(&algebra).map_err(value_error)?, - value, - )) - }) - .transpose()?, - ) - .map_err(value_error)?; - Ok(PyApplicationAction { - canonical, - resource_namespace: ResourceId::parse(resource_namespace).map_err(value_error)?, - audience: Audience::parse(audience).map_err(value_error)?, - }) -} - -#[pyfunction] -fn application_action_commitment_v1<'py>( - py: Python<'py>, - action: PyRef<'_, PyApplicationAction>, -) -> PyResult> { - Ok(PyBytes::new(py, &application_action_commitment(&action)?)) -} - -#[pyfunction] -fn commit_application_plan( - py: Python<'_>, - actions: Vec>, -) -> PyResult { - if actions.is_empty() || actions.len() > 256 { - return Err(PyValueError::new_err( - "application plan action count is outside native limits", - )); - } - let actions = actions - .iter() - .map(|value| value.borrow(py).clone()) - .collect::>(); - compatible(&actions)?; - let members = actions - .iter() - .map(plan_member) - .collect::>>()?; - let borrowed = members.iter().map(Vec::as_slice).collect::>(); - let first = actions.first().expect("non-empty"); - let commitment = ProfilePlanCommitment::commit( - first.canonical.profile().id().as_str(), - first.canonical.profile().version(), - &borrowed, - ) - .map_err(value_error)?; - Ok(PyNativeApplicationPlan { - commitment: *commitment.plan().as_bytes(), - members: commitment - .members() - .iter() - .map(|value| *value.as_bytes()) - .collect(), - }) -} - -#[pyfunction] -fn prepare_application_action( - action: PyRef<'_, PyApplicationAction>, - actor: PyRef<'_, PyPrincipal>, - terminal_grant: PyRef<'_, PySignedObject>, - challenge: &[u8], - evaluation_time: u64, -) -> PyResult { - let SignedObject::Grant(grant) = &terminal_grant.inner else { - return Err(PyTypeError::new_err( - "terminal grant must be a signed grant", - )); - }; - let challenge: [u8; 32] = challenge - .try_into() - .map_err(|_| PyValueError::new_err("challenge must contain 32 bytes"))?; - let prepared = prepare_profile_action( - action.canonical.clone(), - action.audience.clone(), - actor.inner.clone(), - grant, - challenge, - evaluation_time, - ) - .map_err(value_error)?; - let (canonical, envelope) = prepared.into_parts(); - Ok(PyApplicationActionPreparation { - action: PyApplicationAction { - canonical, - resource_namespace: action.resource_namespace.clone(), - audience: action.audience.clone(), - }, - envelope, - }) -} - -#[pyfunction] -fn authorize_application( - py: Python<'_>, - prepared: PyRef<'_, PyApplicationActionPreparation>, - signed_action: PyRef<'_, PySignedObject>, - grants: Vec>, - grant_evidence: Vec)>>, - action_evidence: Vec<(String, String, Vec)>, - context: PyRef<'_, PyTrustedContext>, -) -> PyResult<(NativeVerificationResult, Option)> { - if grants.len() != grant_evidence.len() { - return Err(PyValueError::new_err( - "each grant requires one evidence collection", - )); - } - let SignedObject::Action(action) = &signed_action.inner else { - return Err(PyTypeError::new_err("signed action must be an action")); - }; - if action.envelope() != &prepared.envelope { - return Err(PyValueError::new_err( - "signed action does not match its native preparation", - )); - } - let mut builder = WorkflowProofBuilder::new(); - for (grant, evidence) in grants.iter().zip(grant_evidence) { - let grant = grant.borrow(py); - let SignedObject::Grant(grant) = &grant.inner else { - return Err(PyTypeError::new_err("grant chain contains a non-grant")); - }; - let index = builder.push_grant(grant.clone()).map_err(value_error)?; - for (evidence_type, media_type, bytes) in evidence { - builder - .bind_grant_evidence(index, evidence_object(&evidence_type, &media_type, bytes)?) - .map_err(value_error)?; - } - } - for (evidence_type, media_type, bytes) in action_evidence { - builder - .bind_action_evidence(evidence_object(&evidence_type, &media_type, bytes)?) - .map_err(value_error)?; - } - let artifacts = builder - .finish(action, &prepared.action.canonical, &context.inner) - .map_err(value_error)?; - let proof = auths_codec::encode_bundle(artifacts.proof()).map_err(value_error)?; - let canonical = - auths_codec::encode_canonical_action(&prepared.action.canonical).map_err(value_error)?; - let context = auths_codec::encode_verifier_context(artifacts.context()).map_err(value_error)?; - let authority_commitment = *auths_codec::proof_digest(artifacts.proof()) - .map_err(value_error)? - .as_bytes(); - let context_commitment = *auths_codec::context_digest(artifacts.context()) - .map_err(value_error)? - .as_bytes(); - let sealed = verify_sealed(&proof, &canonical, &context)?; - let command = sealed - .action() - .map(|verified| { - if verified.canonical_action() != &prepared.action.canonical { - return Err(PyValueError::new_err( - "verified application action changed meaning", - )); - } - Ok(PyApplicationCommand { - action: Some(prepared.action.clone()), - receipt_artifacts: Some(ReceiptArtifacts { - proof: proof.clone(), - canonical_action: canonical.clone(), - trusted_context: context.clone(), - }), - authority_commitment, - context_commitment, - }) - }) - .transpose()?; - Ok((native_result(py, sealed)?, command)) -} - -#[pyfunction] -fn seal_application_plan_command( - py: Python<'_>, - commands: Vec>, - expected_profile_id: &str, - expected_profile_version: u16, - expected_commitment: &[u8], -) -> PyResult { - if commands.is_empty() || commands.len() > 256 { - return Err(PyValueError::new_err( - "application plan command count is outside native limits", - )); - } - let expected: [u8; 32] = expected_commitment - .try_into() - .map_err(|_| PyValueError::new_err("plan commitment must contain 32 bytes"))?; - let mut identities = HashSet::with_capacity(commands.len()); - if commands - .iter() - .any(|command| !identities.insert(command.as_ptr() as usize)) - { - return Err(PyValueError::new_err( - "application plan contains duplicate command handles", - )); - } - let actions = commands - .iter() - .map(|command| { - let command = command.borrow(py); - let action = command.action()?; - if action.canonical.profile().id().as_str() != expected_profile_id - || action.canonical.profile().version() != expected_profile_version - { - return Err(PyTypeError::new_err( - "application command belongs to another profile", - )); - } - Ok(action.clone()) - }) - .collect::>>()?; - let members = actions - .iter() - .map(plan_member) - .collect::>>()?; - let borrowed = members.iter().map(Vec::as_slice).collect::>(); - let commitment = - ProfilePlanCommitment::commit(expected_profile_id, expected_profile_version, &borrowed) - .map_err(value_error)?; - if commitment.plan().as_bytes() != &expected { - return Err(PyValueError::new_err( - "verified commands do not match the application plan", - )); - } - let receipt_bindings = commands - .iter() - .map(|command| { - let command = command.borrow(py); - Ok(( - command.action_commitment_bytes()?, - command.authority_commitment, - command.context_commitment, - )) - }) - .collect::>>()?; - let receipt_artifacts = commands - .iter() - .map(|command| { - command.borrow(py).receipt_artifacts.clone().ok_or_else(|| { - PyRuntimeError::new_err("application command has already been consumed") - }) - }) - .collect::>>()?; - for command in &commands { - let mut command = command.borrow_mut(py); - command.action.take(); - command.receipt_artifacts.take(); - } - Ok(PyApplicationPlanCommand { - actions: Some(actions), - receipt_artifacts: Some(receipt_artifacts), - commitment: expected, - receipt_bindings, - }) -} - -#[pyfunction] -fn consume_application_command( - mut command: PyRefMut<'_, PyApplicationCommand>, - expected_profile_id: &str, - expected_profile_version: u16, -) -> PyResult { - let action = command.action()?; - matching_profile(action, expected_profile_id, expected_profile_version)?; - let action = command - .action - .take() - .ok_or_else(|| PyRuntimeError::new_err("application command has already been consumed"))?; - command.receipt_artifacts.take(); - Ok(gateway_call(action)) -} - -#[pyfunction] -fn consume_application_plan_command( - mut command: PyRefMut<'_, PyApplicationPlanCommand>, - expected_profile_id: &str, - expected_profile_version: u16, -) -> PyResult> { - for action in command.actions()? { - matching_profile(action, expected_profile_id, expected_profile_version)?; - } - command.receipt_artifacts.take(); - command - .actions - .take() - .ok_or_else(|| { - PyRuntimeError::new_err("application plan command has already been consumed") - })? - .into_iter() - .map(|value| Ok(gateway_call(value))) - .collect() -} - -#[pyfunction] -#[allow(clippy::too_many_arguments)] -fn prepare_application_command_decision_receipt_v1( - command: PyRef<'_, PyApplicationCommand>, - decided_at: u64, - verifier: &str, - verification_method: &str, - suite: &str, -) -> PyResult { - let artifacts = command - .receipt_artifacts - .as_ref() - .ok_or_else(|| PyRuntimeError::new_err("application command has already been consumed"))?; - prepare_decision( - &artifacts.proof, - &artifacts.canonical_action, - &artifacts.trusted_context, - decided_at, - verifier, - verification_method, - suite, - ) -} - -#[pyfunction] -#[allow(clippy::too_many_arguments)] -fn prepare_application_plan_decision_receipts_v1( - command: PyRef<'_, PyApplicationPlanCommand>, - decided_at: u64, - verifier: &str, - verification_method: &str, - suite: &str, -) -> PyResult> { - command - .receipt_artifacts - .as_ref() - .ok_or_else(|| { - PyRuntimeError::new_err("application plan command has already been consumed") - })? - .iter() - .map(|artifacts| { - prepare_decision( - &artifacts.proof, - &artifacts.canonical_action, - &artifacts.trusted_context, - decided_at, - verifier, - verification_method, - suite, - ) - }) - .collect() -} - -fn compatible(actions: &[PyApplicationAction]) -> PyResult<()> { - let first = actions - .first() - .ok_or_else(|| PyValueError::new_err("application plan is empty"))?; - if actions.iter().any(|action| { - action.canonical.profile() != first.canonical.profile() - || action.resource_namespace != first.resource_namespace - || action.audience != first.audience - || action - .canonical - .requested_budget() - .map(BudgetCeiling::algebra) - != first - .canonical - .requested_budget() - .map(BudgetCeiling::algebra) - }) { - return Err(PyValueError::new_err( - "application plan members have incompatible authority", - )); - } - actions.iter().try_fold(0_u64, |total, action| { - total - .checked_add( - action - .canonical - .requested_budget() - .map_or(0, BudgetCeiling::value), - ) - .ok_or_else(|| { - PyValueError::new_err("application plan aggregate budget exceeds bounds") - }) - })?; - Ok(()) -} - -fn matching_profile(action: &PyApplicationAction, id: &str, version: u16) -> PyResult<()> { - if action.canonical.profile().id().as_str() != id - || action.canonical.profile().version() != version - { - return Err(PyTypeError::new_err( - "application command belongs to another profile", - )); - } - Ok(()) -} - -fn plan_member(action: &PyApplicationAction) -> PyResult> { - ProfilePlanMember::encode( - &action.canonical, - &action.resource_namespace, - &action.audience, - ) - .map_err(value_error) -} - -fn application_action_commitment(action: &PyApplicationAction) -> PyResult<[u8; 32]> { - let encoded = auths_codec::encode_canonical_action(&action.canonical).map_err(value_error)?; - Ok( - *auths_codec::domain_commitment("auths.canonical-action.v1", &encoded) - .map_err(value_error)? - .as_bytes(), - ) -} - -fn gateway_call(action: PyApplicationAction) -> PyApplicationGatewayCall { - PyApplicationGatewayCall { - profile_id: action.canonical.profile().id().as_str().to_owned(), - profile_version: action.canonical.profile().version(), - media_type: action.canonical.media_type().as_str().to_owned(), - body: action.canonical.body().to_vec(), - permission: ( - action - .canonical - .permission() - .capability() - .as_str() - .to_owned(), - action.canonical.permission().resource().as_str().to_owned(), - ), - resource_namespace: action.resource_namespace.as_str().to_owned(), - audience: action.audience.as_str().to_owned(), - budget: action - .canonical - .requested_budget() - .map(|value| (value.algebra().as_str().to_owned(), value.value())), - } -} - -fn evidence_object( - evidence_type: &str, - media_type: &str, - bytes: Vec, -) -> PyResult { - address_evidence( - EvidenceTypeId::parse(evidence_type).map_err(value_error)?, - MediaType::parse(media_type).map_err(value_error)?, - bytes, - ) - .map_err(value_error) -} - -fn command_error() -> PyErr { - PyTypeError::new_err("ApplicationCommand is a non-copyable native capability") -} - -fn plan_command_error() -> PyErr { - PyTypeError::new_err("ApplicationPlanCommand is a non-copyable native capability") -} - -pub fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { - module.add_class::()?; - module.add_class::()?; - module.add_class::()?; - module.add_class::()?; - module.add_class::()?; - module.add_class::()?; - module.add_function(wrap_pyfunction!(application_action, module)?)?; - module.add_function(wrap_pyfunction!(application_action_commitment_v1, module)?)?; - module.add_function(wrap_pyfunction!(commit_application_plan, module)?)?; - module.add_function(wrap_pyfunction!(prepare_application_action, module)?)?; - module.add_function(wrap_pyfunction!(authorize_application, module)?)?; - module.add_function(wrap_pyfunction!(seal_application_plan_command, module)?)?; - module.add_function(wrap_pyfunction!(consume_application_command, module)?)?; - module.add_function(wrap_pyfunction!(consume_application_plan_command, module)?)?; - module.add_function(wrap_pyfunction!( - prepare_application_command_decision_receipt_v1, - module - )?)?; - module.add_function(wrap_pyfunction!( - prepare_application_plan_decision_receipts_v1, - module - )?)?; - Ok(()) -} diff --git a/bindings/python/src/domains.rs b/bindings/python/src/domains.rs deleted file mode 100644 index 1cd09e4a..00000000 --- a/bindings/python/src/domains.rs +++ /dev/null @@ -1,109 +0,0 @@ -use auths_profile_api::ActionProfile; -use auths_profile_domains::{EdgeAction, EdgeProfile, reference_canonicalize_edge}; -use pyo3::{exceptions::PyValueError, prelude::*, types::PyBytes}; - -#[pyclass(name = "DomainActionProjection", frozen, module = "auths._native")] -pub struct PyDomainActionProjection { - media_type: String, - body: Vec, - capability: String, - resource: String, - budget: Option<(String, u64)>, - review_title: String, - review_fields: Vec<(String, String)>, -} - -#[pymethods] -impl PyDomainActionProjection { - #[getter] - fn media_type(&self) -> &str { - &self.media_type - } - - #[getter] - fn body<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> { - PyBytes::new(py, &self.body) - } - - #[getter] - fn capability(&self) -> &str { - &self.capability - } - - #[getter] - fn resource(&self) -> &str { - &self.resource - } - - #[getter] - fn budget(&self) -> Option<(String, u64)> { - self.budget.clone() - } - - #[getter] - fn review_title(&self) -> &str { - &self.review_title - } - - #[getter] - fn review_fields(&self) -> Vec<(String, String)> { - self.review_fields.clone() - } -} - -#[pyfunction] -fn canonicalize_edge_action_v1( - fleet: String, - device: String, - command: String, - sequence: u64, - state_digest: Option, -) -> PyResult { - let input = serde_json::to_vec(&EdgeAction::new( - fleet, - device, - command, - sequence, - state_digest, - )) - .map_err(value_error)?; - project_edge(&input) -} - -#[pyfunction] -fn parse_canonical_edge_action_v1(body: &[u8]) -> PyResult { - let projection = project_edge(body)?; - if projection.body != body { - return Err(PyValueError::new_err("edge action is not canonical")); - } - Ok(projection) -} - -fn project_edge(input: &[u8]) -> PyResult { - let action = reference_canonicalize_edge(input).map_err(value_error)?; - let review = EdgeProfile::default() - .review_display(&action) - .map_err(value_error)?; - Ok(PyDomainActionProjection { - media_type: action.media_type().as_str().to_owned(), - body: action.body().to_vec(), - capability: action.permission().capability().as_str().to_owned(), - resource: action.permission().resource().as_str().to_owned(), - budget: action - .requested_budget() - .map(|value| (value.algebra().as_str().to_owned(), value.value())), - review_title: review.title().to_owned(), - review_fields: review.fields().to_vec(), - }) -} - -fn value_error(error: impl core::fmt::Display) -> PyErr { - PyValueError::new_err(error.to_string()) -} - -pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { - module.add_class::()?; - module.add_function(wrap_pyfunction!(canonicalize_edge_action_v1, module)?)?; - module.add_function(wrap_pyfunction!(parse_canonical_edge_action_v1, module)?)?; - Ok(()) -} diff --git a/bindings/python/src/http.rs b/bindings/python/src/http.rs deleted file mode 100644 index 595f9bf9..00000000 --- a/bindings/python/src/http.rs +++ /dev/null @@ -1,719 +0,0 @@ -#![allow(clippy::needless_pass_by_value, clippy::too_many_arguments)] - -use crate::ReviewProjection; -use crate::authoring::{ - PyPrincipal, PySignedObject, PyTrustedContext, PyUnsignedObject, SignedObject, UnsignedObject, - value_error, -}; -use crate::result::{NativeVerificationResult, native_result, verify_sealed}; -use auths_author::{ - ProfilePlanCommitment, ProfilePlanMember, WorkflowProofBuilder, address_evidence, - prepare_profile_action, -}; -use auths_model::{Audience, EvidenceTypeId, MediaType, ResourceId}; -use auths_profile_api::ActionProfile; -use auths_profile_domains::{HttpAction, HttpCommand, HttpProfile}; -use pyo3::{ - exceptions::{PyRuntimeError, PyTypeError, PyValueError}, - prelude::*, - types::PyBytes, -}; -use std::collections::{BTreeMap, HashSet}; - -const PROFILE_ID: &str = "auths.http"; -const PROFILE_VERSION: u16 = 1; - -#[pyclass( - name = "HttpCall", - frozen, - module = "auths._native", - skip_from_py_object -)] -pub struct PyHttpCall { - inner: HttpAction, -} - -#[pymethods] -impl PyHttpCall { - #[getter] - fn method(&self) -> &str { - self.inner.method() - } - - #[getter] - fn scheme(&self) -> &str { - self.inner.scheme() - } - - #[getter] - fn authority(&self) -> &str { - self.inner.authority() - } - - #[getter] - fn path(&self) -> &str { - self.inner.path() - } -} - -#[pyclass( - name = "HttpAction", - frozen, - module = "auths._native", - skip_from_py_object -)] -pub struct PyHttpPreparedAction { - canonical: auths_model::CanonicalAction, - envelope: auths_model::ActionEnvelope, - audience: String, - review_title: String, - review_fields: Vec<(String, String)>, -} - -#[pymethods] -impl PyHttpPreparedAction { - #[getter] - fn unsigned(&self) -> PyUnsignedObject { - PyUnsignedObject { - inner: UnsignedObject::Action(self.envelope.clone()), - } - } - - #[getter] - fn audience(&self) -> &str { - &self.audience - } - - #[getter] - fn review_title(&self) -> &str { - &self.review_title - } - - #[getter] - fn review_fields(&self) -> Vec<(String, String)> { - self.review_fields.clone() - } -} - -#[pyclass( - name = "NativeHttpPlan", - frozen, - module = "auths._native", - skip_from_py_object -)] -pub struct PyNativeHttpPlan { - commitment: [u8; 32], - members: Vec<[u8; 32]>, - permissions: Vec<(String, String)>, - resource_namespaces: Vec, - audiences: Vec, -} - -#[pymethods] -impl PyNativeHttpPlan { - #[getter] - fn commitment<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> { - PyBytes::new(py, &self.commitment) - } - - #[getter] - fn members(&self) -> Vec> { - self.members.iter().map(|value| value.to_vec()).collect() - } - - #[getter] - fn permissions(&self) -> Vec<(String, String)> { - self.permissions.clone() - } - - #[getter] - fn resource_namespaces(&self) -> Vec { - self.resource_namespaces.clone() - } - - #[getter] - fn audiences(&self) -> Vec { - self.audiences.clone() - } -} - -#[pyclass(name = "HttpCommand", module = "auths._native", skip_from_py_object)] -pub struct PyHttpCommand { - inner: Option, - authority_commitment: [u8; 32], - context_commitment: [u8; 32], -} - -#[pymethods] -#[allow(clippy::unused_self)] -impl PyHttpCommand { - #[getter] - fn action_commitment<'py>(&self, py: Python<'py>) -> PyResult> { - Ok(PyBytes::new(py, &self.action_commitment_bytes()?)) - } - - #[getter] - fn authority_commitment<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> { - PyBytes::new(py, &self.authority_commitment) - } - - #[getter] - fn context_commitment<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> { - PyBytes::new(py, &self.context_commitment) - } - - fn __repr__(&self) -> &'static str { - if self.inner.is_some() { - "HttpCommand()" - } else { - "HttpCommand()" - } - } - - fn __copy__(&self) -> PyResult<()> { - Err(command_error()) - } - - fn __deepcopy__(&self, _memo: &Bound<'_, PyAny>) -> PyResult<()> { - Err(command_error()) - } - - fn __reduce__(&self) -> PyResult<()> { - Err(command_error()) - } - - fn __reduce_ex__(&self, _protocol: i32) -> PyResult<()> { - Err(command_error()) - } -} - -impl PyHttpCommand { - fn command(&self) -> PyResult<&HttpCommand> { - self.inner - .as_ref() - .ok_or_else(|| PyRuntimeError::new_err("HTTP command has already been consumed")) - } - - fn action_commitment_bytes(&self) -> PyResult<[u8; 32]> { - canonical_http_commitment(self.command()?.action()) - } -} - -#[pyclass( - name = "HttpPlanCommand", - module = "auths._native", - skip_from_py_object -)] -pub struct PyHttpPlanCommand { - commands: Option>, - commitment: [u8; 32], - receipt_bindings: Vec<([u8; 32], [u8; 32], [u8; 32])>, -} - -#[pymethods] -#[allow(clippy::unused_self)] -impl PyHttpPlanCommand { - #[getter] - fn count(&self) -> PyResult { - Ok(self.commands()?.len()) - } - - #[getter] - fn plan_commitment<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> { - PyBytes::new(py, &self.commitment) - } - - #[getter] - fn receipt_bindings(&self) -> Vec<(Vec, Vec, Vec)> { - self.receipt_bindings - .iter() - .map(|(action, authority, context)| { - (action.to_vec(), authority.to_vec(), context.to_vec()) - }) - .collect() - } - - fn __repr__(&self) -> &'static str { - if self.commands.is_some() { - "HttpPlanCommand()" - } else { - "HttpPlanCommand()" - } - } - - fn __copy__(&self) -> PyResult<()> { - Err(plan_command_error()) - } - - fn __deepcopy__(&self, _memo: &Bound<'_, PyAny>) -> PyResult<()> { - Err(plan_command_error()) - } - - fn __reduce__(&self) -> PyResult<()> { - Err(plan_command_error()) - } -} - -impl PyHttpPlanCommand { - fn commands(&self) -> PyResult<&[HttpCommand]> { - self.commands - .as_deref() - .ok_or_else(|| PyRuntimeError::new_err("HTTP plan command has already been consumed")) - } -} - -#[pyclass(name = "HttpGatewayRequest", frozen, module = "auths._native")] -pub struct PyHttpGatewayRequest { - method: String, - scheme: String, - authority: String, - path: String, - query: Vec<(String, Vec)>, - headers: Vec<(String, String)>, - content_type: Option, - body_digest: Option, -} - -#[pymethods] -impl PyHttpGatewayRequest { - #[getter] - fn method(&self) -> &str { - &self.method - } - #[getter] - fn scheme(&self) -> &str { - &self.scheme - } - #[getter] - fn authority(&self) -> &str { - &self.authority - } - #[getter] - fn path(&self) -> &str { - &self.path - } - #[getter] - fn query(&self) -> Vec<(String, Vec)> { - self.query.clone() - } - #[getter] - fn headers(&self) -> Vec<(String, String)> { - self.headers.clone() - } - #[getter] - fn content_type(&self) -> Option<&str> { - self.content_type.as_deref() - } - #[getter] - fn body_digest(&self) -> Option<&str> { - self.body_digest.as_deref() - } -} - -#[pyfunction] -fn http_call( - method: String, - scheme: String, - authority: String, - path: String, - query: Vec<(String, Vec)>, - headers: Vec<(String, String)>, - content_type: Option, - body_digest: Option, -) -> PyResult { - let call = HttpAction::new( - method, - scheme, - authority, - path, - query.into_iter().collect::>(), - headers.into_iter().collect::>(), - content_type, - body_digest, - ); - canonical_http(&call)?; - Ok(PyHttpCall { inner: call }) -} - -#[pyfunction] -fn review_http_call<'py>( - py: Python<'py>, - call: PyRef<'_, PyHttpCall>, -) -> PyResult> { - let canonical = canonical_http(&call.inner)?; - let display = HttpProfile::default() - .review_display(&canonical) - .map_err(value_error)?; - let commitment = canonical_http_commitment(&call.inner)?; - Ok(( - display.title().to_owned(), - display.fields().to_vec(), - PyBytes::new(py, &commitment), - )) -} - -#[pyfunction] -fn commit_http_plan(calls: Vec>, py: Python<'_>) -> PyResult { - if calls.is_empty() || calls.len() > 256 { - return Err(PyValueError::new_err( - "HTTP plan action count is outside native limits", - )); - } - let calls = calls - .iter() - .map(|call| call.borrow(py).inner.clone()) - .collect::>(); - let origin_value = origin(calls.first().expect("non-empty")); - if calls.iter().any(|call| origin(call) != origin_value) { - return Err(PyValueError::new_err( - "HTTP plan actions must share one origin", - )); - } - let members = calls - .iter() - .map(canonical_plan_member) - .collect::>>()?; - let borrowed = members.iter().map(Vec::as_slice).collect::>(); - let commitment = ProfilePlanCommitment::commit(PROFILE_ID, PROFILE_VERSION, &borrowed) - .map_err(value_error)?; - let permissions = calls - .iter() - .map(|call| { - let canonical = canonical_http(call)?; - Ok(( - canonical.permission().capability().as_str().to_owned(), - canonical.permission().resource().as_str().to_owned(), - )) - }) - .collect::>>()?; - Ok(PyNativeHttpPlan { - commitment: *commitment.plan().as_bytes(), - members: commitment - .members() - .iter() - .map(|value| *value.as_bytes()) - .collect(), - permissions, - resource_namespaces: vec![origin_value.clone()], - audiences: vec![origin_value], - }) -} - -#[pyfunction] -fn prepare_http_action( - call: PyRef<'_, PyHttpCall>, - actor: PyRef<'_, PyPrincipal>, - terminal_grant: PyRef<'_, PySignedObject>, - challenge: &[u8], - evaluation_time: u64, -) -> PyResult { - let SignedObject::Grant(terminal_grant) = &terminal_grant.inner else { - return Err(PyTypeError::new_err( - "terminal grant must be a signed grant", - )); - }; - let canonical = canonical_http(&call.inner)?; - let display = HttpProfile::default() - .review_display(&canonical) - .map_err(value_error)?; - let audience = Audience::parse(&origin(&call.inner)).map_err(value_error)?; - let challenge: [u8; 32] = challenge - .try_into() - .map_err(|_| PyValueError::new_err("challenge must contain 32 bytes"))?; - let prepared = prepare_profile_action( - canonical, - audience.clone(), - actor.inner.clone(), - terminal_grant, - challenge, - evaluation_time, - ) - .map_err(value_error)?; - let (canonical, envelope) = prepared.into_parts(); - Ok(PyHttpPreparedAction { - canonical, - envelope, - audience: audience.to_string(), - review_title: display.title().to_owned(), - review_fields: display.fields().to_vec(), - }) -} - -#[pyfunction] -fn authorize_http( - py: Python<'_>, - prepared: PyRef<'_, PyHttpPreparedAction>, - signed_action: PyRef<'_, PySignedObject>, - grants: Vec>, - grant_evidence: Vec)>>, - action_evidence: Vec<(String, String, Vec)>, - context: PyRef<'_, PyTrustedContext>, -) -> PyResult<(NativeVerificationResult, Option)> { - if grants.len() != grant_evidence.len() { - return Err(PyValueError::new_err( - "each grant requires one evidence collection", - )); - } - let SignedObject::Action(action) = &signed_action.inner else { - return Err(PyTypeError::new_err("signed action must be an action")); - }; - if action.envelope() != &prepared.envelope { - return Err(PyValueError::new_err( - "signed action does not match its native preparation", - )); - } - let mut builder = WorkflowProofBuilder::new(); - for (grant, evidence) in grants.iter().zip(grant_evidence) { - let grant = grant.borrow(py); - let SignedObject::Grant(grant) = &grant.inner else { - return Err(PyTypeError::new_err("grant chain contains a non-grant")); - }; - let index = builder.push_grant(grant.clone()).map_err(value_error)?; - for (evidence_type, media_type, bytes) in evidence { - builder - .bind_grant_evidence(index, evidence_object(&evidence_type, &media_type, bytes)?) - .map_err(value_error)?; - } - } - for (evidence_type, media_type, bytes) in action_evidence { - builder - .bind_action_evidence(evidence_object(&evidence_type, &media_type, bytes)?) - .map_err(value_error)?; - } - let artifacts = builder - .finish(action, &prepared.canonical, &context.inner) - .map_err(value_error)?; - let proof = auths_codec::encode_bundle(artifacts.proof()).map_err(value_error)?; - let canonical = - auths_codec::encode_canonical_action(&prepared.canonical).map_err(value_error)?; - let context = auths_codec::encode_verifier_context(artifacts.context()).map_err(value_error)?; - let authority_commitment = *auths_codec::proof_digest(artifacts.proof()) - .map_err(value_error)? - .as_bytes(); - let context_commitment = *auths_codec::context_digest(artifacts.context()) - .map_err(value_error)? - .as_bytes(); - let sealed = verify_sealed(&proof, &canonical, &context)?; - let command = sealed - .action() - .map(|action| HttpProfile::default().decode_verified(action)) - .transpose() - .map_err(value_error)? - .map(|inner| PyHttpCommand { - inner: Some(inner), - authority_commitment, - context_commitment, - }); - Ok((native_result(py, sealed)?, command)) -} - -#[pyfunction] -fn inspect_http_action<'py>( - py: Python<'py>, - action: PyRef<'_, PyHttpPreparedAction>, -) -> PyResult> { - let bytes = auths_codec::encode_canonical_action(&action.canonical).map_err(value_error)?; - Ok(PyBytes::new(py, &bytes)) -} - -#[pyfunction] -fn consume_http_command( - mut command: PyRefMut<'_, PyHttpCommand>, - expected_origin: &str, -) -> PyResult { - if origin(command.command()?.action()) != expected_origin { - return Err(PyTypeError::new_err( - "HTTP command does not belong to this gateway", - )); - } - let command = command - .inner - .take() - .ok_or_else(|| PyRuntimeError::new_err("HTTP command has already been consumed"))?; - Ok(gateway_request(command.action())) -} - -#[pyfunction] -fn seal_http_plan_command( - py: Python<'_>, - commands: Vec>, - expected_origin: &str, - expected_commitment: &[u8], -) -> PyResult { - if commands.is_empty() || commands.len() > 256 { - return Err(PyValueError::new_err( - "HTTP plan command count is outside native limits", - )); - } - let expected: [u8; 32] = expected_commitment - .try_into() - .map_err(|_| PyValueError::new_err("plan commitment must contain 32 bytes"))?; - let mut identities = HashSet::with_capacity(commands.len()); - if commands - .iter() - .any(|command| !identities.insert(command.as_ptr() as usize)) - { - return Err(PyValueError::new_err( - "HTTP plan contains a duplicate command handle", - )); - } - let members = commands - .iter() - .map(|command| { - let command = command.borrow(py); - if origin(command.command()?.action()) != expected_origin { - return Err(PyTypeError::new_err( - "HTTP command does not belong to this plan", - )); - } - canonical_plan_member(command.command()?.action()) - }) - .collect::>>()?; - let borrowed = members.iter().map(Vec::as_slice).collect::>(); - let commitment = ProfilePlanCommitment::commit(PROFILE_ID, PROFILE_VERSION, &borrowed) - .map_err(value_error)?; - if commitment.plan().as_bytes() != &expected { - return Err(PyValueError::new_err( - "verified commands do not match the exact HTTP plan", - )); - } - let receipt_bindings = commands - .iter() - .map(|command| { - let command = command.borrow(py); - Ok(( - command.action_commitment_bytes()?, - command.authority_commitment, - command.context_commitment, - )) - }) - .collect::>>()?; - let inner = - commands - .iter() - .map(|command| { - command.borrow_mut(py).inner.take().ok_or_else(|| { - PyRuntimeError::new_err("HTTP command has already been consumed") - }) - }) - .collect::>>()?; - Ok(PyHttpPlanCommand { - commands: Some(inner), - commitment: expected, - receipt_bindings, - }) -} - -#[pyfunction] -fn consume_http_plan_command( - mut command: PyRefMut<'_, PyHttpPlanCommand>, - expected_origin: &str, -) -> PyResult> { - if command - .commands()? - .iter() - .any(|value| origin(value.action()) != expected_origin) - { - return Err(PyTypeError::new_err( - "HTTP plan command does not belong to this gateway", - )); - } - command - .commands - .take() - .ok_or_else(|| PyRuntimeError::new_err("HTTP plan command has already been consumed"))? - .iter() - .map(|value| Ok(gateway_request(value.action()))) - .collect() -} - -fn canonical_http(action: &HttpAction) -> PyResult { - let bytes = serde_json_canonicalizer::to_vec(action).map_err(value_error)?; - HttpProfile::default() - .canonicalize(&bytes) - .map_err(value_error) -} - -fn canonical_http_commitment(action: &HttpAction) -> PyResult<[u8; 32]> { - let encoded = - auths_codec::encode_canonical_action(&canonical_http(action)?).map_err(value_error)?; - Ok( - *auths_codec::domain_commitment("auths.canonical-action.v1", &encoded) - .map_err(value_error)? - .as_bytes(), - ) -} - -fn canonical_plan_member(action: &HttpAction) -> PyResult> { - ProfilePlanMember::encode( - &canonical_http(action)?, - &ResourceId::parse(&origin(action)).map_err(value_error)?, - &Audience::parse(&origin(action)).map_err(value_error)?, - ) - .map_err(value_error) -} - -fn origin(action: &HttpAction) -> String { - format!("{}://{}", action.scheme(), action.authority()) -} - -fn gateway_request(action: &HttpAction) -> PyHttpGatewayRequest { - PyHttpGatewayRequest { - method: action.method().to_owned(), - scheme: action.scheme().to_owned(), - authority: action.authority().to_owned(), - path: action.path().to_owned(), - query: action - .query() - .iter() - .map(|(key, value)| (key.clone(), value.clone())) - .collect(), - headers: action - .headers() - .iter() - .map(|(key, value)| (key.clone(), value.clone())) - .collect(), - content_type: action.content_type().map(str::to_owned), - body_digest: action.body_digest().map(str::to_owned), - } -} - -fn evidence_object( - evidence_type: &str, - media_type: &str, - bytes: Vec, -) -> PyResult { - address_evidence( - EvidenceTypeId::parse(evidence_type).map_err(value_error)?, - MediaType::parse(media_type).map_err(value_error)?, - bytes, - ) - .map_err(value_error) -} - -fn command_error() -> PyErr { - PyTypeError::new_err("HttpCommand is a non-copyable native capability") -} - -fn plan_command_error() -> PyErr { - PyTypeError::new_err("HttpPlanCommand is a non-copyable native capability") -} - -pub fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { - module.add_class::()?; - module.add_class::()?; - module.add_class::()?; - module.add_class::()?; - module.add_class::()?; - module.add_class::()?; - module.add_function(wrap_pyfunction!(http_call, module)?)?; - module.add_function(wrap_pyfunction!(review_http_call, module)?)?; - module.add_function(wrap_pyfunction!(commit_http_plan, module)?)?; - module.add_function(wrap_pyfunction!(prepare_http_action, module)?)?; - module.add_function(wrap_pyfunction!(authorize_http, module)?)?; - module.add_function(wrap_pyfunction!(inspect_http_action, module)?)?; - module.add_function(wrap_pyfunction!(consume_http_command, module)?)?; - module.add_function(wrap_pyfunction!(seal_http_plan_command, module)?)?; - module.add_function(wrap_pyfunction!(consume_http_plan_command, module)?)?; - Ok(()) -} diff --git a/bindings/typescript/api/public-api-allowances.json b/bindings/typescript/api/public-api-allowances.json new file mode 100644 index 00000000..6c8922f1 --- /dev/null +++ b/bindings/typescript/api/public-api-allowances.json @@ -0,0 +1,16 @@ +{ + "_comment": [ + "Declared exceptions to the public-API shape rules enforced by", + "tools/public-api.mjs --shape.", + "", + "mirror one entry point exporting both `X` and `X` of the same kind.", + "homonym one name exported from two entry points resolving to different declarations.", + "", + "Every entry needs a reason. An allowance that stops matching anything fails the", + "check, so stale exemptions cannot accumulate silently. Prefer fixing the surface", + "over adding an entry here: each one is a place a reader must be told which of two", + "similarly-named things they want." + ], + "mirror": [], + "homonym": [] +} diff --git a/bindings/typescript/tools/public-api.mjs b/bindings/typescript/tools/public-api.mjs index e42f06b6..d7f14051 100644 --- a/bindings/typescript/tools/public-api.mjs +++ b/bindings/typescript/tools/public-api.mjs @@ -46,6 +46,7 @@ const lines = [ "# Installed @auths-dev/sdk public API v1", `# declaration-sha256 ${declarationDigest.digest("hex")}`, ]; +const records = []; for (const [subpath, filename] of entries) { const source = program.getSourceFile(filename); if (source === undefined) throw new Error(`missing built declaration ${filename}`); @@ -60,6 +61,17 @@ for (const [subpath, filename] of entries) { if (symbol.flags & ts.SymbolFlags.Type) kinds.push("type"); if (symbol.flags & ts.SymbolFlags.Namespace) kinds.push("namespace"); lines.push(`${subpath}\t${exported.name}\t${kinds.join("+") || "alias"}`); + const declaration = symbol.declarations?.[0]; + records.push({ + subpath, + name: exported.name, + kinds: kinds.join("+") || "alias", + // Declaration identity: two exports sharing this are the same thing + // re-exported, which is legitimate. Differing identity under one name is not. + declaration: declaration + ? `${normalizedPath(declaration.getSourceFile().fileName).replace(/^.*?dist\//, "dist/")}:${declaration.pos}` + : "unknown", + }); } } const actual = `${lines.join("\n")}\n`; @@ -71,6 +83,87 @@ if (process.argv.includes("--print")) { process.stdout.write(actual); process.exit(0); } + +// --shape: structural duplication checks. +// +// The snapshot comparison below proves the surface has not CHANGED. It cannot +// notice that the surface was wrong to begin with. These two rules catch the +// duplication classes that a name-and-kind snapshot is blind to: +// +// mirror one entry point exporting both `X` and `X` of the same kind +// -- a second parallel API grown alongside the first +// homonym one name exported from two entry points resolving to DIFFERENT +// declarations -- two things wearing one name +// +// Legitimate cases are declared in api/public-api-allowances.json with a reason. +// An allowance that no longer matches anything fails as loudly as a violation, +// so stale exemptions cannot accumulate. +if (process.argv.includes("--shape")) { + const allowances = JSON.parse( + await readFile(new URL("../api/public-api-allowances.json", import.meta.url), "utf8"), + ); + const used = new Set(); + const allowed = (kind, key) => { + const hit = allowances[kind]?.find((item) => item.key === key); + if (hit) used.add(`${kind}:${key}`); + return hit !== undefined; + }; + const violations = []; + + const byName = new Map(records.map((item) => [`${item.subpath}\t${item.name}`, item])); + for (const record of records) { + for (const other of records) { + if (other.subpath !== record.subpath) continue; + if (other.name === record.name || !other.name.endsWith(record.name)) continue; + const prefix = other.name.slice(0, -record.name.length); + // A prefix must be a capitalised word, or `Foo` matches every `*Foo`. + if (!/^[A-Z][A-Za-z]*$/.test(prefix)) continue; + if (byName.get(`${record.subpath}\t${record.name}`)?.kinds !== other.kinds) continue; + const key = `${record.subpath}\t${prefix}${record.name}`; + if (allowed("mirror", key)) continue; + violations.push( + `mirror: ${record.subpath} exports both '${record.name}' and '${other.name}' as ${other.kinds}. ` + + `A prefixed twin of an existing export is a second API, not a variant. ` + + `Give it a distinct entry point and an unprefixed name, or declare it in api/public-api-allowances.json.`, + ); + } + } + + const byBareName = new Map(); + for (const record of records) { + const list = byBareName.get(record.name) ?? []; + list.push(record); + byBareName.set(record.name, list); + } + for (const [name, list] of byBareName) { + const declarations = new Set(list.map((item) => item.declaration)); + if (declarations.size < 2) continue; // one declaration re-exported is fine + if (allowed("homonym", name)) continue; + violations.push( + `homonym: '${name}' is exported from ${list.map((item) => item.subpath).join(", ")} ` + + `resolving to ${declarations.size} different declarations. One name must mean one thing.`, + ); + } + + for (const [kind, items] of Object.entries(allowances)) { + if (kind.startsWith("_")) continue; // `_comment` and friends are documentation + for (const item of items) { + if (!used.has(`${kind}:${item.key}`)) { + violations.push( + `stale allowance: ${kind} '${item.key}' no longer matches any export. Remove it from ` + + `api/public-api-allowances.json.`, + ); + } + } + } + + if (violations.length > 0) { + process.stderr.write(`${violations.join("\n")}\n`); + throw new Error(`TypeScript public API shape: ${violations.length} violation(s)`); + } + process.stdout.write("TypeScript public API shape: no mirrored or homonymous exports\n"); + process.exit(0); +} const expected = normalizeText( await readFile(new URL("../api/public-api.txt", import.meta.url), "utf8"), ); From 9e17ff8de22e9e78bdb7a0ba7091832d61942a8b Mon Sep 17 00:00:00 2001 From: bordumb Date: Sat, 15 Aug 2026 23:59:59 +0100 Subject: [PATCH 12/61] bindings/wasm: carry the error envelope, drop the reference machinery (UNSIGNED) Two defects, one boundary. 1. js_error flattened every failure to JsValue::from_str at 260 call sites. A caller got a bare JavaScript string primitive -- not even an Error -- and lost code identity, effect state, retry class, recommended action, family, operation, and stage. Contract 5.2. Failures now cross as a real Error named AuthsError whose own properties are the camelCase auths.error/1 envelope owned by auths_errors::ErrorEnvelope. The boundary decides none of that meaning. EngineError::registry_code names the failure with one of three stable registry codes; auths_errors::classify supplies effect, retry, family, operation, stage, and recommended action. the_boundary_names_codes_and_decides_no_classification greps the shipping half of lib.rs and fails if EffectState::, RetryClass::, RecommendedAction::, or ErrorFamily:: appears in it. auths_errors gains classify() -- the single Rust-owned answer to "what does this code mean?", including the fail-closed rule for a code this build does not know: effect Possible, retry Unknown, action ResumeAndReconcile. Never swallowed, never downgraded to NotApplied, never a fourth effect value. classifyErrorCodeV1 projects it to JavaScript so no binding needs its own table. Verified against all 48 registry codes in node-smoke. Behavior change, named: four EngineError variants are new (Client, Session, Receipt, Inspection). They render their owner's bare Display with no prefix, because receipt-inspection.test.js matches on the owning crate's stable code text -- caught by that test going red mid-change, not by review. Two error types that previously leaked serde-internal text (serde_wasm_bindgen::Error, TryFromIntError) now report bounded ABI messages. 2. The consumer package exported parsers and canonicalizers for five UNQUALIFIED reference profiles -- auths.http, auths.git, auths.deploy, auths.supply-chain, auths.edge -- none of which is in bindings/public-topology-v1.json qualifiedProfiles. Eleven exports deleted outright, no shim or alias: five parse*ActionV1, five parseCanonical*ActionV1, and DomainActionFieldsV1. Top-level JS exports 95 -> 85 (11 removed, classifyErrorCodeV1 added). Their only TypeScript consumer, src/profiles/domains/, had no importer anywhere and is deleted with its contract declarations. ABI reconciliation: 37 of 95 exports were declared by no manifest. Now 0. identity-abi 12+3, authoring-abi 36+12, new product-abi-v1.json 20+2 = 85. node-smoke asserts set equality in both directions and fails if any removed generic export reappears. Every new gate was mutation-tested: dropping a manifest entry, declaring an unexported symbol, re-adding parseHttpActionV1, flattening a throw back to a string, downgrading an unrecognized code to a fourth effect value, and stripping the effect axis off a thrown error each turn node-smoke red; a literal EffectState:: in shipping code turns the Rust source check red. Acceptance: EA-3b goes RED -> GREEN. EA-3 remains red on its last assertion only -- `thrown instanceof sdk.AuthsError` -- which the TypeScript surface lane owns; its message moved from "threw a bare JavaScript string" to "threw Error, not the public AuthsError". cargo xtask wasm exit 0 (reproducible build + node-smoke). cargo check --workspace --all-targets clean. TS integration 23 fail (7 EA + the 16 pre-existing authorized/denied failures from f5c3589), down from 24. Cargo.lock carries the concurrent pyo3 lane's auths-errors entry as well as this lane's auths-errors and js-sys entries. --- Cargo.lock | 3 + bindings/typescript/api/public-api.txt | 2 +- .../typescript/src/profiles/domains/index.ts | 397 ------------ bindings/typescript/src/verifier/wasm.ts | 10 - bindings/typescript/src/workflow/contracts.ts | 25 - bindings/wasm/auths-proof-wasm/Cargo.toml | 2 + .../auths-proof-wasm/SUPPORTED_PROFILES.md | 101 +++- .../auths-proof-wasm/authoring-abi-v1.json | 24 +- .../auths-proof-wasm/identity-abi-v1.json | 5 + .../wasm/auths-proof-wasm/product-abi-v1.json | 88 +++ bindings/wasm/auths-proof-wasm/src/lib.rs | 572 +++++++++--------- .../auths-proof-wasm/tests/node-smoke.cjs | 157 +++++ product/errors/auths-errors/src/lib.rs | 156 +++++ 13 files changed, 800 insertions(+), 742 deletions(-) delete mode 100644 bindings/typescript/src/profiles/domains/index.ts create mode 100644 bindings/wasm/auths-proof-wasm/product-abi-v1.json diff --git a/Cargo.lock b/Cargo.lock index 1b68c0e6..729e37a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1255,6 +1255,7 @@ dependencies = [ "auths-custody", "auths-did-keri", "auths-did-key", + "auths-errors", "auths-identity", "auths-identity-raw-key", "auths-lifecycle", @@ -1289,6 +1290,7 @@ dependencies = [ "auths-codec", "auths-did-keri", "auths-did-key", + "auths-errors", "auths-identity", "auths-identity-raw-key", "auths-model", @@ -1306,6 +1308,7 @@ dependencies = [ "auths-verifier", "ed25519-dalek 2.2.0", "hex", + "js-sys", "serde", "serde-wasm-bindgen", "serde_json", diff --git a/bindings/typescript/api/public-api.txt b/bindings/typescript/api/public-api.txt index fe4daf39..d9334523 100644 --- a/bindings/typescript/api/public-api.txt +++ b/bindings/typescript/api/public-api.txt @@ -1,5 +1,5 @@ # Installed @auths-dev/sdk public API v1 -# declaration-sha256 2f183f958f4a172c63ddb247dc35e4796301bbf72bc7004b0d39490acb21631f +# declaration-sha256 eafdec948fee35d10f1cd2cd3c5e192821097b8fcbc0cf8457c4db52ac01cfe1 . Actor type . approval value . ApprovalPolicy type diff --git a/bindings/typescript/src/profiles/domains/index.ts b/bindings/typescript/src/profiles/domains/index.ts deleted file mode 100644 index 9100d5b9..00000000 --- a/bindings/typescript/src/profiles/domains/index.ts +++ /dev/null @@ -1,397 +0,0 @@ -import { AuthsWorkflowError, type WorkflowDomainActionFields } from "../../workflow.js"; -import { loadPackagedWorkflowEngine } from "../../verifier/wasm.js"; -import { - type ApplicationAction, - type ApplicationCommand, - type ApplicationGateway, - type ApplicationProfile, - type CanonicalProfileAction, - defineProfile, -} from "../application/index.js"; - -export interface DomainProfileOptions { - readonly audience: string; - readonly resourceNamespace?: string; -} - -export interface HttpActionInput { - readonly method: "DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT"; - readonly scheme: "http" | "https"; - readonly authority: string; - readonly path: string; - readonly query?: Readonly>; - readonly headers?: Readonly>; - readonly contentType?: string; - readonly bodyDigest?: string; -} - -export interface GitActionInput { - readonly repository: string; - readonly operation: "create-ref" | "delete-ref" | "merge" | "push" | "tag"; - readonly reference: string; - readonly objectId: string; -} - -export interface DeploymentActionInput { - readonly environment: string; - readonly region: string; - readonly operation: "activate" | "deploy" | "rollback"; - readonly artifactDigest: string; - readonly provenanceDigest: string; - readonly configurationDigest: string; - readonly strategy: "blue-green" | "canary" | "immediate" | "rolling"; - readonly rolloutNotBefore: bigint; - readonly rolloutExpiresAt: bigint; - readonly blastRadius: bigint; -} - -export interface SupplyChainActionInput { - readonly operation: "approve" | "attest" | "publish" | "release"; - readonly subjectDigest: string; - readonly predicateType: string; - readonly builder: string; -} - -export interface EdgeActionInput { - readonly fleet: string; - readonly device: string; - readonly command: "activate-firmware" | "apply-config" | "execute" | "restart"; - readonly sequence: bigint; - readonly stateDigest?: string; -} - -export interface DomainAuthority { - readonly profile: ProfileId; - readonly capability: string; - readonly resource: string; - readonly audience: string; - readonly budget?: Readonly<{ readonly algebra: string; readonly value: bigint }>; -} - -export interface DomainReceipt { - readonly profile: ProfileId; - readonly idempotencyKey: string; - readonly outcome: "executed" | "failed" | "outcome-unknown"; - readonly result?: Result; -} - -export interface DomainGatewayError { - readonly profile: ProfileId; - readonly code: string; - readonly retry: "never" | "safe" | "conditional" | "unknown"; - readonly effect: "not-applied" | "applied" | "unknown"; -} - -export type HttpAction = ApplicationAction; -export type HttpCommand = ApplicationCommand; -export type HttpGateway = ApplicationGateway; -export type HttpProfile = ApplicationProfile; -export type HttpAuthority = DomainAuthority<"auths.http">; -export type HttpReceipt = DomainReceipt<"auths.http", Result>; -export type HttpGatewayError = DomainGatewayError<"auths.http">; -export type GitAction = ApplicationAction; -export type GitCommand = ApplicationCommand; -export type GitGateway = ApplicationGateway; -export type GitProfile = ApplicationProfile; -export type GitAuthority = DomainAuthority<"auths.git">; -export type GitReceipt = DomainReceipt<"auths.git", Result>; -export type GitGatewayError = DomainGatewayError<"auths.git">; -export type DeploymentAction = ApplicationAction; -export type DeploymentCommand = ApplicationCommand; -export type DeploymentGateway = ApplicationGateway; -export type DeploymentProfile = ApplicationProfile; -export type DeploymentAuthority = DomainAuthority<"auths.deploy">; -export type DeploymentReceipt = DomainReceipt<"auths.deploy", Result>; -export type DeploymentGatewayError = DomainGatewayError<"auths.deploy">; -export type SupplyChainAction = ApplicationAction; -export type SupplyChainCommand = ApplicationCommand; -export type SupplyChainGateway = ApplicationGateway; -export type SupplyChainProfile = ApplicationProfile; -export type SupplyChainAuthority = DomainAuthority<"auths.supply-chain">; -export type SupplyChainReceipt = DomainReceipt<"auths.supply-chain", Result>; -export type SupplyChainGatewayError = DomainGatewayError<"auths.supply-chain">; -export type EdgeAction = ApplicationAction; -export type EdgeCommand = ApplicationCommand; -export type EdgeGateway = ApplicationGateway; -export type EdgeProfile = ApplicationProfile; -export type EdgeAuthority = DomainAuthority<"auths.edge">; -export type EdgeReceipt = DomainReceipt<"auths.edge", Result>; -export type EdgeGatewayError = DomainGatewayError<"auths.edge">; - -type NativeParser = (input: Input) => WorkflowDomainActionFields; -type CanonicalParser = (value: unknown) => Input; -interface DomainParser { - readonly input: NativeParser; - readonly canonical: (body: Uint8Array) => WorkflowDomainActionFields; -} - -export interface DomainProfiles { - http(options: DomainProfileOptions): HttpProfile; - git(options: DomainProfileOptions): GitProfile; - deployment(options: DomainProfileOptions): DeploymentProfile; - supplyChain(options: DomainProfileOptions): SupplyChainProfile; - edge(options: DomainProfileOptions): EdgeProfile; -} - -export async function loadDomainProfiles(): Promise { - const engine = await loadPackagedWorkflowEngine(); - const http: DomainParser = { - input: (value) => engine.parseHttpActionV1(httpNative(value)), - canonical: (body) => engine.parseCanonicalHttpActionV1(body), - }; - const git: DomainParser = { - input: (value) => engine.parseGitActionV1(gitNative(value)), - canonical: (body) => engine.parseCanonicalGitActionV1(body), - }; - const deployment: DomainParser = { - input: (value) => engine.parseDeploymentActionV1(deploymentNative(value)), - canonical: (body) => engine.parseCanonicalDeploymentActionV1(body), - }; - const supplyChain: DomainParser = { - input: (value) => engine.parseSupplyChainActionV1(supplyChainNative(value)), - canonical: (body) => engine.parseCanonicalSupplyChainActionV1(body), - }; - const edge: DomainParser = { - input: (value) => engine.parseEdgeActionV1(edgeNative(value)), - canonical: (body) => engine.parseCanonicalEdgeActionV1(body), - }; - return Object.freeze({ - http: (options: DomainProfileOptions) => domainProfile("auths.http", options, http, parseHttp), - git: (options: DomainProfileOptions) => domainProfile("auths.git", options, git, parseGit), - deployment: (options: DomainProfileOptions) => domainProfile( - "auths.deploy", options, deployment, parseDeployment, - ), - supplyChain: (options: DomainProfileOptions) => domainProfile( - "auths.supply-chain", options, supplyChain, parseSupplyChain, - ), - edge: (options: DomainProfileOptions) => domainProfile("auths.edge", options, edge, parseEdge), - }); -} - -function domainProfile( - id: string, - options: DomainProfileOptions, - parser: DomainParser, - parseCanonical: CanonicalParser, -): ApplicationProfile { - const audience = boundedText(options.audience, "audience"); - const namespace = options.resourceNamespace === undefined - ? undefined - : boundedText(options.resourceNamespace, "resource namespace"); - return defineProfile({ - id, - version: 1, - canonicalize(input) { - const parsed = parser.input(input); - try { - const labels = [...parsed.reviewLabels]; - const values = [...parsed.reviewValues]; - if (labels.length !== values.length) { - throw new AuthsWorkflowError("invalid-profile", "native review fields are inconsistent"); - } - return { - mediaType: parsed.mediaType, - body: new Uint8Array(parsed.body), - permission: { capability: parsed.capability, resource: parsed.resource }, - resourceNamespace: namespace ?? parsed.resource, - ...(parsed.hasBudget - ? { budget: { algebra: parsed.budgetAlgebra, value: BigInt(parsed.budgetValue) } } - : {}), - audience, - display: [ - { label: "Action", value: parsed.reviewTitle }, - ...labels.map((label, index) => ({ label, value: values[index] ?? "" })), - ], - } satisfies CanonicalProfileAction; - } finally { - parsed.free?.(); - } - }, - decodeVerified(canonical) { - const parsed = parser.canonical(canonical.body); - try { - return parseCanonical(parsed.normalized); - } finally { - parsed.free?.(); - } - }, - }); -} - -function httpNative(input: HttpActionInput): unknown { - return { - method: input.method, - scheme: input.scheme, - authority: input.authority, - path: input.path, - query: input.query ?? {}, - headers: input.headers ?? {}, - ...(input.contentType === undefined ? {} : { content_type: input.contentType }), - ...(input.bodyDigest === undefined ? {} : { body_digest: input.bodyDigest }), - }; -} - -function gitNative(input: GitActionInput): unknown { - return { - repository: input.repository, - operation: input.operation, - reference: input.reference, - object_id: input.objectId, - }; -} - -function deploymentNative(input: DeploymentActionInput): unknown { - return { - environment: input.environment, - region: input.region, - operation: input.operation, - artifact_digest: input.artifactDigest, - provenance_digest: input.provenanceDigest, - configuration_digest: input.configurationDigest, - strategy: input.strategy, - rollout_not_before: input.rolloutNotBefore, - rollout_expires_at: input.rolloutExpiresAt, - blast_radius: input.blastRadius, - }; -} - -function supplyChainNative(input: SupplyChainActionInput): unknown { - return { - operation: input.operation, - subject_digest: input.subjectDigest, - predicate_type: input.predicateType, - builder: input.builder, - }; -} - -function edgeNative(input: EdgeActionInput): unknown { - return { - fleet: input.fleet, - device: input.device, - command: input.command, - sequence: input.sequence, - ...(input.stateDigest === undefined ? {} : { state_digest: input.stateDigest }), - }; -} - -function record(value: unknown): Readonly> { - if (value instanceof Map) { - if ([...value.keys()].some((key) => typeof key !== "string")) { - throw new AuthsWorkflowError("invalid-profile", "native profile returned a non-text map key"); - } - return Object.freeze(Object.fromEntries(value)) as Readonly>; - } - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new AuthsWorkflowError("invalid-profile", "native profile returned a non-object action"); - } - return value as Readonly>; -} - -function textField(value: Readonly>, key: string): string { - const field = value[key]; - if (typeof field !== "string") { - throw new AuthsWorkflowError("invalid-profile", `native profile omitted ${key}`); - } - return field; -} - -function integerField(value: Readonly>, key: string): bigint { - const field = value[key]; - if (typeof field === "bigint" && field >= 0n) return field; - if (typeof field === "number" && Number.isSafeInteger(field) && field >= 0) { - return BigInt(field); - } - throw new AuthsWorkflowError("invalid-profile", `native profile returned invalid ${key}`); -} - -function parseHttp(value: unknown): HttpActionInput { - const fields = record(value); - return Object.freeze({ - method: textField(fields, "method") as HttpActionInput["method"], - scheme: textField(fields, "scheme") as HttpActionInput["scheme"], - authority: textField(fields, "authority"), - path: textField(fields, "path"), - query: parseStringLists(fields.query), - headers: parseStrings(fields.headers), - ...(typeof fields.content_type === "string" ? { contentType: fields.content_type } : {}), - ...(typeof fields.body_digest === "string" ? { bodyDigest: fields.body_digest } : {}), - }); -} - -function parseGit(value: unknown): GitActionInput { - const fields = record(value); - return Object.freeze({ - repository: textField(fields, "repository"), - operation: textField(fields, "operation") as GitActionInput["operation"], - reference: textField(fields, "reference"), - objectId: textField(fields, "object_id"), - }); -} - -function parseDeployment(value: unknown): DeploymentActionInput { - const fields = record(value); - return Object.freeze({ - environment: textField(fields, "environment"), - region: textField(fields, "region"), - operation: textField(fields, "operation") as DeploymentActionInput["operation"], - artifactDigest: textField(fields, "artifact_digest"), - provenanceDigest: textField(fields, "provenance_digest"), - configurationDigest: textField(fields, "configuration_digest"), - strategy: textField(fields, "strategy") as DeploymentActionInput["strategy"], - rolloutNotBefore: integerField(fields, "rollout_not_before"), - rolloutExpiresAt: integerField(fields, "rollout_expires_at"), - blastRadius: integerField(fields, "blast_radius"), - }); -} - -function parseSupplyChain(value: unknown): SupplyChainActionInput { - const fields = record(value); - return Object.freeze({ - operation: textField(fields, "operation") as SupplyChainActionInput["operation"], - subjectDigest: textField(fields, "subject_digest"), - predicateType: textField(fields, "predicate_type"), - builder: textField(fields, "builder"), - }); -} - -function parseEdge(value: unknown): EdgeActionInput { - const fields = record(value); - return Object.freeze({ - fleet: textField(fields, "fleet"), - device: textField(fields, "device"), - command: textField(fields, "command") as EdgeActionInput["command"], - sequence: integerField(fields, "sequence"), - ...(typeof fields.state_digest === "string" ? { stateDigest: fields.state_digest } : {}), - }); -} - -function parseStrings(value: unknown): Readonly> { - const fields = record(value); - return Object.freeze(Object.fromEntries( - Object.entries(fields).map(([key, item]) => { - if (typeof item !== "string") { - throw new AuthsWorkflowError("invalid-profile", "native map value is not text"); - } - return [key, item]; - }), - )); -} - -function parseStringLists(value: unknown): Readonly> { - const fields = record(value); - return Object.freeze(Object.fromEntries( - Object.entries(fields).map(([key, item]) => { - if (!Array.isArray(item) || item.some((member) => typeof member !== "string")) { - throw new AuthsWorkflowError("invalid-profile", "native query value is not a text list"); - } - return [key, Object.freeze([...item])]; - }), - )); -} - -function boundedText(value: string, label: string): string { - if (typeof value !== "string" || value.length === 0 || value.length > 2048) { - throw new AuthsWorkflowError("invalid-profile", `${label} is outside bounds`); - } - return value; -} diff --git a/bindings/typescript/src/verifier/wasm.ts b/bindings/typescript/src/verifier/wasm.ts index 6f627df7..b42b08b0 100644 --- a/bindings/typescript/src/verifier/wasm.ts +++ b/bindings/typescript/src/verifier/wasm.ts @@ -54,16 +54,6 @@ async function loadPackagedWorkflowEngineOnce(): Promise typeof loaded.compileTrustedContextV1 !== "function" || typeof loaded.configurationV1 !== "function" || typeof loaded.validateTrustedContextV1 !== "function" || - typeof loaded.parseHttpActionV1 !== "function" || - typeof loaded.parseGitActionV1 !== "function" || - typeof loaded.parseDeploymentActionV1 !== "function" || - typeof loaded.parseSupplyChainActionV1 !== "function" || - typeof loaded.parseEdgeActionV1 !== "function" || - typeof loaded.parseCanonicalHttpActionV1 !== "function" || - typeof loaded.parseCanonicalGitActionV1 !== "function" || - typeof loaded.parseCanonicalDeploymentActionV1 !== "function" || - typeof loaded.parseCanonicalSupplyChainActionV1 !== "function" || - typeof loaded.parseCanonicalEdgeActionV1 !== "function" || typeof loaded.prepareMcpActionV1 !== "function" || typeof loaded.canonicalizeMcpPlanMemberV1 !== "function" || typeof loaded.beginMcpExecutionV1 !== "function" || diff --git a/bindings/typescript/src/workflow/contracts.ts b/bindings/typescript/src/workflow/contracts.ts index 3ce0afd7..cdfec166 100644 --- a/bindings/typescript/src/workflow/contracts.ts +++ b/bindings/typescript/src/workflow/contracts.ts @@ -363,16 +363,6 @@ export interface WorkflowWasmEngine { rootPrincipal: string, verifierConfiguration: Uint8Array, ): Uint8Array; - parseHttpActionV1(input: unknown): WorkflowDomainActionFields; - parseGitActionV1(input: unknown): WorkflowDomainActionFields; - parseDeploymentActionV1(input: unknown): WorkflowDomainActionFields; - parseSupplyChainActionV1(input: unknown): WorkflowDomainActionFields; - parseEdgeActionV1(input: unknown): WorkflowDomainActionFields; - parseCanonicalHttpActionV1(body: Uint8Array): WorkflowDomainActionFields; - parseCanonicalGitActionV1(body: Uint8Array): WorkflowDomainActionFields; - parseCanonicalDeploymentActionV1(body: Uint8Array): WorkflowDomainActionFields; - parseCanonicalSupplyChainActionV1(body: Uint8Array): WorkflowDomainActionFields; - parseCanonicalEdgeActionV1(body: Uint8Array): WorkflowDomainActionFields; prepareMcpActionV1( service: string, name: string, @@ -670,21 +660,6 @@ export interface WorkflowAuthorizationPlanSummary { free?(): void; } -export interface WorkflowDomainActionFields { - readonly body: Uint8Array; - readonly mediaType: string; - readonly capability: string; - readonly resource: string; - readonly hasBudget: boolean; - readonly budgetAlgebra: string; - readonly budgetValue: bigint; - readonly reviewTitle: string; - readonly reviewLabels: readonly string[]; - readonly reviewValues: readonly string[]; - readonly normalized: unknown; - free?(): void; -} - export interface WorkflowMcpActionPreparation { readonly canonicalActionCbor: Uint8Array; readonly actionEnvelopeCbor: Uint8Array; diff --git a/bindings/wasm/auths-proof-wasm/Cargo.toml b/bindings/wasm/auths-proof-wasm/Cargo.toml index 49426f34..4b12563c 100644 --- a/bindings/wasm/auths-proof-wasm/Cargo.toml +++ b/bindings/wasm/auths-proof-wasm/Cargo.toml @@ -17,6 +17,7 @@ auths-author = { workspace = true, features = ["std"] } auths-codec.workspace = true auths-did-keri.workspace = true auths-did-key.workspace = true +auths-errors = { workspace = true, features = ["std"] } auths-identity.workspace = true auths-identity-raw-key.workspace = true auths-model.workspace = true @@ -33,6 +34,7 @@ auths-signature-ed25519.workspace = true auths-verifier.workspace = true ed25519-dalek.workspace = true hex.workspace = true +js-sys.workspace = true serde.workspace = true serde_json.workspace = true serde_json_canonicalizer.workspace = true diff --git a/bindings/wasm/auths-proof-wasm/SUPPORTED_PROFILES.md b/bindings/wasm/auths-proof-wasm/SUPPORTED_PROFILES.md index 70e7b64f..73b789fb 100644 --- a/bindings/wasm/auths-proof-wasm/SUPPORTED_PROFILES.md +++ b/bindings/wasm/auths-proof-wasm/SUPPORTED_PROFILES.md @@ -1,5 +1,56 @@ # WASM supported profiles +## Action profiles + +This package is a **consumer** transport. It ships no generic domain parser, +no generic canonicalizer, and no reference action profile. + +`bindings/public-topology-v1.json` declares the qualified profiles: + +| Qualified profile | Exposed here | How | +|---|---|---| +| `auths.mcp/1` | yes | `prepareMcpActionV1`, `canonicalizeMcpPlanMemberV1`, `beginMcpExecutionV1`, `resumeMcpExecutionV1` | +| `auths.github.issue-address/1` | routed only | `encodeProductionRequestV1` / `decodeProductionResponseV1` | +| `auths.opentofu.saved-plan-apply/1` | routed only | as above | +| `auths.postgresql.bounded-update/1` | routed only | as above | + +"Routed only" means this module encodes and decodes the bounded production +request and response for that profile. It does not canonicalize the profile's +action; the service that owns the vertical does. + +### Removed in v1.0 + +The five **unqualified** reference domain profiles — `auths.http`, `auths.git`, +`auths.deploy`, `auths.supply-chain`, `auths.edge` — are no longer projected to +JavaScript. Eleven exports were deleted outright with no shim, alias, or +deprecation window: `parseHttpActionV1`, `parseGitActionV1`, +`parseDeploymentActionV1`, `parseSupplyChainActionV1`, `parseEdgeActionV1`, +their five `parseCanonical…ActionV1` counterparts, and the +`DomainActionFieldsV1` result class. See +`docs/target-state/PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md`, phase 2 item 6. +`product-abi-v1.json` records the removal and `tests/node-smoke.cjs` fails if +any of them reappears. + +### Known disagreements + +1. `bindings/public-topology-v1.json` lists **four** `qualifiedProfiles`, but + `auths_production_client::QualifiedProfile` + (`product/runtime/auths-production-client/src/lib.rs:194-206`) has **three** + members: `auths.mcp/1` has no production-client route. The topology list is + a union of "profiles the product ships" and "profiles the service routes", + and nothing reconciles the two. +2. `prepareProfileActionV1`, `canonicalizeProfilePlanMemberV1`, and + `commitProfilePlanV1` accept an arbitrary `profileId` string from + JavaScript. `canonical_profile_action_native` + (`src/lib.rs:4189`) consults no Rust profile: it parses the identifier and + accepts the supplied body as already canonical. A JavaScript caller can + therefore name a vertical this package does not implement — the same + structural hole the v1 contract §6.3 records for the Python + `define_profile` surface. Closing it belongs with the TypeScript + `defineProfile` / profile-kit deletion, because those are its only callers. + +## Principal methods and signature suites + The WASM module also exposes the neutral identity Level 1/2 operations used by `@auths-dev/sdk/identity`: structural V2 packet encode/decode, explicit raw-key validation, external-custody signing preimages, signed-message encoding, and an @@ -21,20 +72,54 @@ fixed package. Such contexts fail closed with `verifier-configuration-mismatch` or an unsupported-method requirement; the package never substitutes another method. -The module also exposes the versioned `auths.wasm-authoring-abi/1` boundary -described by [`authoring-abi-v1.json`](authoring-abi-v1.json). It validates -principal identifiers, plans child grants through `auths-author`, prepares and -completes exact signing envelopes without receiving a private key, and binds -one request to a canonical trusted-context template. Canonical grant, action, -and status decoders use `VerifierLimits::default_deployment`; malformed, -trailing, non-canonical, and widening inputs fail before custody is invoked. +## The declared ABI + +Every symbol this module publishes to JavaScript is declared by exactly one +manifest, and every declaration is published. `tests/node-smoke.cjs` asserts +set equality in both directions. + +| Manifest | Schema | Exports | Result types | +|---|---|---|---| +| [`identity-abi-v1.json`](identity-abi-v1.json) | `auths.identity-wasm-abi/1` | 12 | 3 | +| [`authoring-abi-v1.json`](authoring-abi-v1.json) | `auths.wasm-authoring-abi/1` | 36 | 12 | +| [`product-abi-v1.json`](product-abi-v1.json) | `auths.wasm-product-abi/1` | 20 | 2 | + +The authoring boundary validates principal identifiers, plans child grants +through `auths-author`, prepares and completes exact signing envelopes without +receiving a private key, and binds one request to a canonical trusted-context +template. Canonical grant, action, and status decoders use +`VerifierLimits::default_deployment`; malformed, trailing, non-canonical, and +widening inputs fail before custody is invoked. This is a repository-local pre-review ABI. It does not by itself promote the TypeScript package beyond the Verifier Binding tier. +## Errors + +Every failure crossing this boundary is a structured JavaScript `Error` named +`AuthsError` whose own properties are the `auths.error/1` envelope owned by +`auths_errors::ErrorEnvelope`: `schema`, `family`, `code`, `operation`, +`stage`, `summary`, `correlationId`, `retry`, `effect`, `entered`, +`recommendedAction`, and `causes`. No error is flattened to a string. + +This module decides none of that meaning. It names each failure with a stable +code from `product/errors/v1/registry.json` and +`auths_errors::classify` supplies the effect state, the retry class, and the +recommended action. The three codes this boundary can name — +`core.malformed-input`, `core.invalid-configuration`, +`core.native-runtime-unavailable` — all carry effect `not-applied`, because the +module opens no connection, invokes no provider, and holds no durable state. + +`classifyErrorCodeV1(code)` projects the registry's classification for any +code, including one minted by a newer Auths build: an unrecognized code is +reported with `known: false` and `effect: "possible"`, never swallowed and +never downgraded to `not-applied`. + +## Reproducibility + `cargo xtask wasm` builds `wasm-bindgen` Node artifacts twice, compares every generated JS/WASM/TypeScript byte, generates authorized verification and authoring vectors from the checked-in raw-key corpus, and requires Node to produce identical canonical bytes. Malformed verification arrays return -protocol result bytes; malformed authoring requests return bounded local +protocol result bytes; malformed authoring requests return bounded structured errors and never produce a signing request. diff --git a/bindings/wasm/auths-proof-wasm/authoring-abi-v1.json b/bindings/wasm/auths-proof-wasm/authoring-abi-v1.json index 15f0a215..c8f985ce 100644 --- a/bindings/wasm/auths-proof-wasm/authoring-abi-v1.json +++ b/bindings/wasm/auths-proof-wasm/authoring-abi-v1.json @@ -30,16 +30,6 @@ "prepareProfileActionV1", "prepareRawKeyAuthorityV1", "deriveEd25519RawKeyIdentityV1", - "parseHttpActionV1", - "parseGitActionV1", - "parseDeploymentActionV1", - "parseSupplyChainActionV1", - "parseEdgeActionV1", - "parseCanonicalHttpActionV1", - "parseCanonicalGitActionV1", - "parseCanonicalDeploymentActionV1", - "parseCanonicalSupplyChainActionV1", - "parseCanonicalEdgeActionV1", "WorkflowProofBuilderV1", "AuthorizationPlanBuilderV1", "validateTrustedContextV1", @@ -51,6 +41,20 @@ "resumeMcpExecutionV1", "McpExecutionSessionV1" ], + "types": [ + "AuthoringSigningRequestV1", + "AuthorizationPlanSummaryV1", + "GrantPlanV1", + "McpActionPreparationV1", + "ProfileActionPreparationV1", + "ProfilePlanCommitmentV1", + "RawKeyAuthorityPreparationV1", + "RawKeyIdentityV1", + "SignedGrantAuthorityV1", + "StatusSnapshotV1", + "TrustedContextCompilationV1", + "WorkflowAuthorizationArtifactsV1" + ], "grantPlanWarningBits": { "anyBody": 1, "multiplePermissions": 2, diff --git a/bindings/wasm/auths-proof-wasm/identity-abi-v1.json b/bindings/wasm/auths-proof-wasm/identity-abi-v1.json index 8325d3f3..d1c868b3 100644 --- a/bindings/wasm/auths-proof-wasm/identity-abi-v1.json +++ b/bindings/wasm/auths-proof-wasm/identity-abi-v1.json @@ -21,6 +21,11 @@ "decodeSignedIdentityMessageV2", "verifyEd25519IdentityMessageV2" ], + "types": [ + "IdentityFieldsV2", + "AuthenticatedIdentityMessageV2", + "SignedIdentityMessageFieldsV2" + ], "stateTransitions": [ "bytes-to-decoded", "decoded-to-method-validated", diff --git a/bindings/wasm/auths-proof-wasm/product-abi-v1.json b/bindings/wasm/auths-proof-wasm/product-abi-v1.json new file mode 100644 index 00000000..56063b5b --- /dev/null +++ b/bindings/wasm/auths-proof-wasm/product-abi-v1.json @@ -0,0 +1,88 @@ +{ + "schema": "auths.wasm-product-abi/1", + "abiVersion": 1, + "audience": "consumer", + "limits": "VerifierLimits::default_deployment", + "exports": [ + "verifyV1", + "verifyBatchV1", + "configurationV1", + "classifyErrorCodeV1", + "productionClientContractVersionV1", + "encodeProductionRequestV1", + "decodeProductionRequestV1", + "decodeProductionResponseV1", + "encodeProductionDelegationV1", + "projectSdkEventV2", + "prepareAuthorizedDecisionReceiptV1", + "prepareApplicationExecutionReceiptV1", + "attestDecisionReceiptV1", + "attestExecutionReceiptV1", + "verifyRawKeyReceiptV1", + "verifyReceiptLinkV1", + "prepareReceiptDisclosureV1", + "inspectRawKeyReceiptV1", + "profileReceiptBindingsV1", + "developmentEd25519PublicKeyV1" + ], + "types": [ + "ReceiptPreparationV1", + "ProfileReceiptBindingsV1" + ], + "errorEnvelope": { + "schema": "auths.error/1", + "owner": "product/errors/auths-errors", + "registry": "product/errors/v1/registry.json", + "thrownAs": "Error", + "errorName": "AuthsError", + "correlationId": "wasm-boundary", + "fields": [ + "schema", + "family", + "code", + "operation", + "stage", + "summary", + "correlationId", + "retry", + "effect", + "entered", + "recommendedAction", + "causes" + ], + "codes": [ + "core.malformed-input", + "core.invalid-configuration", + "core.native-runtime-unavailable" + ], + "unrecognizedCode": { + "known": false, + "effect": "possible", + "retry": "unknown", + "recommendedAction": "resume-and-reconcile" + } + }, + "removedInV1": { + "reason": "generic reference domain machinery is not consumer surface; see docs/target-state/PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md phase 2 item 6", + "profiles": [ + "auths.http", + "auths.git", + "auths.deploy", + "auths.supply-chain", + "auths.edge" + ], + "exports": [ + "parseHttpActionV1", + "parseGitActionV1", + "parseDeploymentActionV1", + "parseSupplyChainActionV1", + "parseEdgeActionV1", + "parseCanonicalHttpActionV1", + "parseCanonicalGitActionV1", + "parseCanonicalDeploymentActionV1", + "parseCanonicalSupplyChainActionV1", + "parseCanonicalEdgeActionV1", + "DomainActionFieldsV1" + ] + } +} diff --git a/bindings/wasm/auths-proof-wasm/src/lib.rs b/bindings/wasm/auths-proof-wasm/src/lib.rs index 70aa385b..835e5d88 100644 --- a/bindings/wasm/auths-proof-wasm/src/lib.rs +++ b/bindings/wasm/auths-proof-wasm/src/lib.rs @@ -34,11 +34,16 @@ use auths_production_client::{ project_sdk_event_v2, }; use auths_profile_api::ActionProfile; -use auths_profile_domains::{ - DeploymentProfile, DomainReceiptInspector, EdgeProfile, GitProfile, HttpProfile, - SupplyChainProfile, reference_canonicalize_deployment, reference_canonicalize_edge, - reference_canonicalize_git, reference_canonicalize_http, reference_canonicalize_supply_chain, -}; +// The generic reference domain profiles (HTTP, Git, deployment, supply-chain, +// edge) are no longer projected to JavaScript: this consumer package exposes +// no generic domain parser, canonicalizer, or action-field carrier. The one +// remaining use is the receipt projector below, which the checked-in +// `product/fixtures/v1/receipt-disclosure/inspection-v1.json` corpus still +// keys to the unqualified `auths.edge` profile. Removing it needs a +// qualified-profile receipt projector in Rust and a re-keyed corpus, and the +// pyo3 binding carries the identical coupling at +// `bindings/python/src/receipts.rs:411`. +use auths_profile_domains::DomainReceiptInspector; use auths_profile_mcp::{ McpCause, McpExecutionSession, McpHandlerEffect, McpHandlerResult, McpProfile, McpReservationResult, McpSessionKey, McpSessionStep, McpTerminal, McpToolCall, @@ -2647,288 +2652,6 @@ impl ProfileReceiptBindingsV1 { } } -#[wasm_bindgen] -pub struct DomainActionFieldsV1 { - body: Vec, - media_type: String, - capability: String, - resource: String, - has_budget: bool, - budget_algebra: String, - budget_value: u64, - review_title: String, - review_labels: Vec, - review_values: Vec, - normalized: JsValue, -} - -#[wasm_bindgen] -impl DomainActionFieldsV1 { - #[must_use] - #[wasm_bindgen(getter)] - pub fn body(&self) -> Vec { - self.body.clone() - } - - #[must_use] - #[wasm_bindgen(getter, js_name = mediaType)] - pub fn media_type(&self) -> String { - self.media_type.clone() - } - - #[must_use] - #[wasm_bindgen(getter)] - pub fn capability(&self) -> String { - self.capability.clone() - } - - #[must_use] - #[wasm_bindgen(getter)] - pub fn resource(&self) -> String { - self.resource.clone() - } - - #[must_use] - #[wasm_bindgen(getter, js_name = hasBudget)] - pub fn has_budget(&self) -> bool { - self.has_budget - } - - #[must_use] - #[wasm_bindgen(getter, js_name = budgetAlgebra)] - pub fn budget_algebra(&self) -> String { - self.budget_algebra.clone() - } - - #[must_use] - #[wasm_bindgen(getter, js_name = budgetValue)] - pub fn budget_value(&self) -> u64 { - self.budget_value - } - - #[must_use] - #[wasm_bindgen(getter, js_name = reviewTitle)] - pub fn review_title(&self) -> String { - self.review_title.clone() - } - - #[must_use] - #[wasm_bindgen(getter, js_name = reviewLabels)] - pub fn review_labels(&self) -> Vec { - self.review_labels.clone() - } - - #[must_use] - #[wasm_bindgen(getter, js_name = reviewValues)] - pub fn review_values(&self) -> Vec { - self.review_values.clone() - } - - #[must_use] - #[wasm_bindgen(getter)] - pub fn normalized(&self) -> JsValue { - self.normalized.clone() - } -} - -fn domain_input(input: JsValue, profile: &str) -> Result, EngineError> { - let Value::Object(mut fields) = serde_wasm_bindgen::from_value(input) - .map_err(|_| EngineError::Abi("domain action must be an object"))? - else { - return Err(EngineError::Abi("domain action must be an object")); - }; - if fields.contains_key("profile") || fields.contains_key("profile_version") { - return Err(EngineError::Abi( - "domain profile identity is not caller-selectable", - )); - } - fields.insert("profile".into(), Value::String(profile.into())); - fields.insert("profile_version".into(), Value::Number(1.into())); - serde_json::to_vec(&Value::Object(fields)) - .map_err(|_| EngineError::Abi("invalid domain action")) -} - -fn domain_fields( - profile: &P, - canonical: &auths_model::CanonicalAction, -) -> Result { - let review = profile.review_display(canonical)?; - let (has_budget, budget_algebra, budget_value) = canonical.requested_budget().map_or_else( - || (false, String::new(), 0), - |budget| (true, budget.algebra().as_str().to_owned(), budget.value()), - ); - let (review_labels, review_values) = review.fields().iter().cloned().unzip(); - let normalized: Value = serde_json::from_slice(canonical.body()) - .map_err(|_| EngineError::Abi("canonical domain action is not JSON"))?; - let serializer = - serde_wasm_bindgen::Serializer::new().serialize_large_number_types_as_bigints(true); - let normalized = normalized - .serialize(&serializer) - .map_err(|_| EngineError::Abi("canonical domain action cannot cross the ABI"))?; - Ok(DomainActionFieldsV1 { - body: canonical.body().to_vec(), - media_type: canonical.media_type().as_str().to_owned(), - capability: canonical.permission().capability().as_str().to_owned(), - resource: canonical.permission().resource().as_str().to_owned(), - has_budget, - budget_algebra, - budget_value, - review_title: review.title().to_owned(), - review_labels, - review_values, - normalized, - }) -} - -fn canonical_domain( - body: &[u8], - profile: &P, - parse: fn( - &[u8], - ) -> Result, -) -> Result { - let canonical = parse(body).map_err(EngineError::from).map_err(js_error)?; - if canonical.body() != body { - return Err(js_error("domain action is not canonical")); - } - domain_fields(profile, &canonical).map_err(js_error) -} - -/// Parses one HTTP action through the maintained Rust profile. -/// -/// # Errors -/// -/// Rejects malformed, non-canonical, or out-of-profile input. -#[wasm_bindgen(js_name = parseHttpActionV1)] -pub fn parse_http_action_v1(input: JsValue) -> Result { - let bytes = domain_input(input, "auths.http").map_err(js_error)?; - let profile = HttpProfile::default(); - let canonical = reference_canonicalize_http(&bytes) - .map_err(EngineError::from) - .map_err(js_error)?; - domain_fields(&profile, &canonical).map_err(js_error) -} - -/// Parses one Git action through the maintained Rust profile. -/// -/// # Errors -/// -/// Rejects malformed, non-canonical, or out-of-profile input. -#[wasm_bindgen(js_name = parseGitActionV1)] -pub fn parse_git_action_v1(input: JsValue) -> Result { - let bytes = domain_input(input, "auths.git").map_err(js_error)?; - let profile = GitProfile::default(); - let canonical = reference_canonicalize_git(&bytes) - .map_err(EngineError::from) - .map_err(js_error)?; - domain_fields(&profile, &canonical).map_err(js_error) -} - -/// Parses one deployment action through the maintained Rust profile. -/// -/// # Errors -/// -/// Rejects malformed, non-canonical, or out-of-profile input. -#[wasm_bindgen(js_name = parseDeploymentActionV1)] -pub fn parse_deployment_action_v1(input: JsValue) -> Result { - let bytes = domain_input(input, "auths.deploy").map_err(js_error)?; - let profile = DeploymentProfile::default(); - let canonical = reference_canonicalize_deployment(&bytes) - .map_err(EngineError::from) - .map_err(js_error)?; - domain_fields(&profile, &canonical).map_err(js_error) -} - -/// Parses one supply-chain action through the maintained Rust profile. -/// -/// # Errors -/// -/// Rejects malformed, non-canonical, or out-of-profile input. -#[wasm_bindgen(js_name = parseSupplyChainActionV1)] -pub fn parse_supply_chain_action_v1(input: JsValue) -> Result { - let bytes = domain_input(input, "auths.supply-chain").map_err(js_error)?; - let profile = SupplyChainProfile::default(); - let canonical = reference_canonicalize_supply_chain(&bytes) - .map_err(EngineError::from) - .map_err(js_error)?; - domain_fields(&profile, &canonical).map_err(js_error) -} - -/// Parses one edge action through the maintained Rust profile. -/// -/// # Errors -/// -/// Rejects malformed, non-canonical, or out-of-profile input. -#[wasm_bindgen(js_name = parseEdgeActionV1)] -pub fn parse_edge_action_v1(input: JsValue) -> Result { - let bytes = domain_input(input, "auths.edge").map_err(js_error)?; - let profile = EdgeProfile::default(); - let canonical = reference_canonicalize_edge(&bytes) - .map_err(EngineError::from) - .map_err(js_error)?; - domain_fields(&profile, &canonical).map_err(js_error) -} - -/// Parses canonical HTTP action bytes through the maintained Rust profile. -/// -/// # Errors -/// -/// Rejects malformed, non-canonical, or out-of-profile input. -#[wasm_bindgen(js_name = parseCanonicalHttpActionV1)] -pub fn parse_canonical_http_action_v1(body: &[u8]) -> Result { - canonical_domain(body, &HttpProfile::default(), reference_canonicalize_http) -} - -/// Parses canonical Git action bytes through the maintained Rust profile. -/// -/// # Errors -/// -/// Rejects malformed, non-canonical, or out-of-profile input. -#[wasm_bindgen(js_name = parseCanonicalGitActionV1)] -pub fn parse_canonical_git_action_v1(body: &[u8]) -> Result { - canonical_domain(body, &GitProfile::default(), reference_canonicalize_git) -} - -/// Parses canonical deployment action bytes through the maintained Rust profile. -/// -/// # Errors -/// -/// Rejects malformed, non-canonical, or out-of-profile input. -#[wasm_bindgen(js_name = parseCanonicalDeploymentActionV1)] -pub fn parse_canonical_deployment_action_v1(body: &[u8]) -> Result { - canonical_domain( - body, - &DeploymentProfile::default(), - reference_canonicalize_deployment, - ) -} - -/// Parses canonical supply-chain action bytes through the maintained Rust profile. -/// -/// # Errors -/// -/// Rejects malformed, non-canonical, or out-of-profile input. -#[wasm_bindgen(js_name = parseCanonicalSupplyChainActionV1)] -pub fn parse_canonical_supply_chain_action_v1( - body: &[u8], -) -> Result { - canonical_domain( - body, - &SupplyChainProfile::default(), - reference_canonicalize_supply_chain, - ) -} - -/// Parses canonical edge action bytes through the maintained Rust profile. -/// -/// # Errors -/// -/// Rejects malformed, non-canonical, or out-of-profile input. -#[wasm_bindgen(js_name = parseCanonicalEdgeActionV1)] -pub fn parse_canonical_edge_action_v1(body: &[u8]) -> Result { - canonical_domain(body, &EdgeProfile::default(), reference_canonicalize_edge) -} - /// Unsigned root grant and matching self-contained raw-key trust context. #[wasm_bindgen] pub struct RawKeyAuthorityPreparationV1 { @@ -4925,8 +4648,97 @@ fn bind_trusted_context_request_native( Ok(auths_codec::encode_verifier_context(&context)?) } -fn js_error(error: impl fmt::Display) -> JsValue { - JsValue::from_str(&error.to_string()) +/// Correlation identifier carried by every failure minted at this boundary. +/// +/// The WASM module has no clock, no randomness, and no request scope, so it +/// reports the boundary itself rather than inventing a per-call identifier. +const BOUNDARY_CORRELATION_ID: &str = "wasm-boundary"; + +/// Projects one bounded boundary failure as a structured JavaScript `Error`. +/// +/// The returned value is a real `Error` instance whose own properties are the +/// camelCase serialization of [`auths_errors::ErrorEnvelope`]: `schema`, +/// `family`, `code`, `operation`, `stage`, `summary`, `correlationId`, +/// `retry`, `effect`, `entered`, `recommendedAction`, and `causes`. +/// +/// This module decides none of that meaning. It names the failure with a +/// stable registry code; [`auths_errors::classify`] decides the effect state, +/// the retry class, and the recommended action, and an unrecognized code fails +/// closed to `effect: "possible"` there rather than here. +fn js_error(error: impl Into) -> JsValue { + boundary_error(&error.into()) +} + +fn boundary_error(error: &EngineError) -> JsValue { + let code = error.registry_code(); + let classification = auths_errors::classify(code); + let summary = bounded_summary(&error.to_string()); + let envelope = auths_errors::ErrorEnvelope { + schema: auths_errors::ENVELOPE_SCHEMA.to_owned(), + family: classification.family, + code: code.to_owned(), + operation: classification.operation.to_owned(), + stage: classification.stage().to_owned(), + summary: summary.clone(), + correlation_id: BOUNDARY_CORRELATION_ID.to_owned(), + retry: classification.retry, + effect: classification.effect, + entered: auths_errors::EnteredBoundaries::default(), + recommended_action: classification.recommended_action, + execution_reference: None, + decision_reference: None, + receipt_reference: None, + causes: Vec::new(), + }; + let failure = js_sys::Error::new(&summary); + failure.set_name("AuthsError"); + let value = JsValue::from(failure); + if let Ok(fields) = serde_wasm_bindgen::to_value(&envelope) + && let Some(fields) = fields.dyn_ref::() + { + for entry in js_sys::Object::entries(fields).iter() { + let pair = js_sys::Array::from(&entry); + let _ = js_sys::Reflect::set(&value, &pair.get(0), &pair.get(1)); + } + } + value +} + +/// Truncates a human summary onto a character boundary within the registry's +/// bound without ever producing the empty summary the contract forbids. +fn bounded_summary(message: &str) -> String { + let trimmed = message.trim(); + if trimmed.is_empty() { + return "bounded Auths boundary failure".to_owned(); + } + if trimmed.len() <= auths_errors::MAX_SUMMARY_BYTES { + return trimmed.to_owned(); + } + let mut end = auths_errors::MAX_SUMMARY_BYTES; + while end > 0 && !trimmed.is_char_boundary(end) { + end -= 1; + } + trimmed[..end].to_owned() +} + +/// Classification of one stable code, projected for a JavaScript caller. +/// +/// A caller that receives a code from a newer Auths build asks this function +/// what the code means. It never fails: an unrecognized code is reported with +/// `known: false` and `effect: "possible"`, so a newer code is never swallowed +/// and never downgraded to `not-applied`. +/// # Errors +/// +/// Returns a structured Auths error only when the classification cannot cross +/// the ABI. +#[wasm_bindgen(js_name = classifyErrorCodeV1)] +pub fn classify_error_code_v1(code: &str) -> Result { + let classification = auths_errors::classify(code); + serde_wasm_bindgen::to_value(&classification).map_err(|_| { + js_error(EngineError::Abi( + "error classification cannot cross the ABI", + )) + }) } /// Verifies with the self-contained target V1 principal methods. @@ -4988,7 +4800,7 @@ pub fn self_contained_v1_configuration() -> Result<[u8; 32], EngineError> { pub fn configuration_v1() -> Result, JsValue> { self_contained_v1_configuration() .map(|bytes| bytes.to_vec()) - .map_err(|error| JsValue::from_str(&error.to_string())) + .map_err(js_error) } /// JavaScript-facing three-input portable V1 verifier. @@ -5008,7 +4820,7 @@ pub fn verify_v1( trusted_context_cbor: &[u8], ) -> Result, JsValue> { verify_self_contained_v1(proof_cbor, canonical_action_cbor, trusted_context_cbor) - .map_err(|error| JsValue::from_str(&error.to_string())) + .map_err(js_error) } #[derive(Deserialize)] @@ -5080,10 +4892,59 @@ pub enum EngineError { Profile(auths_profile_api::ProfileContractError), /// General identity encoding or validation failed. Identity(auths_identity::IdentityError), + /// A bounded production-client request or response was invalid. + Client(auths_production_client::ProductionClientError), + /// An MCP execution session rejected a transition or a bounded input. + Session(auths_profile_mcp::McpSessionError), + /// Receipt preparation, encoding, or attestation failed. + Receipt(auths_receipts::ReceiptError), + /// Receipt inspection or disclosure projection failed. + Inspection(auths_receipts::ReceiptInspectionError), /// A binding-level invariant could not be represented. Abi(&'static str), } +impl EngineError { + /// Names the stable registry code for one boundary failure. + /// + /// This module names the failure; `auths_errors` decides what the name + /// means. No effect state, retry class, or recommended action is chosen + /// here — see [`boundary_error`]. + /// + /// Every code named here is a pre-effect failure. The WASM module encodes, + /// decodes, plans, and prepares signing inputs; it opens no connection, + /// invokes no provider, and holds no durable state, so no failure it can + /// produce could have applied a real-world effect. `wasm_boundary_codes_ + /// are_registered_and_pre_effect` proves that against the registry. + const fn registry_code(&self) -> &'static str { + match self { + Self::Keri(_) => "core.native-runtime-unavailable", + Self::Registry(_) => "core.invalid-configuration", + Self::Model(_) + | Self::Codec(_) + | Self::Planning(_) + | Self::Author(_) + | Self::Workflow(_) + | Self::Mcp(_) + | Self::Profile(_) + | Self::Identity(_) + | Self::Client(_) + | Self::Session(_) + | Self::Receipt(_) + | Self::Inspection(_) + | Self::Abi(_) => "core.malformed-input", + } + } + + /// Every stable code this boundary can name. + #[cfg(test)] + const CODES: &'static [&'static str] = &[ + "core.native-runtime-unavailable", + "core.invalid-configuration", + "core.malformed-input", + ]; +} + impl fmt::Display for EngineError { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -5107,6 +4968,14 @@ impl fmt::Display for EngineError { Self::Mcp(error) => write!(formatter, "could not construct MCP action: {error}"), Self::Profile(error) => write!(formatter, "MCP profile contract failed: {error}"), Self::Identity(error) => write!(formatter, "identity descriptor failed: {error}"), + // These four variants exist only so the boundary can name a + // registry code for an error that previously reached JavaScript + // as its own bare `Display`. They add no prefix, because callers + // and tests match on the owning crate's stable code text. + Self::Client(error) => fmt::Display::fmt(error, formatter), + Self::Session(error) => fmt::Display::fmt(error, formatter), + Self::Receipt(error) => fmt::Display::fmt(error, formatter), + Self::Inspection(error) => fmt::Display::fmt(error, formatter), Self::Abi(message) => formatter.write_str(message), } } @@ -5174,10 +5043,131 @@ impl From for EngineError { } } +impl From for EngineError { + fn from(error: auths_production_client::ProductionClientError) -> Self { + Self::Client(error) + } +} + +impl From for EngineError { + fn from(error: auths_profile_mcp::McpSessionError) -> Self { + Self::Session(error) + } +} + +impl From for EngineError { + fn from(error: auths_receipts::ReceiptError) -> Self { + Self::Receipt(error) + } +} + +impl From for EngineError { + fn from(error: auths_receipts::ReceiptInspectionError) -> Self { + Self::Inspection(error) + } +} + +impl From for EngineError { + fn from(_: serde_wasm_bindgen::Error) -> Self { + Self::Abi("bounded value cannot cross the ABI") + } +} + +impl From for EngineError { + fn from(_: core::num::TryFromIntError) -> Self { + Self::Abi("bounded value is outside its integer range") + } +} + +impl From<&'static str> for EngineError { + fn from(message: &'static str) -> Self { + Self::Abi(message) + } +} + #[cfg(test)] mod tests { use super::*; + #[test] + fn wasm_boundary_codes_are_registered_and_pre_effect() { + for code in EngineError::CODES { + let classification = auths_errors::classify(code); + assert!( + classification.known, + "{code} is not in product/errors/v1/registry.json; this boundary mints no codes" + ); + assert_eq!( + classification.effect, + auths_errors::EffectState::NotApplied, + "{code} claims an effect this module cannot cause: it opens no connection, \ + invokes no provider, and holds no durable state" + ); + } + } + + #[test] + fn every_engine_error_variant_names_a_registered_code() { + let variants: [EngineError; 5] = [ + EngineError::Abi("bounded"), + EngineError::Keri(auths_did_keri::KeriError::UnsupportedKey), + EngineError::Model(auths_model::ModelError::InvalidPrincipal), + EngineError::Codec(auths_codec::CodecError::Malformed), + EngineError::Profile(auths_profile_api::ProfileContractError::Malformed), + ]; + for variant in &variants { + let code = variant.registry_code(); + assert!( + EngineError::CODES.contains(&code), + "{code} escaped the declared boundary code set" + ); + assert!(auths_errors::classify(code).known, "{code} is unregistered"); + } + } + + /// The boundary must project the registry's classification, never compute + /// one. A literal effect state, retry class, or recommended action in this + /// module would be a second definition of meaning. + #[test] + fn the_boundary_names_codes_and_decides_no_classification() { + // Only the shipping half of the module is under test; the assertions + // below necessarily name the vocabulary they forbid. + let source = include_str!("lib.rs"); + let shipping = source + .split_once("\n#[cfg(test)]\nmod tests {") + .expect("the test module marks the end of the shipping surface") + .0; + for banned in [ + "EffectState::", + "RetryClass::", + "RecommendedAction::", + "ErrorFamily::", + ] { + assert!( + !shipping.contains(banned), + "{banned} appears in the shipping WASM boundary; effect, retry, family, and \ + recommended action are decided by auths_errors::classify, not here" + ); + } + } + + #[test] + fn an_unrecognized_code_reaches_the_caller_as_possible() { + let classification = auths_errors::classify("future.minted-by-a-newer-build"); + assert!(!classification.known); + assert_eq!(classification.effect, auths_errors::EffectState::Possible); + } + + #[test] + fn a_summary_is_bounded_and_never_empty() { + assert_eq!(bounded_summary(" "), "bounded Auths boundary failure"); + let long = "é".repeat(auths_errors::MAX_SUMMARY_BYTES); + let bounded = bounded_summary(&long); + assert!(bounded.len() <= auths_errors::MAX_SUMMARY_BYTES); + assert!(!bounded.is_empty()); + assert!(long.starts_with(&bounded)); + } + fn raw_key_bundle() -> auths_model::ProofBundle { let fixture = auths_testkit::raw_key_chain(); auths_codec::decode_bundle(fixture.proof_bytes(), &VerifierLimits::default_deployment()) diff --git a/bindings/wasm/auths-proof-wasm/tests/node-smoke.cjs b/bindings/wasm/auths-proof-wasm/tests/node-smoke.cjs index 88ef2be8..7c359406 100644 --- a/bindings/wasm/auths-proof-wasm/tests/node-smoke.cjs +++ b/bindings/wasm/auths-proof-wasm/tests/node-smoke.cjs @@ -58,3 +58,160 @@ for (const exported of ["verifyV1", "configurationV1"]) { throw new Error(`generated TypeScript declarations omit ${exported}`); } } + +// --------------------------------------------------------------------------- +// The declared ABI is the whole ABI. +// +// Every symbol this module publishes to JavaScript is declared by exactly one +// manifest, and every declaration is published. Without this, a symbol can be +// added or a manifest can rot and nothing notices. +// --------------------------------------------------------------------------- + +const manifestDirectory = path.resolve(__dirname, ".."); +const manifests = [ + "identity-abi-v1.json", + "authoring-abi-v1.json", + "product-abi-v1.json", +].map((name) => ({ + name, + value: JSON.parse(fs.readFileSync(path.join(manifestDirectory, name), "utf8")), +})); + +const declaredBy = new Map(); +for (const { name, value } of manifests) { + for (const symbol of [...value.exports, ...value.types]) { + const previous = declaredBy.get(symbol); + if (previous !== undefined) { + throw new Error(`${symbol} is declared by both ${previous} and ${name}`); + } + declaredBy.set(symbol, name); + } +} + +const published = new Set(Object.keys(wasm)); +const undeclared = [...published].filter((symbol) => !declaredBy.has(symbol)).sort(); +const unpublished = [...declaredBy.keys()].filter((symbol) => !published.has(symbol)).sort(); +if (undeclared.length > 0 || unpublished.length > 0) { + throw new Error( + "the WASM ABI manifests and the published module disagree\n" + + ` exported but undeclared: ${JSON.stringify(undeclared)}\n` + + ` declared but not exported: ${JSON.stringify(unpublished)}`, + ); +} + +// Generic reference machinery must not come back through a later edit. +const removed = manifests + .flatMap(({ value }) => value.removedInV1?.exports ?? []); +if (removed.length === 0) { + throw new Error("no manifest records the removed generic reference exports"); +} +for (const symbol of removed) { + if (published.has(symbol) || declarations.includes(symbol)) { + throw new Error( + `${symbol} is generic reference machinery removed from the consumer package; ` + + "it must not be re-exported. See docs/target-state/PRELAUNCH_CODEBASE_CONSOLIDATION_SPEC.md.", + ); + } +} + +// --------------------------------------------------------------------------- +// The boundary carries meaning. +// +// A failure crossing the WASM boundary must arrive as a structured Auths error +// carrying a stable registry code, the effect state, the retry class, and the +// recommended action. A bare string destroys all four. +// --------------------------------------------------------------------------- + +const registry = JSON.parse( + fs.readFileSync(path.join(root, "product/errors/v1/registry.json"), "utf8"), +); +const registered = new Set(registry.definitions.map((definition) => definition.code)); +const EFFECT_STATES = ["not-applied", "possible", "applied"]; + +const probes = [ + ["decodePublicIdentityV2", () => wasm.decodePublicIdentityV2(new Uint8Array([0xff, 0xff, 0xff]))], + ["decodeSignedIdentityMessageV2", () => wasm.decodeSignedIdentityMessageV2(new Uint8Array([1, 2, 3]))], + ["canonicalPrincipalV1", () => wasm.canonicalPrincipalV1("!!!")], + ["decodeProductionResponseV1", () => wasm.decodeProductionResponseV1(new Uint8Array([0xff]))], + ["parsePrincipalStatusSnapshotV1", () => wasm.parsePrincipalStatusSnapshotV1(null)], +]; +for (const [name, probe] of probes) { + let thrown; + try { + probe(); + throw new Error(`${name}: adversarial input did not fail; the probe proves nothing`); + } catch (error) { + thrown = error; + } + if (typeof thrown === "string") { + throw new Error(`${name} threw the bare string ${JSON.stringify(thrown)}`); + } + if (!(thrown instanceof Error)) { + throw new Error(`${name} threw a ${typeof thrown}, not an Error`); + } + if (thrown.schema !== "auths.error/1") { + throw new Error(`${name} threw schema ${JSON.stringify(thrown.schema)}`); + } + if (!registered.has(thrown.code)) { + throw new Error( + `${name} reported code ${JSON.stringify(thrown.code)}, which is in no registry. ` + + "Bindings mint no error codes.", + ); + } + if (!EFFECT_STATES.includes(thrown.effect)) { + throw new Error(`${name} reported effect ${JSON.stringify(thrown.effect)}`); + } + for (const field of ["retry", "recommendedAction", "operation", "stage", "family", "summary"]) { + if (typeof thrown[field] !== "string" || thrown[field].length === 0) { + throw new Error(`${name} lost ${field} at the WASM boundary`); + } + } + const owner = registry.definitions.find((definition) => definition.code === thrown.code); + if (thrown.recommendedAction !== owner.recommendedAction) { + throw new Error( + `${name} reported recommendedAction ${JSON.stringify(thrown.recommendedAction)}; ` + + `the registry says ${JSON.stringify(owner.recommendedAction)}`, + ); + } + if (!owner.outcomes.some((outcome) => + outcome.effect === thrown.effect && outcome.retry === thrown.retry)) { + throw new Error( + `${name} reported an outcome ${thrown.code} does not declare: ` + + `${thrown.effect}/${thrown.retry}`, + ); + } +} + +// Rust owns the classification; this module projects it for all 48 codes. +for (const definition of registry.definitions) { + const classification = wasm.classifyErrorCodeV1(definition.code); + if (classification.known !== true) { + throw new Error(`${definition.code} is in the registry but classified as unknown`); + } + if (classification.recommendedAction !== definition.recommendedAction) { + throw new Error(`${definition.code} projected the wrong recommended action`); + } + if (!definition.outcomes.some((outcome) => + outcome.effect === classification.effect && outcome.retry === classification.retry)) { + throw new Error(`${definition.code} projected an outcome it does not declare`); + } +} + +// The fail-closed rule: a code minted by a newer build is never swallowed and +// never downgraded to not-applied. +for (const unknownCode of ["future.not-yet-invented", "", "core.", "mcp.handler-failed-v2"]) { + const classification = wasm.classifyErrorCodeV1(unknownCode); + if (classification.known !== false) { + throw new Error(`${JSON.stringify(unknownCode)} was reported as a known code`); + } + if (classification.effect !== "possible") { + throw new Error( + `an unrecognized code mapped to effect ${JSON.stringify(classification.effect)}; ` + + "contract 4.1 requires 'possible'", + ); + } + if (classification.retry !== "unknown" || + classification.recommendedAction !== "resume-and-reconcile") { + throw new Error("an unrecognized code must ask the caller to resume and reconcile"); + } +} diff --git a/product/errors/auths-errors/src/lib.rs b/product/errors/auths-errors/src/lib.rs index 9e8a63bc..d4ecd2d6 100644 --- a/product/errors/auths-errors/src/lib.rs +++ b/product/errors/auths-errors/src/lib.rs @@ -227,6 +227,95 @@ pub fn registry() -> impl Iterator { .chain(CUSTODY_ERRORS) } +/// Operation reported for a code this build's registry does not contain. +pub const UNRECOGNIZED_CODE_OPERATION: &str = "execute"; +/// Stage reported for a code this build's registry does not contain. +pub const UNRECOGNIZED_CODE_STAGE: &str = "unrecognized-code"; + +/// The registry's own classification of one stable code. +/// +/// This is the single owner of the answer to "what does this code mean?". +/// Transports and language bindings project it; they never recompute it, and +/// they never define a fourth [`EffectState`]. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CodeClassification { + /// False when this build's registry does not contain the code. + pub known: bool, + pub family: ErrorFamily, + pub operation: &'static str, + pub stages: &'static [&'static str], + pub retry: RetryClass, + pub effect: EffectState, + pub recommended_action: RecommendedAction, +} + +impl CodeClassification { + /// Reports the first declared stage, which is the only stage for every + /// single-stage definition and the default for the rest. + #[must_use] + pub fn stage(&self) -> &'static str { + self.stages + .first() + .copied() + .unwrap_or(UNRECOGNIZED_CODE_STAGE) + } +} + +/// Classifies one stable code, failing closed for a code this build does not +/// know. +/// +/// An unrecognized code is reported as [`EffectState::Possible`] with +/// [`RetryClass::Unknown`] and [`RecommendedAction::ResumeAndReconcile`]. A +/// newer code minted by a newer Auths build therefore reaches the caller +/// intact and is never swallowed, never downgraded to +/// [`EffectState::NotApplied`], and never renamed to a fourth effect value. +/// +/// When a definition permits several outcomes the projection reports the one a +/// caller must plan for: `Possible` dominates `Applied`, which dominates +/// `NotApplied`, because a caller who must reconcile has strictly more work +/// than one who must not repeat, who in turn has strictly more work than one +/// for whom nothing happened. +#[must_use] +pub fn classify(code: &str) -> CodeClassification { + let Some(definition) = registry().find(|candidate| candidate.code == code) else { + return CodeClassification { + known: false, + family: ErrorFamily::Runtime, + operation: UNRECOGNIZED_CODE_OPERATION, + stages: UNRECOGNIZED_CODE_STAGES, + retry: RetryClass::Unknown, + effect: EffectState::Possible, + recommended_action: RecommendedAction::ResumeAndReconcile, + }; + }; + let mut dominant = definition.outcomes[0]; + for outcome in definition.outcomes { + if effect_rank(outcome.effect) > effect_rank(dominant.effect) { + dominant = *outcome; + } + } + CodeClassification { + known: true, + family: definition.family, + operation: definition.operation, + stages: definition.stages, + retry: dominant.retry, + effect: dominant.effect, + recommended_action: definition.recommended_action, + } +} + +const UNRECOGNIZED_CODE_STAGES: &[&str] = &[UNRECOGNIZED_CODE_STAGE]; + +const fn effect_rank(effect: EffectState) -> u8 { + match effect { + EffectState::NotApplied => 0, + EffectState::Applied => 1, + EffectState::Possible => 2, + } +} + /// Validates namespaces, identities, bounds, and recovery combinations. /// /// # Errors @@ -1027,6 +1116,73 @@ mod tests { ); } + #[test] + fn classify_projects_the_registry_for_every_known_code() { + for definition in registry() { + let classification = classify(definition.code); + assert!( + classification.known, + "{} is in the registry", + definition.code + ); + assert_eq!(classification.family, definition.family); + assert_eq!(classification.operation, definition.operation); + assert_eq!(classification.stages, definition.stages); + assert_eq!( + classification.recommended_action, + definition.recommended_action + ); + assert!( + definition.outcomes.contains(&AllowedOutcome { + retry: classification.retry, + effect: classification.effect, + }), + "{} projected an outcome it does not declare", + definition.code + ); + for outcome in definition.outcomes { + assert!( + effect_rank(outcome.effect) <= effect_rank(classification.effect), + "{} projected a less demanding effect than it permits", + definition.code + ); + } + } + } + + #[test] + fn classify_fails_closed_for_a_code_this_build_does_not_know() { + let classification = classify("future.not-yet-invented"); + assert!(!classification.known); + assert_eq!(classification.effect, EffectState::Possible); + assert_eq!(classification.retry, RetryClass::Unknown); + assert_eq!( + classification.recommended_action, + RecommendedAction::ResumeAndReconcile + ); + assert_eq!(classification.operation, UNRECOGNIZED_CODE_OPERATION); + assert_eq!(classification.stage(), UNRECOGNIZED_CODE_STAGE); + } + + #[test] + fn classify_never_downgrades_an_unknown_code_to_not_applied() { + for code in [ + "", + "core.", + "mcp.handler-failed-v2", + "x".repeat(200).as_str(), + ] { + let classification = classify(code); + if !classification.known { + assert_eq!( + classification.effect, + EffectState::Possible, + "unknown code {code:?} must fail closed" + ); + } + } + } + #[test] fn possible_effect_requires_provider_entry_and_reference() { let mut value = input("mcp.handler-failed"); From ad8e25b209d1589e85a9fac7f8ed7ed9d963f172 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 00:32:19 +0100 Subject: [PATCH 13/61] bindings/python: make the pyo3 boundary a transport, not a tier (UNSIGNED) The Python native layer defined meaning it does not own and published machinery no product caller can reach. Three fixes, one gate each. Errors now cross structured. A new `errors` module raises `NativeAuthsError` carrying the stable code plus the effect state, retry class, and recommended action READ OUT OF `auths_errors::registry()` -- never written by hand here. A code the registry does not define fails closed to effect `possible` / retry `unknown`, so a newer Rust code can never be downgraded to "nothing happened" by an older binding. All ~90 `PyValueError::new_err` sites funnel through it. `TypeError` and the "already consumed" `RuntimeError`s deliberately stay unstructured: contract 5.7 forbids relabelling a programmer error as an authorization outcome. `decode_production_response_v1` now fails closed to `possible` -- the service may already have applied the effect and we merely cannot read what it said (contract 5.3). The generic reference verticals are gone. 34 symbols withdrawn: the 15 HTTP and 3 edge projections of `auths-profile-domains` (tier-1 reference Rust, never projected) and the 16-symbol `Application*` family, whose only constructor `application_action` let a Python caller name any profile id and hand Rust a body a PYTHON callback had canonicalised. That made Python a semantic owner. 147 module attributes -> 115. `check_contract.py` is now bidirectional: exported-and-undeclared fails too, which is how 23 symbols reached callers unreviewed. Panic safety, measured: an isolated pyo3 cdylib built `panic = "abort"` takes SIGABRT (exit 134) on a panic instead of raising -- pyo3's catch_unwind is defeated. The extension now builds under a new `python-extension` profile that inherits release and restores unwinding, and `src/lib.rs` refuses to compile under an aborting strategy. The crate additionally denies unwrap/expect/panic/indexing at the boundary; one real indexing panic in `plan_child_fields` was removed. Evidence: 3 Rust unit tests, 14 Python tests in `tests/test_native_boundary_contract.py`. Differential run of the pre-change extension against the same tree: 34 failures -> 22, zero regressions, the 12 new checks flipping red to green. Mutations proven: release build fails / python-extension build succeeds; fail-closed default flipped -> test red; smuggled export -> bidirectional gate fires. Co-Authored-By: Claude Opus 5 --- .../workflows/open-production-reference.yml | 2 +- .github/workflows/python-sdk.yml | 2 +- .github/workflows/sdk-recipes.yml | 2 +- Cargo.toml | 9 + bindings/python/Cargo.toml | 1 + bindings/python/native-abi-v2.json | 183 +++++----- bindings/python/src/authoring.rs | 40 ++- bindings/python/src/errors.rs | 264 ++++++++++++++ bindings/python/src/identity.rs | 8 +- bindings/python/src/lib.rs | 49 ++- bindings/python/src/mcp.rs | 68 ++-- bindings/python/src/production_client.rs | 17 +- bindings/python/src/receipts.rs | 36 +- bindings/python/src/result.rs | 8 +- bindings/python/src/runtime.rs | 12 +- bindings/python/src/workflow.rs | 60 ++-- bindings/python/tests/test_mcp_workflow.py | 8 +- .../tests/test_native_boundary_contract.py | 326 ++++++++++++++++++ bindings/python/tools/check_contract.py | 15 + .../agent-service/Dockerfile | 2 +- 20 files changed, 908 insertions(+), 204 deletions(-) create mode 100644 bindings/python/src/errors.rs create mode 100644 bindings/python/tests/test_native_boundary_contract.py diff --git a/.github/workflows/open-production-reference.yml b/.github/workflows/open-production-reference.yml index 045133ca..3c073992 100644 --- a/.github/workflows/open-production-reference.yml +++ b/.github/workflows/open-production-reference.yml @@ -109,7 +109,7 @@ jobs: npm pack ./bindings/typescript --pack-destination "$PWD/target/open-reference-artifacts" python -m venv .reference-venv .reference-venv/bin/python -m pip install maturin==1.9.6 pytest==9.0.2 pytest-asyncio==1.3.0 - .reference-venv/bin/maturin build --release --locked --manifest-path bindings/python/Cargo.toml --out target/open-reference-artifacts + .reference-venv/bin/maturin build --profile python-extension --locked --manifest-path bindings/python/Cargo.toml --out target/open-reference-artifacts cargo build --locked -p auths-node - name: Run packed TypeScript client across replicas working-directory: target/open-reference-consumer diff --git a/.github/workflows/python-sdk.yml b/.github/workflows/python-sdk.yml index 6f6ae8d0..3c9ad61c 100644 --- a/.github/workflows/python-sdk.yml +++ b/.github/workflows/python-sdk.yml @@ -128,7 +128,7 @@ jobs: python-version: "3.9" - run: python -m pip install maturin==1.9.6 - run: >- - maturin build --release --locked + maturin build --profile python-extension --locked --manifest-path bindings/python/Cargo.toml --out target/python-release-wheels - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 diff --git a/.github/workflows/sdk-recipes.yml b/.github/workflows/sdk-recipes.yml index d4ccc0f5..413f0dfe 100644 --- a/.github/workflows/sdk-recipes.yml +++ b/.github/workflows/sdk-recipes.yml @@ -64,7 +64,7 @@ jobs: - run: npm pack --pack-destination ../../target/recipe-artifacts working-directory: bindings/typescript - run: >- - .recipe-venv/bin/maturin build --release --locked + .recipe-venv/bin/maturin build --profile python-extension --locked --manifest-path bindings/python/Cargo.toml --out target/recipe-artifacts - run: npm ci diff --git a/Cargo.toml b/Cargo.toml index 403df624..2214ff7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -280,3 +280,12 @@ pedantic = "deny" lto = "thin" codegen-units = 1 panic = "abort" + +# The CPython extension module. `panic = "abort"` is correct for the standalone +# binaries and wrong for an extension: a panic there aborts the host +# interpreter instead of raising (measured: exit status 134), so pyo3's +# catch_unwind never runs. `bindings/python/build.rs` refuses to compile the +# extension under an aborting strategy. +[profile.python-extension] +inherits = "release" +panic = "unwind" diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml index 0aa95685..be83ccf9 100644 --- a/bindings/python/Cargo.toml +++ b/bindings/python/Cargo.toml @@ -17,6 +17,7 @@ auths-codec = { workspace = true, features = ["std"] } auths-custody.workspace = true auths-did-keri = { workspace = true, features = ["std"] } auths-did-key = { workspace = true, features = ["std"] } +auths-errors = { workspace = true, features = ["std"] } auths-identity.workspace = true auths-identity-raw-key.workspace = true auths-lifecycle = { workspace = true, features = ["std"] } diff --git a/bindings/python/native-abi-v2.json b/bindings/python/native-abi-v2.json index 8842c723..b32bae40 100644 --- a/bindings/python/native-abi-v2.json +++ b/bindings/python/native-abi-v2.json @@ -5,133 +5,124 @@ "wheelAbi": "abi3-py39", "semanticOwner": "Rust", "types": [ - "Principal", - "PrincipalDescriptor", "ApprovalPolicyReference", - "UnsignedObject", - "SignedObject", - "GrantRequest", + "AssurancePolicy", "AuthorityDiff", - "GrantPlan", - "GrantAuthority", - "SigningRequest", - "SigningTransaction", "AuthorizationPlan", "AuthorizationPlanBuilder", + "DevelopmentEd25519Key", + "GrantAuthority", + "GrantPlan", + "GrantRequest", + "IdentityDescriptorProjection", + "IdentityProjection", "McpAction", "McpCall", - "NativeMcpPlan", "McpCommand", - "McpPlanCommand", + "McpExecutionSession", "McpGatewayCall", - "AssurancePolicy", - "TrustAnchor", + "McpPlanCommand", + "McpSessionStep", + "McpSessionTerminal", + "NativeAuthsError", + "NativeDelegationExpandedError", + "NativeMcpPlan", + "NativeVerificationResult", + "Principal", + "PrincipalDescriptor", + "ReceiptPreparation", + "SignedObject", + "SigningRequest", + "SigningTransaction", "StatusSnapshot", + "TrustAnchor", "TrustedContext", - "VerifiedAction", - "NativeVerificationResult", - "IdentityProjection", - "IdentityDescriptorProjection", - "HttpCall", - "HttpAction", - "NativeHttpPlan", - "HttpCommand", - "HttpPlanCommand", - "HttpGatewayRequest", - "ApplicationAction", - "ApplicationActionPreparation", - "NativeApplicationPlan", - "ApplicationCommand", - "ApplicationPlanCommand", - "ApplicationGatewayCall" + "UnsignedObject", + "VerifiedAction" ], "operations": [ - "native_abi_version", - "generate_challenge_v1", "approval_policy_reference", - "validate_trusted_authority", - "validate_root_authority", + "attest_decision_receipt_v1", + "attest_execution_receipt_v1", + "authorize_mcp", + "begin_mcp_execution", + "begin_mcp_plan_member_execution", "bind_delegated_authority", - "root_grant", - "grant_request_from_statement", - "plan_child", - "plan_child_statement", - "plan_child_fields", - "principal_status_statement", - "grant_status_statement", - "prepare_signing", - "prepare_signing_transaction", - "prepare_mcp_action", - "validate_mcp_service", - "mcp_call", - "review_mcp_call", - "prepare_mcp_call_action", + "commit_canonical_v1", "commit_mcp_plan", "commit_plan_approval", - "authorize_mcp", + "commitments_equal_v1", + "compact_identity_descriptor_v1", + "compile_trusted_context", "consume_mcp_command", - "seal_mcp_plan_command", "consume_mcp_plan_command", - "status_snapshot", - "compile_trusted_context", - "self_contained_configuration", - "verify_v1", - "verify_many_v1", + "decode_diagnostic_result_v1", + "decode_identity_descriptor_v1", "decode_identity_v1", + "decode_production_request_v1", + "decode_production_response_v1", + "diagnostic_input_limits_v1", "encode_identity_descriptor_v1", - "decode_identity_descriptor_v1", - "compact_identity_descriptor_v1", - "identity_descriptor_signing_preimage_v1", + "encode_production_delegation_v1", + "encode_production_request_v1", "encode_public_identity_v1", - "raw_key_identity_v2", - "validate_raw_key_identity_v2", + "error_classification_v1", + "generate_challenge_v1", + "grant_request_from_statement", + "grant_status_statement", + "identity_descriptor_signing_preimage_v1", "identity_signing_preimage_v1", - "verify_ed25519_preimage_v1", - "http_call", - "review_http_call", - "commit_http_plan", - "prepare_http_action", - "authorize_http", - "consume_http_command", - "seal_http_plan_command", - "consume_http_plan_command", - "application_action", - "application_action_commitment_v1", - "commit_application_plan", - "prepare_application_action", - "authorize_application", - "consume_application_command", - "seal_application_plan_command", - "consume_application_plan_command", - "runtime_transition_v1", - "runtime_replay_v1", + "mcp_call", + "native_abi_version", + "plan_child", + "plan_child_fields", + "plan_child_statement", + "prepare_application_execution_receipt_v1", + "prepare_authorized_decision_receipt_v1", + "prepare_mcp_action", + "prepare_mcp_call_action", + "prepare_mcp_command_decision_receipt_v1", + "prepare_mcp_plan_decision_receipts_v1", + "prepare_receipt_disclosure_v1", + "prepare_signing", + "prepare_signing_transaction", + "principal_status_statement", + "production_client_contract_version_v1", + "project_sdk_event_json_v2", + "raw_key_identity_v2", + "resume_mcp_execution", + "review_mcp_call", + "root_grant", "runtime_additive_capacity_v1", + "runtime_application_execution_state_v1", "runtime_exclusive_capacity_v1", "runtime_execution_state_v1", - "runtime_application_execution_state_v1", - "decode_diagnostic_result_v1", - "commit_canonical_v1", - "diagnostic_input_limits_v1", - "commitments_equal_v1", - "prepare_receipt_disclosure_v1", - "production_client_contract_version_v1", - "encode_production_request_v1", - "decode_production_request_v1", - "decode_production_response_v1", - "encode_production_delegation_v1", - "project_sdk_event_json_v2" + "runtime_replay_v1", + "runtime_transition_v1", + "seal_mcp_plan_command", + "self_contained_configuration", + "status_snapshot", + "validate_mcp_service", + "validate_raw_key_identity_v2", + "validate_root_authority", + "validate_trusted_authority", + "verify_ed25519_preimage_v1", + "verify_many_v1", + "verify_raw_key_receipt_v1", + "verify_receipt_link_v1", + "verify_v1" ], "inspection": [ - "inspect_verified_action", - "inspect_unsigned", - "inspect_signed", - "inspect_plan", "inspect_mcp_action", - "inspect_trusted_context", + "inspect_plan", "inspect_raw_key_receipt_v1", - "parse_unsigned", + "inspect_signed", + "inspect_trusted_context", + "inspect_unsigned", + "inspect_verified_action", "parse_signed", "parse_trusted_context", + "parse_unsigned", "unsigned_from_signed" ], "capabilityInvariants": [ diff --git a/bindings/python/src/authoring.rs b/bindings/python/src/authoring.rs index 5b2a67b9..44e29b7b 100644 --- a/bindings/python/src/authoring.rs +++ b/bindings/python/src/authoring.rs @@ -28,7 +28,7 @@ use auths_ports::{PrincipalMethod, SignatureSuite}; use auths_profile_api::ActionProfile; use auths_profile_mcp::{McpProfile, McpToolCall}; use pyo3::{ - exceptions::{PyRuntimeError, PyTypeError, PyValueError}, + exceptions::{PyRuntimeError, PyTypeError}, prelude::*, types::PyBytes, }; @@ -694,11 +694,13 @@ fn prepare_mcp_action( let Value::Object(arguments) = serde_json::from_slice::(arguments_json).map_err(value_error)? else { - return Err(PyValueError::new_err("MCP arguments must be a JSON object")); + return Err(crate::errors::malformed_input( + "MCP arguments must be a JSON object", + )); }; let canonical_arguments = serde_json_canonicalizer::to_vec(&arguments).map_err(value_error)?; if canonical_arguments != arguments_json { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "MCP arguments must use canonical JSON encoding", )); } @@ -920,7 +922,7 @@ fn status_snapshot( .map_err(value_error)?, ), _ => { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "status kind must be principal or grant", )); } @@ -1139,7 +1141,11 @@ fn parse_signed(kind: &str, value: &[u8]) -> PyResult { "grant-status" => SignedObject::GrantStatus( auths_codec::decode_signed_grant_status(value, &limits).map_err(value_error)?, ), - _ => return Err(PyValueError::new_err("unsupported signed object kind")), + _ => { + return Err(crate::errors::malformed_input( + "unsupported signed object kind", + )); + } }; Ok(PySignedObject { inner }) } @@ -1160,7 +1166,11 @@ fn parse_unsigned(kind: &str, value: &[u8]) -> PyResult { "grant-status" => UnsignedObject::GrantStatus( auths_codec::decode_grant_status_statement(value, &limits).map_err(value_error)?, ), - _ => return Err(PyValueError::new_err("unsupported unsigned object kind")), + _ => { + return Err(crate::errors::malformed_input( + "unsupported unsigned object kind", + )); + } }; Ok(PyUnsignedObject { inner }) } @@ -1357,7 +1367,9 @@ fn principal_state(value: &str) -> PyResult { "active" => Ok(PrincipalState::Active), "revoked" => Ok(PrincipalState::Revoked), "superseded" => Ok(PrincipalState::Superseded), - _ => Err(PyValueError::new_err("invalid principal status state")), + _ => Err(crate::errors::malformed_input( + "invalid principal status state", + )), } } @@ -1366,7 +1378,7 @@ fn grant_state(value: &str) -> PyResult { "active" => Ok(GrantState::Active), "revoked" => Ok(GrantState::Revoked), "superseded" => Ok(GrantState::Superseded), - _ => Err(PyValueError::new_err("invalid grant status state")), + _ => Err(crate::errors::malformed_input("invalid grant status state")), } } @@ -1431,7 +1443,9 @@ fn participant_role(value: &str) -> PyResult { "intermediate" => Ok(ParticipantRole::Intermediate), "actor" => Ok(ParticipantRole::Actor), "external-issuer" => Ok(ParticipantRole::ExternalIssuer), - _ => Err(PyValueError::new_err("invalid assurance participant role")), + _ => Err(crate::errors::malformed_input( + "invalid assurance participant role", + )), } } @@ -1439,7 +1453,9 @@ fn assurance_quantifier(value: &str) -> PyResult { match value { "any" => Ok(AssuranceQuantifier::Any), "every" => Ok(AssuranceQuantifier::Every), - _ => Err(PyValueError::new_err("invalid assurance quantifier")), + _ => Err(crate::errors::malformed_input( + "invalid assurance quantifier", + )), } } @@ -1459,9 +1475,9 @@ pub(crate) fn configuration() -> PyResult<[u8; 32]> { fn array32(value: &[u8], label: &str) -> PyResult<[u8; 32]> { value .try_into() - .map_err(|_| PyValueError::new_err(format!("{label} must contain 32 bytes"))) + .map_err(|_| crate::errors::malformed_input(format!("{label} must contain 32 bytes"))) } pub(crate) fn value_error(error: impl std::fmt::Display) -> PyErr { - PyValueError::new_err(error.to_string()) + crate::errors::malformed_input(error) } diff --git a/bindings/python/src/errors.rs b/bindings/python/src/errors.rs new file mode 100644 index 00000000..9ea6b0bc --- /dev/null +++ b/bindings/python/src/errors.rs @@ -0,0 +1,264 @@ +//! Registry-bound error transport for the Python boundary. +//! +//! Rust owns what an error *means*. This module owns only how that meaning +//! survives the pyo3 call boundary, and it may not invent any of it: +//! +//! * The stable code is one of the codes `auths_errors::registry()` defines. +//! The boundary never mints a code of its own. +//! * `effect`, `retry`, and `recommended_action` are **read out of the +//! registry** for that code. They are never written here by hand. +//! * A code the registry does not define fails closed to +//! `effect = "possible"` / `retry = "unknown"` / +//! `recommended_action = "resume-and-reconcile"`, so a newer Rust code can +//! never be silently downgraded to "nothing happened" by an older binding +//! (contract 4.1, the fail-closed rule). +//! +//! `EffectState` has exactly three members, and this module can only ever +//! produce those three, because it projects `auths_errors::EffectState`. + +use auths_errors::{EffectState, ErrorDefinition, RecommendedAction, RetryClass, registry}; +use pyo3::{create_exception, exceptions::PyValueError, prelude::*}; + +create_exception!( + auths._native, + NativeAuthsError, + PyValueError, + "A boundary failure carrying its registry classification." +); + +/// The classifications the pyo3 boundary is allowed to assert. +/// +/// This set is deliberately tiny. Every member names a registry code and +/// carries the justification for why that code — and therefore that effect +/// state — is true of the code path that raises it. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum Boundary { + /// Bytes or identifiers the caller supplied that the canonical Rust model + /// rejected. Every such entry point is a pure decoder, canonicaliser, or + /// authoring step: it performs no effect, so `not-applied` is provable + /// rather than assumed. + MalformedInput, + /// The attenuation algebra refused what the caller asked for. Planning is + /// pure, so nothing was applied. + AuthorizationDenied, + /// A facility the native layer requires was unavailable (for example the + /// operating system's randomness). Nothing was attempted. + RuntimeUnavailable, + /// The boundary could not classify the failure. Fails closed to + /// `possible`: the caller must reconcile, never blindly retry. + Unclassified, +} + +impl Boundary { + pub(crate) const fn code(self) -> &'static str { + match self { + Self::MalformedInput => "core.malformed-input", + Self::AuthorizationDenied => "core.authorization-denied", + Self::RuntimeUnavailable => "core.native-runtime-unavailable", + Self::Unclassified => "core.outcome-unknown", + } + } +} + +/// The registry's answer for one stable code, or the fail-closed answer. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) struct Classification { + pub(crate) effect: EffectState, + pub(crate) retry: RetryClass, + pub(crate) recommended_action: RecommendedAction, + pub(crate) operation: &'static str, + pub(crate) stage: &'static str, + pub(crate) registered: bool, +} + +const UNCLASSIFIED: Classification = Classification { + effect: EffectState::Possible, + retry: RetryClass::Unknown, + recommended_action: RecommendedAction::ResumeAndReconcile, + operation: "execute", + stage: "unknown", + registered: false, +}; + +fn definition(code: &str) -> Option<&'static ErrorDefinition> { + registry().find(|candidate| candidate.code == code) +} + +/// Reads the registry's classification of `code`. +/// +/// An unrecognised code fails closed to `possible` / `unknown` / +/// `resume-and-reconcile`. A recognised code whose outcomes disagree about the +/// effect also fails closed, because in that case the boundary genuinely does +/// not know which outcome occurred. +pub(crate) fn classify(code: &str) -> Classification { + let Some(definition) = definition(code) else { + return UNCLASSIFIED; + }; + let Some(first) = definition.outcomes.first() else { + return UNCLASSIFIED; + }; + let effect = if definition + .outcomes + .iter() + .all(|outcome| outcome.effect == first.effect) + { + first.effect + } else { + EffectState::Possible + }; + let retry = if definition + .outcomes + .iter() + .all(|outcome| outcome.retry == first.retry) + { + first.retry + } else { + RetryClass::Unknown + }; + Classification { + effect, + retry, + recommended_action: definition.recommended_action, + operation: definition.operation, + stage: definition.stages.first().copied().unwrap_or("unknown"), + registered: true, + } +} + +pub(crate) const fn effect_wire(value: EffectState) -> &'static str { + match value { + EffectState::NotApplied => "not-applied", + EffectState::Possible => "possible", + EffectState::Applied => "applied", + } +} + +pub(crate) const fn retry_wire(value: RetryClass) -> &'static str { + match value { + RetryClass::Never => "never", + RetryClass::Safe => "safe", + RetryClass::Conditional => "conditional", + RetryClass::Unknown => "unknown", + } +} + +pub(crate) const fn action_wire(value: RecommendedAction) -> &'static str { + match value { + RecommendedAction::CorrectInput => "correct-input", + RecommendedAction::CorrectConfiguration => "correct-configuration", + RecommendedAction::InstallCompatibleRuntime => "install-compatible-runtime", + RecommendedAction::RetryExecution => "retry-execution", + RecommendedAction::SatisfyCondition => "satisfy-condition", + RecommendedAction::ResumeAndReconcile => "resume-and-reconcile", + RecommendedAction::InspectReceipt => "inspect-receipt", + RecommendedAction::ContactSupport => "contact-support", + } +} + +/// Builds the structured exception for `code`, attaching the registry's own +/// classification. Every attribute is always present, so a caller never has to +/// test for its existence before branching on the effect axis. +pub(crate) fn structured_as(code: &str, summary: &str) -> PyErr +where + T: pyo3::type_object::PyTypeInfo, +{ + let classification = classify(code); + let error = PyErr::new::(summary.to_owned()); + let attach = Python::attach(|py| -> PyResult<()> { + let value = error.value(py); + value.setattr("code", code)?; + value.setattr("effect", effect_wire(classification.effect))?; + value.setattr("retry", retry_wire(classification.retry))?; + value.setattr( + "recommended_action", + action_wire(classification.recommended_action), + )?; + value.setattr("operation", classification.operation)?; + value.setattr("stage", classification.stage)?; + value.setattr("summary", summary)?; + value.setattr("registered", classification.registered)?; + Ok(()) + }); + match attach { + Ok(()) => error, + Err(failure) => failure, + } +} + +/// Converts a Rust failure into the structured Python exception for `boundary`. +pub(crate) fn boundary_error(boundary: Boundary, error: impl core::fmt::Display) -> PyErr { + structured_as::(boundary.code(), &error.to_string()) +} + +/// The boundary's answer to "the caller handed us something the canonical Rust +/// model rejects". Pure entry point, so the effect axis is provably +/// `not-applied`. +pub(crate) fn malformed_input(error: impl core::fmt::Display) -> PyErr { + boundary_error(Boundary::MalformedInput, error) +} + +/// The classification `code` carries, as `(code, effect, retry, +/// recommended_action, registered)`. +/// +/// This is the only way a projection is allowed to learn what a code means: +/// it reads Rust's registry rather than keeping a copy of it. +#[pyfunction] +fn error_classification_v1(code: &str) -> (String, &'static str, &'static str, &'static str, bool) { + let classification = classify(code); + ( + code.to_owned(), + effect_wire(classification.effect), + retry_wire(classification.retry), + action_wire(classification.recommended_action), + classification.registered, + ) +} + +pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { + module.add( + "NativeAuthsError", + module.py().get_type::(), + )?; + module.add_function(wrap_pyfunction!(error_classification_v1, module)?)?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::{Boundary, EffectState, RetryClass, classify}; + + #[test] + fn an_unregistered_code_fails_closed_to_possible() { + let classification = classify("not.a.registry.code"); + assert_eq!(classification.effect, EffectState::Possible); + assert_eq!(classification.retry, RetryClass::Unknown); + assert!(!classification.registered); + } + + #[test] + fn every_boundary_classification_names_a_registry_code() { + for boundary in [ + Boundary::MalformedInput, + Boundary::RuntimeUnavailable, + Boundary::Unclassified, + ] { + assert!( + classify(boundary.code()).registered, + "{} is not in the Rust registry", + boundary.code() + ); + } + } + + #[test] + fn the_unclassified_boundary_is_the_fail_closed_one() { + assert_eq!( + classify(Boundary::Unclassified.code()).effect, + EffectState::Possible + ); + assert_eq!( + classify(Boundary::MalformedInput.code()).effect, + EffectState::NotApplied + ); + } +} diff --git a/bindings/python/src/identity.rs b/bindings/python/src/identity.rs index b3279fe3..348483b7 100644 --- a/bindings/python/src/identity.rs +++ b/bindings/python/src/identity.rs @@ -4,7 +4,7 @@ use auths_identity::{ }; use auths_identity_raw_key::RawKeyIdentityMethod; use auths_signature_ed25519::Ed25519Verifier; -use pyo3::{exceptions::PyValueError, prelude::*, types::PyBytes}; +use pyo3::{prelude::*, types::PyBytes}; #[pyclass(name = "IdentityProjection", frozen, module = "auths._native")] pub struct PyIdentityProjection { @@ -146,7 +146,9 @@ fn compact_identity_descriptor_v1<'py>( let identity = match IdentityPacket::decode(packet).map_err(value_error)? { IdentityPacket::PublicIdentity(value) => value, IdentityPacket::SignedMessage(_) => { - return Err(PyValueError::new_err("expected a public identity packet")); + return Err(crate::errors::malformed_input( + "expected a public identity packet", + )); } }; let encoded = identity @@ -311,5 +313,5 @@ pub fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { } fn value_error(error: impl std::fmt::Display) -> PyErr { - PyValueError::new_err(error.to_string()) + crate::errors::malformed_input(error) } diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index 8618ae3b..3d89652f 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -1,12 +1,43 @@ //! Native Python boundary for Auths protocol semantics. +//! +//! This crate is a transport, not a tier. It carries meaning that Rust already +//! owns across the pyo3 call boundary, and it defines none of its own: +//! +//! * Failures cross as [`errors::NativeAuthsError`], carrying the stable code +//! and the registry's own effect state, retry class, and recommended action. +//! * It projects no generic reference vertical. `auths-profile-domains` is +//! tier-1 reference Rust and is not reachable from Python, so a Python caller +//! cannot introduce a vertical whose canonical form lives in Python. +//! +//! The boundary must also be panic-free: see `deny` below. The workspace +//! release profile sets `panic = "abort"`, so a panic here does not raise in +//! Python — it aborts the host interpreter. The lints make the panicking +//! constructs unwritable rather than relying on catching them. #![forbid(unsafe_code)] +#![deny( + clippy::unwrap_used, + clippy::expect_used, + clippy::panic, + clippy::todo, + clippy::unimplemented, + clippy::unreachable, + clippy::indexing_slicing, + clippy::panic_in_result_fn, + clippy::exit +)] + +#[cfg(panic = "abort")] +compile_error!( + "the Python extension may not be built with panic = \"abort\": a panic would abort the host \ + CPython interpreter instead of raising. Build it with the `python-extension` profile \ + (`maturin build --profile python-extension`), which inherits release and restores unwinding \ + so pyo3 can convert a panic into a Python exception." +); -mod application; mod authoring; mod development; -mod domains; -mod http; +mod errors; mod identity; mod mcp; mod production_client; @@ -23,8 +54,12 @@ type ReviewProjection<'py> = (String, Vec<(String, String)>, Bound<'py, PyBytes> #[pyfunction] fn generate_challenge_v1(py: Python<'_>) -> PyResult> { let mut challenge = [0_u8; 32]; - getrandom::fill(&mut challenge) - .map_err(|_| pyo3::exceptions::PyRuntimeError::new_err("secure randomness unavailable"))?; + getrandom::fill(&mut challenge).map_err(|_| { + errors::boundary_error( + errors::Boundary::RuntimeUnavailable, + "secure randomness unavailable", + ) + })?; Ok(PyBytes::new(py, &challenge)) } @@ -32,12 +67,10 @@ fn generate_challenge_v1(py: Python<'_>) -> PyResult> { #[pymodule] fn _native(module: &Bound<'_, PyModule>) -> PyResult<()> { module.add_function(wrap_pyfunction!(generate_challenge_v1, module)?)?; + errors::register(module)?; authoring::register(module)?; - application::register(module)?; development::register(module)?; - domains::register(module)?; identity::register(module)?; - http::register(module)?; mcp::register(module)?; result::register(module)?; receipts::register(module)?; diff --git a/bindings/python/src/mcp.rs b/bindings/python/src/mcp.rs index c758477c..2f54140b 100644 --- a/bindings/python/src/mcp.rs +++ b/bindings/python/src/mcp.rs @@ -22,7 +22,7 @@ use auths_profile_mcp::{ McpToolCall, PROFILE_ID, PROFILE_VERSION, mcp_authority_commitment, }; use pyo3::{ - exceptions::{PyRuntimeError, PyTypeError, PyValueError}, + exceptions::{PyRuntimeError, PyTypeError}, prelude::*, types::PyBytes, }; @@ -434,7 +434,11 @@ impl PyMcpExecutionSession { "acquired" => McpReservationResult::Acquired, "exact-replay" => McpReservationResult::ExactReplay, "conflict" => McpReservationResult::Conflict, - _ => return Err(PyValueError::new_err("invalid MCP reservation result")), + _ => { + return Err(crate::errors::malformed_input( + "invalid MCP reservation result", + )); + } }; self.inner.accept_reservation(result).map_err(session_error) } @@ -458,7 +462,7 @@ impl PyMcpExecutionSession { "not-applied" => McpHandlerEffect::NotApplied, "applied" => McpHandlerEffect::Applied, "possible" => McpHandlerEffect::Possible, - _ => return Err(PyValueError::new_err("invalid MCP handler effect")), + _ => return Err(crate::errors::malformed_input("invalid MCP handler effect")), }; let cause = cause.map(parse_cause).transpose()?; let result = McpHandlerResult::parse(effect, output_json, cause).map_err(session_error)?; @@ -509,12 +513,12 @@ fn begin_mcp_execution( ) -> PyResult { let key: [u8; 32] = session_key .try_into() - .map_err(|_| PyValueError::new_err("MCP session key must contain 32 bytes"))?; + .map_err(|_| crate::errors::malformed_input("MCP session key must contain 32 bytes"))?; let action_commitment = command.action_commitment_bytes()?; let canonical_action = command.canonical_action_bytes()?; - let decision_receipt_id: [u8; 32] = decision_receipt_id - .try_into() - .map_err(|_| PyValueError::new_err("MCP decision receipt ID must contain 32 bytes"))?; + let decision_receipt_id: [u8; 32] = decision_receipt_id.try_into().map_err(|_| { + crate::errors::malformed_input("MCP decision receipt ID must contain 32 bytes") + })?; let authority_commitment = command.authority_commitment; let context_commitment = command.context_commitment; let inner = command @@ -569,7 +573,7 @@ fn resume_mcp_execution( ) -> PyResult { let key: [u8; 32] = session_key .try_into() - .map_err(|_| PyValueError::new_err("MCP session key must contain 32 bytes"))?; + .map_err(|_| crate::errors::malformed_input("MCP session key must contain 32 bytes"))?; let inner = McpExecutionSession::resume(McpSessionKey::new(key), reference, record_json) .map_err(session_error)?; Ok(PyMcpExecutionSession { inner }) @@ -602,12 +606,16 @@ fn validate_mcp_service(service: &str) -> PyResult<()> { #[pyfunction] fn mcp_call(service: &str, name: &str, arguments_json: &[u8]) -> PyResult { if arguments_json.is_empty() || arguments_json.len() > MAX_CANONICAL_CALL_BYTES { - return Err(PyValueError::new_err("MCP arguments exceed native limits")); + return Err(crate::errors::malformed_input( + "MCP arguments exceed native limits", + )); } let Value::Object(arguments) = serde_json::from_slice::(arguments_json).map_err(value_error)? else { - return Err(PyValueError::new_err("MCP arguments must be a JSON object")); + return Err(crate::errors::malformed_input( + "MCP arguments must be a JSON object", + )); }; Ok(PyMcpCall { inner: McpToolCall::new(service, name, arguments).map_err(value_error)?, @@ -634,7 +642,7 @@ fn review_mcp_call<'py>( #[pyfunction] fn commit_mcp_plan(py: Python<'_>, calls: Vec>) -> PyResult { if calls.is_empty() || calls.len() > 256 { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "MCP plan action count is outside native limits", )); } @@ -661,9 +669,9 @@ fn commit_mcp_plan(py: Python<'_>, calls: Vec>) -> PyResult>>()?; - let first = calls - .first() - .ok_or_else(|| PyValueError::new_err("MCP plan action count is outside native limits"))?; + let first = calls.first().ok_or_else(|| { + crate::errors::malformed_input("MCP plan action count is outside native limits") + })?; let resource_namespaces = vec![format!("mcp://{}", first.service())]; let audiences = vec![first.audience().map_err(value_error)?.to_string()]; Ok(PyNativeMcpPlan { @@ -699,7 +707,7 @@ fn prepare_mcp_call_action( let display = profile.review_display(&canonical).map_err(value_error)?; let challenge: [u8; 32] = challenge .try_into() - .map_err(|_| PyValueError::new_err("challenge must contain 32 bytes"))?; + .map_err(|_| crate::errors::malformed_input("challenge must contain 32 bytes"))?; let prepared = prepare_profile_action( canonical, call.inner.audience().map_err(value_error)?, @@ -734,7 +742,7 @@ fn authorize_mcp( context: PyRef<'_, PyTrustedContext>, ) -> PyResult<(NativeVerificationResult, Option)> { if grants.len() != grant_evidence.len() { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "each grant requires one evidence collection", )); } @@ -742,7 +750,7 @@ fn authorize_mcp( return Err(PyTypeError::new_err("signed action must be an action")); }; if action.envelope() != &prepared.envelope { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "signed action does not match its native preparation", )); } @@ -826,19 +834,19 @@ fn seal_mcp_plan_command( expected_commitment: &[u8], ) -> PyResult { if commands.is_empty() || commands.len() > 256 { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "MCP plan command count is outside native limits", )); } let expected: [u8; 32] = expected_commitment .try_into() - .map_err(|_| PyValueError::new_err("plan commitment must contain 32 bytes"))?; + .map_err(|_| crate::errors::malformed_input("plan commitment must contain 32 bytes"))?; let mut identities = std::collections::HashSet::with_capacity(commands.len()); if commands .iter() .any(|command| !identities.insert(command.as_ptr() as usize)) { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "MCP plan contains a duplicate command handle", )); } @@ -858,7 +866,7 @@ fn seal_mcp_plan_command( let commitment = ProfilePlanCommitment::commit(PROFILE_ID, PROFILE_VERSION, &borrowed) .map_err(value_error)?; if commitment.plan().as_bytes() != &expected { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "verified commands do not match the exact MCP plan", )); } @@ -941,20 +949,20 @@ fn begin_mcp_plan_member_execution( request_id: Option<&str>, ) -> PyResult { if member_index != command.next_member { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "MCP plan members must execute in order", )); } let member_count = command.receipt_bindings.len(); let plan_commitment = command.commitment; let member_index_u16 = u16::try_from(member_index) - .map_err(|_| PyValueError::new_err("MCP plan member is outside bounds"))?; + .map_err(|_| crate::errors::malformed_input("MCP plan member is outside bounds"))?; let member_count_u16 = u16::try_from(member_count) - .map_err(|_| PyValueError::new_err("MCP plan member count is outside bounds"))?; + .map_err(|_| crate::errors::malformed_input("MCP plan member count is outside bounds"))?; let binding = *command .receipt_bindings .get(member_index) - .ok_or_else(|| PyValueError::new_err("MCP plan member is outside bounds"))?; + .ok_or_else(|| crate::errors::malformed_input("MCP plan member is outside bounds"))?; let commands = command .commands .as_mut() @@ -971,10 +979,10 @@ fn begin_mcp_plan_member_execution( let canonical_action = auths_codec::encode_canonical_action(&canonical).map_err(value_error)?; let key: [u8; 32] = session_key .try_into() - .map_err(|_| PyValueError::new_err("MCP session key must contain 32 bytes"))?; - let receipt_id: [u8; 32] = decision_receipt_id - .try_into() - .map_err(|_| PyValueError::new_err("MCP decision receipt ID must contain 32 bytes"))?; + .map_err(|_| crate::errors::malformed_input("MCP session key must contain 32 bytes"))?; + let receipt_id: [u8; 32] = decision_receipt_id.try_into().map_err(|_| { + crate::errors::malformed_input("MCP decision receipt ID must contain 32 bytes") + })?; let session = McpExecutionSession::begin_plan_member( inner, binding.0, @@ -1185,7 +1193,7 @@ fn parse_cause(value: &str) -> PyResult { "timeout" => Ok(McpCause::Timeout), "unavailable" => Ok(McpCause::Unavailable), "unknown" => Ok(McpCause::Unknown), - _ => Err(PyValueError::new_err("invalid MCP cause category")), + _ => Err(crate::errors::malformed_input("invalid MCP cause category")), } } diff --git a/bindings/python/src/production_client.rs b/bindings/python/src/production_client.rs index b836f233..5bbb819a 100644 --- a/bindings/python/src/production_client.rs +++ b/bindings/python/src/production_client.rs @@ -3,7 +3,7 @@ use auths_production_client::{ RecoveryReference, decode_request, decode_response, encode_delegation_body, encode_request, project_sdk_event_v2, }; -use pyo3::{exceptions::PyValueError, prelude::*, types::PyBytes}; +use pyo3::{prelude::*, types::PyBytes}; #[pyfunction] fn production_client_contract_version_v1() -> u16 { @@ -37,11 +37,20 @@ fn encode_production_request_v1<'py>( Ok(PyBytes::new(py, &encoded)) } +/// Projects a response the service already produced. +/// +/// A response that cannot be decoded is **not** a caller input error. The +/// service may already have applied the effect and this client simply cannot +/// read what it said, so the failure fails closed to `effect: "possible"` +/// (contract 5.3). Reporting `not-applied` here would tell a caller that a +/// possibly-committed write is safe to retry. #[pyfunction] fn decode_production_response_v1(input: &[u8]) -> PyResult { decode_response(input) .and_then(|response| response.projection_json()) - .map_err(value_error) + .map_err(|error| { + crate::errors::boundary_error(crate::errors::Boundary::Unclassified, error) + }) } #[pyfunction] @@ -67,8 +76,8 @@ fn project_sdk_event_json_v2(input: &str) -> PyResult { } #[allow(clippy::needless_pass_by_value)] -fn value_error(error: impl ToString) -> PyErr { - PyValueError::new_err(error.to_string()) +fn value_error(error: impl core::fmt::Display) -> PyErr { + crate::errors::malformed_input(error) } pub fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { diff --git a/bindings/python/src/receipts.rs b/bindings/python/src/receipts.rs index 639836be..10d15195 100644 --- a/bindings/python/src/receipts.rs +++ b/bindings/python/src/receipts.rs @@ -13,7 +13,7 @@ use auths_receipts::{ prepare_decision_receipt, prepare_execution_receipt, verify_attested_decision_bytes, verify_attested_execution_bytes, verify_decision_attestation, verify_execution_attestation, }; -use pyo3::{exceptions::PyValueError, prelude::*, types::PyBytes}; +use pyo3::{prelude::*, types::PyBytes}; use serde_json::{Value, json}; #[derive(Clone)] @@ -63,7 +63,7 @@ pub(crate) fn prepare_decision( .as_slice() != proof_cbor { - return Err(PyValueError::new_err("proof is not canonical")); + return Err(crate::errors::malformed_input("proof is not canonical")); } let action = auths_codec::decode_canonical_action(canonical_action_cbor, &limits) .map_err(value_error)?; @@ -72,7 +72,7 @@ pub(crate) fn prepare_decision( .as_slice() != canonical_action_cbor { - return Err(PyValueError::new_err("action is not canonical")); + return Err(crate::errors::malformed_input("action is not canonical")); } let context = auths_codec::decode_verifier_context(trusted_context_cbor).map_err(value_error)?; @@ -81,7 +81,9 @@ pub(crate) fn prepare_decision( .as_slice() != trusted_context_cbor { - return Err(PyValueError::new_err("trusted context is not canonical")); + return Err(crate::errors::malformed_input( + "trusted context is not canonical", + )); } let signer = receipt_signer(verifier, verification_method, suite)?; let authority_commitment = auths_codec::proof_digest(&proof).map_err(value_error)?; @@ -140,7 +142,9 @@ fn prepare_application_execution_receipt_v1( suite: &str, ) -> PyResult { if command_bytes.is_empty() || command_bytes.len() > auths_model::HARD_MAX_ACTION_BYTES { - return Err(PyValueError::new_err("command bytes are outside bounds")); + return Err(crate::errors::malformed_input( + "command bytes are outside bounds", + )); } let decision = ReceiptId::new(array32(decision_receipt_id_bytes, "decision receipt id")?); let plan = plan_commitment @@ -149,7 +153,11 @@ fn prepare_application_execution_receipt_v1( let member = match (member_index, member_count) { (Some(index), Some(count)) => Some((index, count)), (None, None) => None, - _ => return Err(PyValueError::new_err("plan member position is incomplete")), + _ => { + return Err(crate::errors::malformed_input( + "plan member position is incomplete", + )); + } }; application_execution_lease_digest(idempotency_key, plan, member).map_err(value_error)?; let signer = receipt_signer(verifier, verification_method, suite)?; @@ -164,7 +172,7 @@ fn prepare_application_execution_receipt_v1( "failed" => ExecutionOutcome::Failed, "indeterminate" => ExecutionOutcome::Indeterminate, _ => { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "execution outcome cannot be attested", )); } @@ -239,11 +247,13 @@ fn verify_raw_key_receipt_v1( SignatureSuiteId::parse(suite).map_err(value_error)?, ); let descriptor = RawKeyDescriptor::decode(raw_key_evidence) - .map_err(|_| PyValueError::new_err("invalid raw-key receipt evidence"))?; + .map_err(|_| crate::errors::malformed_input("invalid raw-key receipt evidence"))?; if descriptor.principal().map_err(value_error)?.as_str() != verifier || descriptor.suite() != suite { - return Err(PyValueError::new_err("receipt key does not match signer")); + return Err(crate::errors::malformed_input( + "receipt key does not match signer", + )); } let expected = ReceiptId::new(array32(expected_id, "receipt id")?); let suite = auths_signature::Ed25519Suite::new().map_err(value_error)?; @@ -257,7 +267,7 @@ fn verify_raw_key_receipt_v1( verify_execution_attestation(attested, expected, &expected_verifier, &configured) .map_err(value_error)?; } - _ => return Err(PyValueError::new_err("unsupported receipt kind")), + _ => return Err(crate::errors::malformed_input("unsupported receipt kind")), } Ok(()) } @@ -275,7 +285,7 @@ fn verify_receipt_link_v1( let execution = verify_attested_execution_bytes(execution, execution_id).map_err(value_error)?; if execution.receipt().decision_receipt() != decision_id { - return Err(PyValueError::new_err("receipt linkage mismatch")); + return Err(crate::errors::malformed_input("receipt linkage mismatch")); } Ok(()) } @@ -508,11 +518,11 @@ fn receipt_signer( fn array32(value: &[u8], label: &str) -> PyResult<[u8; 32]> { value .try_into() - .map_err(|_| PyValueError::new_err(format!("{label} must contain 32 bytes"))) + .map_err(|_| crate::errors::malformed_input(format!("{label} must contain 32 bytes"))) } fn value_error(error: impl core::fmt::Display) -> PyErr { - PyValueError::new_err(error.to_string()) + crate::errors::malformed_input(error) } pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { diff --git a/bindings/python/src/result.rs b/bindings/python/src/result.rs index b58b4b93..d7804d49 100644 --- a/bindings/python/src/result.rs +++ b/bindings/python/src/result.rs @@ -6,7 +6,7 @@ use auths_model::{ }; use auths_ports::{PrincipalMethod, SignatureSuite}; use pyo3::{ - exceptions::{PyRuntimeError, PyTypeError, PyValueError}, + exceptions::{PyRuntimeError, PyTypeError}, prelude::*, types::PyBytes, }; @@ -134,7 +134,7 @@ fn verify_many_v1( inputs: Vec<(Vec, Vec, Vec)>, ) -> PyResult> { if inputs.is_empty() || inputs.len() > MAX_VERIFY_BATCH { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "verification batch is outside native limits", )); } @@ -145,7 +145,7 @@ fn verify_many_v1( .checked_add(value.2.len()) }); if total_bytes.is_none_or(|total| total > MAX_VERIFY_BATCH_BYTES) { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "verification batch is outside native limits", )); } @@ -193,7 +193,7 @@ const fn diagnostic_input_limits_v1() -> (usize, usize, usize) { #[pyfunction] fn commitments_equal_v1(left: &[u8], right: &[u8]) -> PyResult { if left.len() != 32 || right.len() != 32 { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "native commitments must contain 32 bytes", )); } diff --git a/bindings/python/src/runtime.rs b/bindings/python/src/runtime.rs index 12bf4ed8..d06750cc 100644 --- a/bindings/python/src/runtime.rs +++ b/bindings/python/src/runtime.rs @@ -5,7 +5,7 @@ use auths_lifecycle::{ exclusive_capacity_available, replay_code, transition_code, }, }; -use pyo3::{exceptions::PyValueError, prelude::*}; +use pyo3::prelude::*; #[pyfunction] #[allow(clippy::fn_params_excessive_bools, clippy::too_many_arguments)] @@ -80,7 +80,7 @@ fn runtime_execution_state_v1(outcome: &str) -> PyResult<&'static str> { match outcome { "succeeded" => Ok("committed"), "cancelled" | "outcome-unknown" => Ok("outcome-unknown"), - _ => Err(PyValueError::new_err( + _ => Err(crate::errors::malformed_input( "unsupported observed execution outcome", )), } @@ -92,7 +92,7 @@ fn runtime_application_execution_state_v1(outcome: &str) -> PyResult<&'static st "succeeded" => Ok("committed"), "failed" | "cancelled" => Ok("released"), "outcome-unknown" => Ok("outcome-unknown"), - _ => Err(PyValueError::new_err( + _ => Err(crate::errors::malformed_input( "unsupported application execution outcome", )), } @@ -109,7 +109,7 @@ fn parse_state(value: &str) -> PyResult { "outcome-unknown" => Ok(LifecycleState::OutcomeUnknown), "reconciled-committed" => Ok(LifecycleState::ReconciledCommitted), "reconciled-released" => Ok(LifecycleState::ReconciledReleased), - _ => Err(PyValueError::new_err("unsupported runtime state")), + _ => Err(crate::errors::malformed_input("unsupported runtime state")), } } @@ -141,7 +141,9 @@ fn parse_operation(value: &str) -> PyResult { "reconcile-effect" => Ok(OperationCode::ReconcileEffect), "reconcile-non-effect" => Ok(OperationCode::ReconcileNonEffect), "reconcile-inconclusive" => Ok(OperationCode::ReconcileInconclusive), - _ => Err(PyValueError::new_err("unsupported runtime operation")), + _ => Err(crate::errors::malformed_input( + "unsupported runtime operation", + )), } } diff --git a/bindings/python/src/workflow.rs b/bindings/python/src/workflow.rs index 431a4389..4ee8aecc 100644 --- a/bindings/python/src/workflow.rs +++ b/bindings/python/src/workflow.rs @@ -21,15 +21,15 @@ use auths_model::{ ResourceId, SignatureBytes, SignatureDescriptor, SignedGrant, StatusMethodId, StatusPolicy, Timestamp, ValidityWindow, }; -use pyo3::{ - create_exception, - exceptions::{PyRuntimeError, PyValueError}, - prelude::*, - types::PyBytes, -}; +use pyo3::{create_exception, exceptions::PyRuntimeError, prelude::*, types::PyBytes}; use subtle::ConstantTimeEq as _; -create_exception!(auths._native, NativeDelegationExpandedError, PyValueError); +create_exception!( + auths._native, + NativeDelegationExpandedError, + crate::errors::NativeAuthsError, + "An attenuation refusal, carrying its registry classification." +); const MAX_IDENTIFIER_BYTES: usize = 128; @@ -350,7 +350,7 @@ fn validate_trusted_authority( root: PyRef<'_, PyPrincipal>, ) -> PyResult<()> { if context.inner.configuration().as_bytes() != &configuration()? { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "trusted authority requires a different verifier configuration", )); } @@ -360,7 +360,7 @@ fn validate_trusted_authority( .iter() .any(|anchor| anchor.principal() == &root.inner) { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "trusted context does not contain the configured root", )); } @@ -376,7 +376,7 @@ fn validate_root_authority( profile_version: u16, ) -> PyResult { let SignedObject::Grant(grant) = &signed.inner else { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "root authority must be a signed grant", )); }; @@ -391,7 +391,7 @@ fn validate_root_authority( || statement.subject() != &subject.principal || statement.profile() != &profile { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "signed grant does not bind the trusted root, agent, and profile", )); } @@ -411,7 +411,7 @@ fn bind_delegated_authority( profile_version: u16, ) -> PyResult { let SignedObject::Grant(grant) = &signed.inner else { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "delegated authority must be a signed grant", )); }; @@ -428,7 +428,7 @@ fn bind_delegated_authority( || statement.parent() != Some(expected_parent) || grant.signature().descriptor() != &issuer.descriptor { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "signed child grant does not match its native delegation plan", )); } @@ -478,9 +478,12 @@ fn plan_child_fields( let action_constraint = match action_mode { "inherit" if action_digests.is_empty() => statement.action_constraint().clone(), "any-body" if action_digests.is_empty() => ActionConstraint::AnyBody, - "exact-body" if action_digests.len() == 1 => ActionConstraint::ExactBodyDigest( - Digest::new(array32(&action_digests[0], "exact body digest")?), - ), + "exact-body" if action_digests.len() == 1 => { + let digest = action_digests + .first() + .ok_or_else(|| value_error("exact body digest is required"))?; + ActionConstraint::ExactBodyDigest(Digest::new(array32(digest, "exact body digest")?)) + } "allowed-bodies" if !action_digests.is_empty() => { let values = action_digests .iter() @@ -489,7 +492,9 @@ fn plan_child_fields( ActionConstraint::AllowedBodyDigests(BodyDigestSet::new(values).map_err(value_error)?) } _ => { - return Err(PyValueError::new_err("invalid delegated action constraint")); + return Err(crate::errors::malformed_input( + "invalid delegated action constraint", + )); } }; let budget_ceiling = match (budget_mode, budget) { @@ -499,7 +504,7 @@ fn plan_child_fields( BudgetAlgebraId::parse(&algebra).map_err(value_error)?, value, )), - _ => return Err(PyValueError::new_err("invalid delegated budget")), + _ => return Err(crate::errors::malformed_input("invalid delegated budget")), }; let status_policy = match (status_mode, status) { ("inherit", None) => statement.status_policy().clone(), @@ -508,7 +513,11 @@ fn plan_child_fields( method: StatusMethodId::parse(&method).map_err(value_error)?, max_age: FreshnessLimit::new(maximum_age).map_err(value_error)?, }, - _ => return Err(PyValueError::new_err("invalid delegated status policy")), + _ => { + return Err(crate::errors::malformed_input( + "invalid delegated status policy", + )); + } }; let assurance_floor = assurance_floor.map_or_else( || Ok(statement.assurance_floor().clone()), @@ -712,7 +721,7 @@ impl PySigningTransaction { || !constant_time_equal(&request.transaction_digest(), &response_digest) || !policy_references_equal(&self.policy, &policy) { - return Err(PyValueError::new_err( + return Err(crate::errors::malformed_input( "approval response is not bound to the exact transaction", )); } @@ -723,7 +732,7 @@ impl PySigningTransaction { Ok(true) } "rejected" => Ok(false), - _ => Err(PyValueError::new_err("invalid approval decision")), + _ => Err(crate::errors::malformed_input("invalid approval decision")), } } @@ -831,7 +840,7 @@ pub fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { fn bounded_identifier(value: &str, label: &str) -> PyResult { if value.is_empty() || value.len() > MAX_IDENTIFIER_BYTES || value.chars().any(char::is_control) { - return Err(PyValueError::new_err(format!("invalid {label}"))); + return Err(crate::errors::malformed_input(format!("invalid {label}"))); } Ok(value.to_owned()) } @@ -852,7 +861,10 @@ fn constant_time_equal(left: &[u8; 32], right: &[u8; 32]) -> bool { fn planning_error(error: PlanningError) -> PyErr { match error { PlanningError::Expanded(dimension) => { - PyErr::new::(authority_dimension(dimension)) + crate::errors::structured_as::( + crate::errors::Boundary::AuthorizationDenied.code(), + authority_dimension(dimension), + ) } other => value_error(other), } @@ -876,5 +888,5 @@ const fn authority_dimension(value: AuthorityDimension) -> &'static str { fn array32(value: &[u8], label: &str) -> PyResult<[u8; 32]> { value .try_into() - .map_err(|_| PyValueError::new_err(format!("{label} must contain 32 bytes"))) + .map_err(|_| crate::errors::malformed_input(format!("{label} must contain 32 bytes"))) } diff --git a/bindings/python/tests/test_mcp_workflow.py b/bindings/python/tests/test_mcp_workflow.py index df29094a..73712440 100644 --- a/bindings/python/tests/test_mcp_workflow.py +++ b/bindings/python/tests/test_mcp_workflow.py @@ -712,7 +712,13 @@ async def sign(self, request: SigningRequest) -> SigningResponse: ) ) - await signer.started.wait() + # Bounded on purpose. If the plan is refused at member 0 the second + # signature never starts, `started` is never set, and an unbounded wait + # hangs the whole suite instead of reporting anything. Today that is + # exactly what happens: the plan is denied `budget-ceiling-exceeded` + # (the absent-budget convergence in f5c3589), so this raises TimeoutError + # and the failure is legible. No assertion below is relaxed. + await asyncio.wait_for(signer.started.wait(), timeout=30) operation.cancel() with pytest.raises(asyncio.CancelledError): await operation diff --git a/bindings/python/tests/test_native_boundary_contract.py b/bindings/python/tests/test_native_boundary_contract.py new file mode 100644 index 00000000..f1a64002 --- /dev/null +++ b/bindings/python/tests/test_native_boundary_contract.py @@ -0,0 +1,326 @@ +"""The pyo3 transport carries meaning; it does not define any. + +Three properties, each of which was false before this suite existed: + +1. A failure crossing the pyo3 boundary arrives with the Rust registry's own + classification attached -- stable code, effect state, retry class, + recommended action -- and an unrecognised code fails closed to ``possible``. +2. The boundary exports no generic reference vertical, so a Python caller + cannot define a vertical whose canonical form lives in Python. +3. A panic in the native layer cannot take the host interpreter down. + +Every expected value here is derived from ``product/errors/v1/registry.json`` +or from ``auths._native`` itself. Nothing is hardcoded from memory. +""" + +from __future__ import annotations + +import json +import subprocess +import sys +import types +from pathlib import Path + +import pytest + +from auths import _native + +_REPO_ROOT = Path(__file__).resolve().parents[3] +_PACKAGE_ROOT = Path(__file__).resolve().parents[1] +_REGISTRY = json.loads( + (_REPO_ROOT / "product/errors/v1/registry.json").read_text(encoding="utf-8") +) +_ABI = json.loads( + (_PACKAGE_ROOT / "native-abi-v2.json").read_text(encoding="utf-8") +) + +BY_CODE = {definition["code"]: definition for definition in _REGISTRY["definitions"]} + +# auths_errors::EffectState has exactly three members. There is no fourth. +EFFECT_STATES = ("not-applied", "possible", "applied") +RETRY_CLASSES = ("never", "safe", "conditional", "unknown") + + +def _exported() -> set[str]: + return {name for name in dir(_native) if not name.startswith("__")} + + +# --------------------------------------------------------------------------- +# 1. Errors cross structured. +# --------------------------------------------------------------------------- + + +def test_the_boundary_exception_carries_the_registry_classification() -> None: + with pytest.raises(_native.NativeAuthsError) as caught: + _native.decode_identity_v1(b"\xff\xff\xff") + error = caught.value + assert error.code in BY_CODE, ( + f"the boundary reported code {error.code!r}, which exists in no registry. " + "Bindings mint no error codes." + ) + definition = BY_CODE[error.code] + outcomes = definition["outcomes"] + assert error.effect in EFFECT_STATES + assert error.retry in RETRY_CLASSES + assert error.effect == outcomes[0]["effect"], ( + "the boundary reported an effect the registry does not declare for this code" + ) + assert error.retry == outcomes[0]["retry"] + assert error.recommended_action == definition["recommendedAction"] + assert error.registered is True + assert error.summary + + +def test_a_decoder_failure_is_provably_not_applied() -> None: + """A pure decoder performed no effect, so it may say so.""" + with pytest.raises(_native.NativeAuthsError) as caught: + _native.decode_identity_v1(b"\xff\xff\xff") + assert caught.value.effect == "not-applied" + + +def test_an_undecodable_service_response_fails_closed_to_possible() -> None: + """The service may already have applied the effect (contract 5.3). + + Reading `not-applied` off a response we could not parse would tell a caller + that a possibly-committed write is safe to retry blindly. + """ + with pytest.raises(_native.NativeAuthsError) as caught: + _native.decode_production_response_v1(b"\x01\x02\x03") + error = caught.value + assert error.effect == "possible", ( + "an unreadable service response was classified as 'nothing happened'" + ) + assert error.retry == "unknown" + assert error.recommended_action == "resume-and-reconcile" + + +def test_an_unregistered_code_fails_closed_to_possible() -> None: + code, effect, retry, action, registered = _native.error_classification_v1( + "not.a.registry.code" + ) + assert code == "not.a.registry.code" + assert registered is False + assert effect == "possible", ( + "an unknown code was not failed closed to 'possible'. A newer Rust code " + "must never be silently downgraded to 'nothing happened' by an older binding." + ) + assert retry == "unknown" + assert action == "resume-and-reconcile" + + +def test_every_registry_code_classifies_through_the_native_layer() -> None: + """Differential: the native classifier and the registry agree on all 48.""" + disagreements = [] + for definition in _REGISTRY["definitions"]: + code, effect, retry, action, registered = _native.error_classification_v1( + definition["code"] + ) + outcomes = definition["outcomes"] + expected_effect = ( + outcomes[0]["effect"] + if len({outcome["effect"] for outcome in outcomes}) == 1 + else "possible" + ) + expected_retry = ( + outcomes[0]["retry"] + if len({outcome["retry"] for outcome in outcomes}) == 1 + else "unknown" + ) + actual = (registered, effect, retry, action) + expected = (True, expected_effect, expected_retry, definition["recommendedAction"]) + if actual != expected: + disagreements.append(f"{code}: {actual} != {expected}") + assert not disagreements, "\n".join(disagreements) + assert len(_REGISTRY["definitions"]) > 0 + + +def test_the_native_layer_admits_no_effect_value_outside_the_three() -> None: + observed = { + _native.error_classification_v1(definition["code"])[1] + for definition in _REGISTRY["definitions"] + } + observed.add(_native.error_classification_v1("not.a.registry.code")[1]) + assert observed <= set(EFFECT_STATES), ( + f"the native layer produced effect value(s) {sorted(observed - set(EFFECT_STATES))} " + f"outside {list(EFFECT_STATES)}" + ) + + +def test_the_attenuation_refusal_is_a_structured_auths_error() -> None: + assert issubclass( + _native.NativeDelegationExpandedError, _native.NativeAuthsError + ), "a delegation refusal must carry the effect axis like every other failure" + assert issubclass(_native.NativeAuthsError, ValueError), ( + "the structured exception must stay catchable as ValueError; the boundary " + "already raised ValueError everywhere and callers depend on it" + ) + + +def test_a_contract_violation_is_not_dressed_up_as_an_authorization_outcome() -> None: + """Contract 5.7. Passing the wrong type is a programmer error, not a denial.""" + with pytest.raises(TypeError) as caught: + _native.decode_identity_v1("not bytes") + assert not isinstance(caught.value, _native.NativeAuthsError) + + +# --------------------------------------------------------------------------- +# 2. No generic reference vertical, and no Python-defined vertical. +# --------------------------------------------------------------------------- + +# The generic reference machinery that used to reach Python. `HttpAction` and +# `EdgeAction` come from `auths-profile-domains`, which is tier-1 reference +# Rust: broad by design and never projected. The `Application*` family was the +# generic "bring your own vertical" constructor set. +WITHDRAWN = ( + # auths-profile-domains, HTTP + "HttpCall", + "HttpAction", + "NativeHttpPlan", + "HttpCommand", + "HttpPlanCommand", + "HttpGatewayRequest", + "http_call", + "review_http_call", + "commit_http_plan", + "prepare_http_action", + "authorize_http", + "inspect_http_action", + "consume_http_command", + "seal_http_plan_command", + "consume_http_plan_command", + # auths-profile-domains, edge + "DomainActionProjection", + "canonicalize_edge_action_v1", + "parse_canonical_edge_action_v1", + # the generic vertical constructor and everything only it could build + "application_action", + "application_action_commitment_v1", + "commit_application_plan", + "prepare_application_action", + "authorize_application", + "seal_application_plan_command", + "consume_application_command", + "consume_application_plan_command", + "prepare_application_command_decision_receipt_v1", + "prepare_application_plan_decision_receipts_v1", + "ApplicationAction", + "ApplicationActionPreparation", + "ApplicationCommand", + "ApplicationGatewayCall", + "ApplicationPlanCommand", + "NativeApplicationPlan", +) + + +def test_the_generic_reference_verticals_are_not_reachable_from_python() -> None: + present = sorted(name for name in WITHDRAWN if hasattr(_native, name)) + assert not present, ( + "generic reference-vertical symbols are exported from the pyo3 layer again: " + + ", ".join(present) + + ". The pyo3 layer is a transport, not a tier: it may expose no symbol its " + "host tier does not expose." + ) + + +def test_a_python_caller_cannot_mint_a_canonical_action_for_its_own_profile() -> None: + """The inverted form of the capability this wave removed. + + Before: ``define_profile`` plus ``_native.application_action`` let a caller + name any profile id and hand the native layer a body that a *Python* + callback had canonicalised. That made Python a semantic owner. + """ + generic = [ + name + for name in _exported() + if isinstance(getattr(_native, name), types.BuiltinFunctionType) + and name.startswith("application_") + ] + assert not generic, ( + "the native layer exports a generic action constructor again: " + ", ".join(generic) + ) + + +def test_no_native_symbol_is_exported_without_being_declared() -> None: + declared = {*_ABI["types"], *_ABI["operations"], *_ABI["inspection"]} + undeclared = sorted(_exported() - declared) + assert not undeclared, ( + "native symbols are exported but undeclared in native-abi-v2.json: " + + ", ".join(undeclared) + ) + + +def test_every_declared_native_symbol_exists() -> None: + declared = {*_ABI["types"], *_ABI["operations"], *_ABI["inspection"]} + missing = sorted(declared - _exported()) + assert not missing, "declared but missing: " + ", ".join(missing) + + +# --------------------------------------------------------------------------- +# 3. Panic safety. +# --------------------------------------------------------------------------- + +_ADVERSARIAL_DRIVER = r""" +import itertools, sys, types +from auths import _native + +POOL = [ + b"", b"\xff" * 3, b"\x00" * 64, b"\x00" * 33, "", "\x00", "a" * 4096, + "auths.mcp/1", "not-a-profile", 0, 1, -1, 2**31, 2**63, 2**64 - 1, None, + [], {}, (), [b"\xff"], [("a", "b")], [None], "-", "/", "\x00\uffff", +] + +targets = [] +for name in sorted(dir(_native)): + if name.startswith("__"): + continue + value = getattr(_native, name) + if isinstance(value, (types.BuiltinFunctionType, type)): + targets.append((name, value)) + +for name, target in targets: + for arity in range(0, 4): + for args in itertools.product(POOL, repeat=arity): + try: + target(*args) + except BaseException as error: + if type(error).__name__ == "PanicException": + print("PANIC", name, args, file=sys.stderr) + raise SystemExit(3) +print("SURVIVED") +""" + + +def test_no_native_entry_point_can_be_panicked_from_python_input() -> None: + """Runs in a subprocess on purpose. + + Under ``panic = "abort"`` a panic is SIGABRT, not an exception: it would + kill the test runner rather than fail a test. A subprocess turns either + outcome -- abort or ``PanicException`` -- into a readable failure. + """ + completed = subprocess.run( # noqa: S603 + [sys.executable, "-c", _ADVERSARIAL_DRIVER], + capture_output=True, + text=True, + timeout=900, + cwd=str(_PACKAGE_ROOT), + ) + assert completed.returncode == 0, ( + "driving every native entry point with adversarial input killed the " + f"interpreter (returncode {completed.returncode}). " + f"stderr tail: {completed.stderr[-2000:]}" + ) + assert "SURVIVED" in completed.stdout + + +def test_the_extension_refuses_to_be_built_with_an_aborting_panic_strategy() -> None: + """The guard that makes the property above enforceable rather than lucky. + + `bindings/python/src/lib.rs` fails to compile under `panic = "abort"`, + because pyo3's catch_unwind cannot run when the process aborts first. + """ + source = (_PACKAGE_ROOT / "src/lib.rs").read_text(encoding="utf-8") + assert '#[cfg(panic = "abort")]' in source + assert "compile_error!" in source + workspace = (_REPO_ROOT / "Cargo.toml").read_text(encoding="utf-8") + assert "[profile.python-extension]" in workspace + assert 'panic = "unwind"' in workspace diff --git a/bindings/python/tools/check_contract.py b/bindings/python/tools/check_contract.py index bc3eb99d..689d5ef9 100644 --- a/bindings/python/tools/check_contract.py +++ b/bindings/python/tools/check_contract.py @@ -29,6 +29,21 @@ def main() -> None: for operation in (*abi["operations"], *abi["inspection"]): if not callable(getattr(_native, operation, None)): raise SystemExit(f"native ABI operation is unavailable: {operation}") + # The manifest is a two-way contract. Checking only that every declared + # symbol exists lets the native layer publish anything it likes as long as + # it also keeps its promises, which is how 23 undeclared symbols -- among + # them a whole generic reference vertical -- reached callers unreviewed. + declared = {*abi["types"], *abi["operations"], *abi["inspection"]} + exported = {name for name in dir(_native) if not name.startswith("__")} + undeclared = sorted(exported - declared) + if undeclared: + raise SystemExit( + "native symbols are exported but undeclared in native-abi-v2.json: " + + ", ".join(undeclared) + ) + for native_type in abi["types"]: + if native_type in abi["operations"] or native_type in abi["inspection"]: + raise SystemExit(f"native ABI symbol is declared twice: {native_type}") if capability["implementationStatus"] != "elite-repository-implementation-complete": raise SystemExit("capability evidence does not describe the implemented SDK") for module in runtime["excludedModules"]: diff --git a/demos/cross-company-incident-response/agent-service/Dockerfile b/demos/cross-company-incident-response/agent-service/Dockerfile index c640d173..e19bdee4 100644 --- a/demos/cross-company-incident-response/agent-service/Dockerfile +++ b/demos/cross-company-incident-response/agent-service/Dockerfile @@ -3,7 +3,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends python3 python3 RUN python3 -m venv /venv && /venv/bin/pip install --no-cache-dir maturin==1.9.4 WORKDIR /src COPY . . -RUN /venv/bin/maturin build --manifest-path bindings/python/Cargo.toml --release --out /wheels +RUN /venv/bin/maturin build --manifest-path bindings/python/Cargo.toml --profile python-extension --out /wheels FROM python:3.12-slim WORKDIR /app From 59642794e1680a4ec72422cc4d9e0e1f1adcb559 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 00:51:34 +0100 Subject: [PATCH 14/61] bindings/typescript: one effect axis, and a WASM boundary that carries it (UNSIGNED) EffectState is now exactly the three Rust-owned values. The invented fourth 'unknown' is deleted, and the fail-closed answer for a code this build does not recognize is no longer a TypeScript literal: cargo xtask error-registry now projects auths_errors::classify's own unrecognized-code branch into both bindings as UNRECOGNIZED_CODE, and product-errors.ts reads it. The packaged WASM namespace is wrapped once at load so every failure it raises arrives as the public AuthsError instead of a bare Error. wasm-bindgen handles are deliberately NOT wrapped: proxying one makes FinalizationRegistry.unregister miss and frees a live borrow, proved by test/unit/conformance.test.js. BEHAVIOUR CHANGE, recorded in the test that asserted the old contract: an unrecognized code was family/effect 'unknown' with action 'contact-support'; it is now the Rust classification -- effect 'possible', retry 'unknown', action 'resume-and-reconcile'. Acceptance: EA-2, EA-2b, EA-3 RED -> GREEN. --- .../python/python/auths/_error_registry.py | 15 +++ .../src/generated/error-registry.ts | 17 +++ bindings/typescript/src/identity.ts | 6 +- bindings/typescript/src/product-errors.ts | 114 ++++++++++++++++-- .../typescript/src/verifier/wasm-boundary.ts | 87 +++++++++++++ bindings/typescript/src/verifier/wasm.ts | 6 +- .../test/unit/product-errors.test.js | 26 +++- xtask/src/error_registry.rs | 25 ++-- 8 files changed, 272 insertions(+), 24 deletions(-) create mode 100644 bindings/typescript/src/verifier/wasm-boundary.ts diff --git a/bindings/python/python/auths/_error_registry.py b/bindings/python/python/auths/_error_registry.py index c5bf599e..71ea0567 100644 --- a/bindings/python/python/auths/_error_registry.py +++ b/bindings/python/python/auths/_error_registry.py @@ -1113,3 +1113,18 @@ ] } ''') + +# `auths_errors::classify` applied to a code this build's registry does not +# contain. A binding projects this; it never recomputes it. +UNRECOGNIZED_CODE: Final[dict[str, Any]] = json.loads(r'''{ + "known": false, + "family": "runtime", + "operation": "execute", + "stages": [ + "unrecognized-code" + ], + "retry": "unknown", + "effect": "possible", + "recommendedAction": "resume-and-reconcile" +} +''') diff --git a/bindings/typescript/src/generated/error-registry.ts b/bindings/typescript/src/generated/error-registry.ts index dc77563d..064e4521 100644 --- a/bindings/typescript/src/generated/error-registry.ts +++ b/bindings/typescript/src/generated/error-registry.ts @@ -1107,3 +1107,20 @@ export const ERROR_REGISTRY = { } ] } as const; + +/** + * `auths_errors::classify` applied to a code this build's registry does not + * contain. A binding projects this; it never recomputes it and never invents a + * fourth effect state. + */ +export const UNRECOGNIZED_CODE = { + "known": false, + "family": "runtime", + "operation": "execute", + "stages": [ + "unrecognized-code" + ], + "retry": "unknown", + "effect": "possible", + "recommendedAction": "resume-and-reconcile" +} as const; diff --git a/bindings/typescript/src/identity.ts b/bindings/typescript/src/identity.ts index d2c4be41..9552571d 100644 --- a/bindings/typescript/src/identity.ts +++ b/bindings/typescript/src/identity.ts @@ -6,6 +6,8 @@ * callers select concrete identity-method and signature-suite adapters explicitly. */ +import { guardWasmBoundary } from "./verifier/wasm-boundary.js"; + const DECODED_IDENTITY = Symbol("auths-decoded-identity"); const VALIDATED_IDENTITY = Symbol("auths-validated-identity"); const DECODED_MESSAGE = Symbol("auths-decoded-identity-message"); @@ -769,7 +771,9 @@ async function loadPackagedIdentityEngine(): Promise { if (loaded.identityAbiVersionV1() !== 1) { throw new TypeError("Auths WASM module has an unsupported neutral identity ABI"); } - return loaded; + // Same guard, same WASM namespace object, therefore the same proxy the + // workflow loader hands out: a failure on this path is an AuthsError too. + return guardWasmBoundary(loaded); })(); return packaged; } diff --git a/bindings/typescript/src/product-errors.ts b/bindings/typescript/src/product-errors.ts index 605589d4..7ec6dc05 100644 --- a/bindings/typescript/src/product-errors.ts +++ b/bindings/typescript/src/product-errors.ts @@ -1,12 +1,97 @@ -import { ERROR_REGISTRY } from "./generated/error-registry.js"; +import { ERROR_REGISTRY, UNRECOGNIZED_CODE } from "./generated/error-registry.js"; type Definition = (typeof ERROR_REGISTRY.definitions)[number]; export type AuthsErrorCode = Definition["code"] | (string & {}); -export type ErrorFamily = Definition["family"] | "unknown"; +export type ErrorFamily = Definition["family"]; + +/** + * Answers *may I retry?* — `auths_errors::RetryClass`. + * + * This is not the same question as {@link NextCall}, which answers *what should + * I call next?*. The two shared an identifier once; they never will again. + */ export type RetryClass = Definition["outcomes"][number]["retry"]; -export type EffectState = Definition["outcomes"][number]["effect"] | "unknown"; + +/** + * Answers *did the real-world effect happen?* — `auths_errors::EffectState`. + * + * Exactly three members, owned by Rust. `possible` means WE DO NOT KNOW: a + * caller who reads it must reconcile before retrying. There is no fourth value + * and no second spelling; a code this build does not recognize fails closed to + * `possible`, never to `not-applied`. + */ +export type EffectState = Definition["outcomes"][number]["effect"]; export type RecommendedAction = Definition["recommendedAction"]; +export type ProductStage = Definition["stages"][number] | typeof UNRECOGNIZED_CODE.stages[number]; + +/** + * The five product verbs — `ProductVerb` in Rust. The wire field is `verb`. + */ +export type ProductVerb = "create" | "delegate" | "execute" | "resume" | "verify"; + +const PRODUCT_VERBS: readonly ProductVerb[] = Object.freeze([ + "create", "delegate", "execute", "resume", "verify", +]); + +export function isProductVerb(value: unknown): value is ProductVerb { + return typeof value === "string" && (PRODUCT_VERBS as readonly string[]).includes(value); +} + +/** + * Rust's classification of one stable code, projected from the generated + * registry. TypeScript never recomputes a classification and never mints a + * code: an unrecognized code takes the generated fail-closed answer. + */ +export interface CodeClassification { + /** False when this build's registry does not contain the code. */ + readonly known: boolean; + readonly family: ErrorFamily; + readonly operation: string; + readonly stage: string; + readonly retry: RetryClass; + readonly effect: EffectState; + readonly recommendedAction: RecommendedAction; +} + +/** + * Classifies one stable code exactly as `auths_errors::classify` does. + * + * When a definition permits several outcomes the dominant one is reported — + * `possible` over `applied` over `not-applied` — because a caller who must + * reconcile has strictly more work than one who must not repeat. + */ +export function classifyErrorCode(code: string): CodeClassification { + const definition = definitions.get(code); + if (definition === undefined) { + return Object.freeze({ + known: false, + family: UNRECOGNIZED_CODE.family, + operation: UNRECOGNIZED_CODE.operation, + stage: UNRECOGNIZED_CODE.stages[0], + retry: UNRECOGNIZED_CODE.retry, + effect: UNRECOGNIZED_CODE.effect, + recommendedAction: UNRECOGNIZED_CODE.recommendedAction, + }); + } + let dominant = definition.outcomes[0]!; + for (const outcome of definition.outcomes) { + if (effectRank(outcome.effect) > effectRank(dominant.effect)) dominant = outcome; + } + return Object.freeze({ + known: true, + family: definition.family, + operation: definition.operation, + stage: definition.stages[0], + retry: dominant.retry, + effect: dominant.effect, + recommendedAction: definition.recommendedAction, + }); +} + +function effectRank(effect: EffectState): number { + return effect === "possible" ? 2 : effect === "applied" ? 1 : 0; +} export type CauseCategory = | "cancelled" | "conflict" @@ -225,6 +310,14 @@ function parseDetails(input: unknown): AuthsErrorDetails { }); } +/** + * Fails closed for a code this build's registry does not contain. + * + * Every classification field is the generated projection of + * `auths_errors::classify`, so a code minted by a newer Auths reaches the caller + * with its identity intact and with `effect: "possible"` — never swallowed, + * never downgraded to `not-applied`, and never renamed to a fourth value. + */ function parseUnknownDetails( value: Record, code: string, @@ -236,18 +329,19 @@ function parseUnknownDetails( const rawCauses = array(value.causes); if (rawCauses.length > 8) throw new TypeError("Auths error has too many cause categories"); const unknownCauses: readonly CauseCategory[] = rawCauses.length === 0 ? [] : ["unknown"]; + const classification = classifyErrorCode(code); return Object.freeze({ schema: "auths.error/1", - family: "unknown", + family: classification.family, code, - operation: "unknown", - stage: "unknown", - summary: "Unknown Auths error code", + operation: classification.operation, + stage: classification.stage, + summary: "Unrecognized Auths error code", correlationId, - retry: "unknown", - effect: "unknown", + retry: classification.retry, + effect: classification.effect, entered: Object.freeze({ approval: false, signer: false, state: false, credential: false, provider: false }), - recommendedAction: "contact-support", + recommendedAction: classification.recommendedAction, causes: Object.freeze(unknownCauses), }); } diff --git a/bindings/typescript/src/verifier/wasm-boundary.ts b/bindings/typescript/src/verifier/wasm-boundary.ts new file mode 100644 index 00000000..68cc3c3c --- /dev/null +++ b/bindings/typescript/src/verifier/wasm-boundary.ts @@ -0,0 +1,87 @@ +import { AuthsError } from "../product-errors.js"; + +/** + * The WASM boundary guard. + * + * Rust hands JavaScript a real `Error` whose own properties are an + * `auths.error/1` envelope. That envelope is the Rust-owned error model; this + * module is the single place where it becomes the public {@link AuthsError}, so + * no caller of any entry point ever has to know that a failure originated + * across an ABI. + * + * The guard wraps the packaged module once, at load, rather than asking every + * call site to remember a `try`/`catch`. A call site that forgets is a silent + * hole in the effect axis; there is no call site to forget here. + */ + +const wrappers = new WeakMap(); + +/** + * Rehydrates one thrown value into the public error type. + * + * A value that is not a Rust error envelope is returned untouched: a + * programmer error or a contract violation raised by TypeScript itself must + * never be relabelled as an authorization outcome (contract 5.7). + */ +export function boundaryError(thrown: unknown): unknown { + if (thrown instanceof AuthsError) return thrown; + if (typeof thrown !== "object" || thrown === null) return thrown; + if ((thrown as { readonly schema?: unknown }).schema !== "auths.error/1") return thrown; + return AuthsError.parse(thrown); +} + +/** + * Wraps the packaged WASM namespace so every failure it raises — from a + * top-level call, a constructor, or a method on an object it returned — + * reaches the caller as an {@link AuthsError}. + */ +export function guardWasmBoundary(module: T): T { + return guardObject(module) as T; +} + +function guardObject(value: T): T { + const existing = wrappers.get(value); + if (existing !== undefined) return existing as T; + const proxy = new Proxy(value, { + get(target, property, receiver) { + const member: unknown = Reflect.get(target, property, receiver); + return typeof member === "function" ? guardFunction(member as CallableFunction) : member; + }, + }); + wrappers.set(value, proxy); + return proxy as T; +} + +/** + * Guards one boundary-crossing call. + * + * The value a guarded call RETURNS is handed back untouched, including a + * `wasm-bindgen` handle. Wrapping a handle is not safe: `wasm-bindgen` tracks + * ownership and finalization by object identity, so a proxy makes + * `FinalizationRegistry.unregister` miss and lets a live borrow be freed — + * observed as "attempted to take ownership of Rust value while it was + * borrowed". Handles therefore stay raw, and the methods that own them are + * guarded where the SDK holds them, never by re-wrapping the handle. + */ +function guardFunction(value: CallableFunction): CallableFunction { + const existing = wrappers.get(value); + if (existing !== undefined) return existing as CallableFunction; + const proxy = new Proxy(value, { + apply(target, thisArg, argumentsList) { + try { + return Reflect.apply(target as (...args: unknown[]) => unknown, thisArg, argumentsList); + } catch (error) { + throw boundaryError(error); + } + }, + construct(target, argumentsList, newTarget) { + try { + return Reflect.construct(target as unknown as new (...args: unknown[]) => object, argumentsList, newTarget); + } catch (error) { + throw boundaryError(error); + } + }, + }); + wrappers.set(value, proxy); + return proxy; +} diff --git a/bindings/typescript/src/verifier/wasm.ts b/bindings/typescript/src/verifier/wasm.ts index b42b08b0..6063ed22 100644 --- a/bindings/typescript/src/verifier/wasm.ts +++ b/bindings/typescript/src/verifier/wasm.ts @@ -1,6 +1,7 @@ import type { WorkflowWasmEngine } from "../workflow.js"; import type { PortableWasmEngine } from "./result.js"; import { registerPackagedEngine } from "./packaged-registry.js"; +import { guardWasmBoundary } from "./wasm-boundary.js"; export type PackagedWorkflowEngine = WorkflowWasmEngine & PortableWasmEngine; @@ -94,5 +95,8 @@ async function loadPackagedWorkflowEngineOnce(): Promise ) { throw new TypeError("Auths WASM module omitted workflow authoring exports"); } - return registerPackagedEngine(loaded); + // Registered AFTER guarding so the guarded namespace is the only engine + // object any consumer ever holds: `isPackagedEngine` compares identity, and + // an unguarded engine must not be able to satisfy it. + return registerPackagedEngine(guardWasmBoundary(loaded)); } diff --git a/bindings/typescript/test/unit/product-errors.test.js b/bindings/typescript/test/unit/product-errors.test.js index d8919fe0..8e8c0e5e 100644 --- a/bindings/typescript/test/unit/product-errors.test.js +++ b/bindings/typescript/test/unit/product-errors.test.js @@ -10,6 +10,10 @@ import { } from "../../dist/product-errors.js"; import { mcp } from "../../dist/profiles.js"; import { decodeReceipt } from "../../dist/verify.js"; +// Rust's own fail-closed answer, generated by `cargo xtask error-registry`. +// Asserting against this rather than against literals means the test tracks +// `auths_errors::classify` instead of freezing a second copy of it here. +import { UNRECOGNIZED_CODE } from "../../dist/generated/error-registry.js"; const fixtures = JSON.parse(await readFile( new URL("../../../../product/fixtures/v1/errors/manifest.json", import.meta.url), @@ -58,7 +62,13 @@ test("provider failures collapse to bounded cause categories", () => { assert.doesNotMatch(causeCategoryFrom(failure), /credential|boundary/); }); -test("future error codes remain bounded without inferring retry or effect", () => { +// BEHAVIOUR CHANGE (contract 4.1, 5.5): an unrecognized code used to be +// reported as family "unknown" / effect "unknown" / action "contact-support". +// `EffectState` has exactly three members and "unknown" was a binding-invented +// fourth, so the whole fallback now projects `auths_errors::classify`, which +// fails closed to `possible` / `unknown` retry / `resume-and-reconcile`. The +// untrusted values on the envelope are still ignored — that part is unchanged. +test("future error codes fail closed to the Rust classification, ignoring what the envelope claims", () => { const future = { ...fixtures.fixtures[0], code: "future.new-code", @@ -69,11 +79,17 @@ test("future error codes remain bounded without inferring retry or effect", () = }; const error = AuthsError.parse(future); assert.equal(error.code, "future.new-code"); - assert.equal(error.family, "unknown"); - assert.equal(error.retry, "unknown"); - assert.equal(error.effect, "unknown"); + assert.equal(error.family, UNRECOGNIZED_CODE.family); + assert.equal(error.retry, UNRECOGNIZED_CODE.retry); + assert.equal(error.effect, UNRECOGNIZED_CODE.effect); assert.equal(error.executionReference, undefined); - assert.equal(error.recommendedAction, "contact-support"); + assert.equal(error.recommendedAction, UNRECOGNIZED_CODE.recommendedAction); + // The envelope asked for retry "safe" and effect "applied"; neither survived. + assert.notEqual(error.retry, future.retry); + assert.notEqual(error.effect, future.effect); + // Fail CLOSED specifically: never the effect a caller may blindly retry on. + assert.equal(error.effect, "possible"); + assert.notEqual(error.effect, "not-applied"); }); test("future profile and receipt versions fail before interpretation", () => { diff --git a/xtask/src/error_registry.rs b/xtask/src/error_registry.rs index e9b25647..c8267fa8 100644 --- a/xtask/src/error_registry.rs +++ b/xtask/src/error_registry.rs @@ -3,6 +3,10 @@ use auths_errors::{ CauseCategory, EffectState, EnteredBoundaries, ErrorEnvelope, ErrorEnvelopeInput, }; +/// A code no registry may ever contain, used to read the fail-closed branch of +/// `auths_errors::classify` without hardcoding its answer. +const UNRECOGNIZED_PROBE: &str = "auths.unrecognized-code-probe"; + const OUTPUTS: [&str; 5] = [ "product/errors/v1/registry.json", "product/fixtures/v1/errors/manifest.json", @@ -49,11 +53,15 @@ pub(crate) fn error_registry(update: bool) -> Result<(), String> { .collect::>()?, }; let registry_json = pretty_json(®istry)?; + // The fail-closed answer for a code this build's registry does not contain + // is `auths_errors::classify`'s, not a binding's. Projecting it here is what + // stops TypeScript and Python from each inventing their own fourth state. + let unrecognized_json = pretty_json(&auths_errors::classify(UNRECOGNIZED_PROBE))?; let outputs = [ registry_json.clone(), pretty_json(&fixtures)?, - render_typescript(®istry_json), - render_python(®istry_json), + render_typescript(®istry_json, &unrecognized_json), + render_python(®istry_json, &unrecognized_json), render_docs(®istry), ]; for (path, bytes) in OUTPUTS.iter().zip(outputs) { @@ -125,19 +133,22 @@ fn pretty_json(value: &impl Serialize) -> Result, String> { Ok(bytes) } -fn render_typescript(registry: &[u8]) -> Vec { +fn render_typescript(registry: &[u8], unrecognized: &[u8]) -> Vec { let json = String::from_utf8_lossy(registry); + let unrecognized = String::from_utf8_lossy(unrecognized); format!( - "export const ERROR_REGISTRY = {} as const;\n", - json.trim_end() + "export const ERROR_REGISTRY = {} as const;\n\n/**\n * `auths_errors::classify` applied to a code this build's registry does not\n * contain. A binding projects this; it never recomputes it and never invents a\n * fourth effect state.\n */\nexport const UNRECOGNIZED_CODE = {} as const;\n", + json.trim_end(), + unrecognized.trim_end() ) .into_bytes() } -fn render_python(registry: &[u8]) -> Vec { +fn render_python(registry: &[u8], unrecognized: &[u8]) -> Vec { let json = String::from_utf8_lossy(registry); + let unrecognized = String::from_utf8_lossy(unrecognized); format!( - "from __future__ import annotations\n\nimport json\nfrom typing import Any, Final\n\nERROR_REGISTRY: Final[dict[str, Any]] = json.loads(r'''{json}''')\n" + "from __future__ import annotations\n\nimport json\nfrom typing import Any, Final\n\nERROR_REGISTRY: Final[dict[str, Any]] = json.loads(r'''{json}''')\n\n# `auths_errors::classify` applied to a code this build's registry does not\n# contain. A binding projects this; it never recomputes it.\nUNRECOGNIZED_CODE: Final[dict[str, Any]] = json.loads(r'''{unrecognized}''')\n" ) .into_bytes() } From b4c6674a6f3e5c3c615db7b2681a18679dace94d Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 01:17:46 +0100 Subject: [PATCH 15/61] product/mcp: let the profile name its own outcome, and TypeScript read it (UNSIGNED) A failed MCP execution reached the caller as bare {kind:'recoverable'} with no code, no effect, and no retry class -- a handler that threw and a handler that produced unusable output were the same value. The caller could not tell that a possibly-applied effect must be reconciled rather than blindly retried. The fix puts each answer with its owner: - McpTerminal::registry_code (auths-profile-mcp) names every non-completed outcome with a stable registry code. The session now RETAINS the handler cause it used to discard in accept_effect, and records the recovery kind and whether it was resumed, because those are what separate mcp.handler-failed from mcp.invalid-handler-output, mcp.handler-timeout, mcp.reconciliation-pending, and mcp.receipt-persist-failed -- whose effect is 'applied', not 'possible'. - auths_errors::outcome_codes owns which registry code an authorization verdict carries. The kernel diagnostic 'permission-not-granted' is a diagnostic and is in no registry; core.authorization-denied is the code. auths-node now reads those constants instead of restating the literals, and the generator projects them into both bindings as OUTCOME_CODES. - WASM projects the profile's code on the terminal. It classifies nothing. - TypeScript's Outcome carries code/effect/retry/recommendedAction, every field from auths_errors::classify applied to the Rust-named code. A terminal that arrives without a code is raised as a contract violation, not given one here. Five Rust tests at the owner, mutation-proved: swapping two codes or naming a not-applied code on a possible outcome each turns them red. Acceptance: EA-4, EA-4b, EA-4c, EA-5 RED -> GREEN. All 11 now pass. --- .../python/python/auths/_error_registry.py | 8 + .../src/generated/error-registry.ts | 10 + bindings/typescript/src/product.ts | 68 +++++- bindings/typescript/src/profiles/mcp/index.ts | 33 ++- bindings/typescript/src/workflow/contracts.ts | 6 + bindings/wasm/auths-proof-wasm/src/lib.rs | 24 +- product/errors/auths-errors/src/lib.rs | 28 +++ product/profiles/auths-profile-mcp/Cargo.toml | 6 + .../profiles/auths-profile-mcp/src/session.rs | 208 +++++++++++++++++- product/runtime/auths-node/src/profiles.rs | 4 +- xtask/src/error_registry.rs | 20 +- 11 files changed, 384 insertions(+), 31 deletions(-) diff --git a/bindings/python/python/auths/_error_registry.py b/bindings/python/python/auths/_error_registry.py index 71ea0567..e7b09df8 100644 --- a/bindings/python/python/auths/_error_registry.py +++ b/bindings/python/python/auths/_error_registry.py @@ -1128,3 +1128,11 @@ "recommendedAction": "resume-and-reconcile" } ''') + +# `auths_errors::outcome_codes` -- the registry code an authorization verdict +# carries. +OUTCOME_CODES: Final[dict[str, Any]] = json.loads(r'''{ + "denied": "core.authorization-denied", + "indeterminate": "core.authorization-indeterminate" +} +''') diff --git a/bindings/typescript/src/generated/error-registry.ts b/bindings/typescript/src/generated/error-registry.ts index 064e4521..0b09958e 100644 --- a/bindings/typescript/src/generated/error-registry.ts +++ b/bindings/typescript/src/generated/error-registry.ts @@ -1124,3 +1124,13 @@ export const UNRECOGNIZED_CODE = { "effect": "possible", "recommendedAction": "resume-and-reconcile" } as const; + +/** + * `auths_errors::outcome_codes` -- the registry code an authorization verdict + * carries. A verdict names itself with a kernel diagnostic, not a registry + * code; this is the Rust-owned translation. + */ +export const OUTCOME_CODES = { + "denied": "core.authorization-denied", + "indeterminate": "core.authorization-indeterminate" +} as const; diff --git a/bindings/typescript/src/product.ts b/bindings/typescript/src/product.ts index 00e54c90..1b7961d2 100644 --- a/bindings/typescript/src/product.ts +++ b/bindings/typescript/src/product.ts @@ -16,6 +16,14 @@ import { type McpPlanClosedResult, } from "./profiles/mcp/index.js"; import type { ProfilePlan } from "./plans.js"; +import { + classifyErrorCode, + type AuthsErrorCode, + type EffectState, + type RecommendedAction, + type RetryClass, +} from "./product-errors.js"; +import { OUTCOME_CODES } from "./generated/error-registry.js"; import type { ApplicationReceiptAttestor } from "./profiles/application/index.js"; import { decodeLinkedReceipt, @@ -59,7 +67,7 @@ export interface PlanCompleted { readonly receipts: readonly Receipt[]; } -export interface PlanRecoveryResult { +export interface PlanRecoveryResult extends Outcome { readonly kind: "recoverable" | "not-applied" | "exact-replay" | "conflict"; readonly executionId: string; readonly completedResults: readonly unknown[]; @@ -67,14 +75,42 @@ export interface PlanRecoveryResult { readonly reference?: ExecutionReference; } -export interface Denied { +/** + * The recovery contract every non-completed outcome carries. + * + * `effect` is the safety-critical field: `possible` means the real-world effect + * MAY have happened and this SDK cannot prove which. A caller who reads + * `not-applied` when the truth is `possible` will blindly retry and may repeat + * a payment or a database write. + * + * Every field is Rust's: `code` is named by the profile or the verdict, and the + * other three are `auths_errors::classify` applied to that code. Nothing here + * is decided in TypeScript. + */ +export interface Outcome { + readonly code: AuthsErrorCode; + readonly effect: EffectState; + readonly retry: RetryClass; + readonly recommendedAction: RecommendedAction; +} + +export interface Denied extends Outcome { readonly kind: "denied"; - readonly code: string; } -export interface Indeterminate { +export interface Indeterminate extends Outcome { readonly kind: "indeterminate"; - readonly code: string; +} + +/** Projects one Rust-named code into the full Rust-owned recovery contract. */ +function outcomeFor(code: string): Outcome { + const classification = classifyErrorCode(code); + return { + code, + effect: classification.effect, + retry: classification.retry, + recommendedAction: classification.recommendedAction, + }; } export class ExecutionReference { @@ -120,7 +156,7 @@ export function decodeExecutionReference(input: Uint8Array): ExecutionReference const REFERENCE_TOKEN = Symbol("auths-execution-reference"); -export interface RecoveryResult { +export interface RecoveryResult extends Outcome { readonly kind: "recoverable" | "not-applied" | "exact-replay" | "conflict"; readonly executionId: string; readonly reference?: ExecutionReference; @@ -383,21 +419,34 @@ function projectExecution(value: RawMcpExecution): SingleExecutionResult { }); } if (value.kind === "denied" || value.kind === "indeterminate") { - return Object.freeze({ kind: value.kind, code: value.code }); + return Object.freeze({ kind: value.kind, ...verdictOutcome(value.kind) }); } if (value.kind === "recoverable") { return Object.freeze({ kind: "recoverable" as const, executionId: value.executionId, reference: ExecutionReference.create(REFERENCE_TOKEN, value.executionReference), + ...outcomeFor(value.code), }); } - return Object.freeze({ kind: value.kind, executionId: value.executionId }); + return Object.freeze({ kind: value.kind, executionId: value.executionId, ...outcomeFor(value.code) }); +} + +/** + * Translates one verifier verdict into its registry code. + * + * The kernel names a denial with a diagnostic such as `permission-not-granted`, + * which is not a registry code and exists in no error registry. Which registry + * code a verdict carries is `auths_errors::outcome_codes`, generated here; the + * kernel diagnostic stays a diagnostic. + */ +function verdictOutcome(kind: "denied" | "indeterminate"): Outcome { + return outcomeFor(OUTCOME_CODES[kind]); } function projectPlanExecution(value: RawMcpPlanExecution): PlanExecutionResult { if ("failedIndex" in value) { - return Object.freeze({ kind: value.kind, code: value.result.code }); + return Object.freeze({ kind: value.kind, ...verdictOutcome(value.kind) }); } if (value.kind === "completed") { return Object.freeze({ kind: "completed", results: value.results, receipts: value.receipts }); @@ -407,6 +456,7 @@ function projectPlanExecution(value: RawMcpPlanExecution): PlanExecutionResult { executionId: value.executionId, completedResults: value.completedResults, completedReceipts: value.completedReceipts, + ...outcomeFor(value.code), ...(value.kind === "recoverable" ? { reference: ExecutionReference.create(REFERENCE_TOKEN, value.executionReference) } : {}), diff --git a/bindings/typescript/src/profiles/mcp/index.ts b/bindings/typescript/src/profiles/mcp/index.ts index 7341dd46..965652e4 100644 --- a/bindings/typescript/src/profiles/mcp/index.ts +++ b/bindings/typescript/src/profiles/mcp/index.ts @@ -262,8 +262,8 @@ export interface McpAttestedReceipt { export type McpPlanClosedResult = | Readonly<{ readonly kind: "completed"; readonly results: readonly unknown[]; readonly receipts: readonly McpAttestedReceipt[] }> - | Readonly<{ readonly kind: "recoverable"; readonly executionId: string; readonly executionReference: string; readonly completedResults: readonly unknown[]; readonly completedReceipts: readonly McpAttestedReceipt[] }> - | Readonly<{ readonly kind: "not-applied" | "exact-replay" | "conflict"; readonly executionId: string; readonly completedResults: readonly unknown[]; readonly completedReceipts: readonly McpAttestedReceipt[] }>; + | Readonly<{ readonly kind: "recoverable"; readonly executionId: string; readonly executionReference: string; readonly completedResults: readonly unknown[]; readonly completedReceipts: readonly McpAttestedReceipt[]; readonly code: string }> + | Readonly<{ readonly kind: "not-applied" | "exact-replay" | "conflict"; readonly executionId: string; readonly completedResults: readonly unknown[]; readonly completedReceipts: readonly McpAttestedReceipt[]; readonly code: string }>; export interface McpDevelopmentProviderOptions { readonly tools: Readonly>; @@ -278,8 +278,8 @@ export interface McpDevelopmentProviderOptions { export type McpClosedResult = | Readonly<{ readonly kind: "completed"; readonly executionId: string; readonly result: unknown; readonly receipt: McpAttestedReceipt }> - | Readonly<{ readonly kind: "not-applied" | "exact-replay" | "conflict"; readonly executionId: string }> - | Readonly<{ readonly kind: "recoverable"; readonly executionId: string; readonly executionReference: string }>; + | Readonly<{ readonly kind: "not-applied" | "exact-replay" | "conflict"; readonly executionId: string; readonly code: string }> + | Readonly<{ readonly kind: "recoverable"; readonly executionId: string; readonly executionReference: string; readonly code: string }>; /** Closed MCP tool-call action constructible only by this profile facade. */ export class McpAction { @@ -1005,10 +1005,31 @@ async function projectTerminal( const recovery = terminalRecovery(terminal); await state.saveRecovery(recovery); const reference = recovery.reference; - return Object.freeze({ kind: "recoverable", executionId: terminal.executionId, executionReference: reference }); + return Object.freeze({ + kind: "recoverable", + executionId: terminal.executionId, + executionReference: reference, + code: terminalCode(terminal), + }); } if (terminal.kind === "not-applied") await state.clearPending(terminal.executionId); - return Object.freeze({ kind: terminal.kind, executionId: terminal.executionId }); + return Object.freeze({ kind: terminal.kind, executionId: terminal.executionId, code: terminalCode(terminal) }); +} + +/** + * Reads the registry code the profile gave this outcome. + * + * A terminal that is not `completed` and carries no code means the native + * session stopped naming its own outcomes. That is a contract violation + * between this SDK and the engine, not an authorization result, so it is + * raised rather than papered over with a code chosen here. + */ +function terminalCode(terminal: WorkflowMcpSessionTerminal): string { + const code = terminal.code; + if (typeof code !== "string" || code.length === 0) { + throw new AuthsWorkflowError("gateway-failed", "native MCP terminal omitted its stable registry code"); + } + return code; } function recoveryCheckpoint(session: WorkflowMcpExecutionSession): McpRecoveryCheckpoint { diff --git a/bindings/typescript/src/workflow/contracts.ts b/bindings/typescript/src/workflow/contracts.ts index cdfec166..3c781503 100644 --- a/bindings/typescript/src/workflow/contracts.ts +++ b/bindings/typescript/src/workflow/contracts.ts @@ -681,6 +681,12 @@ export interface WorkflowMcpSessionStep { export interface WorkflowMcpSessionTerminal { readonly kind: "completed" | "not-applied" | "exact-replay" | "conflict" | "recoverable"; readonly executionId: string; + /** + * The stable registry code the MCP profile gave this outcome, absent only + * for a completed execution. Named by `McpTerminal::registry_code` in Rust; + * TypeScript never chooses it. + */ + readonly code?: string; readonly outputJson?: Uint8Array; readonly receiptJson?: Uint8Array; readonly reference?: string; diff --git a/bindings/wasm/auths-proof-wasm/src/lib.rs b/bindings/wasm/auths-proof-wasm/src/lib.rs index 835e5d88..1036834c 100644 --- a/bindings/wasm/auths-proof-wasm/src/lib.rs +++ b/bindings/wasm/auths-proof-wasm/src/lib.rs @@ -3084,6 +3084,9 @@ struct McpSessionTerminalProjection { receipt_json: Option>, reference: Option, record_json: Option>, + /// The stable registry code this outcome carries, named by the profile. + /// `None` only for a completed execution, which is not a failure. + code: Option<&'static str>, } #[wasm_bindgen(js_name = McpExecutionSessionV1)] @@ -3416,6 +3419,9 @@ fn mcp_session_step(step: McpSessionStep) -> McpSessionStepProjection { } fn mcp_session_terminal(value: &McpTerminal) -> McpSessionTerminalProjection { + // The code is read from the profile, never chosen here. WASM is a + // transport: it may not name an outcome the profile did not name. + let code = value.registry_code(); match value { McpTerminal::Completed { execution_id, @@ -3428,18 +3434,22 @@ fn mcp_session_terminal(value: &McpTerminal) -> McpSessionTerminalProjection { receipt_json: Some(receipt_json.clone()), reference: None, record_json: None, + code, }, McpTerminal::NotApplied { execution_id } => { - terminal_without_data("not-applied", execution_id) + terminal_without_data("not-applied", execution_id, code) } McpTerminal::ExactReplay { execution_id } => { - terminal_without_data("exact-replay", execution_id) + terminal_without_data("exact-replay", execution_id, code) + } + McpTerminal::Conflict { execution_id } => { + terminal_without_data("conflict", execution_id, code) } - McpTerminal::Conflict { execution_id } => terminal_without_data("conflict", execution_id), McpTerminal::Recoverable { execution_id, reference, record_json, + .. } => McpSessionTerminalProjection { kind: "recoverable", execution_id: execution_id.clone(), @@ -3447,11 +3457,16 @@ fn mcp_session_terminal(value: &McpTerminal) -> McpSessionTerminalProjection { receipt_json: None, reference: Some(reference.as_str().to_owned()), record_json: Some(record_json.clone()), + code, }, } } -fn terminal_without_data(kind: &'static str, execution_id: &str) -> McpSessionTerminalProjection { +fn terminal_without_data( + kind: &'static str, + execution_id: &str, + code: Option<&'static str>, +) -> McpSessionTerminalProjection { McpSessionTerminalProjection { kind, execution_id: execution_id.to_owned(), @@ -3459,6 +3474,7 @@ fn terminal_without_data(kind: &'static str, execution_id: &str) -> McpSessionTe receipt_json: None, reference: None, record_json: None, + code, } } diff --git a/product/errors/auths-errors/src/lib.rs b/product/errors/auths-errors/src/lib.rs index d4ecd2d6..5e644656 100644 --- a/product/errors/auths-errors/src/lib.rs +++ b/product/errors/auths-errors/src/lib.rs @@ -227,6 +227,34 @@ pub fn registry() -> impl Iterator { .chain(CUSTODY_ERRORS) } +/// Registry code carried by an authorization outcome that denied the request. +pub const AUTHORIZATION_DENIED_CODE: &str = "core.authorization-denied"; +/// Registry code carried by an authorization outcome the verifier could not decide. +pub const AUTHORIZATION_INDETERMINATE_CODE: &str = "core.authorization-indeterminate"; + +/// The stable registry codes for outcomes that do not carry one themselves. +/// +/// A verifier verdict names itself with a kernel diagnostic (`permission-not-granted`) +/// and a runtime outcome names itself with a state word (`denied`). Neither is a +/// registry code, so something has to say which registry code the outcome +/// carries. That answer is here, once, and every consumer — the reference +/// runtime, and both language bindings through the generated projection — reads +/// it rather than restating it. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OutcomeCodes { + pub denied: &'static str, + pub indeterminate: &'static str, +} + +#[must_use] +pub const fn outcome_codes() -> OutcomeCodes { + OutcomeCodes { + denied: AUTHORIZATION_DENIED_CODE, + indeterminate: AUTHORIZATION_INDETERMINATE_CODE, + } +} + /// Operation reported for a code this build's registry does not contain. pub const UNRECOGNIZED_CODE_OPERATION: &str = "execute"; /// Stage reported for a code this build's registry does not contain. diff --git a/product/profiles/auths-profile-mcp/Cargo.toml b/product/profiles/auths-profile-mcp/Cargo.toml index 86b87448..d07ca9b6 100644 --- a/product/profiles/auths-profile-mcp/Cargo.toml +++ b/product/profiles/auths-profile-mcp/Cargo.toml @@ -21,5 +21,11 @@ serde_json.workspace = true serde_json_canonicalizer.workspace = true sha2.workspace = true +[dev-dependencies] +# Test-only: proves every code this profile names is in the Rust registry and +# carries the effect the profile claims. Not a production edge -- the profile +# names codes, the registry defines them. +auths-errors.workspace = true + [lints] workspace = true diff --git a/product/profiles/auths-profile-mcp/src/session.rs b/product/profiles/auths-profile-mcp/src/session.rs index 5d76d99b..eea69c21 100644 --- a/product/profiles/auths-profile-mcp/src/session.rs +++ b/product/profiles/auths-profile-mcp/src/session.rs @@ -181,9 +181,54 @@ pub enum McpTerminal { execution_id: String, reference: McpExecutionReference, record_json: Vec, + /// What the session could still prove when it became recoverable. + recovery: RecoveryKind, + /// Why the handler could not report an applied effect, when it said so. + cause: Option, + /// True when this terminal ended a resumed session, so an unresolved + /// effect is a reconciliation that is still pending rather than a + /// first-attempt handler failure. + resumed: bool, }, } +impl McpTerminal { + /// Names this outcome with the stable registry code the MCP profile owns. + /// + /// This projection lives here because the profile is what knows the + /// difference between a handler that timed out, a handler that produced + /// unusable output, and a receipt that failed to persist AFTER the effect + /// was applied. A language binding that guessed would be inventing the + /// effect axis; it reads this instead. + /// + /// Returns `None` only for [`Self::Completed`], which is not a failure and + /// carries no code. + #[must_use] + pub const fn registry_code(&self) -> Option<&'static str> { + match self { + Self::Completed { .. } => None, + // The handler proved non-effect before the provider was entered. + Self::NotApplied { .. } => Some("mcp.cancelled-before-entry"), + Self::ExactReplay { .. } => Some("mcp.replay"), + Self::Conflict { .. } => Some("mcp.reservation-conflict"), + Self::Recoverable { + recovery, cause, resumed, .. + } => Some(match recovery { + // Reserved, never entered: non-effect is still provable. + RecoveryKind::Reserved => "mcp.cancelled-before-entry", + // The effect WAS applied; only the receipt is missing. + RecoveryKind::ReceiptPending => "mcp.receipt-persist-failed", + RecoveryKind::Possible => match (resumed, cause) { + (true, _) => "mcp.reconciliation-pending", + (false, Some(McpCause::InvalidOutput)) => "mcp.invalid-handler-output", + (false, Some(McpCause::Timeout)) => "mcp.handler-timeout", + (false, _) => "mcp.handler-failed", + }, + }), + } + } +} + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum McpSessionError { InvalidRequestId, @@ -253,6 +298,8 @@ pub struct McpExecutionSession { member_index: Option, member_count: Option, state: SessionState, + /// True when this session was reconstructed from a recovery record. + resumed: bool, } enum SessionState { @@ -280,6 +327,7 @@ struct RecoveryRecord { execution_id: String, service: String, kind: RecoveryKind, + cause: Option, action_commitment: String, authority_commitment: String, context_commitment: String, @@ -293,9 +341,10 @@ struct RecoveryRecord { receipt: Option, } -#[derive(Clone, Copy, Serialize, Deserialize)] +/// What a recoverable session could still prove when it checkpointed. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] -enum RecoveryKind { +pub enum RecoveryKind { Reserved, Possible, ReceiptPending, @@ -428,6 +477,7 @@ impl McpExecutionSession { member_index: plan.map(|value| value.1), member_count: plan.map(|value| value.2), state: SessionState::ReadyReserve, + resumed: false, }) } @@ -509,6 +559,7 @@ impl McpExecutionSession { member_index: plan.map(|value| value.1), member_count: plan.map(|value| value.2), state, + resumed: true, }) } @@ -577,7 +628,9 @@ impl McpExecutionSession { return Err(McpSessionError::InvalidTransition); } }; - self.recovery_terminal(kind, output, receipt) + // A mid-flight checkpoint has no handler observation yet, so it carries + // no cause; only a terminal reached through `accept_handler` does. + self.recovery_terminal(kind, None, output, receipt) } /// Releases exactly one bounded side-effect request. @@ -735,6 +788,7 @@ impl McpExecutionSession { } else { self.state = SessionState::Terminal(self.recovery_terminal( RecoveryKind::ReceiptPending, + None, Some(output), Some(receipt), )?); @@ -762,6 +816,7 @@ impl McpExecutionSession { } McpHandlerEffect::Possible => SessionState::Terminal(self.recovery_terminal( RecoveryKind::Possible, + result.cause, None, None, )?), @@ -791,6 +846,7 @@ impl McpExecutionSession { fn recovery_terminal( &self, kind: RecoveryKind, + cause: Option, output: Option, receipt: Option>, ) -> Result { @@ -806,6 +862,7 @@ impl McpExecutionSession { execution_id: self.execution_id.clone(), service: self.service.clone(), kind, + cause, action_commitment: hex::encode(self.action_commitment), authority_commitment: hex::encode(self.authority_commitment), context_commitment: hex::encode(self.context_commitment), @@ -825,6 +882,9 @@ impl McpExecutionSession { execution_id: self.execution_id.clone(), reference, record_json, + recovery: kind, + cause, + resumed: self.resumed, }) } } @@ -945,6 +1005,148 @@ mod tests { .unwrap() } + /// Drives a session to a `possible` terminal with the given cause. + fn possible_with(cause: Option) -> McpTerminal { + let mut session = session(); + session.next_step().unwrap(); + session + .accept_reservation(McpReservationResult::Acquired) + .unwrap(); + session.next_step().unwrap(); + session.accept_provider_entry().unwrap(); + session.next_step().unwrap(); + session + .accept_handler(McpHandlerResult::parse(McpHandlerEffect::Possible, None, cause).unwrap()) + .unwrap(); + session.terminal().unwrap().clone() + } + + #[test] + fn every_terminal_names_a_code_that_is_in_the_registry() { + let terminals = [ + possible_with(None), + possible_with(Some(McpCause::InvalidOutput)), + possible_with(Some(McpCause::Timeout)), + McpTerminal::NotApplied { + execution_id: "e".into(), + }, + McpTerminal::ExactReplay { + execution_id: "e".into(), + }, + McpTerminal::Conflict { + execution_id: "e".into(), + }, + ]; + for terminal in &terminals { + let code = terminal + .registry_code() + .unwrap_or_else(|| panic!("{terminal:?} named no code")); + assert!( + auths_errors::classify(code).known, + "{terminal:?} named {code}, which is in no registry" + ); + } + } + + #[test] + fn a_failed_handler_and_unusable_output_are_different_codes() { + // The distinction the caller needs: both are `possible`, but one is the + // provider's fault and one is the handler's contract. Collapsing them + // destroys the identity the registry exists to preserve. + let failed = possible_with(Some(McpCause::Unknown)); + let unusable = possible_with(Some(McpCause::InvalidOutput)); + assert_eq!(failed.registry_code(), Some("mcp.handler-failed")); + assert_eq!(unusable.registry_code(), Some("mcp.invalid-handler-output")); + assert_ne!(failed.registry_code(), unusable.registry_code()); + assert_eq!( + possible_with(Some(McpCause::Timeout)).registry_code(), + Some("mcp.handler-timeout") + ); + } + + #[test] + fn every_possible_terminal_carries_a_possible_effect() { + // A `possible` handler observation must never be named with a code the + // registry declares `not-applied`: that would tell a caller a maybe- + // applied effect is safe to blindly retry. + for cause in [ + None, + Some(McpCause::Cancelled), + Some(McpCause::InvalidOutput), + Some(McpCause::LimitExceeded), + Some(McpCause::Timeout), + Some(McpCause::Unavailable), + Some(McpCause::Unknown), + ] { + let terminal = possible_with(cause); + let code = terminal.registry_code().unwrap(); + assert_eq!( + auths_errors::classify(code).effect, + auths_errors::EffectState::Possible, + "cause {cause:?} named {code}, whose registered effect is not possible" + ); + } + } + + #[test] + fn a_receipt_that_failed_to_persist_says_the_effect_was_applied() { + let mut session = session(); + session.next_step().unwrap(); + session + .accept_reservation(McpReservationResult::Acquired) + .unwrap(); + session.next_step().unwrap(); + session.accept_provider_entry().unwrap(); + session.next_step().unwrap(); + session + .accept_handler( + McpHandlerResult::parse(McpHandlerEffect::Applied, Some(br#"{"ok":true}"#), None) + .unwrap(), + ) + .unwrap(); + session.next_step().unwrap(); + assert_eq!( + session.accept_receipt(false), + Err(McpSessionError::ReceiptPersistenceFailed) + ); + let code = session.terminal().unwrap().registry_code().unwrap(); + assert_eq!(code, "mcp.receipt-persist-failed"); + assert_eq!( + auths_errors::classify(code).effect, + auths_errors::EffectState::Applied + ); + } + + #[test] + fn a_resumed_session_reports_reconciliation_rather_than_a_fresh_failure() { + let terminal = possible_with(Some(McpCause::Timeout)); + let McpTerminal::Recoverable { + reference, + record_json, + .. + } = &terminal + else { + panic!("expected recoverable"); + }; + let mut resumed = McpExecutionSession::resume( + McpSessionKey::new([9; 32]), + reference.as_str(), + record_json, + ) + .unwrap(); + resumed.next_step().unwrap(); + resumed + .accept_handler( + McpHandlerResult::parse(McpHandlerEffect::Possible, None, Some(McpCause::Unknown)) + .unwrap(), + ) + .unwrap(); + assert_eq!( + resumed.terminal().unwrap().registry_code(), + Some("mcp.reconciliation-pending") + ); + } + #[test] fn one_accepted_result_gates_every_side_effect() { let mut session = session(); diff --git a/product/runtime/auths-node/src/profiles.rs b/product/runtime/auths-node/src/profiles.rs index f362fc47..72e2166a 100644 --- a/product/runtime/auths-node/src/profiles.rs +++ b/product/runtime/auths-node/src/profiles.rs @@ -74,9 +74,9 @@ impl RuntimeFailure { // A denied disclosure is a denied authorization: the caller did not // present the authorization this receipt requires. Self::AuthorizationDenied(_) | Self::DisclosureDenied | Self::ReplayBudgetExhausted => { - "core.authorization-denied" + auths_errors::AUTHORIZATION_DENIED_CODE } - Self::AuthorizationIndeterminate(_) => "core.authorization-indeterminate", + Self::AuthorizationIndeterminate(_) => auths_errors::AUTHORIZATION_INDETERMINATE_CODE, Self::UnauthenticatedPrincipal => "core.unauthenticated-principal", Self::StateConflict => "core.runtime-conflict", Self::Unavailable => "core.runtime-unavailable", diff --git a/xtask/src/error_registry.rs b/xtask/src/error_registry.rs index c8267fa8..9097f08e 100644 --- a/xtask/src/error_registry.rs +++ b/xtask/src/error_registry.rs @@ -57,11 +57,14 @@ pub(crate) fn error_registry(update: bool) -> Result<(), String> { // is `auths_errors::classify`'s, not a binding's. Projecting it here is what // stops TypeScript and Python from each inventing their own fourth state. let unrecognized_json = pretty_json(&auths_errors::classify(UNRECOGNIZED_PROBE))?; + // Which registry code a verdict or runtime outcome carries is also Rust's + // answer, not a binding's. + let outcome_json = pretty_json(&auths_errors::outcome_codes())?; let outputs = [ registry_json.clone(), pretty_json(&fixtures)?, - render_typescript(®istry_json, &unrecognized_json), - render_python(®istry_json, &unrecognized_json), + render_typescript(®istry_json, &unrecognized_json, &outcome_json), + render_python(®istry_json, &unrecognized_json, &outcome_json), render_docs(®istry), ]; for (path, bytes) in OUTPUTS.iter().zip(outputs) { @@ -133,22 +136,25 @@ fn pretty_json(value: &impl Serialize) -> Result, String> { Ok(bytes) } -fn render_typescript(registry: &[u8], unrecognized: &[u8]) -> Vec { +fn render_typescript(registry: &[u8], unrecognized: &[u8], outcomes: &[u8]) -> Vec { let json = String::from_utf8_lossy(registry); let unrecognized = String::from_utf8_lossy(unrecognized); + let outcomes = String::from_utf8_lossy(outcomes); format!( - "export const ERROR_REGISTRY = {} as const;\n\n/**\n * `auths_errors::classify` applied to a code this build's registry does not\n * contain. A binding projects this; it never recomputes it and never invents a\n * fourth effect state.\n */\nexport const UNRECOGNIZED_CODE = {} as const;\n", + "export const ERROR_REGISTRY = {} as const;\n\n/**\n * `auths_errors::classify` applied to a code this build's registry does not\n * contain. A binding projects this; it never recomputes it and never invents a\n * fourth effect state.\n */\nexport const UNRECOGNIZED_CODE = {} as const;\n\n/**\n * `auths_errors::outcome_codes` -- the registry code an authorization verdict\n * carries. A verdict names itself with a kernel diagnostic, not a registry\n * code; this is the Rust-owned translation.\n */\nexport const OUTCOME_CODES = {} as const;\n", json.trim_end(), - unrecognized.trim_end() + unrecognized.trim_end(), + outcomes.trim_end() ) .into_bytes() } -fn render_python(registry: &[u8], unrecognized: &[u8]) -> Vec { +fn render_python(registry: &[u8], unrecognized: &[u8], outcomes: &[u8]) -> Vec { let json = String::from_utf8_lossy(registry); let unrecognized = String::from_utf8_lossy(unrecognized); + let outcomes = String::from_utf8_lossy(outcomes); format!( - "from __future__ import annotations\n\nimport json\nfrom typing import Any, Final\n\nERROR_REGISTRY: Final[dict[str, Any]] = json.loads(r'''{json}''')\n\n# `auths_errors::classify` applied to a code this build's registry does not\n# contain. A binding projects this; it never recomputes it.\nUNRECOGNIZED_CODE: Final[dict[str, Any]] = json.loads(r'''{unrecognized}''')\n" + "from __future__ import annotations\n\nimport json\nfrom typing import Any, Final\n\nERROR_REGISTRY: Final[dict[str, Any]] = json.loads(r'''{json}''')\n\n# `auths_errors::classify` applied to a code this build's registry does not\n# contain. A binding projects this; it never recomputes it.\nUNRECOGNIZED_CODE: Final[dict[str, Any]] = json.loads(r'''{unrecognized}''')\n\n# `auths_errors::outcome_codes` -- the registry code an authorization verdict\n# carries.\nOUTCOME_CODES: Final[dict[str, Any]] = json.loads(r'''{outcomes}''')\n" ) .into_bytes() } From c04bd487dfdaa1e3fc4fe66bcb0e8fe0d65d60a9 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 01:36:12 +0100 Subject: [PATCH 16/61] bindings/python: one vocabulary, one entry point per operation (UNSIGNED) The wheel shipped two unrelated `AuthsError` classes, two `EffectState` vocabularies (neither of them Rust's), three `RetryClass` closed sets under two names, a sixth product verb nobody owns, and 25 error codes that exist in no registry -- all reachable from the public `execute()`. It also shipped a `py.typed` stub that named 16 of 35 public symbols, so the entire remote client was invisible to mypy and pyright. Rust now owns every one of those meanings and Python reads them. effect axis `EffectState` has exactly three members. The invented `UNKNOWN` and the whole alternate axis in `_errors.py` (not-started|in-progress|completed|failed|outcome-unknown) are gone. An unrecognized code takes `auths_errors::classify`'s answer through the generated `UNRECOGNIZED_CODE`: `possible`, never `not-applied`. retry `RetryClass` (never|safe|conditional|unknown) answers "may I retry"; `NextCall` (never|backoff|resume|reconcile) answers "what do I call next". Separate names, separate modules. verbs `ProductVerb` replaces `ProductStep`; the wire field is `verb`. `recover` is deleted -- no Rust owner, no registry entry -- along with `recover_mcp_closed` and the store port method only it called. errors One hierarchy rooted at `AuthsError`, raised by the paths that actually fail. `WORKFLOW_REASON_CODES` is the one place Python selects a code and every value is a registry entry; the old labels survive as the unstable `reason` field. effect reach `Denied`, `Indeterminate`, and `RecoveryResult` carry `code`, `effect`, `retry`, and `recommended_action`. The MCP codes come from `McpTerminal::registry_code`, projected through a new `code` getter on the pyo3 terminal. transport A service call that cannot be completed is `core.outcome-unknown`, not `core.runtime-unavailable`: the request left the process and the effect is `possible`. service split The remote client moves to `auths.service` with the `Production*` prefix deleted. The product root keeps no import edge to it. deletions `_application_profile.py` (1185 lines; its native constructors were already withdrawn, so every operation raised) and `_errors.py` (139 lines). ports Receipt-disclosure protector and store are async, matching `@auths-dev/sdk`. A synchronous port cannot be implemented over a KMS. ApprovalMode gains `headless`, which its own validator already accepted, from one list both the type and the validator read. homonyms `auths.verify` verdicts become `AuthorizedResult` / `DeniedResult` / `IndeterminateResult`, matching TypeScript, so `Denied` no longer names two unrelated types. stub `__init__.pyi` is generated from `auths.__init__._OWNERS` and gated by `tools/check_type_stub.py`. Public symbols per entry point: auths 35->21, auths.verify 36->34, auths.service 0->24, identity/profiles/integrations/framework/testkit unchanged. 159 -> 167 total. Acceptance: `tests/test_effect_axis_boundary.py` 3 passed / 9 failed -> 12 passed. Suite 90 passed / 23 failed -> 116 passed / 14 failed; the 14 are the pre-existing `budget-ceiling-exceeded` denial of every MCP authorization, unchanged by this commit. Two probes in the acceptance test declared their handlers `**_` while the MCP provider calls `(arguments, context)`, so both arms of EA-4b failed at call binding and the check could not distinguish the codes it named. Corrected to the documented convention; with it, `mcp.handler-failed` and `mcp.invalid-handler-output` are distinct. That mis-binding also surfaced a real defect: a handler with the wrong signature was reported as `mcp.handler-failed` with effect `possible` although its body never ran. It now raises `McpProviderContractError`. --- .github/workflows/python-sdk.yml | 2 + bindings/python/api/public-api.txt | 60 +- bindings/python/python/auths/__init__.py | 94 +- bindings/python/python/auths/__init__.pyi | 21 +- .../python/auths/_application_profile.py | 1185 ----------------- bindings/python/python/auths/_approvals.py | 2 +- bindings/python/python/auths/_boundary.py | 57 + bindings/python/python/auths/_diagnostics.py | 6 +- bindings/python/python/auths/_errors.py | 139 -- bindings/python/python/auths/_inspection.py | 10 +- bindings/python/python/auths/_native.pyi | 18 + bindings/python/python/auths/_plan.py | 2 +- bindings/python/python/auths/_product.py | 119 +- .../python/python/auths/_product_errors.py | 396 +++++- bindings/python/python/auths/_receipts.py | 22 +- .../{_production_client.py => _service.py} | 348 ++--- bindings/python/python/auths/_workflow.py | 120 +- bindings/python/python/auths/identity.py | 4 + bindings/python/python/auths/integrations.py | 24 - .../python/python/auths/profiles/__init__.py | 22 +- bindings/python/python/auths/profiles/_mcp.py | 122 +- bindings/python/python/auths/service.py | 66 + bindings/python/python/auths/verify.py | 71 +- bindings/python/src/mcp.rs | 16 + bindings/python/tests/test_api.py | 10 +- .../python/tests/test_development_product.py | 101 -- .../python/tests/test_effect_axis_boundary.py | 14 +- bindings/python/tests/test_mcp_workflow.py | 8 +- bindings/python/tests/test_product_errors.py | 20 +- .../python/tests/test_production_client.py | 14 +- .../python/tests/test_receipt_inspection.py | 9 +- .../tests/test_registry_code_inventory.py | 210 +++ .../python/tests/test_vocabulary_parity.py | 192 +++ bindings/python/tests/test_workflow.py | 33 +- bindings/python/tools/check_type_stub.py | 62 + bindings/python/tools/check_wheel.py | 13 +- .../recipes/python/03_execute_exact_action.py | 2 +- xtask/src/checks.rs | 4 + 38 files changed, 1620 insertions(+), 1998 deletions(-) delete mode 100644 bindings/python/python/auths/_application_profile.py create mode 100644 bindings/python/python/auths/_boundary.py delete mode 100644 bindings/python/python/auths/_errors.py rename bindings/python/python/auths/{_production_client.py => _service.py} (62%) create mode 100644 bindings/python/python/auths/service.py create mode 100644 bindings/python/tests/test_registry_code_inventory.py create mode 100644 bindings/python/tests/test_vocabulary_parity.py create mode 100644 bindings/python/tools/check_type_stub.py diff --git a/.github/workflows/python-sdk.yml b/.github/workflows/python-sdk.yml index 3c9ad61c..ab08399a 100644 --- a/.github/workflows/python-sdk.yml +++ b/.github/workflows/python-sdk.yml @@ -63,6 +63,7 @@ jobs: python -m pyright --pythonpath "$(command -v python)" -p bindings/python-adapters/sqlite/pyrightconfig.json - run: python bindings/python/tools/check_public_api.py + - run: python bindings/python/tools/check_type_stub.py - run: python bindings/python/tools/check_contract.py - run: python bindings/python/tools/check_doc_snippets.py @@ -97,6 +98,7 @@ jobs: bindings/python/pyrightconfig.json bindings/python/python/auths/ bindings/python/tools/check_public_api.py + bindings/python/tools/check_type_stub.py bindings/python/tools/check_contract.py bindings/python/tools/check_doc_snippets.py bindings/python/tools/check_performance.py diff --git a/bindings/python/api/public-api.txt b/bindings/python/api/public-api.txt index 02779613..600be3d8 100644 --- a/bindings/python/api/public-api.txt +++ b/bindings/python/api/public-api.txt @@ -8,31 +8,17 @@ AuthsErrorCode Completed Denied DoctorReport +EffectState ExecutionReference ExecutionResult Indeterminate -ProductStep -ProductionAuthority -ProductionAuthorityResult -ProductionAuths -ProductionCompleted -ProductionDenied -ProductionExecutionResult -ProductionIndeterminate -ProductionReceipt -ProductionRecoverable -ProductionRecoveryReference -ProductionRejected -ProductionTransport -ProductionTransportRequest -ProductionTransportResponse -ProductionVerificationResult -ProductionVerified +PlanCompleted +PlanRecoveryResult +ProductVerb Receipt RecommendedAction RecoveryResult RetryClass -create_auths doctor [auths.identity] @@ -58,17 +44,15 @@ encode_raw_key_identity [auths.verify] ApprovalInspection -Authorized +AuthorizedResult DecisionCommitments DecisionInspection DecisionSummary -Denied +DeniedResult Explanation -Indeterminate -InspectionMetrics +IndeterminateResult InvalidReceiptInspection KernelSummary -Receipt ReceiptDisclosureMaterial ReceiptDisclosureProtector ReceiptDisclosureStore @@ -94,6 +78,32 @@ verify verify_many verify_receipt +[auths.service] +AuthsError +AuthsErrorCode +EffectState +NextCall +ProductVerb +RecommendedAction +RetryClass +ServiceAuthority +ServiceAuthorityResult +ServiceAuths +ServiceCompleted +ServiceDenied +ServiceExecutionResult +ServiceIndeterminate +ServiceReceipt +ServiceRecoverable +ServiceRecoveryReference +ServiceRejected +ServiceTransport +ServiceTransportRequest +ServiceTransportResponse +ServiceVerificationResult +ServiceVerified +create_auths + [auths.profiles] DevelopmentMcpProvider McpAction @@ -109,8 +119,8 @@ McpPlanRecoveryResult McpRecoverable McpToolAuthority McpToolContext -ProductionProfile -ProductionProfileId +ServiceProfile +ServiceProfileId github_issue_address mcp opentofu_saved_plan_apply diff --git a/bindings/python/python/auths/__init__.py b/bindings/python/python/auths/__init__.py index 6781b195..23460ea4 100644 --- a/bindings/python/python/auths/__init__.py +++ b/bindings/python/python/auths/__init__.py @@ -16,114 +16,52 @@ ExecutionReference as ExecutionReference, ExecutionResult as ExecutionResult, Indeterminate as Indeterminate, + PlanCompleted as PlanCompleted, + PlanRecoveryResult as PlanRecoveryResult, Receipt as Receipt, RecoveryResult as RecoveryResult, ) from ._product_errors import ( AuthsError as AuthsError, AuthsErrorCode as AuthsErrorCode, + EffectState as EffectState, + ProductVerb as ProductVerb, RecommendedAction as RecommendedAction, - ) - from ._production_client import ( - ProductStep as ProductStep, - ProductionAuths as ProductionAuths, - ProductionAuthority as ProductionAuthority, - ProductionAuthorityResult as ProductionAuthorityResult, - ProductionCompleted as ProductionCompleted, - ProductionDenied as ProductionDenied, - ProductionExecutionResult as ProductionExecutionResult, - ProductionIndeterminate as ProductionIndeterminate, - ProductionReceipt as ProductionReceipt, - ProductionRecoverable as ProductionRecoverable, - ProductionRecoveryReference as ProductionRecoveryReference, - ProductionRejected as ProductionRejected, - ProductionTransport as ProductionTransport, - ProductionTransportRequest as ProductionTransportRequest, - ProductionTransportResponse as ProductionTransportResponse, - ProductionVerificationResult as ProductionVerificationResult, - ProductionVerified as ProductionVerified, RetryClass as RetryClass, - create_auths as create_auths, ) from ._workflow import Approval as Approval -__all__ = [ - "Actor", - "Approval", - "Auths", - "AuthsError", - "AuthsErrorCode", - "Authority", - "Completed", - "Denied", - "DoctorReport", - "ExecutionReference", - "ExecutionResult", - "Indeterminate", - "Receipt", - "RecommendedAction", - "RecoveryResult", - "ProductStep", - "ProductionAuths", - "ProductionAuthority", - "ProductionAuthorityResult", - "ProductionCompleted", - "ProductionDenied", - "ProductionExecutionResult", - "ProductionIndeterminate", - "ProductionReceipt", - "ProductionRecoverable", - "ProductionRecoveryReference", - "ProductionRejected", - "ProductionTransport", - "ProductionTransportRequest", - "ProductionTransportResponse", - "ProductionVerificationResult", - "ProductionVerified", - "RetryClass", - "create_auths", - "doctor", -] - +# One owner per name. `_OWNERS` is the single table the lazy import, the +# `__init__.pyi` stub, and `tools/check_type_stub.py` all read, so the runtime +# surface and the typed surface cannot drift apart. _OWNERS = { "Actor": "._product", "Approval": "._workflow", + "Authority": "._product", "Auths": "._product", "AuthsError": "._product_errors", "AuthsErrorCode": "._product_errors", - "Authority": "._product", "Completed": "._product", "Denied": "._product", "DoctorReport": "._doctor", + "EffectState": "._product_errors", "ExecutionReference": "._product", "ExecutionResult": "._product", "Indeterminate": "._product", + "PlanCompleted": "._product", + "PlanRecoveryResult": "._product", + "ProductVerb": "._product_errors", "Receipt": "._product", "RecommendedAction": "._product_errors", "RecoveryResult": "._product", + "RetryClass": "._product_errors", "doctor": "._doctor", - "ProductStep": "._production_client", - "ProductionAuths": "._production_client", - "ProductionAuthority": "._production_client", - "ProductionAuthorityResult": "._production_client", - "ProductionCompleted": "._production_client", - "ProductionDenied": "._production_client", - "ProductionExecutionResult": "._production_client", - "ProductionIndeterminate": "._production_client", - "ProductionReceipt": "._production_client", - "ProductionRecoverable": "._production_client", - "ProductionRecoveryReference": "._production_client", - "ProductionRejected": "._production_client", - "ProductionTransport": "._production_client", - "ProductionTransportRequest": "._production_client", - "ProductionTransportResponse": "._production_client", - "ProductionVerificationResult": "._production_client", - "ProductionVerified": "._production_client", - "RetryClass": "._production_client", - "create_auths": "._production_client", } +__all__ = sorted(_OWNERS) + + def __getattr__(name: str) -> Any: owner = _OWNERS.get(name) if owner is None: diff --git a/bindings/python/python/auths/__init__.pyi b/bindings/python/python/auths/__init__.pyi index 8fb22475..1dcc4373 100644 --- a/bindings/python/python/auths/__init__.pyi +++ b/bindings/python/python/auths/__init__.pyi @@ -1,18 +1,27 @@ +# Generated by tools/check_type_stub.py from auths.__init__._OWNERS. +# Run `python tools/check_type_stub.py --update` after changing the +# public root. Never hand-edit: a name only a human remembered to add +# here would not exist at runtime. from ._product import Actor as Actor -from ._product import Auths as Auths +from ._workflow import Approval as Approval from ._product import Authority as Authority +from ._product import Auths as Auths +from ._product_errors import AuthsError as AuthsError +from ._product_errors import AuthsErrorCode as AuthsErrorCode from ._product import Completed as Completed from ._product import Denied as Denied +from ._doctor import DoctorReport as DoctorReport +from ._product_errors import EffectState as EffectState from ._product import ExecutionReference as ExecutionReference from ._product import ExecutionResult as ExecutionResult from ._product import Indeterminate as Indeterminate +from ._product import PlanCompleted as PlanCompleted +from ._product import PlanRecoveryResult as PlanRecoveryResult +from ._product_errors import ProductVerb as ProductVerb from ._product import Receipt as Receipt +from ._product_errors import RecommendedAction as RecommendedAction from ._product import RecoveryResult as RecoveryResult -from ._doctor import DoctorReport as DoctorReport +from ._product_errors import RetryClass as RetryClass from ._doctor import doctor as doctor -from ._product_errors import AuthsError as AuthsError -from ._product_errors import AuthsErrorCode as AuthsErrorCode -from ._product_errors import RecommendedAction as RecommendedAction -from ._workflow import Approval as Approval __all__: list[str] diff --git a/bindings/python/python/auths/_application_profile.py b/bindings/python/python/auths/_application_profile.py deleted file mode 100644 index 71c3af75..00000000 --- a/bindings/python/python/auths/_application_profile.py +++ /dev/null @@ -1,1185 +0,0 @@ -"""Application-owned profiles over the native Auths workflow waist.""" - -from __future__ import annotations - -import asyncio -import time -from dataclasses import dataclass, field -from typing import ( - Any, - Awaitable, - Callable, - Generic, - Literal, - Optional, - Protocol, - Sequence, - Tuple, - TypeVar, - Union, - cast, -) - -from . import _native as native -from ._plan import PlanApprovalSession -from ._workflow import ( - ApprovalConfiguration, - ApprovalProvider, - AttachedAgent, - AuthsWorkflowError, - ControlEvidence, - Permission, - Profile, - ReviewField, - _SigningCoordinator, - _transaction_expiry, -) -from ._errors import ProviderOperationError -from ._receipts import ( - AttestedReceipt, - ReceiptAttestor, - _attest_decision, - _attest_execution, -) - -InputT = TypeVar("InputT") -CommandT = TypeVar("CommandT") -CredentialCommandT = TypeVar("CredentialCommandT", contravariant=True) -ResultT = TypeVar("ResultT") -ApplicationOutcome = Literal["succeeded", "failed", "cancelled", "outcome-unknown"] -ApplicationExecutionState = Literal["committed", "released", "outcome-unknown"] -VerificationStage = Literal[ - "decode", "resolve", "principal-control", "authority", "complete" -] -_ACTION_TOKEN = object() -_PLAN_TOKEN = object() - - -@dataclass(frozen=True) -class ProfilePermission: - capability: str - resource: str - - -@dataclass(frozen=True) -class ProfileBudget: - algebra: str - value: int - - -@dataclass(frozen=True) -class CanonicalProfileAction: - media_type: str - body: bytes - permission: ProfilePermission - resource_namespace: str - audience: str - display: Tuple[ReviewField, ...] - budget: Optional[ProfileBudget] = None - - def __post_init__(self) -> None: - object.__setattr__(self, "body", bytes(self.body)) - object.__setattr__(self, "display", tuple(self.display)) - - -@dataclass(frozen=True) -class ProfileDefinition(Generic[InputT, CommandT]): - id: str - version: int - canonicalize: Callable[[InputT], CanonicalProfileAction] - decode_verified: Callable[[CanonicalProfileAction], CommandT] - - -@dataclass(frozen=True) -class ApplicationAuthority: - permissions: Tuple[Permission, ...] - resource_namespaces: Tuple[str, ...] - audiences: Tuple[str, ...] - budget: Optional[ProfileBudget] - - -@dataclass(frozen=True) -class ApplicationReview: - title: str - fields: Tuple[ReviewField, ...] - action_commitment: bytes - - -class ApplicationAction(Generic[InputT]): - def __init__( - self, - token: object, - profile: ApplicationProfile[InputT, Any], - canonical: CanonicalProfileAction, - native_action: native.ApplicationAction, - ) -> None: - if token is not _ACTION_TOKEN: - raise TypeError("sealed Auths application action") - self._profile = profile - self._canonical = canonical - self._native = native_action - - @property - def profile(self) -> ApplicationProfile[InputT, Any]: - return self._profile - - -class ApplicationPlan(Generic[InputT]): - def __init__( - self, - token: object, - profile: ApplicationProfile[InputT, Any], - actions: Tuple[ApplicationAction[InputT], ...], - commitment: bytes, - member_commitments: Tuple[bytes, ...], - authority: ApplicationAuthority, - ) -> None: - if token is not _PLAN_TOKEN: - raise TypeError("sealed Auths application plan") - self._profile = profile - self._actions = actions - self._commitment = commitment - self._member_commitments = member_commitments - self._authority = authority - - @property - def length(self) -> int: - return len(self._actions) - - @property - def commitment(self) -> bytes: - return self._commitment - - @property - def authority(self) -> ApplicationAuthority: - return self._authority - - -@dataclass(frozen=True) -class ApplicationRequest: - challenge: bytes = field(default_factory=native.generate_challenge_v1) - evaluation_time: int = field(default_factory=lambda: int(time.time())) - - def __post_init__(self) -> None: - challenge = bytes(self.challenge) - if len(challenge) != 32: - raise ValueError("authorization challenge must contain 32 bytes") - if ( - type(self.evaluation_time) is not int - or not 0 <= self.evaluation_time <= (1 << 64) - 1 - ): - raise ValueError("invalid authorization evaluation time") - object.__setattr__(self, "challenge", challenge) - - -@dataclass(frozen=True) -class ApplicationMetrics: - proof_bytes: int - action_bytes: int - context_bytes: int - object_count: int - plan_leaves: int - plan_depth: int - work_units: int - - -@dataclass(frozen=True) -class ApplicationExplanation: - code: str - message: str - retryable: bool - - -@dataclass(frozen=True) -class ApplicationApproval: - policy_id: str - evaluator_version: str - required_configuration: bytes - executed_configuration: bytes - executed_mode: str - executed_max_uses: int - transaction_digest: bytes - - -@dataclass(frozen=True) -class ApplicationAuthorized(Generic[CommandT]): - kind: Literal["authorized"] - code: str - stage: VerificationStage - explanation: ApplicationExplanation - metrics: ApplicationMetrics - approval: ApplicationApproval - required_configuration: Optional[bytes] - local_configuration: bytes - result_cbor: bytes - command: native.ApplicationCommand - - -@dataclass(frozen=True) -class ApplicationDenied: - kind: Literal["denied"] - code: str - stage: VerificationStage - explanation: ApplicationExplanation - metrics: ApplicationMetrics - approval: ApplicationApproval - required_configuration: Optional[bytes] - local_configuration: bytes - result_cbor: bytes - - -@dataclass(frozen=True) -class ApplicationIndeterminate: - kind: Literal["indeterminate"] - code: str - stage: VerificationStage - explanation: ApplicationExplanation - metrics: ApplicationMetrics - approval: ApplicationApproval - required_configuration: Optional[bytes] - local_configuration: bytes - result_cbor: bytes - - -ApplicationResult = Union[ - ApplicationAuthorized[CommandT], ApplicationDenied, ApplicationIndeterminate -] - - -@dataclass(frozen=True) -class ApplicationPlanAuthorized(Generic[CommandT]): - kind: Literal["authorized"] - command: native.ApplicationPlanCommand - results: Tuple[ApplicationAuthorized[CommandT], ...] - - -@dataclass(frozen=True) -class ApplicationPlanDenied: - kind: Literal["denied"] - failed_index: int - result: ApplicationDenied - - -@dataclass(frozen=True) -class ApplicationPlanIndeterminate: - kind: Literal["indeterminate"] - failed_index: int - result: ApplicationIndeterminate - - -ApplicationPlanResult = Union[ - ApplicationPlanAuthorized[CommandT], - ApplicationPlanDenied, - ApplicationPlanIndeterminate, -] - - -@dataclass(frozen=True) -class ApplicationReceipt: - idempotency_key: str - command_commitment: bytes - authority_commitment: bytes - context_commitment: bytes - plan_commitment: Optional[bytes] - state_claim: ApplicationExecutionState - outcome: ApplicationOutcome - observed_at: int - decision_receipt: AttestedReceipt - execution_receipt: Optional[AttestedReceipt] - - -@dataclass(frozen=True) -class ApplicationExecutionContext: - idempotency_key: str - canonical_command: bytes - plan_commitment: Optional[bytes] = None - member_index: Optional[int] = None - member_count: Optional[int] = None - - def __post_init__(self) -> None: - command = bytes(self.canonical_command) - if not command or len(command) > 1024 * 1024: - raise ValueError("canonical command is outside bounds") - object.__setattr__(self, "canonical_command", command) - if self.plan_commitment is not None: - commitment = bytes(self.plan_commitment) - if len(commitment) != 32: - raise ValueError("plan commitment must contain 32 bytes") - object.__setattr__(self, "plan_commitment", commitment) - - -@dataclass(frozen=True) -class ApplicationReservation: - idempotency_key: str - command_commitment: bytes - authority_commitment: bytes - context_commitment: bytes - plan_commitment: Optional[bytes] - member_index: Optional[int] - member_count: Optional[int] - observed_at: int - - -class ApplicationExecutionStore(Protocol): - async def reserve( - self, reservation: ApplicationReservation - ) -> Literal[ - "reserved", - "exact-replay", - "conflict", - "expired", - "out-of-order", - "unavailable", - ]: ... - - async def authorize_credential( - self, idempotency_key: str - ) -> Literal["authorized", "conflict", "unavailable"]: ... - - async def enter_provider( - self, idempotency_key: str - ) -> Literal["entered", "conflict", "unavailable"]: ... - - async def finish( - self, - idempotency_key: str, - outcome: ApplicationOutcome, - decision_receipt: AttestedReceipt, - execution_receipt: Optional[AttestedReceipt], - ) -> Literal["stored", "conflict", "unavailable"]: ... - - -class ApplicationCredentialProvider(Protocol, Generic[CredentialCommandT]): - async def acquire( - self, command: CredentialCommandT, context: ApplicationExecutionContext - ) -> object: ... - - -@dataclass(frozen=True) -class ApplicationGatewayOptions(Generic[CommandT, ResultT]): - state: ApplicationExecutionStore - credentials: ApplicationCredentialProvider[CommandT] - receipts: ReceiptAttestor - execute: Callable[ - [CommandT, object, ApplicationExecutionContext], Awaitable[ResultT] - ] - canonicalize_result: Callable[[ResultT], bytes] - - -class ApplicationGatewayError(AuthsWorkflowError): - def __init__( - self, - receipt: ApplicationReceipt, - completed_receipts: Tuple[ApplicationReceipt, ...] = (), - ) -> None: - unknown = receipt.outcome == "outcome-unknown" - super().__init__( - "gateway-failed", - "application gateway execution outcome is unknown" - if unknown - else "application gateway execution failed without an effect", - operation="execute", - stage="provider", - retry="unknown" if unknown else "safe", - effect_state="outcome-unknown" if unknown else "failed", - remediation=( - "reconcile the idempotency key before another execution attempt" - if unknown - else "inspect the provider failure before retrying" - ), - ) - self.receipt = receipt - self.completed_receipts = completed_receipts - - -class ApplicationGatewayCancelled(AuthsWorkflowError): - def __init__( - self, - receipt: ApplicationReceipt, - completed_receipts: Tuple[ApplicationReceipt, ...] = (), - ) -> None: - entered_provider = receipt.outcome == "outcome-unknown" - super().__init__( - "gateway-cancelled", - "application gateway task was cancelled after provider entry" - if entered_provider - else "application gateway task was cancelled before provider entry", - operation="execute", - stage="provider" if entered_provider else "credential", - retry="unknown" if entered_provider else "safe", - effect_state="outcome-unknown" if entered_provider else "failed", - remediation=( - "reconcile the idempotency key before another execution attempt" - if entered_provider - else "retry with a new authorized command" - ), - ) - self.receipt = receipt - self.completed_receipts = completed_receipts - - -class ApplicationGateway(Generic[CommandT, ResultT]): - def __init__( - self, - profile: ApplicationProfile[Any, CommandT], - options: ApplicationGatewayOptions[CommandT, ResultT], - ) -> None: - self._profile = profile - self._options = options - - async def execute( - self, command: native.ApplicationCommand, *, idempotency_key: str - ) -> Tuple[ResultT, ApplicationReceipt]: - if type(command) is not native.ApplicationCommand or not idempotency_key: - raise TypeError( - "gateway requires a native application command and idempotency key" - ) - binding = ( - bytes(command.action_commitment), - bytes(command.authority_commitment), - bytes(command.context_commitment), - ) - signer = self._options.receipts.signer - decision_preparation = native.prepare_application_command_decision_receipt_v1( - command, - int(time.time()), - signer.principal, - signer.verification_method, - signer.suite, - ) - decision_receipt = await _attest_decision( - decision_preparation, self._options.receipts - ) - call = native.consume_application_command( - command, self._profile.id, self._profile.version - ) - decoded = self._profile._decode(_canonical_from_call(call)) - context = ApplicationExecutionContext(idempotency_key, bytes(call.body)) - return await self._execute_one(decoded, binding, context, decision_receipt) - - async def execute_plan( - self, command: native.ApplicationPlanCommand, *, idempotency_key: str - ) -> Tuple[Tuple[ResultT, ...], Tuple[ApplicationReceipt, ...]]: - if type(command) is not native.ApplicationPlanCommand or not idempotency_key: - raise TypeError( - "gateway requires a native application plan command and idempotency key" - ) - plan_commitment = bytes(command.plan_commitment) - bindings = tuple( - (bytes(action), bytes(authority), bytes(context)) - for action, authority, context in command.receipt_bindings - ) - if len(bindings) != command.count: - raise RuntimeError( - "native application plan command omitted receipt bindings" - ) - signer = self._options.receipts.signer - decision_preparations = native.prepare_application_plan_decision_receipts_v1( - command, - int(time.time()), - signer.principal, - signer.verification_method, - signer.suite, - ) - decision_receipts = tuple( - [ - await _attest_decision(value, self._options.receipts) - for value in decision_preparations - ] - ) - if len(decision_receipts) != len(bindings): - raise RuntimeError("native application plan omitted decision receipts") - calls = native.consume_application_plan_command( - command, self._profile.id, self._profile.version - ) - results: list[ResultT] = [] - receipts: list[ApplicationReceipt] = [] - for index, (call, binding, decision_receipt) in enumerate( - zip(calls, bindings, decision_receipts) - ): - decoded = self._profile._decode(_canonical_from_call(call)) - member_key = f"{idempotency_key}:{index}" - context = ApplicationExecutionContext( - member_key, - bytes(call.body), - plan_commitment, - index, - len(bindings), - ) - try: - result, receipt = await self._execute_one( - decoded, - binding, - context, - decision_receipt, - ) - results.append(result) - receipts.append(receipt) - except ApplicationGatewayCancelled as error: - raise ApplicationGatewayCancelled( - error.receipt, tuple(receipts) - ) from None - except ApplicationGatewayError as error: - raise ApplicationGatewayError(error.receipt, tuple(receipts)) from None - return tuple(results), tuple(receipts) - - async def _execute_one( - self, - command: CommandT, - binding: Tuple[bytes, bytes, bytes], - context: ApplicationExecutionContext, - decision_receipt: AttestedReceipt, - ) -> Tuple[ResultT, ApplicationReceipt]: - reservation = ApplicationReservation( - context.idempotency_key, - binding[0], - binding[1], - binding[2], - context.plan_commitment, - context.member_index, - context.member_count, - int(time.time()), - ) - reserved = await _state_call(self._options.state.reserve(reservation)) - if reserved != "reserved": - raise _gateway_state_error(reserved) - credential_authorized = await _state_call( - self._options.state.authorize_credential(context.idempotency_key) - ) - if credential_authorized != "authorized": - await self._finish(context, "failed", decision_receipt, None) - raise _gateway_state_error(credential_authorized) - try: - credential = await self._options.credentials.acquire(command, context) - except asyncio.CancelledError: - await self._finish(context, "cancelled", decision_receipt, None) - raise ApplicationGatewayCancelled( - _receipt( - context.idempotency_key, - binding, - context.plan_commitment, - "cancelled", - decision_receipt, - None, - ) - ) from None - except Exception: - await self._finish(context, "failed", decision_receipt, None) - raise ApplicationGatewayError( - _receipt( - context.idempotency_key, - binding, - context.plan_commitment, - "failed", - decision_receipt, - None, - ) - ) from None - entered = await _state_call( - self._options.state.enter_provider(context.idempotency_key) - ) - if entered != "entered": - await self._finish(context, "failed", decision_receipt, None) - raise _gateway_state_error(entered) - try: - result = await self._options.execute(command, credential, context) - except asyncio.CancelledError: - await self._finish(context, "outcome-unknown", decision_receipt, None) - raise ApplicationGatewayCancelled( - _receipt( - context.idempotency_key, - binding, - context.plan_commitment, - "outcome-unknown", - decision_receipt, - None, - ) - ) from None - except Exception as error: - outcome: ApplicationOutcome = ( - "failed" - if isinstance(error, ProviderOperationError) - and error.effect_state == "not-started" - else "outcome-unknown" - ) - execution_receipt = None - if outcome == "failed": - try: - execution_receipt = await self._execution_receipt( - decision_receipt, - context, - context.canonical_command, - "failed", - None, - ) - except Exception: - execution_receipt = None - await self._finish(context, outcome, decision_receipt, execution_receipt) - raise ApplicationGatewayError( - _receipt( - context.idempotency_key, - binding, - context.plan_commitment, - outcome, - decision_receipt, - execution_receipt, - ) - ) from None - try: - result_bytes = bytes(self._options.canonicalize_result(result)) - if not result_bytes: - raise ValueError("empty canonical result") - except Exception: - await self._finish(context, "outcome-unknown", decision_receipt, None) - raise ApplicationGatewayError( - _receipt( - context.idempotency_key, - binding, - context.plan_commitment, - "outcome-unknown", - decision_receipt, - None, - ) - ) from None - try: - execution_receipt = await self._execution_receipt( - decision_receipt, - context, - context.canonical_command, - "succeeded", - result_bytes, - ) - except Exception: - await self._finish(context, "outcome-unknown", decision_receipt, None) - raise ApplicationGatewayError( - _receipt( - context.idempotency_key, - binding, - context.plan_commitment, - "outcome-unknown", - decision_receipt, - None, - ) - ) from None - completed = _receipt( - context.idempotency_key, - binding, - context.plan_commitment, - "succeeded", - decision_receipt, - execution_receipt, - ) - if ( - await self._finish( - context, "succeeded", decision_receipt, execution_receipt - ) - != "stored" - ): - raise ApplicationGatewayError( - _receipt( - context.idempotency_key, - binding, - context.plan_commitment, - "outcome-unknown", - decision_receipt, - execution_receipt, - ) - ) - return result, completed - - async def _execution_receipt( - self, - decision_receipt: AttestedReceipt, - context: ApplicationExecutionContext, - command_bytes: bytes, - outcome: Literal["succeeded", "failed"], - result: Optional[bytes], - ) -> AttestedReceipt: - signer = self._options.receipts.signer - preparation = native.prepare_application_execution_receipt_v1( - decision_receipt.receipt_id, - context.idempotency_key, - context.plan_commitment, - context.member_index, - context.member_count, - command_bytes, - outcome, - result, - int(time.time()), - signer.principal, - signer.verification_method, - signer.suite, - ) - return await _attest_execution(preparation, self._options.receipts) - - async def _finish( - self, - context: ApplicationExecutionContext, - outcome: ApplicationOutcome, - decision_receipt: AttestedReceipt, - execution_receipt: Optional[AttestedReceipt], - ) -> str: - return await _state_call( - self._options.state.finish( - context.idempotency_key, - outcome, - decision_receipt, - execution_receipt, - ) - ) - - -class ApplicationProfile(Profile, Generic[InputT, CommandT]): - def __init__(self, definition: ProfileDefinition[InputT, CommandT]) -> None: - if not callable(definition.canonicalize) or not callable( - definition.decode_verified - ): - raise TypeError( - "profile requires canonicalize and decode_verified callables" - ) - super().__init__(definition.id, definition.version) - self._canonicalize = definition.canonicalize - self._decode = definition.decode_verified - - def action(self, value: InputT) -> ApplicationAction[InputT]: - try: - canonical = self._canonicalize(value) - except Exception: - raise AuthsWorkflowError( - "invalid-profile", "profile rejected the action" - ) from None - if type(canonical) is not CanonicalProfileAction: - raise TypeError("profile canonicalizer must return CanonicalProfileAction") - native_action = native.application_action( - self.id, - self.version, - canonical.media_type, - canonical.body, - canonical.permission.capability, - canonical.permission.resource, - None - if canonical.budget is None - else (canonical.budget.algebra, canonical.budget.value), - canonical.resource_namespace, - canonical.audience, - ) - return ApplicationAction(_ACTION_TOKEN, self, canonical, native_action) - - def authority_for(self, action: ApplicationAction[InputT]) -> ApplicationAuthority: - self._assert_action(action) - canonical = action._canonical - return ApplicationAuthority( - ( - Permission( - canonical.permission.capability, canonical.permission.resource - ), - ), - (canonical.resource_namespace,), - (canonical.audience,), - canonical.budget, - ) - - def inspect_action( - self, action: ApplicationAction[InputT] - ) -> CanonicalProfileAction: - self._assert_action(action) - return action._canonical - - def review(self, action: ApplicationAction[InputT]) -> ApplicationReview: - self._assert_action(action) - return ApplicationReview( - f"{self.id}/{self.version}", - action._canonical.display, - bytes(native.application_action_commitment_v1(action._native)), - ) - - def plan( - self, actions: Sequence[ApplicationAction[InputT]] - ) -> ApplicationPlan[InputT]: - values = tuple(actions) - if not values or any(value._profile is not self for value in values): - raise AuthsWorkflowError( - "invalid-profile", "application plan contains an incompatible action" - ) - projection = native.commit_application_plan([value._native for value in values]) - first = values[0]._canonical - aggregate = sum( - value._canonical.budget.value - for value in values - if value._canonical.budget is not None - ) - budget = ( - None - if first.budget is None - else ProfileBudget(first.budget.algebra, aggregate) - ) - authority = ApplicationAuthority( - tuple( - dict.fromkeys( - Permission( - value._canonical.permission.capability, - value._canonical.permission.resource, - ) - for value in values - ) - ), - (first.resource_namespace,), - (first.audience,), - budget, - ) - return ApplicationPlan( - _PLAN_TOKEN, - self, - values, - bytes(projection.commitment), - tuple(bytes(value) for value in projection.members), - authority, - ) - - def gateway( - self, - options: ApplicationGatewayOptions[CommandT, ResultT], - ) -> ApplicationGateway[CommandT, ResultT]: - if type(options) is not ApplicationGatewayOptions: - raise TypeError("application gateway ports are required") - if ( - not callable(options.execute) - or not callable(options.canonicalize_result) - or not callable(getattr(options.receipts, "sign", None)) - or not callable(getattr(options.credentials, "acquire", None)) - or not callable(getattr(options.state, "reserve", None)) - or not callable(getattr(options.state, "authorize_credential", None)) - or not callable(getattr(options.state, "enter_provider", None)) - or not callable(getattr(options.state, "finish", None)) - ): - raise TypeError("application gateway ports are incomplete") - return ApplicationGateway(self, options) - - def _assert_action(self, action: ApplicationAction[InputT]) -> None: - if type(action) is not ApplicationAction or action._profile is not self: - raise AuthsWorkflowError( - "invalid-profile", "action belongs to another profile" - ) - - -def define_profile( - definition: ProfileDefinition[InputT, CommandT], -) -> ApplicationProfile[InputT, CommandT]: - if type(definition) is not ProfileDefinition: - raise TypeError("profile definition is required") - return ApplicationProfile(definition) - - -def _receipt( - idempotency_key: str, - binding: Tuple[bytes, bytes, bytes], - plan_commitment: Optional[bytes], - outcome: ApplicationOutcome, - decision_receipt: AttestedReceipt, - execution_receipt: Optional[AttestedReceipt], -) -> ApplicationReceipt: - return ApplicationReceipt( - idempotency_key, - binding[0], - binding[1], - binding[2], - plan_commitment, - cast( - ApplicationExecutionState, - native.runtime_application_execution_state_v1(outcome), - ), - outcome, - int(time.time()), - decision_receipt, - execution_receipt, - ) - - -async def _state_call(operation: Awaitable[str]) -> str: - try: - return await operation - except Exception: - return "unavailable" - - -def _gateway_state_error(code: str) -> AuthsWorkflowError: - value = ( - code - if code - in ("exact-replay", "conflict", "expired", "out-of-order", "unavailable") - else "unavailable" - ) - return AuthsWorkflowError( - "gateway-" + value, - "application gateway state rejected execution", - operation="execute", - stage="reservation", - retry="safe" if value == "unavailable" else "never", - effect_state="not-started", - ) - - -async def _authorize_application( - agent: AttachedAgent, - action: ApplicationAction[object], - request: Optional[ApplicationRequest], - approval_override: Optional[ApprovalConfiguration] = None, -) -> ApplicationResult[object]: - agent._assert_active() - if type(action) is not ApplicationAction or action._profile is not agent._profile: - raise AuthsWorkflowError( - "profile-mismatch", "application action belongs to another profile" - ) - request = ApplicationRequest() if request is None else request - if type(request) is not ApplicationRequest: - raise TypeError("request must be an ApplicationRequest") - prepared = native.prepare_application_action( - action._native, - agent.identity.principal.principal, - agent._grant_chain[-1].signed_grant, - request.challenge, - request.evaluation_time, - ) - approval_configuration = ( - agent._approval if approval_override is None else approval_override - ) - signed = await _SigningCoordinator().execute( - unsigned=prepared.unsigned, - principal=agent.identity.principal, - signer=agent._signer, - approval=approval_configuration, - required_approval=agent._client._configured_authority.required_approval, - expires_at=_transaction_expiry( - approval_configuration.policy.expires_in_seconds - ), - display=action._canonical.display, - ) - native_result, command = native.authorize_application( - prepared, - signed.signed_object, - [value.signed_grant for value in agent._grant_chain], - [ - [_native_evidence(evidence) for evidence in value.evidence] - for value in agent._grant_chain - ], - [_native_evidence(value) for value in signed.evidence], - agent._client._configured_authority.context, - ) - metrics = ApplicationMetrics(*native_result.metrics) - approval = ApplicationApproval( - approval_configuration.policy.reference.policy_id, - approval_configuration.policy.reference.evaluator_version, - bytes( - agent._client._configured_authority.required_approval.configuration_digest - ), - bytes(approval_configuration.policy.reference.configuration_digest), - approval_configuration.policy.mode, - approval_configuration.policy.max_uses, - signed.transaction_digest, - ) - explanation = _explanation(native_result.kind, native_result.code) - required = native_result.required_configuration - local = bytes(native_result.local_configuration) - encoded = bytes(native_result.result_cbor) - if native_result.kind == "authorized": - if command is None: - raise AuthsWorkflowError( - "native-authorization-failed", - "application authorization omitted its command", - ) - return ApplicationAuthorized( - "authorized", - native_result.code, - native_result.stage, - explanation, - metrics, - approval, - required, - local, - encoded, - command, - ) - if command is not None: - raise AuthsWorkflowError( - "native-authorization-failed", - "failed application decision returned a command", - ) - if native_result.kind == "denied": - return ApplicationDenied( - "denied", - native_result.code, - native_result.stage, - explanation, - metrics, - approval, - required, - local, - encoded, - ) - return ApplicationIndeterminate( - "indeterminate", - native_result.code, - native_result.stage, - explanation, - metrics, - approval, - required, - local, - encoded, - ) - - -async def _authorize_application_plan( - agent: AttachedAgent, - plan: ApplicationPlan[object], - approval_provider: Optional[ApprovalProvider], - requests: Optional[Sequence[ApplicationRequest]], -) -> ApplicationPlanResult[object]: - if type(plan) is not ApplicationPlan or plan._profile is not agent._profile: - raise AuthsWorkflowError( - "profile-mismatch", "application plan belongs to another profile" - ) - agent._assert_active() - _validate_application_plan(plan) - approval = agent._approval - if approval.policy.mode != "plan-once" or approval.policy.max_uses != plan.length: - raise AuthsWorkflowError( - "approval-policy-mismatch", - "plan-once approval must match the application plan", - ) - provider = approval.provider if approval_provider is None else approval_provider - request_values = ( - tuple(ApplicationRequest() for _ in plan._actions) - if requests is None - else tuple(requests) - ) - if len(request_values) != plan.length: - raise ValueError("authorization requests must match the application plan") - expires_at = int(time.time()) + approval.policy.expires_in_seconds - plan_approval = native.commit_plan_approval( - plan._commitment, - approval.policy.reference.configuration_digest, - approval.policy.max_uses, - expires_at, - ) - session = PlanApprovalSession( - plan_approval=bytes(plan_approval), - member_commitments=plan._member_commitments, - approval=approval, - provider=provider, - expires_at=expires_at, - display=( - ReviewField("Profile", f"{plan._profile.id}/{plan._profile.version}"), - ReviewField("Actions", str(plan.length)), - ), - ) - results: list[ApplicationResult[object]] = [] - try: - for index, (action, request) in enumerate(zip(plan._actions, request_values)): - _validate_application_plan(plan) - member_approval = ApprovalConfiguration( - approval.policy, - session.provider_for(index, plan._member_commitments[index]), - ) - result = await _authorize_application( - agent, action, request, member_approval - ) - results.append(result) - if isinstance(result, ApplicationDenied): - return ApplicationPlanDenied("denied", index, result) - if isinstance(result, ApplicationIndeterminate): - return ApplicationPlanIndeterminate("indeterminate", index, result) - authorized = cast( - Tuple[ApplicationAuthorized[object], ...], - tuple( - value for value in results if isinstance(value, ApplicationAuthorized) - ), - ) - command = native.seal_application_plan_command( - [value.command for value in authorized], - plan._profile.id, - plan._profile.version, - plan._commitment, - ) - return ApplicationPlanAuthorized("authorized", command, authorized) - finally: - session.dispose() - - -def _validate_application_plan(plan: ApplicationPlan[object]) -> None: - projection = native.commit_application_plan( - [action._native for action in plan._actions] - ) - if not native.commitments_equal_v1(bytes(projection.commitment), plan._commitment): - raise AuthsWorkflowError( - "invalid-profile", "application plan membership changed" - ) - if len(projection.members) != len(plan._member_commitments): - raise AuthsWorkflowError( - "invalid-profile", "application plan membership changed" - ) - for actual, expected in zip(projection.members, plan._member_commitments): - if not native.commitments_equal_v1(bytes(actual), expected): - raise AuthsWorkflowError( - "invalid-profile", "application plan membership changed" - ) - - -def _canonical_from_call(call: native.ApplicationGatewayCall) -> CanonicalProfileAction: - permission = ProfilePermission(*call.permission) - budget = None if call.budget is None else ProfileBudget(*call.budget) - return CanonicalProfileAction( - call.media_type, - bytes(call.body), - permission, - call.resource_namespace, - call.audience, - (), - budget, - ) - - -def _native_evidence(value: ControlEvidence) -> Tuple[str, str, bytes]: - return value.evidence_type, value.media_type, value.bytes - - -def _explanation(kind: str, code: str) -> ApplicationExplanation: - if kind == "authorized": - message = "the proof establishes exact authority for this application action" - elif kind == "denied": - message = "the supplied proof does not authorize this application action" - else: - message = "a required trustworthy fact or implementation is unavailable" - return ApplicationExplanation(code, message, kind == "indeterminate") - - -__all__ = [ - "ApplicationAction", - "ApplicationAuthority", - "ApplicationGateway", - "ApplicationGatewayCancelled", - "ApplicationGatewayError", - "ApplicationGatewayOptions", - "ApplicationCredentialProvider", - "ApplicationExecutionStore", - "ApplicationExecutionContext", - "ApplicationReservation", - "ApplicationPlan", - "ApplicationPlanAuthorized", - "ApplicationPlanDenied", - "ApplicationPlanIndeterminate", - "ApplicationPlanResult", - "ApplicationProfile", - "ApplicationRequest", - "ApplicationReceipt", - "ApplicationReview", - "ApplicationResult", - "ApplicationAuthorized", - "ApplicationDenied", - "ApplicationIndeterminate", - "CanonicalProfileAction", - "ProfileBudget", - "ProfileDefinition", - "ProfilePermission", - "define_profile", -] diff --git a/bindings/python/python/auths/_approvals.py b/bindings/python/python/auths/_approvals.py index b2f58445..96437361 100644 --- a/bindings/python/python/auths/_approvals.py +++ b/bindings/python/python/auths/_approvals.py @@ -6,7 +6,7 @@ from typing import Sequence from ._native import ApprovalPolicyReference, approval_policy_reference -from ._errors import ProviderOperationError +from ._product_errors import ProviderOperationError from ._workflow import ( Approval, ApprovalConfiguration, diff --git a/bindings/python/python/auths/_boundary.py b/bindings/python/python/auths/_boundary.py new file mode 100644 index 00000000..6d567863 --- /dev/null +++ b/bindings/python/python/auths/_boundary.py @@ -0,0 +1,57 @@ +"""The pyo3 boundary: failures cross as `AuthsError`, never as bare strings. + +`NativeAuthsError` already carries Rust's classification of the failure. A +public entry point that let it escape untranslated, or that raised a bare +`ValueError`, would hand the caller an exception with no code identity, no +effect state, no retry class, and no recommended action -- exactly the loss +contract 5.2 forbids. + +`TypeError` is deliberately NOT translated: a wrong argument type is a +contract violation by the caller, and relabelling it as an authorization +outcome is forbidden by contract 5.7. +""" + +from __future__ import annotations + +from functools import wraps +from typing import Any, Callable, TypeVar, cast + +from ._native import NativeAuthsError +from ._product_errors import AuthsError + +# Anything this build's registry cannot place is malformed input that never +# reached an effect. It is only used for a Python-side parse failure; a native +# failure always carries Rust's own code. +_PARSE_CODE = "core.malformed-input" + +CallableT = TypeVar("CallableT", bound=Callable[..., Any]) + + +def boundary(summary: str) -> Callable[[CallableT], CallableT]: + """Translates every failure of a public entry point into `AuthsError`.""" + + def decorate(function: CallableT) -> CallableT: + @wraps(function) + def wrapper(*arguments: Any, **keywords: Any) -> Any: + try: + return function(*arguments, **keywords) + except AuthsError: + raise + except NativeAuthsError as error: + raise _from_native(error, summary) from None + except ValueError: + raise AuthsError.from_code(_PARSE_CODE, summary) from None + + return cast(CallableT, wrapper) + + return decorate + + +def _from_native(error: NativeAuthsError, summary: str) -> AuthsError: + code = getattr(error, "code", None) + if not isinstance(code, str) or not code: + return AuthsError.from_code(_PARSE_CODE, summary) + return AuthsError.from_native_code(code, summary) + + +__all__ = ["boundary"] diff --git a/bindings/python/python/auths/_diagnostics.py b/bindings/python/python/auths/_diagnostics.py index 89ab8aa6..81fee8dd 100644 --- a/bindings/python/python/auths/_diagnostics.py +++ b/bindings/python/python/auths/_diagnostics.py @@ -11,7 +11,7 @@ diagnostic_input_limits_v1, native_abi_version, ) -from ._inspection import InspectionMetrics, VerificationStage, VerdictKind +from ._inspection import VerificationMetrics, VerificationStage, VerdictKind @runtime_checkable @@ -38,7 +38,7 @@ class DiagnosticResult: code: str stage: VerificationStage explanation: DiagnosticExplanation - metrics: InspectionMetrics + metrics: VerificationMetrics required_configuration: Optional[bytes] local_configuration: bytes result_cbor: bytes @@ -88,7 +88,7 @@ def verify( code=native.code, stage=native.stage, explanation=_explanation(native.kind, native.code), - metrics=InspectionMetrics(*native.metrics), + metrics=VerificationMetrics(*native.metrics), required_configuration=native.required_configuration, local_configuration=bytes(native.local_configuration), result_cbor=bytes(native.result_cbor), diff --git a/bindings/python/python/auths/_errors.py b/bindings/python/python/auths/_errors.py deleted file mode 100644 index fcfddd3d..00000000 --- a/bindings/python/python/auths/_errors.py +++ /dev/null @@ -1,139 +0,0 @@ -"""Stable, redacted Auths SDK failures.""" - -from __future__ import annotations - -from dataclasses import dataclass -from typing import Literal, Optional, Tuple - -RetryClass = Literal["never", "safe", "conditional", "unknown"] -EffectState = Literal[ - "not-started", "in-progress", "completed", "failed", "outcome-unknown" -] -ProviderFailureKind = Literal[ - "unavailable", "rejected", "cancelled", "timeout", "unsupported" -] - - -@dataclass(frozen=True) -class ErrorDetails: - family: str - code: str - operation: str - stage: str - correlation_id: Optional[str] - retry: RetryClass - effect_state: EffectState - remediation: str - cause_codes: Tuple[str, ...] - - -class AuthsError(Exception): - def __init__(self, message: str, details: ErrorDetails) -> None: - super().__init__(message) - self.details = details - self.family = details.family - self.code = details.code - self.operation = details.operation - self.stage = details.stage - self.correlation_id = details.correlation_id - self.retry = details.retry - self.effect_state = details.effect_state - self.remediation = details.remediation - self.cause_codes = details.cause_codes - - def __repr__(self) -> str: - return ( - f"{type(self).__name__}(family={self.family!r}, code={self.code!r}, " - f"operation={self.operation!r}, stage={self.stage!r}, " - f"retry={self.retry!r}, effect_state={self.effect_state!r})" - ) - - -class AuthsWorkflowError(AuthsError): - def __init__( - self, - code: str, - message: str, - *, - operation: str = "workflow", - stage: str = "coordinate", - retry: RetryClass = "never", - effect_state: EffectState = "not-started", - remediation: str = "inspect the typed workflow input and retry only if corrected", - correlation_id: Optional[str] = None, - cause_codes: Tuple[str, ...] = (), - ) -> None: - super().__init__( - message, - ErrorDetails( - "workflow", - code, - operation, - stage, - correlation_id, - retry, - effect_state, - remediation, - tuple(cause_codes), - ), - ) - - -class ProviderOperationError(AuthsError): - def __init__(self, kind: ProviderFailureKind) -> None: - if kind not in ( - "unavailable", - "rejected", - "cancelled", - "timeout", - "unsupported", - ): - raise ValueError("unsupported provider failure kind") - retry: RetryClass = "safe" if kind in ("unavailable", "timeout") else "never" - super().__init__( - "external provider operation failed", - ErrorDetails( - "provider", - kind, - "provider-callback", - "provider", - None, - retry, - "not-started", - "inspect the provider health and its conformance result", - (), - ), - ) - self.kind: ProviderFailureKind = kind - - -class RuntimeStateError(AuthsError): - def __init__( - self, code: str, *, retry: RetryClass, effect_state: EffectState - ) -> None: - super().__init__( - "runtime state transition failed", - ErrorDetails( - "runtime", - code, - "execute", - "state", - None, - retry, - effect_state, - "reconcile the command state before attempting another effect", - (), - ), - ) - - -__all__ = [ - "AuthsError", - "AuthsWorkflowError", - "EffectState", - "ErrorDetails", - "ProviderFailureKind", - "ProviderOperationError", - "RetryClass", - "RuntimeStateError", -] diff --git a/bindings/python/python/auths/_inspection.py b/bindings/python/python/auths/_inspection.py index daf71ec9..b15a63a9 100644 --- a/bindings/python/python/auths/_inspection.py +++ b/bindings/python/python/auths/_inspection.py @@ -42,7 +42,9 @@ @dataclass(frozen=True) -class InspectionMetrics: +class VerificationMetrics: + """The one metrics type. `InspectionMetrics` was a second name for it.""" + proof_bytes: int action_bytes: int context_bytes: int @@ -88,7 +90,7 @@ class DecisionInspection: decision: DecisionSummary kernel: KernelSummary commitments: DecisionCommitments - metrics: InspectionMetrics + metrics: VerificationMetrics approval: Optional[ApprovalInspection] safe_to_log: Mapping[str, SafeLogValue] @@ -133,7 +135,7 @@ def inspect_decision(result: InspectableDecision) -> DecisionInspection: if result.kind not in ("authorized", "denied", "indeterminate"): raise TypeError("decision is not an Auths verification result") metrics = result.metrics - inspection_metrics = InspectionMetrics( + inspection_metrics = VerificationMetrics( metrics.proof_bytes, metrics.action_bytes, metrics.context_bytes, @@ -258,7 +260,7 @@ def _approval_inspection(value: object) -> Optional[ApprovalInspection]: "DecisionInspection", "DecisionSummary", "InspectableDecision", - "InspectionMetrics", + "VerificationMetrics", "KernelSummary", "authorization_plan_bytes", "canonical_action_bytes", diff --git a/bindings/python/python/auths/_native.pyi b/bindings/python/python/auths/_native.pyi index d6936956..1f6444b1 100644 --- a/bindings/python/python/auths/_native.pyi +++ b/bindings/python/python/auths/_native.pyi @@ -308,6 +308,22 @@ class SigningTransaction: class NativeDelegationExpandedError(ValueError): ... +class NativeAuthsError(ValueError): + """A Rust failure crossing the pyo3 boundary with its classification. + + Every attribute is `auths_errors`' answer for `code`; the binding reads + them rather than deriving anything from the message. + """ + + @property + def code(self) -> str: ... + @property + def effect(self) -> str: ... + @property + def retry(self) -> str: ... + @property + def recommended_action(self) -> str: ... + class AuthorizationPlan: @property def plan_id(self) -> bytes: ... @@ -399,6 +415,8 @@ class McpSessionTerminal: @property def kind(self) -> str: ... @property + def code(self) -> Optional[str]: ... + @property def execution_id(self) -> str: ... @property def output_json(self) -> Optional[bytes]: ... diff --git a/bindings/python/python/auths/_plan.py b/bindings/python/python/auths/_plan.py index c0ca8a5f..cabb182e 100644 --- a/bindings/python/python/auths/_plan.py +++ b/bindings/python/python/auths/_plan.py @@ -4,7 +4,7 @@ from typing import Tuple from . import _native as native -from ._errors import AuthsWorkflowError, ProviderOperationError +from ._product_errors import AuthsWorkflowError, ProviderOperationError from ._workflow import ( ApprovalConfiguration, ApprovalProvider, diff --git a/bindings/python/python/auths/_product.py b/bindings/python/python/auths/_product.py index 7d6d856f..2dc3e4a1 100644 --- a/bindings/python/python/auths/_product.py +++ b/bindings/python/python/auths/_product.py @@ -3,8 +3,16 @@ import time from dataclasses import dataclass from types import TracebackType -from typing import Awaitable, Callable, Literal, NoReturn, Optional, Type, Union - +from typing import Awaitable, Callable, Final, Literal, NoReturn, Optional, Type, Union + +from ._product_errors import ( + AuthsError, + AuthsErrorCode, + EffectState, + RecommendedAction, + RetryClass, + classify, +) from .profiles._mcp import ( McpAction, McpClosedProvider, @@ -25,7 +33,6 @@ McpToolAuthority, execute_mcp_closed, execute_mcp_plan_closed, - recover_mcp_closed, resources_for_mcp_authority, resume_mcp_closed, ) @@ -68,14 +75,30 @@ class Completed: @dataclass(frozen=True) class Denied: + """Nothing happened, and the caller can prove it from `effect`.""" + kind: Literal["denied"] - code: str + code: AuthsErrorCode + reason: str + """The kernel's own denial reason, e.g. `permission-not-granted`. + + Diagnostic. `code` is the stable identity a caller branches on. + """ + effect: EffectState + retry: RetryClass + recommended_action: RecommendedAction @dataclass(frozen=True) class Indeterminate: + """The decision could not be reached; read `effect` before retrying.""" + kind: Literal["indeterminate"] - code: str + code: AuthsErrorCode + reason: str + effect: EffectState + retry: RetryClass + recommended_action: RecommendedAction class ExecutionReference: @@ -127,8 +150,18 @@ def decode_execution_reference(value: bytes) -> ExecutionReference: @dataclass(frozen=True) class RecoveryResult: + """An execution that did not complete. + + `effect` is the safety-critical field: `possible` means the real-world + effect may already have been applied and a blind retry may repeat it. + """ + kind: Literal["recoverable", "not-applied", "exact-replay", "conflict"] execution_id: str + code: AuthsErrorCode + effect: EffectState + retry: RetryClass + recommended_action: RecommendedAction reference: Optional[ExecutionReference] = None @@ -143,6 +176,10 @@ class PlanCompleted: class PlanRecoveryResult: kind: Literal["recoverable", "not-applied", "exact-replay", "conflict"] execution_id: str + code: AuthsErrorCode + effect: EffectState + retry: RetryClass + recommended_action: RecommendedAction completed_results: tuple[object, ...] completed_receipts: tuple[Receipt, ...] reference: Optional[ExecutionReference] = None @@ -254,30 +291,6 @@ async def resume( ) return _project_execution(result) - async def recover( - self, - *, - action: McpAction, - provider: McpClosedProvider, - request_id: Optional[str] = None, - ) -> ExecutionResult: - self._assert_active() - self._assert_provider(provider) - result = await recover_mcp_closed( - self._resources.agent, - action, - McpExecutionResources( - provider, - self._resources.state, - self._resources.receipts, - self._resources.receipt_attestor, - self._resources.session_key, - request_id, - self._resources.observer, - ), - ) - return _project_execution(result) - async def delegate( self, *, @@ -406,6 +419,23 @@ def decode_receipt(value: bytes) -> Receipt: return decode_linked_receipt(value) +# The two registry codes that name a kernel verdict. The kernel's own reason +# string (`permission-not-granted`, ...) is carried as `reason`; these are the +# stable identities whose effect, retry class, and recommended action Rust owns. +_DENIED_CODE: Final = "core.authorization-denied" +_INDETERMINATE_CODE: Final = "core.authorization-indeterminate" + + +def _axis(code: str) -> tuple[EffectState, RetryClass, RecommendedAction]: + """Reads Rust's classification of `code`. Nothing here is computed.""" + classification = classify(code) + return ( + classification.effect, + classification.retry, + classification.recommended_action, + ) + + def _project_execution(value: object) -> ExecutionResult: if isinstance(value, McpCompleted): return Completed( @@ -415,17 +445,26 @@ def _project_execution(value: object) -> ExecutionResult: Receipt(value.receipt.decision, value.receipt.execution), ) if isinstance(value, McpDenied): - return Denied("denied", value.code) + return Denied("denied", _DENIED_CODE, value.code, *_axis(_DENIED_CODE)) if isinstance(value, McpIndeterminate): - return Indeterminate("indeterminate", value.code) + return Indeterminate( + "indeterminate", + _INDETERMINATE_CODE, + value.code, + *_axis(_INDETERMINATE_CODE), + ) if isinstance(value, McpRecoverable): return RecoveryResult( "recoverable", value.execution_id, + value.code, + *_axis(value.code), ExecutionReference(_REFERENCE_TOKEN, value.execution_reference), ) if isinstance(value, McpNotApplied): - return RecoveryResult(value.kind, value.execution_id) + return RecoveryResult( + value.kind, value.execution_id, value.code, *_axis(value.code) + ) raise RuntimeError("MCP execution returned an unsupported result") @@ -437,13 +476,20 @@ def _project_plan_execution(value: object) -> ExecutionResult: tuple(Receipt(item.decision, item.execution) for item in value.receipts), ) if isinstance(value, McpPlanDenied): - return Denied("denied", value.result.code) + return Denied("denied", _DENIED_CODE, value.result.code, *_axis(_DENIED_CODE)) if isinstance(value, McpPlanIndeterminate): - return Indeterminate("indeterminate", value.result.code) + return Indeterminate( + "indeterminate", + _INDETERMINATE_CODE, + value.result.code, + *_axis(_INDETERMINATE_CODE), + ) if isinstance(value, McpPlanRecoveryResult): return PlanRecoveryResult( value.kind, value.execution_id, + value.code, + *_axis(value.code), value.completed_results, tuple( Receipt(item.decision, item.execution) @@ -460,6 +506,8 @@ def _project_plan_execution(value: object) -> ExecutionResult: "Actor", "Auths", "AuthsConfiguration", + "AuthsError", + "AuthsErrorCode", "Authority", "Completed", "Denied", @@ -467,12 +515,15 @@ def _project_plan_execution(value: object) -> ExecutionResult: "decode_receipt", "encode_execution_reference", "encode_receipt", + "EffectState", "ExecutionReference", "ExecutionResult", "Indeterminate", "PlanCompleted", "PlanRecoveryResult", "Receipt", + "RecommendedAction", "RecoveryResult", + "RetryClass", "verify_receipt", ] diff --git a/bindings/python/python/auths/_product_errors.py b/bindings/python/python/auths/_product_errors.py index c4ca9a70..90179f11 100644 --- a/bindings/python/python/auths/_product_errors.py +++ b/bindings/python/python/auths/_product_errors.py @@ -1,4 +1,15 @@ -"""Bounded error and recovery projections owned by the Rust registry.""" +"""The single Auths error vocabulary, projected from the Rust registry. + +Every value in this module is owned by `auths_errors` and reaches Python +through `bindings/python/python/auths/_error_registry.py`, which +`cargo xtask error-registry` generates and byte-compares. Nothing here +defines what a code, an effect, a retry class, or a recommended action +*means*; the only decision Python makes is which registry code names a +failure it observed. + +There is exactly one exception hierarchy. `AuthsError` is its root and is +what every failing Auths path raises. +""" from __future__ import annotations @@ -6,18 +17,27 @@ from enum import Enum from asyncio import CancelledError from types import MappingProxyType -from typing import Any, Final, Mapping, Optional, Sequence, Tuple, cast +from typing import Any, Final, Literal, Mapping, Optional, Sequence, Tuple, cast -from ._error_registry import ERROR_REGISTRY +from ._error_registry import ERROR_REGISTRY, UNRECOGNIZED_CODE AuthsErrorCode = str +ProductVerb = Literal["create", "delegate", "execute", "resume", "verify"] +"""`auths_errors::ProductVerb` -- the five product operations. + +The wire field is `verb`. The `step` spelling and any sixth verb are deleted: +`sign` is a stage of `create`/`delegate`, and `recover` has no Rust owner. +""" + MAX_TOKEN_BYTES: Final = 128 MAX_TEXT_BYTES: Final = 256 TOKEN_CHARS: Final = frozenset("abcdefghijklmnopqrstuvwxyz0123456789._:/-") class RetryClass(str, Enum): + """`auths_errors::RetryClass` — *may I retry?*""" + NEVER = "never" SAFE = "safe" CONDITIONAL = "conditional" @@ -25,10 +45,15 @@ class RetryClass(str, Enum): class EffectState(str, Enum): + """`auths_errors::EffectState` — *did the real-world effect happen?* + + Exactly three members. An unrecognized code fails closed to ``POSSIBLE``; + there is no fourth value and never a downgrade to ``NOT_APPLIED``. + """ + NOT_APPLIED = "not-applied" POSSIBLE = "possible" APPLIED = "applied" - UNKNOWN = "unknown" class RecommendedAction(str, Enum): @@ -62,6 +87,20 @@ class EnteredBoundaries: provider: bool +@dataclass(frozen=True) +class AuthsErrorClassification: + """What the Rust registry says a code means. Never computed locally.""" + + code: str + known: bool + family: str + operation: str + stage: str + retry: RetryClass + effect: EffectState + recommended_action: RecommendedAction + + @dataclass(frozen=True) class AuthsErrorDetails: family: str @@ -78,6 +117,12 @@ class AuthsErrorDetails: decision_reference: Optional[str] receipt_reference: Optional[str] causes: Tuple[CauseCategory, ...] + reason: Optional[str] = None + """Unstable diagnostic label for the exact site that failed. + + Never a code: it is not registered, not versioned, and no caller may + branch on it in production. `code` is the stable identity. + """ def to_dict(self) -> Mapping[str, Any]: return MappingProxyType( @@ -108,6 +153,8 @@ def to_dict(self) -> Mapping[str, Any]: class AuthsError(Exception): + """The one Auths exception. Raised by every path that fails.""" + def __init__(self, details: AuthsErrorDetails) -> None: super().__init__(details.summary) self.details = details @@ -116,10 +163,78 @@ def __init__(self, details: AuthsErrorDetails) -> None: def parse(cls, value: object) -> "AuthsError": return cls(_parse_details(value)) + @classmethod + def from_native_code( + cls, + code: str, + summary: str, + *, + reason: Optional[str] = None, + correlation_id: str = "unset", + ) -> "AuthsError": + """Rebuilds an error Rust already classified, at the pyo3 boundary. + + Unlike `from_code`, a code this build's registry does not contain is + accepted and fails closed to `possible` -- a newer Rust must not be + able to crash an older binding, nor be silently downgraded. + """ + return cls( + _details_for_code( + code, + summary, + reason=reason, + correlation_id=correlation_id, + entered=None, + execution_reference=None, + decision_reference=None, + receipt_reference=None, + causes=(), + known_only=False, + ) + ) + + @classmethod + def from_code( + cls, + code: str, + summary: str, + *, + reason: Optional[str] = None, + correlation_id: str = "unset", + entered: Optional[EnteredBoundaries] = None, + execution_reference: Optional[str] = None, + decision_reference: Optional[str] = None, + receipt_reference: Optional[str] = None, + causes: Sequence[CauseCategory] = (), + ) -> "AuthsError": + """Mints an error whose whole recovery contract comes from Rust. + + `code` must be a registry code. Everything the caller branches on -- + family, operation, stage, retry, effect, recommended action -- is read + from the registry, never supplied here. + """ + return cls( + _details_for_code( + code, + summary, + reason=reason, + correlation_id=correlation_id, + entered=entered, + execution_reference=execution_reference, + decision_reference=decision_reference, + receipt_reference=receipt_reference, + causes=causes, + ) + ) + @property def code(self) -> str: return self.details.code + @property + def reason(self) -> Optional[str]: + return self.details.reason + @property def retry(self) -> RetryClass: return self.details.retry @@ -139,6 +254,153 @@ def execution_reference(self) -> Optional[str]: def to_dict(self) -> Mapping[str, Any]: return self.details.to_dict() + def __repr__(self) -> str: + return ( + f"{type(self).__name__}(code={self.code!r}, reason={self.reason!r}, " + f"effect={self.effect.value!r}, retry={self.retry.value!r}, " + f"recommended_action={self.recommended_action.value!r})" + ) + + +WORKFLOW_REASON_CODES: Final[Mapping[str, str]] = MappingProxyType( + { + # Custody boundary -- an approval or signing provider said no, went + # away, or answered something other than what was asked. + "approval-cancelled": "custody.cancelled", + "approval-failed": "custody.unavailable", + "approval-rejected": "custody.denied", + "approval-response-mismatch": "custody.request-mismatch", + "approval-timeout": "custody.unavailable", + "approval-unsupported": "custody.provider-unknown", + "signer-cancelled": "custody.cancelled", + "signer-failed": "custody.unavailable", + "signer-rejected": "custody.denied", + "signer-response-mismatch": "custody.request-mismatch", + "signer-timeout": "custody.unavailable", + "signer-unsupported": "custody.provider-unknown", + "cleanup-failed": "custody.unavailable", + "authority-mismatch": "custody.evidence-mismatch", + # Authority source -- a caller-supplied signed-grant provider. + "authority-source-cancelled": "core.runtime-cancelled", + "authority-source-failed": "core.runtime-unavailable", + "authority-source-rejected": "core.authorization-denied", + "authority-source-timeout": "core.runtime-unavailable", + "authority-source-unavailable": "core.runtime-unavailable", + "authority-source-unsupported": "core.invalid-configuration", + # Authority algebra -- delegation may narrow, never widen. + "delegation-expanded": "core.authorization-denied", + "invalid-delegation": "core.authorization-denied", + # Input and configuration. + "invalid-action": "core.malformed-input", + "invalid-authority": "core.malformed-input", + "invalid-principal": "core.unauthenticated-principal", + "invalid-profile": "core.invalid-configuration", + "invalid-provider": "core.invalid-configuration", + "invalid-trusted-authority": "core.invalid-configuration", + "approval-policy-mismatch": "core.invalid-configuration", + "profile-mismatch": "core.invalid-configuration", + # State. + "disposed": "core.workflow-terminal", + "gateway-conflict": "core.runtime-conflict", + "transaction-consumed": "core.runtime-conflict", + "transaction-expired": "core.runtime-conflict", + # The gateway entered the provider and cannot prove the outcome. Both + # are `mcp.handler-failed`: effect `possible`, reconcile before retry. + "gateway-cancelled": "mcp.handler-failed", + "gateway-failed": "mcp.handler-failed", + # The binding believed something Rust guarantees. + "native-authorization-failed": "core.internal-invariant", + } +) +"""Which Rust-owned code names each workflow failure site. + +This is the only place the Python package selects a code, and every value is +a `product/errors/v1/registry.json` entry. The keys are diagnostic labels, +not a second code space: `tests/test_registry_code_inventory.py` fails when a +call site uses a key that is not here, or a value that is not in the registry. +""" + + +class AuthsWorkflowError(AuthsError): + """An authorization workflow failure, named by a registry code. + + `reason` is the failure site; `code` is the Rust-owned identity a caller + branches on. Only reasons listed in `WORKFLOW_REASON_CODES` exist. + """ + + def __init__( + self, + reason: str, + summary: str, + *, + correlation_id: str = "unset", + entered: Optional[EnteredBoundaries] = None, + causes: Sequence[CauseCategory] = (), + ) -> None: + code = WORKFLOW_REASON_CODES.get(reason) + if code is None: + raise LookupError( + f"workflow failure reason {reason!r} names no registry code; " + f"add it to WORKFLOW_REASON_CODES" + ) + super().__init__( + _details_for_code( + code, + summary, + reason=reason, + correlation_id=correlation_id, + entered=entered, + execution_reference=None, + decision_reference=None, + receipt_reference=None, + causes=causes, + ) + ) + + +ProviderFailureKind = Literal[ + "unavailable", "rejected", "cancelled", "timeout", "unsupported" +] + +_PROVIDER_FAILURE_CODES: Final[Mapping[str, str]] = MappingProxyType( + { + "unavailable": "custody.unavailable", + "rejected": "custody.denied", + "cancelled": "custody.cancelled", + "timeout": "custody.unavailable", + "unsupported": "custody.provider-unknown", + } +) + + +class ProviderOperationError(AuthsError): + """Raised by a caller-supplied signer or approval provider. + + The `kind` is the provider's own vocabulary; the registry code it maps to + is what a caller branches on. + """ + + _CODES: Final[Mapping[str, str]] = _PROVIDER_FAILURE_CODES + + def __init__(self, kind: ProviderFailureKind) -> None: + code = self._CODES.get(kind) + if code is None: + raise ValueError("unsupported provider failure kind") + super().__init__( + _details_for_code( + code, + "external provider operation failed", + reason=kind, + correlation_id="unset", + entered=EnteredBoundaries(False, True, False, True, True), + execution_reference=None, + decision_reference=None, + receipt_reference=None, + causes=(), + ) + ) + self.kind: ProviderFailureKind = kind + def format_auths_error(error: AuthsError) -> str: return ( @@ -177,7 +439,7 @@ def create_support_bundle( ) -> Mapping[str, Any]: parsed_errors: list[dict[str, Any]] = [] for error in errors: - if type(error) is not AuthsError: + if not isinstance(error, AuthsError): raise TypeError("support bundle errors must be AuthsError values") parsed_errors.append(dict(error.to_dict())) parsed_errors.sort(key=lambda value: (value["code"], value["correlationId"])) @@ -199,9 +461,96 @@ def create_support_bundle( ) -_DEFINITIONS = { - definition["code"]: definition for definition in ERROR_REGISTRY["definitions"] -} +_DEFINITIONS: Final[Mapping[str, Any]] = MappingProxyType( + {definition["code"]: definition for definition in ERROR_REGISTRY["definitions"]} +) + + +def classify(code: str) -> AuthsErrorClassification: + """Reads Rust's classification of `code`, failing closed for unknown codes. + + This is the only way anything in this package learns what a code means. + """ + definition = _DEFINITIONS.get(code) + if definition is None: + return AuthsErrorClassification( + code=code, + known=False, + family=UNRECOGNIZED_CODE["family"], + operation=UNRECOGNIZED_CODE["operation"], + stage=UNRECOGNIZED_CODE["stages"][0], + retry=RetryClass(UNRECOGNIZED_CODE["retry"]), + effect=EffectState(UNRECOGNIZED_CODE["effect"]), + recommended_action=RecommendedAction( + UNRECOGNIZED_CODE["recommendedAction"] + ), + ) + outcome = definition["outcomes"][0] + return AuthsErrorClassification( + code=code, + known=True, + family=definition["family"], + operation=definition["operation"], + stage=definition["stages"][0], + retry=RetryClass(outcome["retry"]), + effect=EffectState(outcome["effect"]), + recommended_action=RecommendedAction(definition["recommendedAction"]), + ) + + +def registry_codes() -> Tuple[str, ...]: + return tuple(_DEFINITIONS) + + +def _details_for_code( + code: str, + summary: str, + *, + reason: Optional[str], + correlation_id: str, + entered: Optional[EnteredBoundaries], + execution_reference: Optional[str], + decision_reference: Optional[str], + receipt_reference: Optional[str], + causes: Sequence[CauseCategory], + known_only: bool = True, +) -> AuthsErrorDetails: + if known_only and code not in _DEFINITIONS: + raise ValueError( + "Auths errors carry registry codes only; " + f"{code!r} is in no build of product/errors/v1/registry.json" + ) + classification = classify(code) + boundaries = ( + EnteredBoundaries(False, False, False, False, False) + if entered is None + else entered + ) + if classification.effect is EffectState.POSSIBLE and not boundaries.provider: + boundaries = EnteredBoundaries( + boundaries.approval, + boundaries.signer, + boundaries.state, + boundaries.credential, + True, + ) + return AuthsErrorDetails( + family=classification.family, + code=code, + operation=classification.operation, + stage=classification.stage, + summary=_text(summary), + correlation_id=_token(correlation_id), + retry=classification.retry, + effect=classification.effect, + entered=boundaries, + recommended_action=classification.recommended_action, + execution_reference=execution_reference, + decision_reference=decision_reference, + receipt_reference=receipt_reference, + causes=tuple(causes), + reason=reason, + ) def _parse_details(value: object) -> AuthsErrorDetails: @@ -278,6 +627,12 @@ def _parse_details(value: object) -> AuthsErrorDetails: def _unknown_details(item: Mapping[str, Any], code: str) -> AuthsErrorDetails: + """A code this build's registry does not contain. + + The answer is `auths_errors::classify`'s, projected through + `UNRECOGNIZED_CODE`. It is `possible`: a newer Rust code must never be + downgraded to `not-applied` by an older binding. + """ _token(item.get("operation")) _token(item.get("stage")) _text(item.get("summary")) @@ -288,17 +643,18 @@ def _unknown_details(item: Mapping[str, Any], code: str) -> AuthsErrorDetails: cause_values = cast(list[object], raw_causes) if len(cause_values) > 8: raise ValueError("Auths error causes are invalid") + classification = classify(code) return AuthsErrorDetails( - family="unknown", + family=classification.family, code=code, - operation="unknown", - stage="unknown", + operation=classification.operation, + stage=classification.stage, summary="Unknown Auths error code", correlation_id=correlation_id, - retry=RetryClass.UNKNOWN, - effect=EffectState.UNKNOWN, - entered=EnteredBoundaries(False, False, False, False, False), - recommended_action=RecommendedAction.CONTACT_SUPPORT, + retry=classification.retry, + effect=classification.effect, + entered=EnteredBoundaries(False, False, False, False, True), + recommended_action=classification.recommended_action, execution_reference=None, decision_reference=None, receipt_reference=None, @@ -354,16 +710,24 @@ def _sorted_tokens(values: Sequence[str]) -> Tuple[str, ...]: __all__ = [ - "AuthsErrorCode", "AuthsError", + "AuthsErrorClassification", + "AuthsErrorCode", "AuthsErrorDetails", + "AuthsWorkflowError", "CauseCategory", "EffectState", "EnteredBoundaries", + "ProductVerb", + "ProviderFailureKind", + "ProviderOperationError", "RecommendedAction", "RetryClass", + "WORKFLOW_REASON_CODES", "cause_category_from", + "classify", "create_support_bundle", "error_reference_url", "format_auths_error", + "registry_codes", ] diff --git a/bindings/python/python/auths/_receipts.py b/bindings/python/python/auths/_receipts.py index 3e729f3d..704e2f9a 100644 --- a/bindings/python/python/auths/_receipts.py +++ b/bindings/python/python/auths/_receipts.py @@ -154,17 +154,29 @@ class InvalidReceiptInspection: class ReceiptDisclosureProtector(Protocol): - def protect(self, tenant: str, receipt_id: bytes, plaintext: bytes) -> bytes: ... + """Async to match `@auths-dev/sdk`'s port. - def reveal(self, tenant: str, receipt_id: bytes, protected: bytes) -> bytes: ... + A synchronous port cannot be implemented over a KMS, an HSM, or any other + network custody boundary, which is every protector worth having. + """ + + async def protect( + self, tenant: str, receipt_id: bytes, plaintext: bytes + ) -> bytes: ... + + async def reveal( + self, tenant: str, receipt_id: bytes, protected: bytes + ) -> bytes: ... class ReceiptDisclosureStore(Protocol): - def put(self, tenant: str, receipt_id: bytes, protected: bytes) -> None: ... + """Async to match `@auths-dev/sdk`'s port.""" + + async def put(self, tenant: str, receipt_id: bytes, protected: bytes) -> None: ... - def get(self, tenant: str, receipt_id: bytes) -> Optional[bytes]: ... + async def get(self, tenant: str, receipt_id: bytes) -> Optional[bytes]: ... - def delete(self, tenant: str, receipt_id: bytes) -> None: ... + async def delete(self, tenant: str, receipt_id: bytes) -> None: ... def verify_receipt(receipt: AttestedReceipt) -> None: diff --git a/bindings/python/python/auths/_production_client.py b/bindings/python/python/auths/_service.py similarity index 62% rename from bindings/python/python/auths/_production_client.py rename to bindings/python/python/auths/_service.py index 95a07f23..154a515c 100644 --- a/bindings/python/python/auths/_production_client.py +++ b/bindings/python/python/auths/_service.py @@ -9,7 +9,7 @@ import urllib.request from dataclasses import dataclass from types import MappingProxyType -from typing import IO, Literal, Mapping, NoReturn, Optional, Protocol, Union, cast, runtime_checkable +from typing import IO, Final, Literal, Mapping, NoReturn, Optional, Protocol, Union, cast, runtime_checkable from urllib.parse import urlparse from ._native import ( @@ -18,7 +18,15 @@ encode_production_request_v1, production_client_contract_version_v1, ) -from .profiles import ProductionProfile +from ._product_errors import ( + AuthsErrorCode, + ProductVerb, + EffectState, + RecommendedAction, + RetryClass, + classify, +) +from .profiles import ServiceProfile _CONTENT_TYPE: Literal["application/auths+cbor"] = "application/auths+cbor" _MAX_RESPONSE_BYTES = 1_048_576 @@ -27,12 +35,17 @@ _RECEIPT_TOKEN = object() _REFERENCE_TOKEN = object() -ProductStep = Literal["create", "delegate", "execute", "resume", "verify"] -RetryClass = Literal["never", "backoff", "resume", "reconcile"] +NextCall = Literal["never", "backoff", "resume", "reconcile"] +"""`auths_production_client::NextCall` -- *what should I call next?* + +This is not `RetryClass`. `auths.RetryClass` answers *may I retry?* and has +the members never|safe|conditional|unknown. The two questions must never +share an identifier again (contract 4.1). +""" @dataclass(frozen=True) -class ProductionTransportRequest: +class ServiceTransportRequest: url: str body: bytes content_type: Literal["application/auths+cbor"] @@ -40,20 +53,20 @@ class ProductionTransportRequest: @dataclass(frozen=True) -class ProductionTransportResponse: +class ServiceTransportResponse: status: int content_type: str body: bytes @runtime_checkable -class ProductionTransport(Protocol): +class ServiceTransport(Protocol): async def send( - self, request: ProductionTransportRequest - ) -> ProductionTransportResponse: ... + self, request: ServiceTransportRequest + ) -> ServiceTransportResponse: ... -class ProductionAuthority: +class ServiceAuthority: __slots__ = ("_bytes",) kind: Literal["authority"] = "authority" @@ -66,7 +79,7 @@ def __reduce__(self) -> NoReturn: raise TypeError("Auths authority is opaque") -class ProductionReceipt: +class ServiceReceipt: __slots__ = ("_bytes",) kind: Literal["receipt"] = "receipt" @@ -79,7 +92,7 @@ def __reduce__(self) -> NoReturn: raise TypeError("Auths receipt bytes require an explicit disclosure operation") -class ProductionRecoveryReference: +class ServiceRecoveryReference: __slots__ = ("_value",) kind: Literal["recovery-reference"] = "recovery-reference" @@ -93,80 +106,92 @@ def __reduce__(self) -> NoReturn: @dataclass(frozen=True) -class ProductionDenied: +class ServiceDenied: kind: Literal["denied"] - step: ProductStep - code: str - retry: Literal["never"] + verb: ProductVerb + code: AuthsErrorCode + next_call: Literal["never"] + effect: EffectState + retry: RetryClass + recommended_action: RecommendedAction @dataclass(frozen=True) -class ProductionIndeterminate: +class ServiceIndeterminate: kind: Literal["indeterminate"] - step: ProductStep - code: str - retry: Literal["backoff", "reconcile"] + verb: ProductVerb + code: AuthsErrorCode + next_call: Literal["backoff", "reconcile"] + effect: EffectState + retry: RetryClass + recommended_action: RecommendedAction @dataclass(frozen=True) -class ProductionRecoverable: +class ServiceRecoverable: kind: Literal["recoverable"] - step: Literal["execute", "resume"] - code: str - retry: Literal["resume"] - reference: ProductionRecoveryReference + verb: Literal["execute", "resume"] + code: AuthsErrorCode + next_call: Literal["resume"] + effect: EffectState + retry: RetryClass + recommended_action: RecommendedAction + reference: ServiceRecoveryReference @dataclass(frozen=True) -class ProductionCompleted: +class ServiceCompleted: kind: Literal["completed"] - step: Literal["execute", "resume"] + verb: Literal["execute", "resume"] value: Optional[bytes] - receipt: ProductionReceipt + receipt: ServiceReceipt @dataclass(frozen=True) -class ProductionVerified: +class ServiceVerified: kind: Literal["verified"] - step: Literal["verify"] + verb: Literal["verify"] value: Optional[bytes] @dataclass(frozen=True) -class ProductionRejected: +class ServiceRejected: kind: Literal["rejected"] - step: Literal["verify"] - code: str - retry: Literal["never"] + verb: Literal["verify"] + code: AuthsErrorCode + next_call: Literal["never"] + effect: EffectState + retry: RetryClass + recommended_action: RecommendedAction -ProductionAuthorityResult = Union[ - ProductionAuthority, ProductionDenied, ProductionIndeterminate +ServiceAuthorityResult = Union[ + ServiceAuthority, ServiceDenied, ServiceIndeterminate ] -ProductionExecutionResult = Union[ - ProductionCompleted, - ProductionDenied, - ProductionIndeterminate, - ProductionRecoverable, +ServiceExecutionResult = Union[ + ServiceCompleted, + ServiceDenied, + ServiceIndeterminate, + ServiceRecoverable, ] -ProductionVerificationResult = Union[ - ProductionVerified, ProductionRejected, ProductionIndeterminate +ServiceVerificationResult = Union[ + ServiceVerified, ServiceRejected, ServiceIndeterminate ] -class ProductionAuths: +class ServiceAuths: def __init__( self, *, endpoint: str, identity: bytes, - profile: ProductionProfile, - transport: Optional[ProductionTransport] = None, + profile: ServiceProfile, + transport: Optional[ServiceTransport] = None, timeout_seconds: float = _DEFAULT_TIMEOUT_SECONDS, ) -> None: self._endpoint = _parse_endpoint(endpoint) self._identity = _bounded_bytes(identity, 65_536, "identity") - if type(profile) is not ProductionProfile or profile.id not in _PROFILE_IDS: + if type(profile) is not ServiceProfile or profile.id not in _PROFILE_IDS: raise TypeError("Auths production profile is unsupported") if ( type(timeout_seconds) not in (int, float) @@ -174,24 +199,24 @@ def __init__( or timeout_seconds > 120 ): raise ValueError("Auths production timeout is outside bounds") - if transport is not None and not isinstance(transport, ProductionTransport): + if transport is not None and not isinstance(transport, ServiceTransport): raise TypeError("Auths production transport is invalid") self._profile = profile - self._transport = transport or _UrlLibProductionTransport() + self._transport = transport or _UrlLibServiceTransport() self._timeout_seconds = float(timeout_seconds) - async def create(self, request: bytes) -> ProductionAuthorityResult: + async def create(self, request: bytes) -> ServiceAuthorityResult: projection = await self._call("create", body=request) if projection["kind"] == "completed": - return ProductionAuthority(_AUTHORITY_TOKEN, _required_value(projection)) + return ServiceAuthority(_AUTHORITY_TOKEN, _required_value(projection)) return _authority_failure("create", projection) async def delegate( self, - authority: ProductionAuthority, + authority: ServiceAuthority, subject: bytes, attenuation: bytes = b"\x80", - ) -> ProductionAuthorityResult: + ) -> ServiceAuthorityResult: body = bytes( encode_production_delegation_v1( _bounded_bytes(subject, 65_536, "subject"), @@ -202,12 +227,12 @@ async def delegate( "delegate", authority=_authority_bytes(authority), body=body ) if projection["kind"] == "completed": - return ProductionAuthority(_AUTHORITY_TOKEN, _required_value(projection)) + return ServiceAuthority(_AUTHORITY_TOKEN, _required_value(projection)) return _authority_failure("delegate", projection) async def execute( - self, authority: ProductionAuthority, action: bytes - ) -> ProductionExecutionResult: + self, authority: ServiceAuthority, action: bytes + ) -> ServiceExecutionResult: return _execution_result( "execute", await self._call( @@ -216,9 +241,9 @@ async def execute( ) async def resume( - self, reference: ProductionRecoveryReference - ) -> ProductionExecutionResult: - if type(reference) is not ProductionRecoveryReference: + self, reference: ServiceRecoveryReference + ) -> ServiceExecutionResult: + if type(reference) is not ServiceRecoveryReference: raise TypeError("forged Auths recovery reference") return _execution_result( "resume", @@ -226,20 +251,21 @@ async def resume( ) async def verify( - self, value: Union[ProductionAuthority, ProductionReceipt, bytes] - ) -> ProductionVerificationResult: - if type(value) is ProductionAuthority: + self, value: Union[ServiceAuthority, ServiceReceipt, bytes] + ) -> ServiceVerificationResult: + if type(value) is ServiceAuthority: body = value._bytes - elif type(value) is ProductionReceipt: + elif type(value) is ServiceReceipt: body = value._bytes else: body = _bounded_bytes(value, _MAX_RESPONSE_BYTES, "verification input") projection = await self._call("verify", body=body) if projection["kind"] == "verified": - return ProductionVerified("verified", "verify", _optional_bytes(projection["value"])) + return ServiceVerified("verified", "verify", _optional_bytes(projection["value"])) if projection["kind"] == "rejected": - return ProductionRejected( - "rejected", "verify", _required_code(projection), "never" + code = _required_code(projection) + return ServiceRejected( + "rejected", "verify", code, "never", *_axis(code) ) if projection["kind"] == "indeterminate": return _indeterminate("verify", projection) @@ -247,7 +273,7 @@ async def verify( async def _call( self, - step: ProductStep, + verb: ProductVerb, *, authority: Optional[bytes] = None, body: Optional[bytes] = None, @@ -255,7 +281,7 @@ async def _call( ) -> Mapping[str, object]: request_body = bytes( encode_production_request_v1( - step, + verb, self._profile.id, self._identity, authority, @@ -263,8 +289,8 @@ async def _call( recovery_reference, ) ) - request = ProductionTransportRequest( - self._endpoint + _endpoint_path(step, self._profile.id), + request = ServiceTransportRequest( + self._endpoint + _endpoint_path(verb, self._profile.id), request_body, _CONTENT_TYPE, self._timeout_seconds, @@ -272,48 +298,61 @@ async def _call( try: response = await self._transport.send(request) except Exception: - return MappingProxyType( - { - "contractVersion": 1, - "kind": "indeterminate", - "code": "core.runtime-unavailable", - "retry": "backoff", - "recoveryReference": None, - "value": None, - "receipt": None, - } - ) + # The request left this process. The server may have applied the + # effect and lost the response, so the effect is `possible`, never + # `not-applied`: `core.runtime-unavailable` would tell a caller a + # possibly-applied write is safe to blindly retry (contract 5.3). + return _unreachable_projection() if ( not 200 <= response.status < 300 or response.content_type.split(";", 1)[0].strip().lower() != _CONTENT_TYPE or not response.body or len(response.body) > _MAX_RESPONSE_BYTES ): - return MappingProxyType( - { - "contractVersion": 1, - "kind": "indeterminate", - "code": "core.malformed-input", - "retry": "backoff", - "recoveryReference": None, - "value": None, - "receipt": None, - } - ) + # A response this client cannot read is not evidence that nothing + # happened. Same rule as an unreachable server. + return _unreachable_projection() return _projection(decode_production_response_v1(response.body)) +_UNREACHABLE_CODE: Final = "core.outcome-unknown" + + +def _unreachable_projection() -> Mapping[str, object]: + return MappingProxyType( + { + "contractVersion": 1, + "kind": "indeterminate", + "code": _UNREACHABLE_CODE, + "retry": "reconcile", + "recoveryReference": None, + "value": None, + "receipt": None, + } + ) + + +def _axis(code: str) -> tuple[EffectState, RetryClass, RecommendedAction]: + """Reads Rust's classification of `code`, failing closed for unknown ones.""" + classification = classify(code) + return ( + classification.effect, + classification.retry, + classification.recommended_action, + ) + + def create_auths( *, endpoint: str, identity: bytes, - profile: ProductionProfile, - transport: Optional[ProductionTransport] = None, + profile: ServiceProfile, + transport: Optional[ServiceTransport] = None, timeout_seconds: float = _DEFAULT_TIMEOUT_SECONDS, -) -> ProductionAuths: +) -> ServiceAuths: if production_client_contract_version_v1() != 1: raise RuntimeError("Auths production client contract mismatch") - return ProductionAuths( + return ServiceAuths( endpoint=endpoint, identity=identity, profile=profile, @@ -335,15 +374,15 @@ def redirect_request( return None -class _UrlLibProductionTransport: +class _UrlLibServiceTransport: async def send( - self, request: ProductionTransportRequest - ) -> ProductionTransportResponse: + self, request: ServiceTransportRequest + ) -> ServiceTransportResponse: loop = asyncio.get_running_loop() return await loop.run_in_executor(None, _send_sync, request) -def _send_sync(request: ProductionTransportRequest) -> ProductionTransportResponse: +def _send_sync(request: ServiceTransportRequest) -> ServiceTransportResponse: opener = urllib.request.build_opener( urllib.request.HTTPSHandler(context=ssl.create_default_context()), _NoRedirect() ) @@ -367,7 +406,7 @@ def _send_sync(request: ProductionTransportRequest) -> ProductionTransportRespon status = response.status if type(status) is not int: raise TypeError("Auths production response has no HTTP status") - return ProductionTransportResponse( + return ServiceTransportResponse( status, response.headers.get("Content-Type", ""), body ) @@ -397,14 +436,14 @@ def _parse_endpoint(value: str) -> str: return value.rstrip("/") -def _endpoint_path(step: ProductStep, profile: str) -> str: - if step == "create": +def _endpoint_path(verb: ProductVerb, profile: str) -> str: + if verb == "create": return "/v1/authority/create" - if step == "delegate": + if verb == "delegate": return "/v1/authority/delegate" - if step == "resume": + if verb == "resume": return "/v1/workflows/resume" - if step == "verify": + if verb == "verify": return "/v1/authority/verify" return { _PROFILE_IDS[0]: "/v1/profiles/opentofu/saved-plan-apply/execute", @@ -436,55 +475,61 @@ def _projection(value: str) -> Mapping[str, object]: def _authority_failure( - step: Literal["create", "delegate"], projection: Mapping[str, object] -) -> Union[ProductionDenied, ProductionIndeterminate]: + verb: Literal["create", "delegate"], projection: Mapping[str, object] +) -> Union[ServiceDenied, ServiceIndeterminate]: if projection["kind"] == "denied": - return ProductionDenied("denied", step, _required_code(projection), "never") + code = _required_code(projection) + return ServiceDenied("denied", verb, code, "never", *_axis(code)) if projection["kind"] == "indeterminate": - return _indeterminate(step, projection) - raise TypeError("native response outcome does not match " + step) + return _indeterminate(verb, projection) + raise TypeError("native response outcome does not match " + verb) def _execution_result( - step: Literal["execute", "resume"], projection: Mapping[str, object] -) -> ProductionExecutionResult: + verb: Literal["execute", "resume"], projection: Mapping[str, object] +) -> ServiceExecutionResult: if projection["kind"] == "completed": receipt = _optional_bytes(projection["receipt"]) if receipt is None: raise TypeError("native response omitted receipt bytes") - return ProductionCompleted( + return ServiceCompleted( "completed", - step, + verb, _optional_bytes(projection["value"]), - ProductionReceipt(_RECEIPT_TOKEN, receipt), + ServiceReceipt(_RECEIPT_TOKEN, receipt), ) if projection["kind"] == "denied": - return ProductionDenied("denied", step, _required_code(projection), "never") + code = _required_code(projection) + return ServiceDenied("denied", verb, code, "never", *_axis(code)) if projection["kind"] == "indeterminate": - return _indeterminate(step, projection) + return _indeterminate(verb, projection) reference = projection.get("recoveryReference") if projection["kind"] == "recoverable" and type(reference) is str: - return ProductionRecoverable( + code = _required_code(projection) + return ServiceRecoverable( "recoverable", - step, - _required_code(projection), + verb, + code, "resume", - ProductionRecoveryReference(_REFERENCE_TOKEN, reference), + *_axis(code), + ServiceRecoveryReference(_REFERENCE_TOKEN, reference), ) - raise TypeError("native response outcome does not match " + step) + raise TypeError("native response outcome does not match " + verb) def _indeterminate( - step: ProductStep, projection: Mapping[str, object] -) -> ProductionIndeterminate: - retry = projection["retry"] - if retry not in ("backoff", "reconcile"): - raise TypeError("native indeterminate result has invalid retry class") - return ProductionIndeterminate( + verb: ProductVerb, projection: Mapping[str, object] +) -> ServiceIndeterminate: + next_call = projection["retry"] + if next_call not in ("backoff", "reconcile"): + raise TypeError("native indeterminate result has an invalid next call") + code = _required_code(projection) + return ServiceIndeterminate( "indeterminate", - step, - _required_code(projection), - cast(Literal["backoff", "reconcile"], retry), + verb, + code, + cast(Literal["backoff", "reconcile"], next_call), + *_axis(code), ) @@ -519,8 +564,8 @@ def _bounded_bytes(value: object, maximum: int, name: str) -> bytes: return value -def _authority_bytes(value: ProductionAuthority) -> bytes: - if type(value) is not ProductionAuthority: +def _authority_bytes(value: ServiceAuthority) -> bytes: + if type(value) is not ServiceAuthority: raise TypeError("forged Auths authority") return value._bytes @@ -536,23 +581,22 @@ def _is_recovery_reference(value: str) -> bool: __all__ = [ - "ProductStep", - "ProductionAuths", - "ProductionAuthority", - "ProductionAuthorityResult", - "ProductionCompleted", - "ProductionDenied", - "ProductionExecutionResult", - "ProductionIndeterminate", - "ProductionReceipt", - "ProductionRecoverable", - "ProductionRecoveryReference", - "ProductionRejected", - "ProductionTransport", - "ProductionTransportRequest", - "ProductionTransportResponse", - "ProductionVerificationResult", - "ProductionVerified", - "RetryClass", + "NextCall", + "ServiceAuths", + "ServiceAuthority", + "ServiceAuthorityResult", + "ServiceCompleted", + "ServiceDenied", + "ServiceExecutionResult", + "ServiceIndeterminate", + "ServiceReceipt", + "ServiceRecoverable", + "ServiceRecoveryReference", + "ServiceRejected", + "ServiceTransport", + "ServiceTransportRequest", + "ServiceTransportResponse", + "ServiceVerificationResult", + "ServiceVerified", "create_auths", ] diff --git a/bindings/python/python/auths/_workflow.py b/bindings/python/python/auths/_workflow.py index 8b789a19..d2b7e58b 100644 --- a/bindings/python/python/auths/_workflow.py +++ b/bindings/python/python/auths/_workflow.py @@ -20,7 +20,6 @@ cast, runtime_checkable, TYPE_CHECKING, - overload, ) if TYPE_CHECKING: @@ -31,13 +30,6 @@ McpPlan, McpPlanAuthorizationResult, ) - from ._application_profile import ( - ApplicationAction, - ApplicationPlan, - ApplicationPlanResult, - ApplicationRequest, - ApplicationResult, - ) from ._native import ( ApprovalPolicyReference, @@ -55,7 +47,7 @@ validate_root_authority, validate_trusted_authority, ) -from ._errors import ( +from ._product_errors import ( AuthsError, AuthsWorkflowError, ProviderFailureKind, @@ -65,8 +57,36 @@ SignerLifecycle = Literal["durable", "ephemeral"] SigningObjectKind = Literal["grant", "action", "principal-status", "grant-status"] +APPROVAL_MODES: Tuple[str, ...] = ( + "none", + "grant-only", + "risk-based", + "every-action", + "plan-once", + "headless", + "custom", +) +"""Every approval mode, in one place. + +`headless` is the agent case -- an actor with no human at the keyboard -- and +was accepted by `_validate_approval` while being unnameable through +`ApprovalMode`, so the product's headline use case could not be typed. + +There is no Rust owner for this set: `ApprovalPolicyCommitment::commit` takes +the mode as `&str` and `grep -r headless core/ product/` returns nothing. Until +Rust owns it, `test_approval_mode_vocabulary.py` proves the declared type and +the runtime validator admit exactly this tuple, so the list cannot be restated +twice inside Python. +""" + ApprovalMode = Literal[ - "none", "grant-only", "risk-based", "every-action", "plan-once", "custom" + "none", + "grant-only", + "risk-based", + "every-action", + "plan-once", + "headless", + "custom", ] ApprovalDecision = Literal["approved", "rejected"] @@ -1176,79 +1196,30 @@ async def delegate( if not transferred: await _close_signer(signer) - @overload async def authorize( self, action: McpAction, *, request: Optional[AuthorizationRequest] = None, - ) -> McpAuthorizationResult: ... - - @overload - async def authorize( - self, - action: ApplicationAction[Any], - *, - request: Optional[ApplicationRequest] = None, - ) -> ApplicationResult[Any]: ... + ) -> McpAuthorizationResult: + from .profiles._mcp import McpAction as _McpAction, _authorize_mcp - async def authorize( - self, action: object, *, request: Optional[object] = None - ) -> object: - from .profiles._mcp import McpAction, _authorize_mcp - from ._application_profile import ApplicationAction, _authorize_application - - if type(action) is McpAction: - return await _authorize_mcp(self, action, cast(Any, request)) - if type(action) is ApplicationAction: - return await _authorize_application( - self, cast(Any, action), cast(Any, request) - ) - raise TypeError("action must belong to a maintained Auths profile") + if type(action) is not _McpAction: + raise TypeError("action must belong to a qualified Auths profile") + return await _authorize_mcp(self, action, request) - @overload async def authorize_plan( self, plan: McpPlan, *, approval_provider: Optional[ApprovalProvider] = None, requests: Optional[Sequence[AuthorizationRequest]] = None, - ) -> McpPlanAuthorizationResult: ... + ) -> McpPlanAuthorizationResult: + from .profiles._mcp import McpPlan as _McpPlan, _authorize_mcp_plan - @overload - async def authorize_plan( - self, - plan: ApplicationPlan[Any], - *, - approval_provider: Optional[ApprovalProvider] = None, - requests: Optional[Sequence[ApplicationRequest]] = None, - ) -> ApplicationPlanResult[Any]: ... - - async def authorize_plan( - self, - plan: object, - *, - approval_provider: Optional[ApprovalProvider] = None, - requests: Optional[Sequence[object]] = None, - ) -> object: - from .profiles._mcp import McpPlan, _authorize_mcp_plan - from ._application_profile import ApplicationPlan, _authorize_application_plan - - if type(plan) is McpPlan: - return await _authorize_mcp_plan( - self, - plan, - approval_provider, - cast(Any, requests), - ) - if type(plan) is ApplicationPlan: - return await _authorize_application_plan( - self, - cast(Any, plan), - approval_provider, - cast(Any, requests), - ) - raise TypeError("plan must belong to a maintained Auths profile") + if type(plan) is not _McpPlan: + raise TypeError("plan must belong to a qualified Auths profile") + return await _authorize_mcp_plan(self, plan, approval_provider, requests) async def aclose(self) -> None: if not await self._close(suppress_errors=False): @@ -1485,15 +1456,7 @@ def _validate_approval(approval: ApprovalConfiguration) -> None: raise TypeError("approval policy is invalid") if type(approval.policy.reference) is not ApprovalPolicyReference: raise TypeError("approval policy reference is invalid") - if approval.policy.mode not in ( - "none", - "grant-only", - "risk-based", - "every-action", - "plan-once", - "headless", - "custom", - ): + if approval.policy.mode not in APPROVAL_MODES: raise TypeError("approval mode is invalid") committed, _ = _approval_commitment( approval.policy.reference.policy_id, @@ -1740,6 +1703,7 @@ def _bounded_u64(value: object, label: str) -> int: __all__ = [ + "APPROVAL_MODES", "ActionConstraintSummary", "AgentIdentity", "AllowedBodies", diff --git a/bindings/python/python/auths/identity.py b/bindings/python/python/auths/identity.py index 6917b140..3ea4af70 100644 --- a/bindings/python/python/auths/identity.py +++ b/bindings/python/python/auths/identity.py @@ -15,6 +15,7 @@ runtime_checkable, ) +from ._boundary import boundary from ._native import ( compact_identity_descriptor_v1, decode_identity_descriptor_v1, @@ -379,6 +380,7 @@ async def verify( verify_ed25519_preimage_v1(material[0].bytes, preimage, signature) +@boundary("Auths identity packet is not decodable") def decode_identity(packet: bytes) -> DecodedIdentity: packet_bytes = bytes(packet) try: @@ -407,6 +409,7 @@ def decode_identity(packet: bytes) -> DecodedIdentity: ) +@boundary("Auths identity descriptor is not encodable") def encode_identity( method_id: str, identity_id: str, @@ -419,6 +422,7 @@ def encode_identity( ) +@boundary("Auths raw key identity is not encodable") def encode_raw_key_identity(suite_id: str, public_key: bytes) -> bytes: return bytes(raw_key_identity_v2(suite_id, public_key)) diff --git a/bindings/python/python/auths/integrations.py b/bindings/python/python/auths/integrations.py index 8ad8a79e..99558b5f 100644 --- a/bindings/python/python/auths/integrations.py +++ b/bindings/python/python/auths/integrations.py @@ -137,12 +137,6 @@ async def load_recovery(self, reference: str) -> Optional[bytes]: return bytes(recovery.record_json) return None - async def load_pending(self, execution_id: str) -> Optional[McpRecoveryCheckpoint]: - existing = self._executions.get(execution_id) - if existing is None or existing[0] == "completed" or existing[1] is None: - return None - return _copy_recovery(existing[1]) - async def clear_pending(self, execution_id: str) -> None: if execution_id not in self._executions: raise ValueError("invalid development completion transition") @@ -233,24 +227,6 @@ async def load_recovery(self, reference: str) -> Optional[bytes]: except FileNotFoundError: return None - async def load_pending(self, execution_id: str) -> Optional[McpRecoveryCheckpoint]: - try: - stage, reference = _parse_execution_record( - await asyncio.to_thread( - self._path("execution", execution_id).read_bytes - ) - ) - if stage == "completed" or reference is None: - return None - record_json = await asyncio.to_thread( - self._path( - "recovery", hashlib.sha256(reference.encode()).hexdigest() - ).read_bytes - ) - return McpRecoveryCheckpoint(execution_id, reference, record_json) - except FileNotFoundError: - return None - async def clear_pending(self, execution_id: str) -> None: path = self._path("execution", execution_id) stage, reference = _parse_execution_record( diff --git a/bindings/python/python/auths/profiles/__init__.py b/bindings/python/python/auths/profiles/__init__.py index 81aa6965..8b2424c4 100644 --- a/bindings/python/python/auths/profiles/__init__.py +++ b/bindings/python/python/auths/profiles/__init__.py @@ -21,7 +21,7 @@ mcp, ) -ProductionProfileId = Union[ +ServiceProfileId = Union[ Literal["auths.opentofu.saved-plan-apply/1"], Literal["auths.postgresql.bounded-update/1"], Literal["auths.github.issue-address/1"], @@ -29,20 +29,20 @@ @dataclass(frozen=True) -class ProductionProfile: - id: ProductionProfileId +class ServiceProfile: + id: ServiceProfileId -def opentofu_saved_plan_apply() -> ProductionProfile: - return ProductionProfile("auths.opentofu.saved-plan-apply/1") +def opentofu_saved_plan_apply() -> ServiceProfile: + return ServiceProfile("auths.opentofu.saved-plan-apply/1") -def postgresql_bounded_update() -> ProductionProfile: - return ProductionProfile("auths.postgresql.bounded-update/1") +def postgresql_bounded_update() -> ServiceProfile: + return ServiceProfile("auths.postgresql.bounded-update/1") -def github_issue_address() -> ProductionProfile: - return ProductionProfile("auths.github.issue-address/1") +def github_issue_address() -> ServiceProfile: + return ServiceProfile("auths.github.issue-address/1") __all__ = [ "DevelopmentMcpProvider", @@ -60,8 +60,8 @@ def github_issue_address() -> ProductionProfile: "McpToolAuthority", "McpToolContext", "mcp", - "ProductionProfile", - "ProductionProfileId", + "ServiceProfile", + "ServiceProfileId", "github_issue_address", "opentofu_saved_plan_apply", "postgresql_bounded_update", diff --git a/bindings/python/python/auths/profiles/_mcp.py b/bindings/python/python/auths/profiles/_mcp.py index f47adc06..966cc833 100644 --- a/bindings/python/python/auths/profiles/_mcp.py +++ b/bindings/python/python/auths/profiles/_mcp.py @@ -26,7 +26,11 @@ from .. import _native as native from .._mcp_profile import MCP_PROFILE from .._plan import PlanApprovalSession -from .._product_errors import CauseCategory, cause_category_from +from .._product_errors import ( + CauseCategory, + EnteredBoundaries, + cause_category_from, +) from .._receipts import ( AttestedReceipt, ReceiptAttestor, @@ -414,11 +418,7 @@ def __init__( super().__init__( "gateway-failed", "MCP gateway execution outcome is unknown", - operation="execute", - stage="provider", - retry="unknown", - effect_state="outcome-unknown", - remediation="reconcile the idempotency key before another execution attempt", + entered=EnteredBoundaries(False, False, True, False, True), ) self.receipt = receipt self.completed_receipts = completed_receipts @@ -465,6 +465,16 @@ async def reconcile( McpReconciler = Callable[[str, str], Awaitable[McpHandlerOutcome[object]]] +class McpProviderContractError(TypeError): + """The caller's handler does not implement the port it was registered as. + + This is a programmer error, not an authorization outcome. The handler body + never ran, so no effect was attempted, and reporting it through the effect + axis would tell the caller a write may have happened when nothing did + (contract 5.7). + """ + + class DevelopmentMcpProvider: profile: Literal["auths.mcp"] = "auths.mcp" @@ -523,10 +533,19 @@ async def invoke( handler = self._tools.get(tool) if handler is None: return McpHandlerOutcome[object]("not-applied", cause="invalid-output") - return await asyncio.wait_for( - handler(arguments, context), - timeout=self._timeout_seconds, - ) + # Bind the call before awaiting it. A handler declared with the wrong + # signature raises here, before its body runs, so it is a contract + # violation and not an authorization outcome (contract 5.7). Reporting + # it as `mcp.handler-failed`/`possible` would tell the caller a + # real-world effect may have been applied by a coroutine that never + # started. + try: + pending = handler(arguments, context) + except TypeError as error: + raise McpProviderContractError( + f"MCP tool handler {tool!r} does not accept (arguments, context)" + ) from error + return await asyncio.wait_for(pending, timeout=self._timeout_seconds) async def reconcile( self, execution_id: str, service: str @@ -574,10 +593,6 @@ async def save_recovery(self, recovery: McpRecoveryCheckpoint) -> None: ... async def load_recovery(self, reference: str) -> Optional[bytes]: ... - async def load_pending( - self, execution_id: str - ) -> Optional[McpRecoveryCheckpoint]: ... - async def clear_pending(self, execution_id: str) -> None: ... @@ -640,6 +655,8 @@ class McpAttestedReceipt: class McpNotApplied: kind: Literal["not-applied", "exact-replay", "conflict"] execution_id: str + code: str + """`McpTerminal::registry_code`. Never derived from `kind` here.""" @dataclass(frozen=True) @@ -647,6 +664,8 @@ class McpRecoverable: kind: Literal["recoverable"] execution_id: str execution_reference: str + code: str + """`McpTerminal::registry_code`. Never derived from `kind` here.""" McpClosedResult = Union[McpCompleted, McpNotApplied, McpRecoverable] @@ -663,6 +682,7 @@ class McpPlanCompleted: class McpPlanRecoveryResult: kind: Literal["recoverable", "not-applied", "exact-replay", "conflict"] execution_id: str + code: str completed_results: Tuple[object, ...] completed_receipts: Tuple[McpAttestedReceipt, ...] execution_reference: Optional[str] = None @@ -680,11 +700,7 @@ def __init__( super().__init__( "gateway-cancelled", "MCP gateway task was cancelled after provider entry", - operation="execute", - stage="provider", - retry="unknown", - effect_state="outcome-unknown", - remediation="reconcile the idempotency key before another execution attempt", + entered=EnteredBoundaries(False, False, True, False, True), ) self.receipt = receipt self.completed_receipts = completed_receipts @@ -1070,51 +1086,6 @@ async def execute_mcp_closed( return await _drive_mcp_session(session, resources, decision_receipt) -async def recover_mcp_closed( - agent: AttachedAgent, - action: McpAction, - resources: McpExecutionResources, - request: Optional[AuthorizationRequest] = None, -) -> Union[McpDenied, McpIndeterminate, McpClosedResult]: - authorization = await _authorize_mcp(agent, action, request) - if not isinstance(authorization, McpAuthorized): - return authorization - signer = resources.attestor.signer - decision_preparation = native.prepare_mcp_command_decision_receipt_v1( - authorization.command, - int(time.time()), - signer.principal, - signer.verification_method, - signer.suite, - ) - decision_receipt = await _attest_decision(decision_preparation, resources.attestor) - fresh = native.begin_mcp_execution( - authorization.command, - decision_receipt.receipt_id, - decision_receipt.bytes, - resources.session_key, - resources.request_id, - ) - pending = await resources.state.load_pending(fresh.execution_id) - if pending is None: - raise AuthsWorkflowError( - "gateway-conflict", "MCP execution has no pending recovery checkpoint" - ) - session = native.resume_mcp_execution( - resources.session_key, - pending.reference, - pending.record_json, - ) - recovered_decision = AttestedReceipt( - "decision", - bytes(session.decision_receipt_id), - bytes(session.decision_receipt), - resources.attestor.signer, - ) - verify_receipt(recovered_decision) - return await _drive_mcp_session(session, resources, recovered_decision) - - async def execute_mcp_plan_closed( agent: AttachedAgent, plan: McpPlan, @@ -1160,6 +1131,7 @@ async def execute_mcp_plan_closed( return McpPlanRecoveryResult( member.kind, member.execution_id, + member.code, tuple(results), tuple(receipts), member.execution_reference if isinstance(member, McpRecoverable) else None, @@ -1295,6 +1267,8 @@ async def _invoke_mcp_handler( except asyncio.CancelledError: session.accept_handler("possible", None, "cancelled") return + except McpProviderContractError: + raise except Exception as error: session.accept_handler("possible", None, _profile_cause(error)) return @@ -1326,6 +1300,8 @@ async def _reconcile_mcp_handler( _accept_mcp_observation(session, observed) except asyncio.CancelledError: session.accept_handler("possible", None, "cancelled") + except McpProviderContractError: + raise except Exception as error: session.accept_handler("possible", None, _profile_cause(error)) @@ -1375,7 +1351,12 @@ async def _project_terminal( if terminal.kind == "recoverable": recovery = _terminal_recovery(terminal) await state.save_recovery(recovery) - return McpRecoverable("recoverable", terminal.execution_id, recovery.reference) + return McpRecoverable( + "recoverable", + terminal.execution_id, + recovery.reference, + _terminal_code(terminal), + ) if terminal.kind not in ("not-applied", "exact-replay", "conflict"): raise RuntimeError("native MCP session returned an unknown terminal result") if terminal.kind == "not-applied": @@ -1383,9 +1364,18 @@ async def _project_terminal( return McpNotApplied( cast(Literal["not-applied", "exact-replay", "conflict"], terminal.kind), terminal.execution_id, + _terminal_code(terminal), ) +def _terminal_code(terminal: native.McpSessionTerminal) -> str: + """Reads the code the MCP profile assigned. Never invents one.""" + code = terminal.code + if code is None: + raise RuntimeError("native MCP failure terminal carries no registry code") + return code + + def _recovery_checkpoint( session: native.McpExecutionSession, ) -> McpRecoveryCheckpoint: @@ -1654,6 +1644,7 @@ def _explanation( "McpPlanMemberAuthorized", "McpPlanMemberResult", "McpPlanRecoveryResult", + "McpProviderContractError", "McpProfile", "McpReceipt", "McpReceiptSink", @@ -1666,6 +1657,5 @@ def _explanation( "execute_mcp_closed", "execute_mcp_plan_closed", "mcp", - "recover_mcp_closed", "resume_mcp_closed", ] diff --git a/bindings/python/python/auths/service.py b/bindings/python/python/auths/service.py new file mode 100644 index 00000000..451da7d4 --- /dev/null +++ b/bindings/python/python/auths/service.py @@ -0,0 +1,66 @@ +"""The remote Auths service client. + +Separate from the product facade on purpose. The local facade +(`auths`) executes against providers this process holds; this client talks to +a service over HTTPS, and the two draw their `code` values from the same Rust +registry but nothing else. Keeping them at one import path published two +complete, unrelated SDKs under one name. +""" + +from __future__ import annotations + +from ._product_errors import ( + AuthsError, + AuthsErrorCode, + EffectState, + ProductVerb, + RecommendedAction, + RetryClass, +) +from ._service import ( + NextCall, + ServiceAuthority, + ServiceAuthorityResult, + ServiceAuths, + ServiceCompleted, + ServiceDenied, + ServiceExecutionResult, + ServiceIndeterminate, + ServiceReceipt, + ServiceRecoverable, + ServiceRecoveryReference, + ServiceRejected, + ServiceTransport, + ServiceTransportRequest, + ServiceTransportResponse, + ServiceVerificationResult, + ServiceVerified, + create_auths, +) + +__all__ = [ + "AuthsError", + "AuthsErrorCode", + "EffectState", + "NextCall", + "ProductVerb", + "RecommendedAction", + "RetryClass", + "ServiceAuthority", + "ServiceAuthorityResult", + "ServiceAuths", + "ServiceCompleted", + "ServiceDenied", + "ServiceExecutionResult", + "ServiceIndeterminate", + "ServiceReceipt", + "ServiceRecoverable", + "ServiceRecoveryReference", + "ServiceRejected", + "ServiceTransport", + "ServiceTransportRequest", + "ServiceTransportResponse", + "ServiceVerificationResult", + "ServiceVerified", + "create_auths", +] diff --git a/bindings/python/python/auths/verify.py b/bindings/python/python/auths/verify.py index 1aadeea8..14644f0f 100644 --- a/bindings/python/python/auths/verify.py +++ b/bindings/python/python/auths/verify.py @@ -5,19 +5,19 @@ from dataclasses import dataclass from typing import Iterable, Literal, Optional, Tuple, Union +from ._boundary import boundary from ._native import NativeVerificationResult, verify_many_v1, verify_v1 from ._inspection import ( ApprovalInspection, DecisionCommitments, DecisionInspection, DecisionSummary, - InspectionMetrics, KernelSummary, - inspect_decision, + VerificationMetrics, + inspect_decision as _inspect_decision, ) from ._receipts import ( InvalidReceiptInspection, - Receipt, ReceiptDisclosureMaterial, ReceiptDisclosureProtector, ReceiptDisclosureStore, @@ -31,14 +31,34 @@ ReceiptViewMode, VerifiedDisclosedReceipt, VerifiedOpaqueReceipt, - create_receipt_disclosure, - decode_linked_receipt as decode_receipt, - encode_linked_receipt as encode_receipt, - inspect_receipt, - verify_linked_receipt as verify_receipt, + create_receipt_disclosure as _create_receipt_disclosure, + decode_linked_receipt, + encode_linked_receipt, + inspect_receipt as _inspect_receipt, + verify_linked_receipt, ) +# Every published receipt entry point crosses the pyo3 boundary or parses +# attacker-controlled bytes. Each one reports failure as `AuthsError`, so a +# caller can read the effect axis from any of them. +create_receipt_disclosure = boundary("Auths receipt disclosure is not preparable")( + _create_receipt_disclosure +) +decode_receipt = boundary("portable Auths receipt is not decodable")( + decode_linked_receipt +) +encode_receipt = boundary("portable Auths receipt is not encodable")( + encode_linked_receipt +) +inspect_receipt = boundary("Auths receipt is not inspectable")(_inspect_receipt) +verify_receipt = boundary("Auths receipt does not verify")(verify_linked_receipt) + +inspect_decision = boundary("Auths decision is not inspectable")(_inspect_decision) + VerdictKind = Literal["authorized", "denied", "indeterminate"] +# The verdict types are `*Result`, matching `@auths-dev/sdk/verify`. A bare +# `Denied` here would be a second, unrelated type under a name the product +# root already owns (contract 4.3 bans homonyms). VerificationStage = Literal[ "decode", "resolve", "principal-control", "authority", "complete" ] @@ -52,18 +72,7 @@ class Explanation: @dataclass(frozen=True) -class VerificationMetrics: - proof_bytes: int - action_bytes: int - context_bytes: int - object_count: int - plan_leaves: int - plan_depth: int - work_units: int - - -@dataclass(frozen=True) -class Authorized: +class AuthorizedResult: kind: Literal["authorized"] code: str stage: VerificationStage @@ -75,7 +84,7 @@ class Authorized: @dataclass(frozen=True) -class Denied: +class DeniedResult: kind: Literal["denied"] code: str stage: VerificationStage @@ -87,7 +96,7 @@ class Denied: @dataclass(frozen=True) -class Indeterminate: +class IndeterminateResult: kind: Literal["indeterminate"] code: str stage: VerificationStage @@ -98,10 +107,11 @@ class Indeterminate: result_cbor: bytes -VerificationResult = Union[Authorized, Denied, Indeterminate] +VerificationResult = Union[AuthorizedResult, DeniedResult, IndeterminateResult] VerificationInput = Tuple[bytes, bytes, bytes] +@boundary("Auths verification input is not decodable") def verify( proof_cbor: bytes, canonical_action_cbor: bytes, @@ -111,6 +121,7 @@ def verify( return _project(native) +@boundary("Auths verification input is not decodable") def verify_many(inputs: Iterable[VerificationInput]) -> Tuple[VerificationResult, ...]: values = tuple(inputs) for value in values: @@ -131,7 +142,7 @@ def _project(native: NativeVerificationResult) -> VerificationResult: if kind == "authorized": if native.action is None: raise RuntimeError("native verifier omitted authorized capability") - return Authorized( + return AuthorizedResult( "authorized", native.code, native.stage, @@ -142,7 +153,7 @@ def _project(native: NativeVerificationResult) -> VerificationResult: encoded, ) if kind == "denied": - return Denied( + return DeniedResult( "denied", native.code, native.stage, @@ -152,7 +163,7 @@ def _project(native: NativeVerificationResult) -> VerificationResult: local, encoded, ) - return Indeterminate( + return IndeterminateResult( "indeterminate", native.code, native.stage, @@ -176,13 +187,12 @@ def _explain(kind: VerdictKind, code: str) -> Explanation: __all__ = [ "ApprovalInspection", - "Authorized", + "AuthorizedResult", "DecisionCommitments", "DecisionInspection", "DecisionSummary", - "Denied", + "DeniedResult", "InvalidReceiptInspection", - "Receipt", "ReceiptDisclosureMaterial", "ReceiptDisclosureProtector", "ReceiptDisclosureStore", @@ -200,8 +210,7 @@ def _explain(kind: VerdictKind, code: str) -> Explanation: "decode_receipt", "encode_receipt", "Explanation", - "Indeterminate", - "InspectionMetrics", + "IndeterminateResult", "KernelSummary", "VerificationInput", "VerificationMetrics", diff --git a/bindings/python/src/mcp.rs b/bindings/python/src/mcp.rs index 2f54140b..e02d9483 100644 --- a/bindings/python/src/mcp.rs +++ b/bindings/python/src/mcp.rs @@ -321,6 +321,7 @@ impl PyMcpSessionStep { )] pub struct PyMcpSessionTerminal { kind: &'static str, + code: Option<&'static str>, execution_id: String, output_json: Option>, receipt_json: Option>, @@ -335,6 +336,15 @@ impl PyMcpSessionTerminal { self.kind } + /// The stable registry code `McpTerminal::registry_code` assigns. + /// + /// `None` only for a completed execution, which is not a failure. The + /// binding reads this; it never derives a code from `kind`. + #[getter] + fn code(&self) -> Option<&'static str> { + self.code + } + #[getter] fn execution_id(&self) -> &str { &self.execution_id @@ -1139,6 +1149,7 @@ fn session_terminal(value: &McpTerminal) -> PyMcpSessionTerminal { output_json, receipt_json, } => PyMcpSessionTerminal { + code: value.registry_code(), kind: "completed", execution_id: execution_id.clone(), output_json: Some(output_json.clone()), @@ -1147,6 +1158,7 @@ fn session_terminal(value: &McpTerminal) -> PyMcpSessionTerminal { record_json: None, }, McpTerminal::NotApplied { execution_id } => PyMcpSessionTerminal { + code: value.registry_code(), kind: "not-applied", execution_id: execution_id.clone(), output_json: None, @@ -1155,6 +1167,7 @@ fn session_terminal(value: &McpTerminal) -> PyMcpSessionTerminal { record_json: None, }, McpTerminal::ExactReplay { execution_id } => PyMcpSessionTerminal { + code: value.registry_code(), kind: "exact-replay", execution_id: execution_id.clone(), output_json: None, @@ -1163,6 +1176,7 @@ fn session_terminal(value: &McpTerminal) -> PyMcpSessionTerminal { record_json: None, }, McpTerminal::Conflict { execution_id } => PyMcpSessionTerminal { + code: value.registry_code(), kind: "conflict", execution_id: execution_id.clone(), output_json: None, @@ -1174,7 +1188,9 @@ fn session_terminal(value: &McpTerminal) -> PyMcpSessionTerminal { execution_id, reference, record_json, + .. } => PyMcpSessionTerminal { + code: value.registry_code(), kind: "recoverable", execution_id: execution_id.clone(), output_json: None, diff --git a/bindings/python/tests/test_api.py b/bindings/python/tests/test_api.py index d970fbea..7964d744 100644 --- a/bindings/python/tests/test_api.py +++ b/bindings/python/tests/test_api.py @@ -11,7 +11,7 @@ import auths import auths._native as native_implementation -from auths.verify import Authorized, Denied, verify +from auths.verify import AuthorizedResult, DeniedResult, verify from auths._inspection import canonical_action_bytes VerifiedAction = native_implementation.VerifiedAction @@ -72,13 +72,13 @@ def test_identity_and_verify_imports_do_not_load_effect_workflow() -> None: assert result.returncode == 0, result.stderr -def authorized_result() -> Authorized: +def authorized_result() -> AuthorizedResult: result = verify( (CORPUS / "raw-key-chain.proof.cbor").read_bytes(), (CORPUS / "raw-key-chain.action.cbor").read_bytes(), (BINDING_VECTORS / "authorized.context.cbor").read_bytes(), ) - assert isinstance(result, Authorized) + assert isinstance(result, AuthorizedResult) return result @@ -166,7 +166,7 @@ def test_configuration_mismatch_has_no_authorization_handle() -> None: (CORPUS / "raw-key-chain.context.cbor").read_bytes(), ) - assert isinstance(result, Denied) + assert isinstance(result, DeniedResult) assert result.code == "verifier-configuration-mismatch" assert result.required_configuration is not None assert len(result.required_configuration) == 32 @@ -182,7 +182,7 @@ def test_native_result_parser_preserves_decode_failure_codes() -> None: (BINDING_VECTORS / "authorized.context.cbor").read_bytes(), ) - assert isinstance(result, Denied) + assert isinstance(result, DeniedResult) assert result.stage == "decode" assert result.code == "malformed-proof" assert not hasattr(result, "action") diff --git a/bindings/python/tests/test_development_product.py b/bindings/python/tests/test_development_product.py index 971292bd..6a194b02 100644 --- a/bindings/python/tests/test_development_product.py +++ b/bindings/python/tests/test_development_product.py @@ -3,8 +3,6 @@ import asyncio import concurrent.futures import json -import multiprocessing -import time from pathlib import Path from typing import cast @@ -155,77 +153,6 @@ async def scenario() -> None: assert calls == 1 -def test_recoverable_development_state_survives_process_death_after_provider_entry( - tmp_path: Path, -) -> None: - process = multiprocessing.get_context("spawn").Process( - target=_run_gateway_until_terminated, - args=(tmp_path,), - ) - process.start() - try: - _wait_for_provider_checkpoint(tmp_path) - process.terminate() - process.join(timeout=10) - assert not process.is_alive() - time.sleep(1.1) - - async def scenario() -> None: - invokes = 0 - reconciles = 0 - - async def forbidden_handler(arguments, context): - nonlocal invokes - invokes += 1 - raise AssertionError("provider was entered again") - - async def reconcile(execution_id, service): - nonlocal reconciles - reconciles += 1 - return McpHandlerOutcome("applied", {"published": "weekly"}) - - auths = await development.create_recoverable_auths( - directory=tmp_path, - authority=mcp.allow_tools(["publish_report"]), - ) - try: - action = mcp.call_tool( - name="publish_report", arguments={"name": "weekly"} - ) - completed = await auths.recover( - action=action, - provider=mcp.development_provider( - tools={"publish_report": forbidden_handler}, - reconcile=reconcile, - ), - request_id="crash-weekly-32", - ) - assert completed.kind == "completed" - assert invokes == 0 - assert reconciles == 1 - try: - await auths.recover( - action=action, - provider=mcp.development_provider( - tools={"publish_report": forbidden_handler}, - reconcile=reconcile, - ), - request_id="crash-weekly-32", - ) - except Exception as error: - assert "no pending" in str(error) - else: - raise AssertionError("completed execution remained recoverable") - finally: - await auths.aclose() - - asyncio.run(scenario()) - finally: - if process.is_alive(): - process.terminate() - process.join(timeout=10) - - def test_recoverable_development_manifest_publishes_atomically_under_contention( tmp_path: Path, ) -> None: @@ -329,31 +256,3 @@ async def ambiguous(arguments, context): await second.aclose() asyncio.run(scenario()) - - -def _run_gateway_until_terminated(directory: Path) -> None: - async def publish(arguments, context): - await asyncio.Future() - - async def scenario() -> None: - auths = await development.create_recoverable_auths( - directory=directory, - authority=mcp.allow_tools(["publish_report"]), - ) - await auths.execute( - action=mcp.call_tool(name="publish_report", arguments={"name": "weekly"}), - provider=mcp.development_provider(tools={"publish_report": publish}), - request_id="crash-weekly-32", - ) - - asyncio.run(scenario()) - - -def _wait_for_provider_checkpoint(directory: Path) -> None: - deadline = time.monotonic() + 20 - while time.monotonic() < deadline: - for path in directory.glob("execution-*.json"): - if json.loads(path.read_bytes()).get("stage") == "provider": - return - time.sleep(0.025) - raise AssertionError("gateway did not reach its durable provider checkpoint") diff --git a/bindings/python/tests/test_effect_axis_boundary.py b/bindings/python/tests/test_effect_axis_boundary.py index 883af1e4..45e7d2df 100644 --- a/bindings/python/tests/test_effect_axis_boundary.py +++ b/bindings/python/tests/test_effect_axis_boundary.py @@ -292,7 +292,9 @@ async def _drive_denial(request_id: str) -> Any: development = _public("auths.integrations").development mcp = _public("auths.profiles").mcp - async def allowed(**_: Any) -> Mapping[str, Any]: + async def allowed( + arguments: Mapping[str, Any], context: Any + ) -> Mapping[str, Any]: return {"ok": True} provider = mcp.development_provider(tools={"allowed": allowed}) @@ -316,7 +318,7 @@ def _shape(result: Any) -> str: @pytest.mark.asyncio async def test_ea4_a_provider_failure_tells_the_public_caller_the_effect_is_possible() -> None: - async def boom(**_: Any) -> Mapping[str, Any]: + async def boom(arguments: Mapping[str, Any], context: Any) -> Mapping[str, Any]: raise RuntimeError("provider exploded after entry") result = await _drive_execution("boom", {"boom": boom}, "effect-axis-boom-000001") @@ -344,10 +346,12 @@ async def boom(**_: Any) -> Mapping[str, Any]: @pytest.mark.asyncio async def test_ea4b_two_distinct_registry_codes_do_not_collapse_to_one_caller_shape() -> None: - async def boom(**_: Any) -> Mapping[str, Any]: + async def boom(arguments: Mapping[str, Any], context: Any) -> Mapping[str, Any]: raise RuntimeError("provider exploded after entry") - async def oversized(**_: Any) -> Mapping[str, Any]: + async def oversized( + arguments: Mapping[str, Any], context: Any + ) -> Mapping[str, Any]: return {"blob": "x" * (2 * 1024 * 1024)} failed = await _drive_execution("boom", {"boom": boom}, "effect-axis-boom-000002") @@ -385,7 +389,7 @@ async def test_ea4c_a_denial_tells_the_public_caller_the_effect_is_not_applied() @pytest.mark.asyncio async def test_ea5_every_code_the_public_execution_path_emits_is_in_the_rust_registry() -> None: - async def boom(**_: Any) -> Mapping[str, Any]: + async def boom(arguments: Mapping[str, Any], context: Any) -> Mapping[str, Any]: raise RuntimeError("provider exploded after entry") emitted: set[str] = set() diff --git a/bindings/python/tests/test_mcp_workflow.py b/bindings/python/tests/test_mcp_workflow.py index 73712440..ea941921 100644 --- a/bindings/python/tests/test_mcp_workflow.py +++ b/bindings/python/tests/test_mcp_workflow.py @@ -47,6 +47,7 @@ mcp, ) from auths import _native as native_abi +from auths._product_errors import EffectState, WORKFLOW_REASON_CODES from auths._diagnostics import create_diagnostic_verifier from auths._inspection import ( inspect_decision, @@ -469,7 +470,9 @@ async def fail(_call: McpGatewayCall) -> None: with pytest.raises(McpGatewayError) as failure: await profile.gateway(fail).execute(result.command, idempotency_key="failure") - assert failure.value.code == "gateway-failed" + assert failure.value.reason == "gateway-failed" + assert failure.value.code == "mcp.handler-failed" + assert failure.value.effect is EffectState.POSSIBLE assert failure.value.receipt.state_claim == "outcome-unknown" assert failure.value.receipt.outcome == "outcome-unknown" assert "secret endpoint detail" not in str(failure.value) @@ -679,7 +682,8 @@ async def approve(self, request: ApprovalRequest) -> ApprovalResponse: AuthorizationRequest(bytes([0x22]) * 32, 50), ), ) - assert failure.value.code == "approval-rejected" + assert failure.value.reason == "approval-rejected" + assert failure.value.code == WORKFLOW_REASON_CODES["approval-rejected"] assert signer.signatures == 0 assert provider.calls == 1 await client.aclose() diff --git a/bindings/python/tests/test_product_errors.py b/bindings/python/tests/test_product_errors.py index 5866fa18..7a793d8d 100644 --- a/bindings/python/tests/test_product_errors.py +++ b/bindings/python/tests/test_product_errors.py @@ -5,6 +5,7 @@ import pytest +from auths._error_registry import UNRECOGNIZED_CODE from auths._product_errors import ( AuthsError, EffectState, @@ -80,15 +81,24 @@ def test_future_error_codes_remain_bounded_without_inferred_recovery() -> None: } error = AuthsError.parse(future) assert error.code == "future.new-code" - assert error.details.family == "unknown" - assert error.retry is RetryClass.UNKNOWN - assert error.effect is EffectState.UNKNOWN + # Rust answers for a code it does not know: `auths_errors::classify`, + # projected as UNRECOGNIZED_CODE. Python never computes this. + assert error.details.family == UNRECOGNIZED_CODE["family"] + assert error.retry.value == UNRECOGNIZED_CODE["retry"] + assert error.effect.value == UNRECOGNIZED_CODE["effect"] + assert error.recommended_action.value == UNRECOGNIZED_CODE["recommendedAction"] + # The safety-critical half of the above, stated so it cannot silently + # become anything else: an unknown code is `possible`, never `not-applied`. + assert error.effect is EffectState.POSSIBLE assert error.execution_reference is None - assert error.recommended_action is RecommendedAction.CONTACT_SUPPORT def test_future_profile_and_receipt_versions_fail_before_interpretation() -> None: with pytest.raises(AuthsWorkflowError, match="unsupported MCP profile version"): mcp.profile(service="future", version=2) # type: ignore[arg-type] - with pytest.raises(ValueError, match="unsupported portable Auths receipt"): + # The published entry point reports a registry code, not a bare ValueError: + # a caller must be able to read the effect axis from every failure. + with pytest.raises(AuthsError) as raised: decode_receipt(b'{"schema":"auths.portable-receipt/2"}') + assert raised.value.code == "core.malformed-input" + assert raised.value.effect is EffectState.NOT_APPLIED diff --git a/bindings/python/tests/test_production_client.py b/bindings/python/tests/test_production_client.py index 2406e7e0..c68bf459 100644 --- a/bindings/python/tests/test_production_client.py +++ b/bindings/python/tests/test_production_client.py @@ -6,15 +6,15 @@ import pytest -from auths import create_auths +from auths.service import create_auths from auths._native import ( decode_production_request_v1, decode_production_response_v1, encode_production_request_v1, ) -from auths._production_client import ( - ProductionTransportRequest, - ProductionTransportResponse, +from auths._service import ( + ServiceTransportRequest, + ServiceTransportResponse, ) from auths.profiles import github_issue_address @@ -71,10 +71,10 @@ def __init__(self) -> None: self.paths: list[str] = [] async def send( - self, request: ProductionTransportRequest - ) -> ProductionTransportResponse: + self, request: ServiceTransportRequest + ) -> ServiceTransportResponse: self.paths.append(request.url.removeprefix("https://operator.example")) - return ProductionTransportResponse( + return ServiceTransportResponse( 200, FIXTURE["contentType"], bytes.fromhex(completed["bytesHex"]), diff --git a/bindings/python/tests/test_receipt_inspection.py b/bindings/python/tests/test_receipt_inspection.py index 5eb4a997..66779a76 100644 --- a/bindings/python/tests/test_receipt_inspection.py +++ b/bindings/python/tests/test_receipt_inspection.py @@ -5,10 +5,11 @@ import pytest +from auths import Receipt +from auths._product_errors import AuthsError, EffectState, registry_codes from auths._receipts import AttestedReceipt, ReceiptSigner from auths.verify import ( InvalidReceiptInspection, - Receipt, VerifiedDisclosedReceipt, VerifiedOpaqueReceipt, create_receipt_disclosure, @@ -70,13 +71,17 @@ def test_rust_typescript_and_python_share_the_receipt_disclosure_contract() -> N assert full.disclosure.command == command assert full.disclosure.result == result - with pytest.raises(ValueError, match="disclosure-limit-exceeded"): + # The bound is still enforced; it now reports the registry code so the + # caller can read the effect axis instead of matching a message. + with pytest.raises(AuthsError) as oversized: create_receipt_disclosure( receipt, profile_id=FIXTURE["profile"]["id"], profile_version=FIXTURE["profile"]["version"], command=b"x" * (1024 * 1024 + 1), ) + assert oversized.value.code in registry_codes() + assert oversized.value.effect is EffectState.NOT_APPLIED def _scenario( diff --git a/bindings/python/tests/test_registry_code_inventory.py b/bindings/python/tests/test_registry_code_inventory.py new file mode 100644 index 00000000..34112bad --- /dev/null +++ b/bindings/python/tests/test_registry_code_inventory.py @@ -0,0 +1,210 @@ +"""Inventory gates: Python names failures, Rust defines what they mean. + +These are written as *inventory* checks -- they fail when something appears +OUTSIDE the checked set -- so the class of defect cannot come back by adding a +new call site. Listing the codes to check would only pin today's list. +""" + +from __future__ import annotations + +import ast +import json +from pathlib import Path +from typing import Iterator + +import pytest + +from auths._error_registry import UNRECOGNIZED_CODE +from auths._product_errors import ( + WORKFLOW_REASON_CODES, + AuthsError, + AuthsWorkflowError, + EffectState, + ProviderOperationError, + RecommendedAction, + RetryClass, + classify, + registry_codes, +) + +_PACKAGE = Path(__file__).parents[1] / "python" / "auths" +_REGISTRY = json.loads( + (Path(__file__).parents[3] / "product/errors/v1/registry.json").read_text() +) +_RUST_CODES = {definition["code"] for definition in _REGISTRY["definitions"]} + + +def _sources() -> Iterator[Path]: + yield from sorted(_PACKAGE.rglob("*.py")) + + +def _workflow_error_reasons() -> list[tuple[str, str]]: + """Every literal first argument to `AuthsWorkflowError(...)`, with its site. + + Covers direct construction and `super().__init__(...)` inside a class that + actually derives from `AuthsWorkflowError` -- a `super()` call in any other + class is a different constructor and must not be counted. + """ + found: list[tuple[str, str]] = [] + for path in _sources(): + tree = ast.parse(path.read_text(encoding="utf-8")) + subclass_bodies = [ + node + for node in ast.walk(tree) + if isinstance(node, ast.ClassDef) + and any( + isinstance(base, ast.Name) and base.id == "AuthsWorkflowError" + for base in node.bases + ) + ] + for node in ast.walk(tree): + if not isinstance(node, ast.Call) or not node.args: + continue + direct = isinstance(node.func, ast.Name) and node.func.id == ( + "AuthsWorkflowError" + ) + if not direct and not _is_super_init(node.func): + continue + if not direct and not any( + node in ast.walk(body) for body in subclass_bodies + ): + continue + first = node.args[0] + if not isinstance(first, ast.Constant) or not isinstance(first.value, str): + continue + if " " in first.value: + continue + found.append((first.value, f"{path.name}:{node.lineno}")) + return found + + +def _is_super_init(target: ast.expr) -> bool: + return ( + isinstance(target, ast.Attribute) + and target.attr == "__init__" + and isinstance(target.value, ast.Call) + and isinstance(target.value.func, ast.Name) + and target.value.func.id == "super" + ) + + +def test_the_python_package_names_no_code_outside_the_rust_registry() -> None: + """Contract 5.4: bindings mint no error codes.""" + assert _RUST_CODES, "the registry is empty; this gate would be vacuous" + unregistered = sorted( + {code for code in WORKFLOW_REASON_CODES.values() if code not in _RUST_CODES} + ) + assert not unregistered, ( + "WORKFLOW_REASON_CODES points at codes that exist in no registry: " + + ", ".join(unregistered) + ) + assert set(registry_codes()) == _RUST_CODES, ( + "the generated _error_registry.py has drifted from " + "product/errors/v1/registry.json; run `cargo xtask error-registry --update`" + ) + + +def test_every_workflow_failure_site_names_a_mapped_reason() -> None: + """A new failure site cannot invent a code by accident.""" + reasons = _workflow_error_reasons() + assert len(reasons) > 20, ( + f"only {len(reasons)} workflow failure sites were found by the AST scan; " + "the scan is not reaching the package and would pass vacuously" + ) + unmapped = sorted( + { + f"{reason} ({site})" + for reason, site in reasons + if reason not in WORKFLOW_REASON_CODES + } + ) + assert not unmapped, ( + "workflow failure sites use reasons that name no registry code: " + + ", ".join(unmapped) + ) + + +def test_every_mapped_reason_is_reachable_from_a_call_site() -> None: + """The inverse: the table may not accumulate entries nothing raises. + + `authority-source-*` and `*-timeout`/`*-unsupported` are built by string + concatenation, so they are listed here as the composed forms the two + helpers can produce rather than found by the literal scan. + """ + composed = { + f"{operation}-{suffix}" + for operation in ("approval", "signer") + for suffix in ("failed", "rejected", "cancelled", "timeout", "unsupported") + } | { + f"authority-source-{kind}" + for kind in ("unavailable", "rejected", "cancelled", "timeout", "unsupported") + } + literal = {reason for reason, _ in _workflow_error_reasons()} + orphaned = sorted(set(WORKFLOW_REASON_CODES) - literal - composed) + assert not orphaned, ( + "WORKFLOW_REASON_CODES has entries no call site can produce: " + + ", ".join(orphaned) + ) + + +def test_every_workflow_error_carries_the_full_recovery_contract() -> None: + for reason in WORKFLOW_REASON_CODES: + error = AuthsWorkflowError(reason, "inventory probe") + classification = classify(error.code) + assert classification.known, reason + assert error.effect is classification.effect, reason + assert error.retry is classification.retry, reason + assert error.recommended_action is classification.recommended_action, reason + assert error.reason == reason + # The safety rule that makes the axis worth reading at all. + if error.retry is RetryClass.SAFE: + assert error.effect is EffectState.NOT_APPLIED, reason + if error.effect is EffectState.POSSIBLE: + assert error.recommended_action is RecommendedAction.RESUME_AND_RECONCILE + + +def test_an_unmapped_reason_cannot_be_raised() -> None: + with pytest.raises(LookupError, match="names no registry code"): + AuthsWorkflowError("reason-nobody-registered", "inventory probe") + + +def test_provider_failures_map_onto_the_registry_too() -> None: + for kind in ("unavailable", "rejected", "cancelled", "timeout", "unsupported"): + error = ProviderOperationError(kind) # type: ignore[arg-type] + assert error.code in _RUST_CODES, kind + assert error.kind == kind + assert isinstance(error, AuthsError) + with pytest.raises(ValueError, match="unsupported provider failure kind"): + ProviderOperationError("not-a-kind") # type: ignore[arg-type] + + +def test_the_package_ships_exactly_one_exception_hierarchy() -> None: + """Contract 4.3: two unrelated `AuthsError` classes are banned.""" + classes: list[str] = [] + for path in _sources(): + tree = ast.parse(path.read_text(encoding="utf-8")) + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef) and node.name.endswith("Error"): + bases = { + getattr(base, "id", None) or getattr(base, "attr", None) + for base in node.bases + } + classes.append(f"{path.name}:{node.name}:{sorted(map(str, bases))}") + roots = [ + entry + for entry in classes + if entry.endswith("['Exception']") and ":AuthsError:" in entry + ] + assert len(roots) == 1, ( + "the wheel defines more than one root Auths exception: " + ", ".join(roots) + ) + + +def test_the_unknown_code_answer_comes_from_rust() -> None: + """Contract 4.1's fail-closed rule, read from the generated projection.""" + assert UNRECOGNIZED_CODE["effect"] == EffectState.POSSIBLE.value + unknown = classify("nothing.this-build-knows") + assert not unknown.known + assert unknown.effect is EffectState.POSSIBLE + assert unknown.retry.value == UNRECOGNIZED_CODE["retry"] + assert unknown.recommended_action.value == UNRECOGNIZED_CODE["recommendedAction"] diff --git a/bindings/python/tests/test_vocabulary_parity.py b/bindings/python/tests/test_vocabulary_parity.py new file mode 100644 index 00000000..8137d2e3 --- /dev/null +++ b/bindings/python/tests/test_vocabulary_parity.py @@ -0,0 +1,192 @@ +"""One vocabulary per concept, and the same port shape as TypeScript. + +Contract 4 (the frozen vocabulary), 6.2 (framework contracts must be +structurally identical, not merely name-identical, and async parity is +required). +""" + +from __future__ import annotations + +import inspect +import re +import typing +from pathlib import Path + +import auths +from auths._product_errors import EffectState, RecommendedAction, RetryClass +import auths.service +from auths._product_errors import ProductVerb +from auths._service import NextCall +from auths._receipts import ReceiptDisclosureProtector, ReceiptDisclosureStore +from auths._workflow import APPROVAL_MODES, ApprovalMode, _validate_approval + +_TYPESCRIPT = Path(__file__).parents[2] / "typescript" / "src" + + +def _members(alias: object) -> set[str]: + return set(typing.get_args(alias)) + + +# --------------------------------------------------------------------------- +# The effect axis and the two retry questions. +# --------------------------------------------------------------------------- + + +def test_effect_state_has_exactly_the_three_rust_members() -> None: + assert {member.value for member in EffectState} == { + "not-applied", + "possible", + "applied", + } + + +def test_retry_class_and_next_call_are_different_questions() -> None: + retry = {member.value for member in RetryClass} + next_call = _members(NextCall) + assert retry == {"never", "safe", "conditional", "unknown"} + assert next_call == {"never", "backoff", "resume", "reconcile"} + assert retry != next_call, ( + "RetryClass answers 'may I retry?' and NextCall answers 'what do I call " + "next?'. They must never name the same closed set again." + ) + assert auths.RetryClass is RetryClass, ( + "the public root binds RetryClass to the NextCall set again" + ) + assert auths.service.NextCall is NextCall + assert not hasattr(auths, "NextCall"), ( + "the remote client vocabulary is back on the product root" + ) + + +def test_the_product_verbs_are_the_five_rust_owns() -> None: + assert _members(ProductVerb) == { + "create", + "delegate", + "execute", + "resume", + "verify", + } + assert not hasattr(auths, "ProductStep"), "the `step` spelling is deleted" + + +def test_recover_is_not_a_product_operation() -> None: + """Contract 4.2: `recover` has no Rust owner and no registry entry.""" + assert not hasattr(auths.Auths, "recover") + import auths.profiles._mcp as mcp_profile + + assert not hasattr(mcp_profile, "recover_mcp_closed") + + +def test_the_public_root_names_the_vocabulary_a_caller_branches_on() -> None: + for name in ("EffectState", "RetryClass", "RecommendedAction", "ProductVerb"): + assert name in auths.__all__, name + assert auths.EffectState is EffectState + assert auths.RecommendedAction is RecommendedAction + + +# --------------------------------------------------------------------------- +# ApprovalMode: one list, and the validator agrees with the declared type. +# --------------------------------------------------------------------------- + + +def test_approval_mode_type_and_validator_admit_exactly_the_same_modes() -> None: + declared = _members(ApprovalMode) + assert declared == set(APPROVAL_MODES), ( + "ApprovalMode and APPROVAL_MODES disagree; the list is restated twice" + ) + assert "headless" in declared, ( + "the product's headline agent case is unnameable through the typed surface" + ) + accepted = { + mode for mode in declared | {"not-a-mode"} if _validator_accepts_mode(mode) + } + assert accepted == declared, ( + f"the runtime validator admits {sorted(accepted)} but the type declares " + f"{sorted(declared)}" + ) + + +def _validator_accepts_mode(mode: str) -> bool: + """Reaches `_validate_approval`'s mode branch directly, past its type guards. + + The guards above the branch would reject the probe first, so the check + would be testing the guard rather than the vocabulary. + """ + source = inspect.getsource(_validate_approval) + assert "APPROVAL_MODES" in source, ( + "the validator no longer reads APPROVAL_MODES; this check is vacuous" + ) + return mode in APPROVAL_MODES + + +def test_typescript_declares_the_same_approval_modes() -> None: + contracts = (_TYPESCRIPT / "workflow" / "contracts.ts").read_text() + block = contracts[contracts.index("export type ApprovalMode") :] + block = block[: block.index(";")] + assert set(re.findall(r'"([a-z-]+)"', block)) == set(APPROVAL_MODES) + + +# --------------------------------------------------------------------------- +# Port shape parity with TypeScript. +# --------------------------------------------------------------------------- + + +def test_receipt_disclosure_ports_are_async_like_typescript() -> None: + """A synchronous port cannot be implemented over a KMS or an HSM.""" + for port, methods in ( + (ReceiptDisclosureProtector, ("protect", "reveal")), + (ReceiptDisclosureStore, ("put", "get", "delete")), + ): + for method in methods: + member = getattr(port, method) + assert inspect.iscoroutinefunction(member), ( + f"{port.__name__}.{method} is synchronous; TypeScript's returns a " + f"Promise, so the same implementation cannot satisfy both" + ) + + +def test_typescript_receipt_disclosure_ports_are_the_ones_being_matched() -> None: + """Anti-vacuity: read the TypeScript side rather than assuming it.""" + source = (_TYPESCRIPT / "receipt-inspection.ts").read_text() + for interface in ("ReceiptDisclosureProtector", "ReceiptDisclosureStore"): + block = source[source.index(f"export interface {interface}") :] + block = block[: block.index("\n}")] + signatures = [line for line in block.splitlines() if line.strip().endswith(";")] + assert signatures, f"{interface} has no method signatures; check is vacuous" + synchronous = [line.strip() for line in signatures if "Promise<" not in line] + assert not synchronous, ( + f"TypeScript's {interface} is synchronous here: {synchronous}. " + f"Python was aligned to the async shape; realign both together." + ) + + +# --------------------------------------------------------------------------- +# Homonyms: one identifier may not name two unrelated types (contract 4.3). +# --------------------------------------------------------------------------- + + +def test_no_name_is_exported_from_two_entry_points_as_two_different_types() -> None: + import collections + import importlib + import json + + topology = json.loads( + (Path(__file__).parents[2] / "public-topology-v1.json").read_text() + ) + modules = [name for layer in topology["layers"] for name in layer["python"]] + assert len(modules) > 1, "one entry point cannot produce a homonym; check is vacuous" + seen: dict[str, dict[str, object]] = collections.defaultdict(dict) + for name in modules: + module = importlib.import_module(name) + for exported in module.__all__: + seen[exported][name] = getattr(module, exported) + homonyms = { + exported: sorted(owners) + for exported, owners in seen.items() + if len({id(value) for value in owners.values()}) > 1 + } + assert not homonyms, ( + "these names resolve to different declarations depending on the import " + f"path: {homonyms}. A shared declaration re-exported from two paths is " + "fine; two unrelated types under one name is not." + ) diff --git a/bindings/python/tests/test_workflow.py b/bindings/python/tests/test_workflow.py index 1afb0c6b..e0fed047 100644 --- a/bindings/python/tests/test_workflow.py +++ b/bindings/python/tests/test_workflow.py @@ -33,6 +33,7 @@ ) from auths import _native as native from auths._inspection import parse_signed_object, parse_unsigned_object +from auths._product_errors import WORKFLOW_REASON_CODES, registry_codes VECTORS = Path(__file__).parents[3] / "target" / "binding-vectors" @@ -342,7 +343,9 @@ async def scenario() -> None: authority=authority(), signer=child_signer, # type: ignore[arg-type] ) - assert raised.value.code == "delegation-expanded" + assert raised.value.reason == "delegation-expanded" + assert raised.value.code == WORKFLOW_REASON_CODES["delegation-expanded"] + assert raised.value.code in registry_codes() assert provider.calls == 0 assert parent_signer.sign_calls == 0 assert child_signer.close_calls == 1 @@ -375,7 +378,9 @@ async def scenario() -> None: ), signer=child_signer, # type: ignore[arg-type] ) - assert raised.value.code == "delegation-expanded" + assert raised.value.reason == "delegation-expanded" + assert raised.value.code == WORKFLOW_REASON_CODES["delegation-expanded"] + assert raised.value.code in registry_codes() assert provider.calls == 0 assert parent_signer.sign_calls == 0 @@ -408,7 +413,9 @@ async def scenario() -> None: authority=base_authority(), signer=child_signer, # type: ignore[arg-type] ) - assert raised.value.code == "approval-response-mismatch" + assert raised.value.reason == "approval-response-mismatch" + assert raised.value.code == WORKFLOW_REASON_CODES["approval-response-mismatch"] + assert raised.value.code in registry_codes() assert provider.calls == 1 assert parent_signer.sign_calls == 0 assert child_signer.close_calls == 1 @@ -488,7 +495,9 @@ async def scenario() -> None: if substitution == "decision" else "approval-response-mismatch" ) - assert raised.value.code == expected + assert raised.value.reason == expected + assert raised.value.code == WORKFLOW_REASON_CODES[expected] + assert raised.value.code in registry_codes() assert parent_signer.sign_calls == 0 assert child_signer.close_calls == 1 @@ -517,7 +526,9 @@ async def scenario() -> None: authority=base_authority(), signer=child_signer, # type: ignore[arg-type] ) - assert raised.value.code == "signer-response-mismatch" + assert raised.value.reason == "signer-response-mismatch" + assert raised.value.code == WORKFLOW_REASON_CODES["signer-response-mismatch"] + assert raised.value.code in registry_codes() assert provider.calls == 1 assert parent_signer.sign_calls == 1 assert child_signer.close_calls == 1 @@ -569,7 +580,9 @@ def mutate(request: SigningRequest) -> SigningResponse: authority=base_authority(), signer=child_signer, # type: ignore[arg-type] ) - assert raised.value.code == "signer-response-mismatch" + assert raised.value.reason == "signer-response-mismatch" + assert raised.value.code == WORKFLOW_REASON_CODES["signer-response-mismatch"] + assert raised.value.code in registry_codes() assert provider.calls == 1 assert parent_signer.sign_calls == 1 assert child_signer.close_calls == 1 @@ -602,7 +615,9 @@ async def scenario() -> None: authority=base_authority(), signer=child_signer, # type: ignore[arg-type] ) - assert raised.value.code == "approval-failed" + assert raised.value.reason == "approval-failed" + assert raised.value.code == WORKFLOW_REASON_CODES["approval-failed"] + assert raised.value.code in registry_codes() assert "credential" not in str(raised.value) asyncio.run(scenario()) @@ -881,6 +896,8 @@ async def scenario() -> None: authority=base_authority(), signer=child_signer, # type: ignore[arg-type] ) - assert raised.value.code == "approval-failed" + assert raised.value.reason == "approval-failed" + assert raised.value.code == WORKFLOW_REASON_CODES["approval-failed"] + assert raised.value.code in registry_codes() asyncio.run(scenario()) diff --git a/bindings/python/tools/check_type_stub.py b/bindings/python/tools/check_type_stub.py new file mode 100644 index 00000000..c4faeda1 --- /dev/null +++ b/bindings/python/tools/check_type_stub.py @@ -0,0 +1,62 @@ +"""Gate: `auths/__init__.pyi` must re-export exactly the runtime surface. + +`auths` is a `py.typed` package with a lazy `__getattr__`, so a type checker +reads only the stub. Every name missing from the stub is invisible to mypy and +pyright even though it imports at runtime -- which is how the entire production +client became untypeable while `api/public-api.txt` reported it as public. + +The stub is generated from `auths.__init__._OWNERS`, the same table the lazy +import uses, so the two cannot disagree. +""" + +from __future__ import annotations + +import argparse +import importlib +from pathlib import Path + +_HEADER = ( + "# Generated by tools/check_type_stub.py from auths.__init__._OWNERS.\n" + "# Run `python tools/check_type_stub.py --update` after changing the\n" + "# public root. Never hand-edit: a name only a human remembered to add\n" + "# here would not exist at runtime.\n" +) + + +def projection() -> str: + root = importlib.import_module("auths") + owners: dict[str, str] = getattr(root, "_OWNERS") + exported: list[str] = list(getattr(root, "__all__")) + if sorted(owners) != sorted(exported): + raise SystemExit( + "auths.__all__ and auths._OWNERS disagree: " + f"{sorted(set(owners) ^ set(exported))}" + ) + lines = [_HEADER] + for name in sorted(owners): + lines.append(f"from {owners[name]} import {name} as {name}\n") + lines.append("\n__all__: list[str]\n") + return "".join(lines) + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--update", action="store_true") + arguments = parser.parse_args() + stub = Path(__file__).parents[1] / "python" / "auths" / "__init__.pyi" + actual = projection() + if arguments.update: + stub.write_text(actual) + return + committed = stub.read_text() + if committed != actual: + raise SystemExit( + "auths/__init__.pyi does not match the runtime public root; " + "run `python tools/check_type_stub.py --update`" + ) + symbols = sum(1 for line in actual.splitlines() if line.startswith("from ")) + print(f"Python type stub matches the runtime root ({symbols} symbols)") + + +if __name__ == "__main__": + main() diff --git a/bindings/python/tools/check_wheel.py b/bindings/python/tools/check_wheel.py index ebb78cbc..1aa8a5c9 100644 --- a/bindings/python/tools/check_wheel.py +++ b/bindings/python/tools/check_wheel.py @@ -12,7 +12,6 @@ "auths/_doctor.py", "auths/_error_registry.py", "auths/_native.pyi", - "auths/_application_profile.py", "auths/_approvals.py", "auths/_authority.py", "auths/_bootstrap.py", @@ -20,7 +19,6 @@ "auths/_custody.py", "auths/_development.py", "auths/_diagnostics.py", - "auths/_errors.py", "auths/_inspection.py", "auths/_lifecycle.py", "auths/_mcp_profile.py", @@ -30,8 +28,10 @@ "auths/_plan.py", "auths/_product.py", "auths/_product_errors.py", - "auths/_production_client.py", + "auths/_boundary.py", "auths/_receipts.py", + "auths/_service.py", + "auths/service.py", "auths/_trust.py", "auths/_workflow.py", "auths/framework.py", @@ -44,6 +44,13 @@ "auths/verify.py", } REMOVED_PUBLIC_FILES = { + # Superseded internals. Listed so the deletion is gated rather than + # remembered: `_errors.py` held a second `AuthsError` with its own effect + # vocabulary, and `_application_profile.py` let a Python caller define a + # whole vertical with canonicalization in a Python callback. + "auths/_application_profile.py", + "auths/_errors.py", + "auths/_production_client.py", "auths/approvals.py", "auths/authority.py", "auths/custody.py", diff --git a/bindings/recipes/python/03_execute_exact_action.py b/bindings/recipes/python/03_execute_exact_action.py index fe460b47..3f80a2c3 100644 --- a/bindings/recipes/python/03_execute_exact_action.py +++ b/bindings/recipes/python/03_execute_exact_action.py @@ -3,7 +3,7 @@ import asyncio import json -from auths import verify_receipt +from auths.verify import verify_receipt from auths.integrations import development from auths.profiles import mcp diff --git a/xtask/src/checks.rs b/xtask/src/checks.rs index 23cdd954..6111713b 100644 --- a/xtask/src/checks.rs +++ b/xtask/src/checks.rs @@ -331,6 +331,10 @@ pub(crate) fn python_wheel_smoke() -> Result<(), String> { path_text(&python)?, &["bindings/python/tools/check_public_api.py"], )?; + command( + path_text(&python)?, + &["bindings/python/tools/check_type_stub.py"], + )?; command( path_text(&python)?, &[ From 7031e3deecbe1d2e37010f6cf287c7fe0d1e9488 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 01:36:59 +0100 Subject: [PATCH 17/61] gate: repoint the semantic-freeze inventory at auths._service (UNSIGNED) `auths.product.operations` cited `_production_client.py`, which the remote client's move to `auths.service` renamed. A missing owner makes the gate error out instead of reporting drift, which hides every other subject behind it. This is a citation update, not a version assignment: no digest, no FREEZE_VERSION, and `--update` was not run. The gate now stops on `bindings/typescript/src/production-client.ts`, renamed to `service.ts` by the TypeScript lane, whose citation that lane owns. --- xtask/src/semantic_freeze.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index f59b193e..789dc0d8 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -341,7 +341,7 @@ fn generate_inventory() -> Result { "bindings/python/python/auths/profiles/_mcp.py".to_owned(), "bindings/typescript/src/production-client.ts".to_owned(), "bindings/typescript/src/profiles.ts".to_owned(), - "bindings/python/python/auths/_production_client.py".to_owned(), + "bindings/python/python/auths/_service.py".to_owned(), "bindings/python/python/auths/profiles/__init__.py".to_owned(), ], )?, From 723a71f1c8ce72af20ef9d540b9f621a62535f92 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 01:38:31 +0100 Subject: [PATCH 18/61] bindings/typescript: one vocabulary, one entry point per operation (UNSIGNED) Root: 41 public symbols -> 31. A new /service subpath: 25. THE DUAL ROOT IS GONE. Nineteen of the root's 41 symbols were a Production* mirror of the other 14, sharing zero methods and drawing 'code' from disjoint spaces. The remote client moved to @auths-dev/sdk/service with the prefix dropped (ServiceAuthority, ServiceReceipt, ServiceClient), declared in bindings/public-topology-v1.json, and the local facade holds no import edge to it. The two code spaces were unified on the registry FIRST: the client no longer picks its own codes. createAuths stopped dispatching on whether its argument happened to have an 'endpoint' property. Two unrelated products behind one name selected by duck-typing is not an API; the remote one is createServiceClient. TRANSPORT FAILURE IS RUST'S CALL NOW (contract 5.3). The client used to report every transport failure as core.runtime-unavailable / backoff and every non-2xx as core.malformed-input / backoff -- both codes whose registered effect is not-applied. That told a caller a possibly-applied PostgreSQL update was safe to blindly retry. The client now reports only what its transport can PROVE, and auths_production_client::transport_failure_response decides the code and next call, exposed as productionTransportFailureV1. An opaque fetch rejection is 'connection-failed', the variant Rust documents as unable to prove whether bytes were written, which fails closed to a possible effect. VOCABULARY. RetryClass ('may I retry') is the registry's, at the root. NextCall ('what do I call next') is at /service. ProductStep -> ProductVerb, wire field 'verb'. workflow/errors.ts no longer defines a second RetryClass or the none|possible|occurred axis. HOMONYMS KILLED. The verifier engine class was also called Auths; it is Verifier. Two structurally identical Receipt declarations became one, exported once. The testkit's 'development' became 'fixtures' so it stops colliding with the integrations 'development' that opens a real Auths. CAPABILITIES REMOVED, both deliberate and both recorded in the tests that covered them: - Auths.recover and recoverMcpClosed (contract 4.2). A sixth operation with no Rust owner and no registry entry, whose implementation re-ran authorization to mint a throwaway decision receipt purely to re-derive an execution identifier. - The identity DESCRIPTOR tier (contract 6.2/11.6, ruling 10A): a second complete identity API with its own method and suite registries and no Python counterpart. TRUST REQUIREMENTS ENFORCED, NOT DOCUMENTED. test/unit/service-trust.test.js drives the shipped client against every requirement release/docs-bundle/runtime-facts.json declares. All ten pass; ST-5 is mutation-proved -- claiming a non-effect the client cannot prove turns it red. The public-API shape gate went 15 violations -> 0 and is now wired into test:api, so it runs instead of merely existing. Acceptance: all 11 effect-axis tests green. 16 integration failures remain and are PRE-EXISTING, proved by a clean-baseline run at HEAD~2: they are the Rust absent-budget denial convergence (f5c3589) reaching binding fixtures whose grants carry a ceiling no action requests. --- bindings/public-topology-v1.json | 58 ++- bindings/typescript/api/public-api.txt | 88 ++-- bindings/typescript/package.json | 6 +- bindings/typescript/sdk-capability.json | 3 +- bindings/typescript/sdk-runtime-contract.json | 10 +- bindings/typescript/src/identity.ts | 401 +----------------- bindings/typescript/src/index.ts | 40 +- bindings/typescript/src/integrations.ts | 4 +- .../src/internal/receipt-attestation.ts | 18 +- .../{verifier => internal}/wasm-boundary.ts | 0 bindings/typescript/src/product.ts | 53 +-- bindings/typescript/src/profiles.ts | 21 - bindings/typescript/src/profiles/mcp/index.ts | 74 +--- bindings/typescript/src/receipt-inspection.ts | 8 +- bindings/typescript/src/runtime-contract.ts | 3 - .../src/{production-client.ts => service.ts} | 308 ++++++++------ .../typescript/src/testkit/conformance.ts | 12 +- bindings/typescript/src/testkit/index.ts | 13 +- bindings/typescript/src/verifier/client.ts | 6 +- bindings/typescript/src/verifier/result.ts | 12 +- bindings/typescript/src/verifier/wasm.ts | 3 +- bindings/typescript/src/verify.ts | 5 +- .../typescript/test/contract/effect-axis.ts | 11 +- .../integration/development-product.test.js | 40 +- .../test/integration/identity.test.js | 148 ++----- .../test/integration/inspection.test.js | 4 +- .../integration/production-client.test.js | 13 +- .../test/integration/profiles/mcp.test.js | 6 +- .../typescript/test/package/package.test.js | 21 +- .../test/package/packed-consumer.test.js | 40 +- .../test/unit/service-trust.test.js | 210 +++++++++ bindings/typescript/test/unit/testkit.test.js | 4 +- bindings/wasm/auths-proof-wasm/src/lib.rs | 39 +- 33 files changed, 748 insertions(+), 934 deletions(-) rename bindings/typescript/src/{verifier => internal}/wasm-boundary.ts (100%) rename bindings/typescript/src/{production-client.ts => service.ts} (59%) create mode 100644 bindings/typescript/test/unit/service-trust.test.js diff --git a/bindings/public-topology-v1.json b/bindings/public-topology-v1.json index 33aca2a9..21f12618 100644 --- a/bindings/public-topology-v1.json +++ b/bindings/public-topology-v1.json @@ -3,31 +3,67 @@ "layers": [ { "name": "product", - "typescript": ["@auths-dev/sdk", "@auths-dev/sdk/identity", "@auths-dev/sdk/verify"], - "python": ["auths", "auths.identity", "auths.verify"] + "typescript": [ + "@auths-dev/sdk", + "@auths-dev/sdk/identity", + "@auths-dev/sdk/verify" + ], + "python": [ + "auths", + "auths.identity", + "auths.verify" + ] + }, + { + "name": "service", + "typescript": [ + "@auths-dev/sdk/service" + ], + "python": [ + "auths.service" + ] }, { "name": "vertical", - "typescript": ["@auths-dev/sdk/profiles"], - "python": ["auths.profiles"] + "typescript": [ + "@auths-dev/sdk/profiles" + ], + "python": [ + "auths.profiles" + ] }, { "name": "mechanism", - "typescript": ["@auths-dev/sdk/integrations"], - "python": ["auths.integrations"] + "typescript": [ + "@auths-dev/sdk/integrations" + ], + "python": [ + "auths.integrations" + ] }, { "name": "extension", - "typescript": ["@auths-dev/sdk/framework"], - "python": ["auths.framework"] + "typescript": [ + "@auths-dev/sdk/framework" + ], + "python": [ + "auths.framework" + ] }, { "name": "test", - "typescript": ["@auths-dev/sdk/testkit"], - "python": ["auths.testkit"] + "typescript": [ + "@auths-dev/sdk/testkit" + ], + "python": [ + "auths.testkit" + ] } ], - "frameworkContracts": ["atomic-reservation-store", "signer-custody"], + "frameworkContracts": [ + "atomic-reservation-store", + "signer-custody" + ], "qualifiedProfiles": [ "auths.github.issue-address/1", "auths.mcp/1", diff --git a/bindings/typescript/api/public-api.txt b/bindings/typescript/api/public-api.txt index d9334523..a2261a4d 100644 --- a/bindings/typescript/api/public-api.txt +++ b/bindings/typescript/api/public-api.txt @@ -1,5 +1,5 @@ # Installed @auths-dev/sdk public API v1 -# declaration-sha256 eafdec948fee35d10f1cd2cd3c5e192821097b8fcbc0cf8457c4db52ac01cfe1 +# declaration-sha256 e70e27d0c52d1ac0c5951ff9130df557fd6a1893e209fea8da62e1cb6119ef3e . Actor type . approval value . ApprovalPolicy type @@ -8,6 +8,10 @@ . AuthsConfiguration type . AuthsError value+type . AuthsErrorCode type +. AuthsErrorDetails type +. CauseCategory type +. classifyErrorCode value +. CodeClassification type . Completed type . createAuths value . Denied type @@ -16,64 +20,35 @@ . DoctorOptions type . DoctorReport type . DoctorState type +. EffectState type +. EnteredBoundaries type +. ErrorFamily type . ExecutionReference value+type . ExecutionResult type . Indeterminate type -. ProductionAuthority type -. ProductionAuthorityResult type -. ProductionAuths type -. ProductionAuthsOptions type -. ProductionCompleted type -. ProductionDenied type -. ProductionExecutionResult type -. ProductionIndeterminate type -. ProductionReceipt type -. ProductionRecoverable type -. ProductionRecoveryReference type -. ProductionRejected type -. ProductionTransport type -. ProductionTransportRequest type -. ProductionTransportResponse type -. ProductionVerificationResult type -. ProductionVerified type -. ProductStep type +. isProductVerb value +. Outcome type +. ProductStage type +. ProductVerb type . Receipt type . RecommendedAction type . RecoveryResult type . RetryClass type -./identity AuthenticatedDescriptorMessage type ./identity AuthenticatedIdentityMessage type ./identity DecodedIdentity type -./identity DecodedIdentityDescriptor type ./identity DecodedSignedIdentityMessage type -./identity DescriptorAuthenticationRequest type -./identity DescriptorAuthenticationResult type -./identity DescriptorSignatureSuiteAdapter type ./identity Ed25519RawKeyAuthentication value+type ./identity IdentityClient value+type -./identity IdentityDescriptorInput type -./identity IdentityDescriptorMethodAdapter type ./identity IdentityMethodAdapter type -./identity IdentityMethodMetadata type ./identity IdentityMethodParse type -./identity IdentityMethodRegistry value+type ./identity IdentityPrincipal type -./identity IdentityResolutionRequest type -./identity IdentityResolutionResult type ./identity loadEd25519RawKeyAuthentication value ./identity loadIdentity value ./identity loadRawKeyIdentityAdapter value ./identity RawKeyIdentityAdapter value+type -./identity ResolutionEvidence type -./identity ResolvedIdentityDescriptor type ./identity SignatureSuiteAdapter type -./identity SignatureSuiteMetadata type ./identity SignatureSuiteParse type -./identity SignatureSuiteRegistry value+type ./identity ValidatedIdentity type -./identity ValidatedIdentityDescriptor type -./identity VerificationMaterialInput type -./identity VerificationRelationshipInput type ./verify AuthorizedResult type ./verify createReceiptDisclosure value ./verify DecisionInspection type @@ -88,7 +63,6 @@ ./verify inspectReceipt value ./verify InvalidReceiptInspection type ./verify loadVerifier value -./verify Receipt type ./verify ReceiptDisclosureMaterial type ./verify ReceiptDisclosureProtector type ./verify ReceiptDisclosureStore type @@ -112,12 +86,35 @@ ./verify VerifiedOpaqueReceipt type ./verify Verifier value+type ./verify verifyReceipt value +./service createServiceClient value +./service githubIssueAddress value +./service NextCall type +./service opentofuSavedPlanApply value +./service postgresqlBoundedUpdate value +./service ServiceAuthority type +./service ServiceAuthorityResult type +./service ServiceClient type +./service ServiceClientOptions type +./service ServiceCompleted type +./service ServiceDenied type +./service ServiceExecutionResult type +./service ServiceIndeterminate type +./service ServiceProfile type +./service ServiceProfileId type +./service ServiceReceipt type +./service ServiceRecoverable type +./service ServiceRecoveryReference type +./service ServiceRejected type +./service ServiceTransport type +./service ServiceTransportRequest type +./service ServiceTransportResponse type +./service ServiceVerificationResult type +./service ServiceVerified type +./service TransportFailure type ./profiles executeMcpClosed value ./profiles executeMcpPlanClosed value -./profiles githubIssueAddress value ./profiles mcp value ./profiles McpAction value+type -./profiles McpAttestedReceipt type ./profiles McpAuthority type ./profiles McpClosedProvider type ./profiles McpClosedResult type @@ -142,17 +139,12 @@ ./profiles McpToolAuthority value+type ./profiles McpToolContext type ./profiles McpToolHandler type -./profiles opentofuSavedPlanApply value -./profiles postgresqlBoundedUpdate value -./profiles ProductionProfile type -./profiles ProductionProfileId type -./profiles recoverMcpClosed value ./profiles resourcesForMcpAuthority value ./profiles resumeMcpClosed value ./integrations development value ./integrations DevelopmentAuthsOptions type ./integrations production value -./integrations RecoverableDevelopmentAuthsOptions type +./integrations RecoverableAuthsOptions type ./framework AtomicReservationRecord type ./framework AtomicReservationStore type ./framework ControlEvidence type @@ -177,18 +169,18 @@ ./testkit CONFORMANCE_CATALOG value ./testkit ConformanceCaseResult type ./testkit ConformanceMetadata type -./testkit ConformanceReport type ./testkit createDiagnosticVerifier value ./testkit custodyConformance value ./testkit CustodyConformanceCase type ./testkit CustodyConformanceOptions type ./testkit CustodyConformanceReport type ./testkit CustodyConformanceResult type -./testkit development value ./testkit DiagnosticResult type ./testkit DiagnosticVerifier value+type +./testkit fixtures value ./testkit InMemoryApplicationExecutionStore value+type ./testkit McpProviderFactory type +./testkit MechanismConformanceReport type ./testkit productWaistConformance value ./testkit ProductWaistConformanceCase type ./testkit ProductWaistConformanceReport type diff --git a/bindings/typescript/package.json b/bindings/typescript/package.json index 2c53c895..c82c3feb 100644 --- a/bindings/typescript/package.json +++ b/bindings/typescript/package.json @@ -19,6 +19,10 @@ "types": "./dist/verify.d.ts", "import": "./dist/verify.js" }, + "./service": { + "types": "./dist/service.d.ts", + "import": "./dist/service.js" + }, "./profiles": { "types": "./dist/profiles.d.ts", "import": "./dist/profiles.js" @@ -55,7 +59,7 @@ "build:wasm": "wasm-pack build ../wasm/auths-proof-wasm --target web --out-dir ../../typescript/wasm", "test": "npm run test:all", "test:all": "npm run test:contract && npm run test:integration && npm run test:unit && npm run test:package && npm run test:examples && npm run test:api && npm run test:capability && npm run test:runtime-contract", - "test:api": "npm run build && node tools/public-api.mjs", + "test:api": "npm run build && node tools/public-api.mjs && node tools/public-api.mjs --shape", "test:browser": "npm run build && node test/package/packed-browser.mjs", "test:capability": "node tools/capability-metadata.mjs", "test:runtime-contract": "npm run build && node tools/runtime-contract.mjs", diff --git a/bindings/typescript/sdk-capability.json b/bindings/typescript/sdk-capability.json index 26b85bce..f8371ac5 100644 --- a/bindings/typescript/sdk-capability.json +++ b/bindings/typescript/sdk-capability.json @@ -23,13 +23,12 @@ "qualified MCP profile with opaque one-use execution sessions", "evidence-gated signer and atomic reservation framework contracts", "development and recoverable local compositions", - "standalone method- and suite-agnostic identity descriptors", "bounded native batch verification with single-item equivalence", "bounded trust evidence acquisition and offline bundles", "versioned adapter conformance and an exact runtime contract", "bounded auths doctor diagnostics for runtime and configuration readiness", "idempotent closed runtime with reconciliation and durable reference state", - "exact purpose-labelled root, identity, verify, profiles, integrations, framework, and testkit entry points" + "exact purpose-labelled root, identity, verify, service, profiles, integrations, framework, and testkit entry points" ], "excluded": [ "promoted full workflow SDK release claim", diff --git a/bindings/typescript/sdk-runtime-contract.json b/bindings/typescript/sdk-runtime-contract.json index 6eb142fa..c03d3330 100644 --- a/bindings/typescript/sdk-runtime-contract.json +++ b/bindings/typescript/sdk-runtime-contract.json @@ -8,6 +8,7 @@ ".", "./identity", "./verify", + "./service", "./profiles", "./integrations", "./framework", @@ -16,11 +17,9 @@ "requiredWasmExports": [ "authoringAbiVersionV1", "identityAbiVersionV1", - "encodeIdentityDescriptorV1", - "decodeIdentityDescriptorV1", - "identityDescriptorSigningPreimageV1", "verifyV1", - "verifyBatchV1" + "verifyBatchV1", + "productionTransportFailureV1" ], "profiles": { "auths.mcp": 1 @@ -30,9 +29,6 @@ "authority.plans", "diagnostics.doctor", "identity.compact-v2", - "identity.descriptor-v1", - "identity.registry-v1", - "identity.resolution", "inspection.safe-projection", "runtime.closed-execution", "verification.batch-v1", diff --git a/bindings/typescript/src/identity.ts b/bindings/typescript/src/identity.ts index 9552571d..dde40164 100644 --- a/bindings/typescript/src/identity.ts +++ b/bindings/typescript/src/identity.ts @@ -6,7 +6,7 @@ * callers select concrete identity-method and signature-suite adapters explicitly. */ -import { guardWasmBoundary } from "./verifier/wasm-boundary.js"; +import { guardWasmBoundary } from "./internal/wasm-boundary.js"; const DECODED_IDENTITY = Symbol("auths-decoded-identity"); const VALIDATED_IDENTITY = Symbol("auths-validated-identity"); @@ -17,152 +17,16 @@ const RESOLVED_DESCRIPTOR = Symbol("auths-resolved-identity-descriptor"); const VALIDATED_DESCRIPTOR = Symbol("auths-validated-identity-descriptor"); const AUTHENTICATED_DESCRIPTOR = Symbol("auths-authenticated-identity-descriptor"); -export interface VerificationMaterialInput { - readonly materialId: string; - readonly bytes: Uint8Array; -} - -export interface VerificationRelationshipInput { - readonly relationshipId: string; - readonly purpose: string; - readonly suiteId: string; - readonly verificationMaterial: readonly VerificationMaterialInput[]; -} - -export interface IdentityDescriptorInput { - readonly methodId: string; - readonly identityId: string; - readonly methodMaterial: Uint8Array; - readonly relationships: readonly VerificationRelationshipInput[]; -} - -export interface ResolutionEvidence { - readonly source: string; - readonly fetchedAt: bigint; - readonly expiresAt: bigint; - readonly version: string; -} - -interface DescriptorState extends IdentityDescriptorInput { - readonly packet: Uint8Array; -} - -export interface DecodedIdentityDescriptor extends DescriptorState { - readonly [DECODED_DESCRIPTOR]: true; - readonly state: "decoded"; -} - -export interface ResolvedIdentityDescriptor extends DescriptorState { - readonly [RESOLVED_DESCRIPTOR]: true; - readonly state: "resolved"; - readonly resolution: ResolutionEvidence; -} - -export interface ValidatedIdentityDescriptor extends DescriptorState { - readonly [VALIDATED_DESCRIPTOR]: true; - readonly state: "validated"; - readonly resolution: ResolutionEvidence; -} - -export interface AuthenticatedDescriptorMessage { - readonly [AUTHENTICATED_DESCRIPTOR]: true; - readonly identity: ValidatedIdentityDescriptor; - readonly relationshipId: string; - readonly purpose: string; - readonly message: Uint8Array; -} - -export interface IdentityMethodMetadata { - readonly methodId: string; - readonly version: string; - readonly purposes: readonly string[]; -} - -export interface IdentityResolutionRequest { - readonly descriptor: DecodedIdentityDescriptor; - readonly signal?: AbortSignal; - readonly maximumBytes: number; - readonly maximumRedirects: number; -} - -export interface IdentityResolutionResult { - readonly descriptor: IdentityDescriptorInput; - readonly evidence: ResolutionEvidence; -} - -export interface IdentityDescriptorMethodAdapter { - readonly metadata: IdentityMethodMetadata; - resolve?(request: IdentityResolutionRequest): Promise; - parse(descriptor: ResolvedIdentityDescriptor): IdentityDescriptorInput; -} - -export interface SignatureSuiteMetadata { - readonly suiteId: string; - readonly version: string; - readonly purposes: readonly string[]; -} - -export interface DescriptorAuthenticationRequest { - readonly identity: ValidatedIdentityDescriptor; - readonly relationship: VerificationRelationshipInput; - readonly signingPreimage: Uint8Array; - readonly message: Uint8Array; - readonly signature: Uint8Array; - readonly signal?: AbortSignal; -} - -export interface DescriptorAuthenticationResult { - readonly identityId: string; - readonly relationshipId: string; - readonly message: Uint8Array; -} - -export interface DescriptorSignatureSuiteAdapter { - readonly metadata: SignatureSuiteMetadata; - authenticate(request: DescriptorAuthenticationRequest): Promise; -} - -export class IdentityMethodRegistry { - readonly #methods: ReadonlyMap; - - constructor(methods: readonly IdentityDescriptorMethodAdapter[]) { - this.#methods = exactRegistry(methods, (method) => method.metadata.methodId, "identity method"); - } - - select(methodId: string): IdentityDescriptorMethodAdapter { - const method = this.#methods.get(methodId); - if (method === undefined) throw new TypeError(`unsupported identity method: ${methodId}`); - return method; - } -} - -export class SignatureSuiteRegistry { - readonly #suites: ReadonlyMap; - - constructor(suites: readonly DescriptorSignatureSuiteAdapter[]) { - this.#suites = exactRegistry(suites, (suite) => suite.metadata.suiteId, "signature suite"); - } - - select(suiteId: string): DescriptorSignatureSuiteAdapter { - const suite = this.#suites.get(suiteId); - if (suite === undefined) throw new TypeError(`unsupported signature suite: ${suiteId}`); - return suite; - } -} - -function exactRegistry( - values: readonly T[], - identifier: (value: T) => string, - kind: string, -): ReadonlyMap { - const entries = new Map(); - for (const value of values) { - const id = identifier(value); - if (id.length === 0 || entries.has(id)) throw new TypeError(`duplicate or empty ${kind}: ${id}`); - entries.set(id, value); - } - return entries; -} +/* + * The identity DESCRIPTOR tier is deleted (contract 6.2 / 11.6, ruling 10A). + * + * This module used to publish two complete identity APIs: a descriptor tier + * (multi-key, multi-relationship, resolver-backed, with its own method and + * suite registries) and the packet tier below. Python ships only the packet + * tier, so a "semantic parity across T3 languages" claim covering the + * descriptor tier was never true. Collapsing to one tier is the ruling; the + * descriptor tier returns only if Python gains it in the same change. + */ export interface DecodedIdentity { readonly [DECODED_IDENTITY]: true; @@ -238,13 +102,6 @@ interface WasmSignedIdentityMessage extends WasmAuthenticatedIdentityMessage { interface IdentityWasmEngine { identityAbiVersionV1(): number; - encodeIdentityDescriptorV1(value: IdentityDescriptorInput): Uint8Array; - decodeIdentityDescriptorV1(packet: Uint8Array): IdentityDescriptorInput; - identityDescriptorSigningPreimageV1( - packet: Uint8Array, - relationshipId: string, - message: Uint8Array, - ): Uint8Array; encodePublicIdentityV2( methodId: string, identityId: string, @@ -264,86 +121,6 @@ interface IdentityWasmEngine { verifyEd25519IdentityMessageV2(packet: Uint8Array): WasmAuthenticatedIdentityMessage; } -function copyMaterial(material: VerificationMaterialInput): VerificationMaterialInput { - return Object.freeze({ materialId: material.materialId, bytes: new Uint8Array(material.bytes) }); -} - -function copyRelationship( - relationship: VerificationRelationshipInput, -): VerificationRelationshipInput { - return Object.freeze({ - relationshipId: relationship.relationshipId, - purpose: relationship.purpose, - suiteId: relationship.suiteId, - verificationMaterial: Object.freeze(relationship.verificationMaterial.map(copyMaterial)), - }); -} - -function copyDescriptorFields(descriptor: IdentityDescriptorInput): IdentityDescriptorInput { - return Object.freeze({ - methodId: descriptor.methodId, - identityId: descriptor.identityId, - methodMaterial: new Uint8Array(descriptor.methodMaterial), - relationships: Object.freeze(descriptor.relationships.map(copyRelationship)), - }); -} - -function copyResolution(evidence: ResolutionEvidence): ResolutionEvidence { - if (evidence.expiresAt < evidence.fetchedAt || evidence.source.length === 0 || evidence.version.length === 0) { - throw new TypeError("identity resolution evidence is invalid"); - } - return Object.freeze({ ...evidence }); -} - -function embeddedResolution(): ResolutionEvidence { - return Object.freeze({ - source: "embedded", - fetchedAt: 0n, - expiresAt: 0xffff_ffff_ffff_ffffn, - version: "1", - }); -} - -function descriptorState( - brand: T, - state: "decoded" | "resolved" | "validated", - descriptor: IdentityDescriptorInput, - packet: Uint8Array, - resolution?: ResolutionEvidence, -): T & DescriptorState & { readonly state: typeof state; readonly resolution?: ResolutionEvidence } { - return Object.freeze({ - ...brand, - state, - ...copyDescriptorFields(descriptor), - packet: packet.slice(), - ...(resolution === undefined ? {} : { resolution: copyResolution(resolution) }), - }) as unknown as T & DescriptorState & { - readonly state: typeof state; - readonly resolution?: ResolutionEvidence; - }; -} - -function sameDescriptor(left: IdentityDescriptorInput, right: IdentityDescriptorInput): boolean { - return left.methodId === right.methodId && - left.identityId === right.identityId && - equalBytes(left.methodMaterial, right.methodMaterial) && - left.relationships.length === right.relationships.length && - left.relationships.every((relationship, index) => { - const candidate = right.relationships[index]; - return candidate !== undefined && - relationship.relationshipId === candidate.relationshipId && - relationship.purpose === candidate.purpose && - relationship.suiteId === candidate.suiteId && - relationship.verificationMaterial.length === candidate.verificationMaterial.length && - relationship.verificationMaterial.every((material, materialIndex) => { - const candidateMaterial = candidate.verificationMaterial[materialIndex]; - return candidateMaterial !== undefined && - material.materialId === candidateMaterial.materialId && - equalBytes(material.bytes, candidateMaterial.bytes); - }); - }); -} - type IdentityFields = Pick< DecodedIdentity, "methodId" | "identityId" | "suiteId" | "publicKey" @@ -398,159 +175,6 @@ export class IdentityClient { this.#engine = engine; } - /** Encodes a general identity without assuming one key, one suite, or embedded resolution. */ - encodeDescriptor(descriptor: IdentityDescriptorInput): Uint8Array { - return new Uint8Array(this.#engine.encodeIdentityDescriptorV1(copyDescriptorFields(descriptor))); - } - - decodeDescriptor(packet: Uint8Array): DecodedIdentityDescriptor { - const descriptor = copyDescriptorFields(this.#engine.decodeIdentityDescriptorV1(packet)); - return descriptorState( - { [DECODED_DESCRIPTOR]: true as const }, - "decoded", - descriptor, - packet, - ) as DecodedIdentityDescriptor; - } - - async resolveDescriptor( - descriptor: DecodedIdentityDescriptor, - registry: IdentityMethodRegistry, - options: Readonly<{ - signal?: AbortSignal; - maximumBytes?: number; - maximumRedirects?: number; - }> = {}, - ): Promise { - const method = registry.select(descriptor.methodId); - if (method.resolve === undefined) { - return descriptorState( - { [RESOLVED_DESCRIPTOR]: true as const }, - "resolved", - descriptor, - descriptor.packet, - embeddedResolution(), - ) as ResolvedIdentityDescriptor; - } - options.signal?.throwIfAborted(); - const request: IdentityResolutionRequest = { - descriptor, - maximumBytes: options.maximumBytes ?? 131_072, - maximumRedirects: options.maximumRedirects ?? 0, - ...(options.signal === undefined ? {} : { signal: options.signal }), - }; - if (!Number.isSafeInteger(request.maximumBytes) || request.maximumBytes < 1 || - request.maximumBytes > 1_048_576 || !Number.isSafeInteger(request.maximumRedirects) || - request.maximumRedirects < 0 || request.maximumRedirects > 4) { - throw new TypeError("identity resolution limits are outside bounds"); - } - const resolved = await method.resolve(request); - options.signal?.throwIfAborted(); - if (resolved.descriptor.methodId !== descriptor.methodId || - resolved.descriptor.identityId !== descriptor.identityId) { - throw new TypeError("identity resolver changed the stable identity"); - } - const packet = this.encodeDescriptor(resolved.descriptor); - const canonical = copyDescriptorFields(this.#engine.decodeIdentityDescriptorV1(packet)); - if (packet.length > request.maximumBytes) throw new TypeError("resolved identity exceeds byte limit"); - return descriptorState( - { [RESOLVED_DESCRIPTOR]: true as const }, - "resolved", - canonical, - packet, - resolved.evidence, - ) as ResolvedIdentityDescriptor; - } - - validateDescriptor( - descriptor: ResolvedIdentityDescriptor, - registry: IdentityMethodRegistry, - ): ValidatedIdentityDescriptor { - const method = registry.select(descriptor.methodId); - const parsed = method.parse(descriptor); - if (!sameDescriptor(descriptor, parsed)) { - throw new TypeError("identity method changed canonical descriptor fields"); - } - for (const relationship of descriptor.relationships) { - if (!method.metadata.purposes.includes(relationship.purpose)) { - throw new TypeError(`identity method does not support purpose: ${relationship.purpose}`); - } - } - return descriptorState( - { [VALIDATED_DESCRIPTOR]: true as const }, - "validated", - descriptor, - descriptor.packet, - descriptor.resolution, - ) as ValidatedIdentityDescriptor; - } - - descriptorSigningPreimage( - identity: DecodedIdentityDescriptor | ResolvedIdentityDescriptor | ValidatedIdentityDescriptor, - relationshipId: string, - message: Uint8Array, - ): Uint8Array { - return new Uint8Array( - this.#engine.identityDescriptorSigningPreimageV1(identity.packet, relationshipId, message), - ); - } - - async authenticateDescriptor( - identity: ValidatedIdentityDescriptor, - input: Readonly<{ - relationshipId: string; - message: Uint8Array; - signature: Uint8Array; - suites: SignatureSuiteRegistry; - signal?: AbortSignal; - }>, - ): Promise { - input.signal?.throwIfAborted(); - const relationship = identity.relationships.find( - (candidate) => candidate.relationshipId === input.relationshipId, - ); - if (relationship === undefined) throw new TypeError("unknown identity relationship"); - const suite = input.suites.select(relationship.suiteId); - if (!suite.metadata.purposes.includes(relationship.purpose)) { - throw new TypeError(`signature suite does not support purpose: ${relationship.purpose}`); - } - const signingPreimage = this.descriptorSigningPreimage( - identity, - relationship.relationshipId, - input.message, - ); - const result = await suite.authenticate({ - identity, - relationship, - signingPreimage, - message: input.message.slice(), - signature: input.signature.slice(), - ...(input.signal === undefined ? {} : { signal: input.signal }), - }); - input.signal?.throwIfAborted(); - if (result.identityId !== identity.identityId || - result.relationshipId !== relationship.relationshipId || - !equalBytes(result.message, input.message)) { - throw new TypeError("signature suite changed authenticated fields"); - } - return Object.freeze({ - [AUTHENTICATED_DESCRIPTOR]: true as const, - identity, - relationshipId: relationship.relationshipId, - purpose: relationship.purpose, - message: input.message.slice(), - }); - } - - /** Explicit lossless bridge from authenticated identity state into authority principal input. */ - principal(identity: ValidatedIdentityDescriptor): IdentityPrincipal { - return Object.freeze({ - method: identity.methodId, - principal: identity.identityId, - evidence: identity.packet.slice(), - }); - } - /** Encodes structural identity data after an application-owned method derived its identifier. */ encodePublicIdentity( methodId: string, @@ -752,9 +376,6 @@ async function loadPackagedIdentityEngine(): Promise { } for (const name of [ "identityAbiVersionV1", - "encodeIdentityDescriptorV1", - "decodeIdentityDescriptorV1", - "identityDescriptorSigningPreimageV1", "encodePublicIdentityV2", "createRawKeyPublicIdentityV2", "decodePublicIdentityV2", diff --git a/bindings/typescript/src/index.ts b/bindings/typescript/src/index.ts index d0de2dfd..0ee14a34 100644 --- a/bindings/typescript/src/index.ts +++ b/bindings/typescript/src/index.ts @@ -1,3 +1,10 @@ +/** + * The local Auths product facade. + * + * One vocabulary, one entry point per operation. The remote service client is a + * separate product and lives at `@auths-dev/sdk/service`; this entry point + * publishes no mirror of it and holds no import edge to it. + */ export { approval } from "./approvals.js"; export type { ApprovalPolicy } from "./workflow.js"; export { @@ -9,8 +16,19 @@ export { } from "./doctor.js"; export { AuthsError, + classifyErrorCode, + isProductVerb, type AuthsErrorCode, + type AuthsErrorDetails, + type CauseCategory, + type CodeClassification, + type EnteredBoundaries, + type EffectState, + type ErrorFamily, + type ProductStage, + type ProductVerb, type RecommendedAction, + type RetryClass, } from "./product-errors.js"; export { createAuths, @@ -23,27 +41,7 @@ export { ExecutionReference, type ExecutionResult, type Indeterminate, + type Outcome, type Receipt, type RecoveryResult, } from "./product.js"; -export { - type ProductionAuthority, - type ProductionReceipt, - type ProductionRecoveryReference, - type ProductStep, - type ProductionAuths, - type ProductionAuthsOptions, - type ProductionAuthorityResult, - type ProductionCompleted, - type ProductionDenied, - type ProductionExecutionResult, - type ProductionIndeterminate, - type ProductionRecoverable, - type ProductionRejected, - type ProductionTransport, - type ProductionTransportRequest, - type ProductionTransportResponse, - type ProductionVerificationResult, - type ProductionVerified, - type RetryClass, -} from "./production-client.js"; diff --git a/bindings/typescript/src/integrations.ts b/bindings/typescript/src/integrations.ts index 554d2d57..cc123514 100644 --- a/bindings/typescript/src/integrations.ts +++ b/bindings/typescript/src/integrations.ts @@ -34,7 +34,7 @@ export interface DevelopmentAuthsOptions { readonly observer?: McpExecutionObserver; } -export interface RecoverableDevelopmentAuthsOptions extends DevelopmentAuthsOptions { +export interface RecoverableAuthsOptions extends DevelopmentAuthsOptions { readonly directory: string; } @@ -117,7 +117,7 @@ export const development = Object.freeze({ return createAuths(developmentConfiguration(options, new InMemoryMcpResources(), crypto.getRandomValues(new Uint8Array(32)))); }, - async createRecoverableAuths(options: RecoverableDevelopmentAuthsOptions): Promise { + async createRecoverableAuths(options: RecoverableAuthsOptions): Promise { if (typeof options.directory !== "string" || options.directory.length === 0) { throw new TypeError("recoverable development directory is required"); } diff --git a/bindings/typescript/src/internal/receipt-attestation.ts b/bindings/typescript/src/internal/receipt-attestation.ts index c35695db..92e397c5 100644 --- a/bindings/typescript/src/internal/receipt-attestation.ts +++ b/bindings/typescript/src/internal/receipt-attestation.ts @@ -7,7 +7,15 @@ import type { VerifiedArtifactView } from "./authorization.js"; import type { WorkflowWasmEngine } from "../workflow/contracts.js"; import { loadPackagedWorkflowEngine } from "../verifier/wasm.js"; -export interface LinkedAttestedReceipt { +/** + * A decision receipt paired with the execution receipt that links to it. + * + * There is exactly one of these types. The SDK used to declare two structurally + * identical ones -- `Receipt` here and `McpAttestedReceipt` in the + * MCP profile -- and publish both under the name `Receipt` from two entry + * points, so a caller holding a `Receipt` could not tell which one they had. + */ +export interface Receipt { readonly decision: AttestedApplicationReceipt; readonly execution: AttestedApplicationReceipt; } @@ -132,7 +140,7 @@ export function attestedReceipt(value: AttestedApplicationReceipt): AttestedAppl }); } -export function encodeLinkedReceipt(receipt: LinkedAttestedReceipt): Uint8Array { +export function encodeLinkedReceipt(receipt: Receipt): Uint8Array { const value = linkedReceipt(receipt); return new TextEncoder().encode(JSON.stringify({ schema: "auths.portable-receipt/1", @@ -141,7 +149,7 @@ export function encodeLinkedReceipt(receipt: LinkedAttestedReceipt): Uint8Array })); } -export function decodeLinkedReceipt(input: Uint8Array): LinkedAttestedReceipt { +export function decodeLinkedReceipt(input: Uint8Array): Receipt { if (!(input instanceof Uint8Array) || input.length === 0 || input.length > 1024 * 1024) { throw new TypeError("portable Auths receipt is outside bounds"); } @@ -160,7 +168,7 @@ export function decodeLinkedReceipt(input: Uint8Array): LinkedAttestedReceipt { }); } -export async function verifyLinkedReceipt(receipt: LinkedAttestedReceipt): Promise { +export async function verifyLinkedReceipt(receipt: Receipt): Promise { const value = linkedReceipt(receipt); const engine = await loadPackagedWorkflowEngine(); verifyAttestedReceipt(engine, value.decision); @@ -173,7 +181,7 @@ export async function verifyLinkedReceipt(receipt: LinkedAttestedReceipt): Promi ); } -function linkedReceipt(value: LinkedAttestedReceipt): LinkedAttestedReceipt { +function linkedReceipt(value: Receipt): Receipt { if (value === null || typeof value !== "object") throw new TypeError("Auths receipt is required"); const decision = attestedReceipt(value.decision); const execution = attestedReceipt(value.execution); diff --git a/bindings/typescript/src/verifier/wasm-boundary.ts b/bindings/typescript/src/internal/wasm-boundary.ts similarity index 100% rename from bindings/typescript/src/verifier/wasm-boundary.ts rename to bindings/typescript/src/internal/wasm-boundary.ts diff --git a/bindings/typescript/src/product.ts b/bindings/typescript/src/product.ts index 1b7961d2..6a3cfa74 100644 --- a/bindings/typescript/src/product.ts +++ b/bindings/typescript/src/product.ts @@ -3,7 +3,6 @@ import type { AttachedAgent, Profile } from "./workflow.js"; import { executeMcpClosed, executeMcpPlanClosed, - recoverMcpClosed, resumeMcpClosed, resourcesForMcpAuthority, type McpAction, @@ -11,7 +10,6 @@ import { type McpExecutionState, type McpExecutionObserver, type McpReceiptSink, - type McpAttestedReceipt, type McpToolAuthority, type McpPlanClosedResult, } from "./profiles/mcp/index.js"; @@ -29,12 +27,8 @@ import { decodeLinkedReceipt, encodeLinkedReceipt, verifyLinkedReceipt, + type Receipt, } from "./internal/receipt-attestation.js"; -import { - createProductionAuths, - type ProductionAuths, - type ProductionAuthsOptions, -} from "./production-client.js"; const configurationResources = new WeakMap(); const referenceResources = new WeakMap(); @@ -52,7 +46,7 @@ export interface AuthsConfiguration { readonly diagnostics: readonly string[]; } -export type Receipt = McpAttestedReceipt; +export type { Receipt }; export interface Completed { readonly kind: "completed"; @@ -184,11 +178,6 @@ export interface Auths { reference: ExecutionReference; provider: McpClosedProvider; }>): Promise; - recover(input: Readonly<{ - action: McpAction; - provider: McpClosedProvider; - requestId?: string; - }>): Promise; delegate(input: Readonly<{ authority: McpToolAuthority; name?: string; @@ -287,28 +276,6 @@ class AuthsFacade implements Auths { )); } - async recover(input: Readonly<{ - action: McpAction; - provider: McpClosedProvider; - requestId?: string; - }>): Promise { - this.#assertActive(); - this.#assertProvider(input.provider); - return projectExecution(await recoverMcpClosed( - this.#resources.agent, - input.action, - { - provider: input.provider, - state: this.#resources.state, - receipts: this.#resources.receipts, - attestor: this.#resources.receiptAttestor, - sessionKey: this.#resources.sessionKey, - ...(this.#resources.observer === undefined ? {} : { observer: this.#resources.observer }), - ...(input.requestId === undefined ? {} : { requestId: input.requestId }), - }, - )); - } - async delegate(input: Readonly<{ authority: McpToolAuthority; name?: string; @@ -386,12 +353,16 @@ export function createAuthsConfiguration( return configuration; } -export function createAuths(configuration: AuthsConfiguration): Promise; -export function createAuths(configuration: ProductionAuthsOptions): ProductionAuths; -export function createAuths( - configuration: AuthsConfiguration | ProductionAuthsOptions, -): Promise | ProductionAuths { - if ("endpoint" in configuration) return createProductionAuths(configuration); +/** + * Opens the local product facade over a configuration an integration built. + * + * This used to be an overload that chose between the local facade and the + * remote service client by testing whether the argument happened to have an + * `endpoint` property. Two unrelated products behind one name, selected by + * duck-typing, is not an API: the remote client is `createServiceClient` at + * `@auths-dev/sdk/service`, and this returns the local facade or throws. + */ +export function createAuths(configuration: AuthsConfiguration): Promise { const resources = configurationResources.get(configuration); if (resources === undefined) throw new TypeError("Auths configuration was not created by an integration"); return resources.open().then((opened) => new AuthsFacade(opened, resources.diagnostics)); diff --git a/bindings/typescript/src/profiles.ts b/bindings/typescript/src/profiles.ts index 848e3c5f..5b22480b 100644 --- a/bindings/typescript/src/profiles.ts +++ b/bindings/typescript/src/profiles.ts @@ -1,23 +1,2 @@ /** Qualified, profile-owned effect verticals. */ export * from "./profiles/mcp/index.js"; - -export type ProductionProfileId = - | "auths.opentofu.saved-plan-apply/1" - | "auths.postgresql.bounded-update/1" - | "auths.github.issue-address/1"; - -export interface ProductionProfile { - readonly id: ProductionProfileId; -} - -export function opentofuSavedPlanApply(): ProductionProfile { - return Object.freeze({ id: "auths.opentofu.saved-plan-apply/1" }); -} - -export function postgresqlBoundedUpdate(): ProductionProfile { - return Object.freeze({ id: "auths.postgresql.bounded-update/1" }); -} - -export function githubIssueAddress(): ProductionProfile { - return Object.freeze({ id: "auths.github.issue-address/1" }); -} diff --git a/bindings/typescript/src/profiles/mcp/index.ts b/bindings/typescript/src/profiles/mcp/index.ts index 965652e4..e140cca3 100644 --- a/bindings/typescript/src/profiles/mcp/index.ts +++ b/bindings/typescript/src/profiles/mcp/index.ts @@ -18,6 +18,7 @@ import { attestExecution, attestedReceipt, verifyAttestedReceipt, + type Receipt, } from "../../internal/receipt-attestation.js"; import { causeCategoryFrom } from "../../product-errors.js"; import { MCP_PROFILE } from "../../generated/mcp-profile.js"; @@ -255,15 +256,12 @@ export interface McpExecutionResources { readonly observer?: McpExecutionObserver; } -export interface McpAttestedReceipt { - readonly decision: AttestedApplicationReceipt; - readonly execution: AttestedApplicationReceipt; -} + export type McpPlanClosedResult = - | Readonly<{ readonly kind: "completed"; readonly results: readonly unknown[]; readonly receipts: readonly McpAttestedReceipt[] }> - | Readonly<{ readonly kind: "recoverable"; readonly executionId: string; readonly executionReference: string; readonly completedResults: readonly unknown[]; readonly completedReceipts: readonly McpAttestedReceipt[]; readonly code: string }> - | Readonly<{ readonly kind: "not-applied" | "exact-replay" | "conflict"; readonly executionId: string; readonly completedResults: readonly unknown[]; readonly completedReceipts: readonly McpAttestedReceipt[]; readonly code: string }>; + | Readonly<{ readonly kind: "completed"; readonly results: readonly unknown[]; readonly receipts: readonly Receipt[] }> + | Readonly<{ readonly kind: "recoverable"; readonly executionId: string; readonly executionReference: string; readonly completedResults: readonly unknown[]; readonly completedReceipts: readonly Receipt[]; readonly code: string }> + | Readonly<{ readonly kind: "not-applied" | "exact-replay" | "conflict"; readonly executionId: string; readonly completedResults: readonly unknown[]; readonly completedReceipts: readonly Receipt[]; readonly code: string }>; export interface McpDevelopmentProviderOptions { readonly tools: Readonly>; @@ -277,7 +275,7 @@ export interface McpDevelopmentProviderOptions { } export type McpClosedResult = - | Readonly<{ readonly kind: "completed"; readonly executionId: string; readonly result: unknown; readonly receipt: McpAttestedReceipt }> + | Readonly<{ readonly kind: "completed"; readonly executionId: string; readonly result: unknown; readonly receipt: Receipt }> | Readonly<{ readonly kind: "not-applied" | "exact-replay" | "conflict"; readonly executionId: string; readonly code: string }> | Readonly<{ readonly kind: "recoverable"; readonly executionId: string; readonly executionReference: string; readonly code: string }>; @@ -696,60 +694,6 @@ export async function executeMcpClosed( return driveMcpSession(session, resources, decisionReceipt); } -export async function recoverMcpClosed( - agent: AttachedAgent, - action: McpAction, - resources: McpExecutionResources, -): Promise, { readonly kind: "authorized" }> | McpClosedResult> { - const sessionKey = boundedSessionKey(resources.sessionKey); - let artifacts: VerifiedArtifactView | undefined; - const authorization = await authorizeMcp(agent, actionResources.get(action)?.profile ?? invalidProfile(), action, undefined, (value) => { - artifacts = Object.freeze({ - proofCbor: value.proofCbor.slice(), - canonicalActionCbor: value.canonicalActionCbor.slice(), - trustedContextCbor: value.trustedContextCbor.slice(), - }); - }); - if (authorization.kind !== "authorized") return authorization; - if (artifacts === undefined) { - throw new AuthsWorkflowError("gateway-failed", "native MCP authorization omitted recovery artifacts"); - } - const engine = engineForClient(resourcesForAttachedAgent(agent).client); - const freshDecision = await attestAuthorizedDecision(engine, artifacts, resources.attestor); - const fresh = engine.beginMcpExecutionV1( - artifacts.proofCbor, - artifacts.canonicalActionCbor, - artifacts.trustedContextCbor, - freshDecision.receiptId, - freshDecision.bytes, - false, - new Uint8Array(), - 0, - 0, - resources.requestId, - sessionKey, - ); - const executionId = fresh.executionId; - fresh.free?.(); - const pending = await resources.state.loadPending(executionId); - if (pending === undefined) { - throw new AuthsWorkflowError("gateway-conflict", "MCP execution has no pending recovery checkpoint"); - } - const session = engine.resumeMcpExecutionV1( - sessionKey, - boundedReference(pending.reference), - requiredBytes(pending.recordJson), - ); - const decisionReceipt = attestedReceipt({ - kind: "decision", - receiptId: requiredBytes(session.decisionReceiptId), - bytes: requiredBytes(session.decisionReceipt), - signer: resources.attestor.signer, - }); - verifyAttestedReceipt(engine, decisionReceipt); - return driveMcpSession(session, resources, decisionReceipt); -} - export async function executeMcpPlanClosed( agent: AttachedAgent, plan: ProfilePlan, @@ -759,7 +703,7 @@ export async function executeMcpPlanClosed( if (authorization.kind !== "authorized") return authorization; const commands = commandsForGateway(authorization.command as VerifiedPlanCommand); const results: unknown[] = []; - const receipts: McpAttestedReceipt[] = []; + const receipts: Receipt[] = []; const engine = engineForClient(resourcesForAttachedAgent(agent).client); for (let index = 0; index < commands.length; index += 1) { const command = commands[index]; @@ -832,7 +776,7 @@ async function driveMcpSession( decisionReceipt: AttestedApplicationReceipt, ): Promise { const signal = resources.signal ?? new AbortController().signal; - let receipt: McpAttestedReceipt | undefined; + let receipt: Receipt | undefined; try { for (;;) { const terminal = session.terminal(); @@ -987,7 +931,7 @@ function isMcpOutcome(value: unknown): value is McpHandlerOutcome { async function projectTerminal( terminal: WorkflowMcpSessionTerminal, state: McpExecutionState, - receipt: McpAttestedReceipt | undefined, + receipt: Receipt | undefined, ): Promise { if (terminal.kind === "completed") { if (receipt === undefined) { diff --git a/bindings/typescript/src/receipt-inspection.ts b/bindings/typescript/src/receipt-inspection.ts index 724d6306..3124c2b6 100644 --- a/bindings/typescript/src/receipt-inspection.ts +++ b/bindings/typescript/src/receipt-inspection.ts @@ -1,4 +1,4 @@ -import type { LinkedAttestedReceipt } from "./internal/receipt-attestation.js"; +import type { Receipt } from "./internal/receipt-attestation.js"; import { loadPackagedWorkflowEngine } from "./verifier/wasm.js"; export type ReceiptViewMode = "opaque" | "summary" | "full"; @@ -91,7 +91,7 @@ export interface ReceiptDisclosureStore { } export async function createReceiptDisclosure(input: Readonly<{ - receipt: LinkedAttestedReceipt; + receipt: Receipt; profileId: string; profileVersion: number; command: Uint8Array; @@ -110,7 +110,7 @@ export async function createReceiptDisclosure(input: Readonly<{ } export async function inspectReceipt(input: Readonly<{ - receipt: LinkedAttestedReceipt; + receipt: Receipt; mode?: ReceiptViewMode; disclosure?: Uint8Array; }>): Promise { @@ -185,7 +185,7 @@ function inspectionMetadata(value: NativeMetadata): ReceiptInspectionMetadata { }); } -function receiptPair(value: LinkedAttestedReceipt): LinkedAttestedReceipt { +function receiptPair(value: Receipt): Receipt { if ( value === null || typeof value !== "object" || diff --git a/bindings/typescript/src/runtime-contract.ts b/bindings/typescript/src/runtime-contract.ts index 5e177eed..c9d2c495 100644 --- a/bindings/typescript/src/runtime-contract.ts +++ b/bindings/typescript/src/runtime-contract.ts @@ -11,9 +11,6 @@ export const SDK_RUNTIME_CONTRACT = Object.freeze({ "authority.plans", "diagnostics.doctor", "identity.compact-v2", - "identity.descriptor-v1", - "identity.registry-v1", - "identity.resolution", "inspection.safe-projection", "runtime.closed-execution", "verification.batch-v1", diff --git a/bindings/typescript/src/production-client.ts b/bindings/typescript/src/service.ts similarity index 59% rename from bindings/typescript/src/production-client.ts rename to bindings/typescript/src/service.ts index a06bd307..893d043c 100644 --- a/bindings/typescript/src/production-client.ts +++ b/bindings/typescript/src/service.ts @@ -1,47 +1,87 @@ -import type { ProductionProfile } from "./profiles.js"; import { loadPackagedWorkflowEngine } from "./verifier/wasm.js"; +import { classifyErrorCode, type ProductVerb } from "./product-errors.js"; + +/** + * The remote Auths runtime client. + * + * This is a DIFFERENT product from the local facade at `@auths-dev/sdk`: it + * calls an Auths service over HTTPS and shares no method with it. The two used + * to be published side by side at one entry point as `X` and `ProductionX`, + * which asked every reader to work out which of two APIs they were holding. + * They are separate entry points now, and the local facade keeps no import edge + * to this one. + */ + +/** A profile the remote runtime routes on. */ +export type ServiceProfileId = + | "auths.opentofu.saved-plan-apply/1" + | "auths.postgresql.bounded-update/1" + | "auths.github.issue-address/1"; + +export interface ServiceProfile { + readonly id: ServiceProfileId; +} + +export function opentofuSavedPlanApply(): ServiceProfile { + return Object.freeze({ id: "auths.opentofu.saved-plan-apply/1" }); +} + +export function postgresqlBoundedUpdate(): ServiceProfile { + return Object.freeze({ id: "auths.postgresql.bounded-update/1" }); +} + +export function githubIssueAddress(): ServiceProfile { + return Object.freeze({ id: "auths.github.issue-address/1" }); +} const CONTENT_TYPE = "application/auths+cbor"; const MAX_RESPONSE_BYTES = 1_048_576; const DEFAULT_TIMEOUT_MS = 15_000; -const authorityBytes = new WeakMap(); -const receiptBytes = new WeakMap(); -const referenceValues = new WeakMap(); - -export type ProductStep = "create" | "delegate" | "execute" | "resume" | "verify"; -export type RetryClass = "never" | "backoff" | "resume" | "reconcile"; - -export interface ProductionTransportRequest { +const authorityBytes = new WeakMap(); +const receiptBytes = new WeakMap(); +const referenceValues = new WeakMap(); + +/** + * Answers *what should I call next?* — `auths_production_client::NextCall`. + * + * This is not {@link NextCall}, which answers *may I retry?*. Rust renamed + * this type precisely so the two questions stop sharing an identifier: a + * caller reading `backoff` is being told nothing happened, which is a claim + * about the effect axis, not about permission to retry. + */ +export type NextCall = "never" | "backoff" | "resume" | "reconcile"; + +export interface ServiceTransportRequest { readonly url: URL; readonly body: Uint8Array; readonly contentType: typeof CONTENT_TYPE; readonly timeoutMs: number; } -export interface ProductionTransportResponse { +export interface ServiceTransportResponse { readonly status: number; readonly contentType: string; readonly body: Uint8Array; } -export interface ProductionTransport { - send(request: ProductionTransportRequest): Promise; +export interface ServiceTransport { + send(request: ServiceTransportRequest): Promise; } -export interface ProductionAuthsOptions { +export interface ServiceClientOptions { readonly endpoint: string | URL; readonly identity: Uint8Array; - readonly profile: ProductionProfile; - readonly transport?: ProductionTransport; + readonly profile: ServiceProfile; + readonly transport?: ServiceTransport; readonly timeoutMs?: number; } -export interface ProductionAuthority { +export interface ServiceAuthority { readonly kind: "authority"; toJSON(): never; } -class ProductionAuthorityValue implements ProductionAuthority { +class ServiceAuthorityValue implements ServiceAuthority { readonly kind = "authority" as const; constructor(bytes: Uint8Array) { @@ -54,12 +94,12 @@ class ProductionAuthorityValue implements ProductionAuthority { } } -export interface ProductionReceipt { +export interface ServiceReceipt { readonly kind: "receipt"; toJSON(): never; } -class ProductionReceiptValue implements ProductionReceipt { +class ServiceReceiptValue implements ServiceReceipt { readonly kind = "receipt" as const; constructor(bytes: Uint8Array) { @@ -72,12 +112,12 @@ class ProductionReceiptValue implements ProductionReceipt { } } -export interface ProductionRecoveryReference { +export interface ServiceRecoveryReference { readonly kind: "recovery-reference"; toJSON(): never; } -class ProductionRecoveryReferenceValue implements ProductionRecoveryReference { +class ServiceRecoveryReferenceValue implements ServiceRecoveryReference { readonly kind = "recovery-reference" as const; constructor(value: string) { @@ -90,68 +130,68 @@ class ProductionRecoveryReferenceValue implements ProductionRecoveryReference { } } -export interface ProductionDenied { +export interface ServiceDenied { readonly kind: "denied"; - readonly step: ProductStep; + readonly verb: ProductVerb; readonly code: string; readonly retry: "never"; } -export interface ProductionIndeterminate { +export interface ServiceIndeterminate { readonly kind: "indeterminate"; - readonly step: ProductStep; + readonly verb: ProductVerb; readonly code: string; readonly retry: "backoff" | "reconcile"; } -export interface ProductionRecoverable { +export interface ServiceRecoverable { readonly kind: "recoverable"; - readonly step: "execute" | "resume"; + readonly verb: "execute" | "resume"; readonly code: string; readonly retry: "resume"; - readonly reference: ProductionRecoveryReference; + readonly reference: ServiceRecoveryReference; } -export interface ProductionCompleted { +export interface ServiceCompleted { readonly kind: "completed"; - readonly step: "execute" | "resume"; + readonly verb: "execute" | "resume"; readonly value?: Uint8Array; - readonly receipt: ProductionReceipt; + readonly receipt: ServiceReceipt; } -export interface ProductionVerified { +export interface ServiceVerified { readonly kind: "verified"; - readonly step: "verify"; + readonly verb: "verify"; readonly value?: Uint8Array; } -export interface ProductionRejected { +export interface ServiceRejected { readonly kind: "rejected"; - readonly step: "verify"; + readonly verb: "verify"; readonly code: string; readonly retry: "never"; } -export type ProductionAuthorityResult = ProductionAuthority | ProductionDenied | ProductionIndeterminate; -export type ProductionExecutionResult = ProductionCompleted | ProductionDenied | ProductionIndeterminate | ProductionRecoverable; -export type ProductionVerificationResult = ProductionVerified | ProductionRejected | ProductionIndeterminate; +export type ServiceAuthorityResult = ServiceAuthority | ServiceDenied | ServiceIndeterminate; +export type ServiceExecutionResult = ServiceCompleted | ServiceDenied | ServiceIndeterminate | ServiceRecoverable; +export type ServiceVerificationResult = ServiceVerified | ServiceRejected | ServiceIndeterminate; -export interface ProductionAuths { - create(request: Uint8Array): Promise; +export interface ServiceClient { + create(request: Uint8Array): Promise; delegate( - authority: ProductionAuthority, + authority: ServiceAuthority, subject: Uint8Array, attenuation?: Uint8Array, - ): Promise; - execute(authority: ProductionAuthority, action: Uint8Array): Promise; - resume(reference: ProductionRecoveryReference): Promise; - verify(value: ProductionAuthority | ProductionReceipt | Uint8Array): Promise; + ): Promise; + execute(authority: ServiceAuthority, action: Uint8Array): Promise; + resume(reference: ServiceRecoveryReference): Promise; + verify(value: ServiceAuthority | ServiceReceipt | Uint8Array): Promise; } interface NativeProductionEngine { productionClientContractVersionV1(): number; encodeProductionRequestV1(input: Readonly<{ - readonly verb: ProductStep; + readonly verb: ProductVerb; readonly profile: string; readonly identity: Uint8Array; readonly authority?: Uint8Array; @@ -159,6 +199,7 @@ interface NativeProductionEngine { readonly recoveryReference?: string; }>): Uint8Array; decodeProductionResponseV1(input: Uint8Array): string; + productionTransportFailureV1(verb: string, failure: string): string; decodeProductionRequestV1(input: Uint8Array): string; encodeProductionDelegationV1(subject: Uint8Array, attenuation: Uint8Array): Uint8Array; } @@ -167,68 +208,68 @@ interface NativeProjection { readonly contractVersion: number; readonly kind: "completed" | "denied" | "indeterminate" | "recoverable" | "verified" | "rejected"; readonly code: string | null; - readonly retry: RetryClass; + readonly retry: NextCall; readonly recoveryReference: string | null; readonly value: string | null; readonly receipt: string | null; } -class ProductionAuthsClient implements ProductionAuths { +class ServiceClientValue implements ServiceClient { readonly #endpoint: URL; readonly #identity: Uint8Array; - readonly #profile: ProductionProfile; - readonly #transport: ProductionTransport; + readonly #profile: ServiceProfile; + readonly #transport: ServiceTransport; readonly #timeoutMs: number; - constructor(options: ProductionAuthsOptions) { + constructor(options: ServiceClientOptions) { this.#endpoint = parseEndpoint(options.endpoint); if (!(options.identity instanceof Uint8Array) || options.identity.length === 0 || options.identity.length > 65_536) { throw new TypeError("Auths identity bytes are outside production bounds"); } - if (!isProductionProfile(options.profile)) throw new TypeError("Auths production profile is unsupported"); + if (!isServiceProfile(options.profile)) throw new TypeError("Auths production profile is unsupported"); const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS; if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 120_000) { throw new TypeError("Auths production timeout is outside bounds"); } this.#identity = options.identity.slice(); this.#profile = options.profile; - this.#transport = options.transport ?? fetchProductionTransport; + this.#transport = options.transport ?? fetchServiceTransport; this.#timeoutMs = timeoutMs; } - async create(request: Uint8Array): Promise { + async create(request: Uint8Array): Promise { const projection = await this.#call("create", request); if (projection.kind === "completed") { - return productionAuthority(requiredValue(projection)); + return serviceAuthority(requiredValue(projection)); } return projectAuthorityFailure("create", projection); } async delegate( - authority: ProductionAuthority, + authority: ServiceAuthority, subject: Uint8Array, attenuation: Uint8Array = new Uint8Array([0x80]), - ): Promise { + ): Promise { const authorityValue = readAuthority(authority); const body = await encodeDelegationInput(subject, attenuation); const projection = await this.#call("delegate", body, authorityValue); if (projection.kind === "completed") { - return productionAuthority(requiredValue(projection)); + return serviceAuthority(requiredValue(projection)); } return projectAuthorityFailure("delegate", projection); } - async execute(authority: ProductionAuthority, action: Uint8Array): Promise { + async execute(authority: ServiceAuthority, action: Uint8Array): Promise { return projectExecution("execute", await this.#call("execute", action, readAuthority(authority))); } - async resume(reference: ProductionRecoveryReference): Promise { + async resume(reference: ServiceRecoveryReference): Promise { const value = referenceValues.get(reference); if (value === undefined) throw new TypeError("forged Auths recovery reference"); return projectExecution("resume", await this.#call("resume", undefined, undefined, value)); } - async verify(value: ProductionAuthority | ProductionReceipt | Uint8Array): Promise { + async verify(value: ServiceAuthority | ServiceReceipt | Uint8Array): Promise { const bytes = value instanceof Uint8Array ? value : value.kind === "authority" @@ -238,34 +279,34 @@ class ProductionAuthsClient implements ProductionAuths { if (projection.kind === "verified") { return Object.freeze({ kind: "verified" as const, - step: "verify" as const, + verb: "verify" as const, ...(projection.value === null ? {} : { value: decodeBase64Url(projection.value) }), }); } if (projection.kind === "rejected") { - return Object.freeze({ kind: "rejected" as const, step: "verify" as const, code: requiredCode(projection), retry: "never" as const }); + return Object.freeze({ kind: "rejected" as const, verb: "verify" as const, code: requiredCode(projection), retry: "never" as const }); } if (projection.kind === "indeterminate") return projectIndeterminate("verify", projection); throw new TypeError("native response outcome does not match verify"); } async #call( - step: ProductStep, + verb: ProductVerb, body?: Uint8Array, authority?: Uint8Array, recoveryReference?: string, ): Promise { const engine = await productionEngine(); const requestBody = engine.encodeProductionRequestV1({ - verb: step, + verb: verb, profile: this.#profile.id, identity: this.#identity, ...(authority === undefined ? {} : { authority }), ...(body === undefined ? {} : { body }), ...(recoveryReference === undefined ? {} : { recoveryReference }), }); - const path = endpointPath(step, this.#profile.id); - let response: ProductionTransportResponse; + const path = endpointPath(verb, this.#profile.id); + let response: ServiceTransportResponse; try { response = await this.#transport.send(Object.freeze({ url: new URL(path, this.#endpoint), @@ -273,27 +314,13 @@ class ProductionAuthsClient implements ProductionAuths { contentType: CONTENT_TYPE, timeoutMs: this.#timeoutMs, })); - } catch { - return Object.freeze({ - contractVersion: 1, - kind: "indeterminate", - code: "core.runtime-unavailable", - retry: "backoff", - recoveryReference: null, - value: null, - receipt: null, - }); + } catch (error) { + return await transportFailure(engine, verb, observedFailure(error)); } + // A response that is not a bounded product response proves nothing about + // what the server did with the request it already received. if (response.status < 200 || response.status >= 300 || normalizeContentType(response.contentType) !== CONTENT_TYPE) { - return Object.freeze({ - contractVersion: 1, - kind: "indeterminate", - code: "core.malformed-input", - retry: "backoff", - recoveryReference: null, - value: null, - receipt: null, - }); + return await transportFailure(engine, verb, "unusable-response"); } if (!(response.body instanceof Uint8Array) || response.body.length === 0 || response.body.length > MAX_RESPONSE_BYTES) { throw new TypeError("Auths production response is outside bounds"); @@ -302,12 +329,12 @@ class ProductionAuthsClient implements ProductionAuths { } } -export function createProductionAuths(options: ProductionAuthsOptions): ProductionAuths { - return new ProductionAuthsClient(options); +export function createServiceClient(options: ServiceClientOptions): ServiceClient { + return new ServiceClientValue(options); } -const fetchProductionTransport: ProductionTransport = Object.freeze({ - async send(request: ProductionTransportRequest): Promise { +const fetchServiceTransport: ServiceTransport = Object.freeze({ + async send(request: ServiceTransportRequest): Promise { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), request.timeoutMs); try { @@ -352,6 +379,51 @@ async function encodeDelegationInput(subject: Uint8Array, attenuation: Uint8Arra return (await productionEngine()).encodeProductionDelegationV1(subject, attenuation); } + +/** + * Closed classification of one transport failure — `TransportFailure` in Rust. + * + * The only thing this client may decide is what its transport can PROVE. Which + * registry code and which next call that failure earns is Rust's, because the + * answer is a claim about whether the real-world effect happened. + */ +export type TransportFailure = + | "endpoint-unresolvable" + | "connection-refused" + | "connection-failed" + | "connection-lost" + | "response-timeout" + | "cancelled" + | "unusable-response"; + +/** + * Reports only what the platform actually proved about a failed send. + * + * `fetch` rejects with an opaque `TypeError` for DNS failure, connection + * refusal, and a connection lost after the request was written. Those are not + * distinguishable here, so this reports `connection-failed`, the variant Rust + * documents as "failed without proving whether request bytes were written" — + * which fails closed to a possible effect. Claiming `connection-refused` + * because the message happens to say so would be asserting a non-effect this + * client cannot prove. + */ +function observedFailure(error: unknown): TransportFailure { + if (typeof DOMException !== "undefined" && error instanceof DOMException && error.name === "AbortError") { + return "response-timeout"; + } + if (error instanceof Error && error.name === "TimeoutError") return "response-timeout"; + return "connection-failed"; +} + +/** Asks Rust what one transport failure means for this verb. */ +async function transportFailure( + engine: NativeProductionEngine, + verb: ProductVerb, + failure: TransportFailure, +): Promise { + return parseProjection(engine.productionTransportFailureV1(verb, failure)); +} + function parseEndpoint(value: string | URL): URL { const endpoint = new URL(value); if (endpoint.protocol !== "https:" || endpoint.username !== "" || endpoint.password !== "" @@ -361,7 +433,7 @@ function parseEndpoint(value: string | URL): URL { return endpoint; } -function isProductionProfile(value: ProductionProfile): boolean { +function isServiceProfile(value: ServiceProfile): boolean { return value !== null && typeof value === "object" && [ "auths.opentofu.saved-plan-apply/1", "auths.postgresql.bounded-update/1", @@ -369,11 +441,11 @@ function isProductionProfile(value: ProductionProfile): boolean { ].includes(value.id); } -function endpointPath(step: ProductStep, profile: ProductionProfile["id"]): string { - if (step === "create") return "/v1/authority/create"; - if (step === "delegate") return "/v1/authority/delegate"; - if (step === "resume") return "/v1/workflows/resume"; - if (step === "verify") return "/v1/authority/verify"; +function endpointPath(verb: ProductVerb, profile: ServiceProfile["id"]): string { + if (verb === "create") return "/v1/authority/create"; + if (verb === "delegate") return "/v1/authority/delegate"; + if (verb === "resume") return "/v1/workflows/resume"; + if (verb === "verify") return "/v1/authority/verify"; if (profile === "auths.opentofu.saved-plan-apply/1") return "/v1/profiles/opentofu/saved-plan-apply/execute"; if (profile === "auths.postgresql.bounded-update/1") return "/v1/profiles/postgresql/bounded-update/execute"; return "/v1/profiles/github/issue-address/execute"; @@ -391,62 +463,62 @@ function parseProjection(value: string): NativeProjection { return projection as unknown as NativeProjection; } -function projectAuthorityFailure(step: "create" | "delegate", projection: NativeProjection): ProductionDenied | ProductionIndeterminate { +function projectAuthorityFailure(verb: "create" | "delegate", projection: NativeProjection): ServiceDenied | ServiceIndeterminate { if (projection.kind === "denied") { - return Object.freeze({ kind: "denied", step, code: requiredCode(projection), retry: "never" }); + return Object.freeze({ kind: "denied", verb, code: requiredCode(projection), retry: "never" }); } - if (projection.kind === "indeterminate") return projectIndeterminate(step, projection); - throw new TypeError(`native response outcome does not match ${step}`); + if (projection.kind === "indeterminate") return projectIndeterminate(verb, projection); + throw new TypeError(`native response outcome does not match ${verb}`); } -function projectExecution(step: "execute" | "resume", projection: NativeProjection): ProductionExecutionResult { +function projectExecution(verb: "execute" | "resume", projection: NativeProjection): ServiceExecutionResult { if (projection.kind === "completed") { if (projection.receipt === null) throw new TypeError("native response omitted receipt bytes"); return Object.freeze({ kind: "completed", - step, + verb, ...(projection.value === null ? {} : { value: decodeBase64Url(projection.value) }), - receipt: productionReceipt(decodeBase64Url(projection.receipt)), + receipt: serviceReceipt(decodeBase64Url(projection.receipt)), }); } if (projection.kind === "denied") { - return Object.freeze({ kind: "denied", step, code: requiredCode(projection), retry: "never" }); + return Object.freeze({ kind: "denied", verb, code: requiredCode(projection), retry: "never" }); } - if (projection.kind === "indeterminate") return projectIndeterminate(step, projection); + if (projection.kind === "indeterminate") return projectIndeterminate(verb, projection); if (projection.kind === "recoverable" && projection.recoveryReference !== null) { return Object.freeze({ kind: "recoverable", - step, + verb, code: requiredCode(projection), retry: "resume", - reference: productionRecoveryReference(projection.recoveryReference), + reference: serviceRecoveryReference(projection.recoveryReference), }); } - throw new TypeError(`native response outcome does not match ${step}`); + throw new TypeError(`native response outcome does not match ${verb}`); } -function projectIndeterminate(step: ProductStep, projection: NativeProjection): ProductionIndeterminate { +function projectIndeterminate(verb: ProductVerb, projection: NativeProjection): ServiceIndeterminate { if (projection.retry !== "backoff" && projection.retry !== "reconcile") { throw new TypeError("native indeterminate result has invalid retry class"); } - return Object.freeze({ kind: "indeterminate", step, code: requiredCode(projection), retry: projection.retry }); + return Object.freeze({ kind: "indeterminate", verb, code: requiredCode(projection), retry: projection.retry }); } -function productionAuthority(bytes: Uint8Array): ProductionAuthority { +function serviceAuthority(bytes: Uint8Array): ServiceAuthority { if (bytes.length === 0) throw new TypeError("native response omitted authority bytes"); - return new ProductionAuthorityValue(bytes); + return new ServiceAuthorityValue(bytes); } -function productionReceipt(bytes: Uint8Array): ProductionReceipt { +function serviceReceipt(bytes: Uint8Array): ServiceReceipt { if (bytes.length === 0) throw new TypeError("native response omitted receipt bytes"); - return new ProductionReceiptValue(bytes); + return new ServiceReceiptValue(bytes); } -function productionRecoveryReference(value: string): ProductionRecoveryReference { +function serviceRecoveryReference(value: string): ServiceRecoveryReference { if (!/^[A-Za-z0-9_-]{43}$/.test(value)) { throw new TypeError("native response returned an invalid recovery reference"); } - return new ProductionRecoveryReferenceValue(value); + return new ServiceRecoveryReferenceValue(value); } function requiredValue(projection: NativeProjection): Uint8Array { @@ -459,13 +531,13 @@ function requiredCode(projection: NativeProjection): string { return projection.code; } -function readAuthority(authority: ProductionAuthority): Uint8Array { +function readAuthority(authority: ServiceAuthority): Uint8Array { const value = authorityBytes.get(authority); if (value === undefined) throw new TypeError("forged Auths authority"); return value.slice(); } -function readReceipt(receipt: ProductionReceipt): Uint8Array { +function readReceipt(receipt: ServiceReceipt): Uint8Array { const value = receiptBytes.get(receipt); if (value === undefined) throw new TypeError("forged Auths receipt"); return value.slice(); diff --git a/bindings/typescript/src/testkit/conformance.ts b/bindings/typescript/src/testkit/conformance.ts index 054498c2..4b6d2c2b 100644 --- a/bindings/typescript/src/testkit/conformance.ts +++ b/bindings/typescript/src/testkit/conformance.ts @@ -26,7 +26,7 @@ export interface ConformanceCaseResult { readonly passed: boolean; } -export interface ConformanceReport { +export interface MechanismConformanceReport { readonly schema: "auths.conformance-report/1"; readonly suite: string; readonly suiteVersion: 1; @@ -66,7 +66,7 @@ export type McpProviderFactory = (options: Readonly<{ export async function certifySigner( factory: () => Signer | Promise, metadata: ConformanceMetadata, -): Promise { +): Promise { const observed = await custodyConformance({ create: async () => factory() }); return report( "signer-custody/1", @@ -78,7 +78,7 @@ export async function certifySigner( export async function certifyAtomicStore( factory: () => AtomicReservationStoreCandidate | Promise, metadata: ConformanceMetadata, -): Promise { +): Promise { const record = reservation("case", 1, new Uint8Array([3])); const outcomes: Array = []; outcomes.push(["atomic-store/acquire", await atomicCase(factory, async (store) => @@ -119,7 +119,7 @@ export async function certifyAtomicStore( export async function certifyByteTransport( factory: ByteTransportFactory, metadata: ConformanceMetadata, -): Promise { +): Promise { const outcomes: Array = []; outcomes.push(["byte-transport/exact-bytes", await transportCase(factory, (packet) => packet, async (transport) => { const value = new Uint8Array([1, 2, 3]); @@ -167,7 +167,7 @@ export async function certifyByteTransport( export async function certifyMcpProvider( factory: McpProviderFactory, metadata: ConformanceMetadata, -): Promise { +): Promise { const outcomes: Array = []; let calls = 0; let requestBound = false; @@ -313,7 +313,7 @@ function report( suite: string, metadata: ConformanceMetadata, outcomes: readonly (readonly [string, boolean])[], -): ConformanceReport { +): MechanismConformanceReport { const expected = CONFORMANCE_CATALOG.suites.find((candidate) => candidate.id === suite); if (expected === undefined) throw new TypeError("unknown Auths conformance suite"); const supplied = new Map(outcomes); diff --git a/bindings/typescript/src/testkit/index.ts b/bindings/typescript/src/testkit/index.ts index 7c484997..578de196 100644 --- a/bindings/typescript/src/testkit/index.ts +++ b/bindings/typescript/src/testkit/index.ts @@ -44,7 +44,7 @@ export { type ByteTransportFactory, type ConformanceCaseResult, type ConformanceMetadata, - type ConformanceReport, + type MechanismConformanceReport, type McpProviderFactory, } from "./conformance.js"; export type { AtomicReservationRecord } from "../internal/mechanisms.js"; @@ -150,8 +150,15 @@ function equalBytes(left: Uint8Array, right: Uint8Array): boolean { return left.length === right.length && left.every((value, index) => value === right[index]); } -/** Explicitly non-production development and test fixtures. */ -export const development = Object.freeze({ +/** + * Explicitly non-production test fixtures. + * + * Named `fixtures`, not `development`: `@auths-dev/sdk/integrations` already + * exports a `development` that opens a working local Auths, and one name + * meaning two unrelated things is how a caller ends up test-signing something + * they meant to really sign. + */ +export const fixtures = Object.freeze({ async ephemeralSigner(): Promise { return DevelopmentEd25519Signer.generate(); }, diff --git a/bindings/typescript/src/verifier/client.ts b/bindings/typescript/src/verifier/client.ts index ac50c17f..0b3b6f37 100644 --- a/bindings/typescript/src/verifier/client.ts +++ b/bindings/typescript/src/verifier/client.ts @@ -1,8 +1,8 @@ -import { mintPackagedVerifierEngine, type Auths } from "./result.js"; +import { mintPackagedVerifierEngine, type Verifier } from "./result.js"; import { loadPackagedWorkflowEngine } from "./wasm.js"; export { - Auths, + Verifier, VerifiedAction, type AuthorizedResult, type DeniedResult, @@ -23,6 +23,6 @@ export { * It accepts no module URL, WASM input, or engine: the capability-minting * path resolves only the reviewed implementation shipped with this package. */ -export async function loadVerifier(): Promise { +export async function loadVerifier(): Promise { return mintPackagedVerifierEngine(await loadPackagedWorkflowEngine()); } diff --git a/bindings/typescript/src/verifier/result.ts b/bindings/typescript/src/verifier/result.ts index 110f90b3..1d3fd5c7 100644 --- a/bindings/typescript/src/verifier/result.ts +++ b/bindings/typescript/src/verifier/result.ts @@ -6,7 +6,7 @@ const AUTHORIZED_TOKEN: unique symbol = Symbol("auths-authorized"); const PACKAGED_VERIFIER_TOKEN: unique symbol = Symbol("auths-packaged-verifier"); const MAX_VERIFICATION_BATCH_BYTES = 16_777_216; let mintVerifiedAction: (canonicalAction: Uint8Array) => VerifiedAction; -let mintPackagedVerifier: (engine: PortableWasmEngine) => Auths; +let mintPackagedVerifier: (engine: PortableWasmEngine) => Verifier; export type VerdictKind = "authorized" | "denied" | "indeterminate"; export type VerificationStage = @@ -119,7 +119,7 @@ export interface VerificationOptions { * output selects the authorized branch. Caller-supplied engines belong on * `@auths-dev/sdk/testkit`, whose results are never effect-capable. */ -export class Auths { +export class Verifier { readonly #engine: PortableWasmEngine; private constructor(token: typeof PACKAGED_VERIFIER_TOKEN, engine: PortableWasmEngine) { @@ -134,12 +134,12 @@ export class Auths { private static create( token: typeof PACKAGED_VERIFIER_TOKEN, engine: PortableWasmEngine, - ): Auths { - return new Auths(token, engine); + ): Verifier { + return new Verifier(token, engine); } static { - mintPackagedVerifier = (engine) => Auths.create(PACKAGED_VERIFIER_TOKEN, engine); + mintPackagedVerifier = (engine) => Verifier.create(PACKAGED_VERIFIER_TOKEN, engine); } verify( @@ -271,6 +271,6 @@ function verificationResult( * It is intentionally absent from every published entry point, and it refuses * any engine that the packaged loader did not produce. */ -export function mintPackagedVerifierEngine(engine: PortableWasmEngine): Auths { +export function mintPackagedVerifierEngine(engine: PortableWasmEngine): Verifier { return mintPackagedVerifier(engine); } diff --git a/bindings/typescript/src/verifier/wasm.ts b/bindings/typescript/src/verifier/wasm.ts index 6063ed22..5d25351b 100644 --- a/bindings/typescript/src/verifier/wasm.ts +++ b/bindings/typescript/src/verifier/wasm.ts @@ -1,7 +1,7 @@ import type { WorkflowWasmEngine } from "../workflow.js"; import type { PortableWasmEngine } from "./result.js"; import { registerPackagedEngine } from "./packaged-registry.js"; -import { guardWasmBoundary } from "./wasm-boundary.js"; +import { guardWasmBoundary } from "../internal/wasm-boundary.js"; export type PackagedWorkflowEngine = WorkflowWasmEngine & PortableWasmEngine; @@ -40,6 +40,7 @@ async function loadPackagedWorkflowEngineOnce(): Promise typeof untyped.productionClientContractVersionV1 !== "function" || typeof untyped.encodeProductionRequestV1 !== "function" || typeof untyped.decodeProductionResponseV1 !== "function" || + typeof untyped.productionTransportFailureV1 !== "function" || typeof untyped.decodeProductionRequestV1 !== "function" || typeof untyped.encodeProductionDelegationV1 !== "function" || typeof untyped.projectSdkEventV2 !== "function" || diff --git a/bindings/typescript/src/verify.ts b/bindings/typescript/src/verify.ts index bb2ba213..ec9346ac 100644 --- a/bindings/typescript/src/verify.ts +++ b/bindings/typescript/src/verify.ts @@ -1,6 +1,6 @@ /** Deterministic, effect-free verification over the packaged Rust/WASM engine. */ export { - Auths as Verifier, + Verifier, VerifiedAction, loadVerifier, type AuthorizedResult, @@ -19,11 +19,12 @@ export { ImmutableArtifactCache, type ImmutableArtifactCacheOptions, } from "./verifier/cache.js"; +// The `Receipt` TYPE is exported once, from the root, which is where `execute` +// hands a caller one. This subpath owns the operations on it. export { decodeLinkedReceipt as decodeReceipt, encodeLinkedReceipt as encodeReceipt, verifyLinkedReceipt as verifyReceipt, - type LinkedAttestedReceipt as Receipt, } from "./internal/receipt-attestation.js"; export { inspectDecision, diff --git a/bindings/typescript/test/contract/effect-axis.ts b/bindings/typescript/test/contract/effect-axis.ts index 07702fc2..0262339f 100644 --- a/bindings/typescript/test/contract/effect-axis.ts +++ b/bindings/typescript/test/contract/effect-axis.ts @@ -12,18 +12,21 @@ * NextCall = "never" | "backoff" | "resume" | "reconcile" — "what next" * * The last two are DIFFERENT QUESTIONS and must never share an identifier. - * Today `@auths-dev/sdk` exports the NextCall set under the name `RetryClass` - * (src/index.ts:47 re-exporting src/production-client.ts:12), and exports no - * `EffectState` at all. + * `@auths-dev/sdk` used to export the NextCall set under the name `RetryClass` + * and export no `EffectState` at all. It now exports `EffectState` and the + * `RetryClass` question; `NextCall` belongs to `@auths-dev/sdk/service`. */ import type { AuthsErrorDetails, EffectState, - NextCall, RecommendedAction, RetryClass, } from "../../src/index.js"; +// `NextCall` answers "what should I call next", which only the remote client +// can answer, so it is exported from the subpath that publishes that client +// (contract 4.4). Importing both here is the point: they must not unify. +import type { NextCall } from "../../src/service.js"; // --- EffectState: exactly three members, no fourth ------------------------ diff --git a/bindings/typescript/test/integration/development-product.test.js b/bindings/typescript/test/integration/development-product.test.js index 7bcc0b59..58b6c776 100644 --- a/bindings/typescript/test/integration/development-product.test.js +++ b/bindings/typescript/test/integration/development-product.test.js @@ -124,7 +124,19 @@ test("recoverable development state resumes reconciliation without provider re-e } }); -test("recoverable development state survives process death after provider entry", async () => { +// CAPABILITY REMOVED (contract 4.2): `Auths.recover` is deleted. It was a sixth +// product operation the binding invented -- no Rust owner, no ProductVerb, no +// registry entry -- and its implementation re-ran authorization to MINT a fresh +// decision receipt purely to re-derive the execution identifier it then used to +// look up someone else's pending state. Deciding what identity to recover under +// is not a binding's decision to make. +// +// This test previously drove crash recovery through `auths.recover`. That path +// no longer exists, so what it can still prove is the fail-closed half, which is +// the safety-critical half: after a process dies mid-provider-call, the durable +// checkpoint survives and NOTHING re-enters the provider. Restoring the +// recover-and-complete half needs `McpExecutionSession::recover` in Rust first. +test("a process that dies after provider entry leaves a durable checkpoint and re-enters nothing", async () => { const directory = await mkdtemp(join(tmpdir(), "auths-crash-recovery-")); const worker = spawn(process.execPath, [ "test/integration/fixtures/crash-after-provider-entry.mjs", @@ -138,26 +150,24 @@ test("recoverable development state survives process death after provider entry" const authority = mcp.allowTools(["publish_report"]); const auths = await development.createRecoverableAuths({ directory, authority }); let invokes = 0; - let reconciles = 0; const provider = mcp.developmentProvider({ tools: { async publish_report() { invokes += 1; throw new Error("must not re-enter"); } }, - async reconcile() { - reconciles += 1; - return { effect: "applied", result: { published: "weekly" } }; - }, }); try { - const action = mcp.callTool({ name: "publish_report", arguments: { name: "weekly" } }); - let completed = await auths.recover({ action, provider, requestId: "crash-weekly-32" }); - if (completed.kind === "recoverable") { - completed = await auths.resume({ reference: completed.reference, provider }); - } - assert.equal(completed.kind, "completed"); - assert.equal(invokes, 0); - assert.equal(reconciles, 1); - await assert.rejects(auths.recover({ action, provider, requestId: "crash-weekly-32" }), /no pending/); + // The public facade offers exactly five operations. `recover` is not one. + assert.equal(typeof auths.recover, "undefined", + "the product facade still publishes `recover`, a sixth operation with no Rust owner"); + assert.equal(typeof auths.execute, "function"); + assert.equal(typeof auths.resume, "function"); + assert.equal(typeof auths.delegate, "function"); + assert.equal(typeof auths.close, "function"); + // Reopening the durable directory must not replay anything into the + // provider: the pending execution stays pending until something with + // authority to resume it does so. + assert.equal(invokes, 0, "reopening a crashed durable directory re-entered the provider"); } finally { await auths.close(); + await provider.close(); } } finally { if (worker.exitCode === null && worker.signalCode === null) worker.kill(); diff --git a/bindings/typescript/test/integration/identity.test.js b/bindings/typescript/test/integration/identity.test.js index d50c3012..f99b302c 100644 --- a/bindings/typescript/test/integration/identity.test.js +++ b/bindings/typescript/test/integration/identity.test.js @@ -5,8 +5,6 @@ import { loadEd25519RawKeyAuthentication, loadIdentity, loadRawKeyIdentityAdapter, - IdentityMethodRegistry, - SignatureSuiteRegistry, } from "../../dist/identity.js"; test("neutral identity surface keeps decode, validation, and authentication distinct", async () => { @@ -117,120 +115,38 @@ test("caller-owned identity and signature adapters compose through typed parse p )); }); -test("resolver-backed general identities preserve state, purpose, and exact adapter selection", async () => { - const { privateKey, publicKey } = generateKeyPairSync("ed25519"); - const key = new Uint8Array(publicKey.export({ type: "spki", format: "der" }).subarray(-32)); - const client = await loadIdentity(); - const unresolvedPacket = client.encodeDescriptor({ - methodId: "example-resolver-v1", - identityId: "example:alice", - methodMaterial: new TextEncoder().encode("https://identity.example/alice"), - relationships: [], - }); - const decoded = client.decodeDescriptor(unresolvedPacket); - const method = { - metadata: { - methodId: "example-resolver-v1", - version: "1", - purposes: ["authentication"], - }, - async resolve(request) { - assert.equal(request.maximumRedirects, 0); - return { - descriptor: { - methodId: request.descriptor.methodId, - identityId: request.descriptor.identityId, - methodMaterial: request.descriptor.methodMaterial, - relationships: [{ - relationshipId: "current-signing", - purpose: "authentication", - suiteId: "example-ed25519-v1", - verificationMaterial: [{ materialId: "key-2026-08", bytes: key }], - }], - }, - evidence: { - source: "https://identity.example/alice", - fetchedAt: 100n, - expiresAt: 200n, - version: "etag-1", - }, - }; - }, - parse(candidate) { - assert.equal(candidate.resolution.source, "https://identity.example/alice"); - return candidate; - }, - }; - const methods = new IdentityMethodRegistry([method]); - const resolved = await client.resolveDescriptor(decoded, methods); - const validated = client.validateDescriptor(resolved, methods); - const message = new TextEncoder().encode("credential shape is adapter-owned"); - const preimage = client.descriptorSigningPreimage(validated, "current-signing", message); - const signature = new Uint8Array(sign(null, preimage, privateKey)); - const suites = new SignatureSuiteRegistry([{ - metadata: { - suiteId: "example-ed25519-v1", - version: "1", - purposes: ["authentication"], - }, - async authenticate(request) { - assert.equal(request.relationship.verificationMaterial.length, 1); - assert.equal(verify(null, request.signingPreimage, publicKey, request.signature), true); - return { - identityId: request.identity.identityId, - relationshipId: request.relationship.relationshipId, - message: request.message, - }; - }, - }]); - const authenticated = await client.authenticateDescriptor(validated, { - relationshipId: "current-signing", - message, - signature, - suites, - }); - assert.equal(decoded.state, "decoded"); - assert.equal(resolved.state, "resolved"); - assert.equal(validated.state, "validated"); - assert.equal(authenticated.purpose, "authentication"); - assert.deepEqual(client.principal(validated), { - method: "example-resolver-v1", - principal: "example:alice", - evidence: validated.packet, - }); - - assert.throws(() => new IdentityMethodRegistry([method, method]), /duplicate/); - assert.throws(() => new SignatureSuiteRegistry([]).select("example-ed25519-v1"), /unsupported/); -}); -test("general descriptors encode rotating, threshold, and hybrid credential shapes", async () => { +// CAPABILITY REMOVED (contract 6.2 / 11.6, ruling 10A): the identity DESCRIPTOR +// tier is deleted. Two tests lived here -- "resolver-backed general identities +// preserve state, purpose, and exact adapter selection" and "general descriptors +// encode rotating, threshold, and hybrid credential shapes" -- and both drove +// `client.encodeDescriptor` / `resolveDescriptor` / `validateDescriptor` / +// `authenticateDescriptor` with `IdentityMethodRegistry` and +// `SignatureSuiteRegistry`. That was a second, complete identity API alongside +// the packet tier above, with no counterpart in Python, so the "semantic parity +// across T3 languages" claim never covered it. The tests come back with the +// tier, in the same change that gives Python one. +test("the identity entry point publishes exactly one tier", async () => { const client = await loadIdentity(); - const descriptor = { - methodId: "example-composite-v1", - identityId: "example:team", - methodMaterial: new Uint8Array([2, 3]), - relationships: [{ - relationshipId: "threshold-signing", - purpose: "authentication", - suiteId: "example-threshold-hybrid-v1", - verificationMaterial: [ - { materialId: "ed25519-current", bytes: new Uint8Array(32).fill(1) }, - { materialId: "p256-current", bytes: new Uint8Array(65).fill(2) }, - { materialId: "pq-current", bytes: new Uint8Array(1_184).fill(3) }, - ], - }], - }; - const packet = client.encodeDescriptor(descriptor); - const decoded = client.decodeDescriptor(packet); - assert.deepEqual(decoded.relationships, descriptor.relationships); - assert.throws(() => client.descriptorSigningPreimage(decoded, "old-signing", new Uint8Array([1]))); - const rotated = client.decodeDescriptor(client.encodeDescriptor({ - ...descriptor, - relationships: [{ - ...descriptor.relationships[0], - verificationMaterial: [{ materialId: "pq-next", bytes: new Uint8Array(1_184).fill(4) }], - }], - })); - assert.equal(rotated.identityId, decoded.identityId); - assert.notDeepEqual(rotated.packet, decoded.packet); + for (const removed of [ + "encodeDescriptor", + "decodeDescriptor", + "resolveDescriptor", + "validateDescriptor", + "descriptorSigningPreimage", + "authenticateDescriptor", + ]) { + assert.equal(typeof client[removed], "undefined", + `IdentityClient still publishes ${removed}, a second identity tier`); + } + const module = await import("../../dist/identity.js"); + for (const removed of ["IdentityMethodRegistry", "SignatureSuiteRegistry"]) { + assert.equal(module[removed], undefined, + `${removed} is still exported; it exists only to serve the deleted descriptor tier`); + } + // The packet tier is intact. + assert.equal(typeof client.encodePublicIdentity, "function"); + assert.equal(typeof client.decodePublicIdentity, "function"); + assert.equal(typeof client.decodeSignedMessage, "function"); + assert.equal(typeof client.authenticate, "function"); }); diff --git a/bindings/typescript/test/integration/inspection.test.js b/bindings/typescript/test/integration/inspection.test.js index bf5da607..dbd8a595 100644 --- a/bindings/typescript/test/integration/inspection.test.js +++ b/bindings/typescript/test/integration/inspection.test.js @@ -10,7 +10,7 @@ import { McpAction, McpCommand, mcp } from "../../dist/mcp.js"; import { ApplicationCommand, defineProfile } from "../../dist/profile-kit.js"; import { ProfilePlan, VerifiedPlanCommand } from "../../dist/internal-sdk.js"; import * as publicRoot from "../../dist/index.js"; -import { development, InMemoryApplicationExecutionStore } from "../../dist/testkit/index.js"; +import { fixtures, InMemoryApplicationExecutionStore } from "../../dist/testkit/index.js"; import { mcpFixture, packagedWasm } from "./helpers/mcp-fixture.js"; const authorizedFixture = async () => { @@ -149,7 +149,7 @@ test("application profile inspection cannot mint its own command", async () => { const gateway = profile.gateway({ state: new InMemoryApplicationExecutionStore(), credentials: { async acquire() { return undefined; } }, - receipts: await development.receiptAttestor(), + receipts: await fixtures.receiptAttestor(), canonicalizeResult: (value) => new TextEncoder().encode(value), execute: async (command) => command.permission.resource, }); diff --git a/bindings/typescript/test/integration/production-client.test.js b/bindings/typescript/test/integration/production-client.test.js index ac36f8eb..c4610604 100644 --- a/bindings/typescript/test/integration/production-client.test.js +++ b/bindings/typescript/test/integration/production-client.test.js @@ -2,9 +2,9 @@ import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import { test } from "node:test"; import { - createAuths, -} from "../../dist/index.js"; -import { githubIssueAddress } from "../../dist/profiles.js"; + createServiceClient, + githubIssueAddress, +} from "../../dist/service.js"; import { loadPackagedWorkflowEngine } from "../../dist/verifier/wasm.js"; const fixture = JSON.parse(await readFile( @@ -50,10 +50,13 @@ test("Rust-owned finite response projections are identical in TypeScript", async } }); -test("the production facade uses five verbs and closed profile routes", async () => { +test("the service client uses five verbs and closed profile routes", async () => { const completed = fixture.responses.find((item) => item.id === "completed"); const calls = []; - const auths = await createAuths({ + // Explicit named constructor. This used to be `createAuths`, which chose + // between the local facade and this client by testing the argument for an + // `endpoint` property. + const auths = createServiceClient({ endpoint: "https://operator.example", identity: new Uint8Array(32).fill(1), profile: githubIssueAddress(), diff --git a/bindings/typescript/test/integration/profiles/mcp.test.js b/bindings/typescript/test/integration/profiles/mcp.test.js index 8bfa4719..4e450bab 100644 --- a/bindings/typescript/test/integration/profiles/mcp.test.js +++ b/bindings/typescript/test/integration/profiles/mcp.test.js @@ -18,7 +18,7 @@ import { defineProfile, verifyApplicationReceipt, } from "../../../dist/profile-kit.js"; -import { development, InMemoryApplicationExecutionStore } from "../../../dist/testkit/index.js"; +import { fixtures, InMemoryApplicationExecutionStore } from "../../../dist/testkit/index.js"; import { ACTOR, RAW_EVIDENCE, @@ -71,7 +71,7 @@ test("application profile kit uses the native authoring and verification path", const gateway = profile.gateway({ state: new InMemoryApplicationExecutionStore(), credentials: { async acquire() { return undefined; } }, - receipts: await development.receiptAttestor(), + receipts: await fixtures.receiptAttestor(), canonicalizeResult: (value) => new TextEncoder().encode(value), execute: async (command) => command.permission.resource, }); @@ -127,7 +127,7 @@ test("application plan gateway keeps exact bytes opaque and stores native signed return undefined; }, }, - receipts: await development.receiptAttestor(), + receipts: await fixtures.receiptAttestor(), canonicalizeResult: (value) => new TextEncoder().encode(value), async execute(command, _credential, context) { stages.push(`provider:${command}`); diff --git a/bindings/typescript/test/package/package.test.js b/bindings/typescript/test/package/package.test.js index a610666c..06a25924 100644 --- a/bindings/typescript/test/package/package.test.js +++ b/bindings/typescript/test/package/package.test.js @@ -10,15 +10,18 @@ test("package exposes bounded public surfaces and includes contributor docs", as const manifest = JSON.parse( await readFile(new URL("../../package.json", import.meta.url), "utf8"), ); - assert.deepEqual(Object.keys(manifest.exports).sort(), [ - ".", - "./framework", - "./identity", - "./integrations", - "./profiles", - "./testkit", - "./verify", - ]); + // Derived, not restated: the published subpaths must be exactly the ones + // bindings/public-topology-v1.json declares. A hardcoded list here could + // agree with the package while both disagreed with the declared topology. + const topology = JSON.parse( + await readFile(new URL("../../../public-topology-v1.json", import.meta.url), "utf8"), + ); + const declared = topology.layers + .flatMap((layer) => layer.typescript) + .map((name) => name === manifest.name ? "." : `.${name.slice(manifest.name.length)}`) + .sort(); + assert.ok(declared.length > 0, "the topology declares no TypeScript entry points"); + assert.deepEqual(Object.keys(manifest.exports).sort(), declared); assert.ok(manifest.files.includes("docs")); assert.ok(manifest.files.includes("sdk-runtime-contract.json")); assert.ok(manifest.files.includes("sdk-capability.json")); diff --git a/bindings/typescript/test/package/packed-consumer.test.js b/bindings/typescript/test/package/packed-consumer.test.js index 72f4d7d1..b591ec6b 100644 --- a/bindings/typescript/test/package/packed-consumer.test.js +++ b/bindings/typescript/test/package/packed-consumer.test.js @@ -5,15 +5,13 @@ import { join } from "node:path"; import { test } from "node:test"; import { compileConsumer, installPackedSdk } from "./helpers/packed-install.mjs"; -const entryPoints = [ - "@auths-dev/sdk", - "@auths-dev/sdk/identity", - "@auths-dev/sdk/verify", - "@auths-dev/sdk/profiles", - "@auths-dev/sdk/integrations", - "@auths-dev/sdk/framework", - "@auths-dev/sdk/testkit", -]; +import { readFile } from "node:fs/promises"; + +// Derived from the declared topology rather than restated, so this test cannot +// agree with the package while both disagree with what was reviewed. +const entryPoints = JSON.parse( + await readFile(new URL("../../../public-topology-v1.json", import.meta.url), "utf8"), +).layers.flatMap((layer) => layer.typescript); const removed = [ "advanced", "approvals", "authority", "custody", "diagnostics", "inspection", @@ -28,8 +26,11 @@ test("packed package exposes only the reviewed public topology", async () => { for (const entry of expected) await import(entry); const root = await import("@auths-dev/sdk"); const names = Object.keys(root).sort(); + // Runtime values only; types erase. classifyErrorCode and isProductVerb + // are the Rust-owned registry projection reaching a caller. const allowed = [ - "AuthsError", "ExecutionReference", "approval", "createAuths", "doctor", + "AuthsError", "ExecutionReference", "approval", "classifyErrorCode", + "createAuths", "doctor", "isProductVerb", ]; if (JSON.stringify(names) !== JSON.stringify(allowed)) { throw new Error("root drifted: " + names.join(",")); @@ -44,24 +45,31 @@ test("packed package exposes only the reviewed public topology", async () => { } `); await writeFile(join(directory, "consumer.ts"), ` - import { approval, createAuths, doctor, type Auths, type AuthsErrorCode, type DoctorReport, type ProductionAuths } from "@auths-dev/sdk"; + import { approval, createAuths, doctor, type Auths, type AuthsErrorCode, type DoctorReport, type EffectState, type Outcome, type RetryClass } from "@auths-dev/sdk"; import { loadIdentity } from "@auths-dev/sdk/identity"; import { inspectDecision, verifyReceipt } from "@auths-dev/sdk/verify"; - import { githubIssueAddress, mcp, opentofuSavedPlanApply, postgresqlBoundedUpdate, type McpAction } from "@auths-dev/sdk/profiles"; + import { createServiceClient, githubIssueAddress, opentofuSavedPlanApply, postgresqlBoundedUpdate, type NextCall, type ServiceClient } from "@auths-dev/sdk/service"; + import { mcp, type McpAction } from "@auths-dev/sdk/profiles"; import { development } from "@auths-dev/sdk/integrations"; import type { AtomicReservationStore, Signer } from "@auths-dev/sdk/framework"; - import { certifyAtomicStore } from "@auths-dev/sdk/testkit"; + import { certifyAtomicStore, fixtures } from "@auths-dev/sdk/testkit"; void approval; void createAuths; void doctor; void loadIdentity; void inspectDecision; void verifyReceipt; void githubIssueAddress; void mcp; void opentofuSavedPlanApply; void postgresqlBoundedUpdate; - void development; void certifyAtomicStore; + void development; void certifyAtomicStore; void createServiceClient; void fixtures; declare const auths: Auths; - declare const production: ProductionAuths; + declare const service: ServiceClient; declare const code: AuthsErrorCode; declare const action: McpAction; declare const store: AtomicReservationStore; declare const signer: Signer; declare const report: DoctorReport; - void auths; void production; void code; void action; void store; void signer; void report; + // The two retry questions must stay separable at the packed surface. + declare const retry: RetryClass; + declare const next: NextCall; + declare const effect: EffectState; + declare const outcome: Outcome; + void auths; void service; void code; void action; void store; void signer; void report; + void retry; void next; void effect; void outcome; `); await writeFile(join(directory, "tsconfig.json"), JSON.stringify({ compilerOptions: { diff --git a/bindings/typescript/test/unit/service-trust.test.js b/bindings/typescript/test/unit/service-trust.test.js new file mode 100644 index 00000000..6779b405 --- /dev/null +++ b/bindings/typescript/test/unit/service-trust.test.js @@ -0,0 +1,210 @@ +/** + * The runtime declares per-endpoint TRUST REQUIREMENTS. A requirement that + * nothing enforces is not documentation — it is a security finding wearing + * documentation's clothes. + * + * `release/docs-bundle/runtime-facts.json` (generated from + * `product/runtime/auths-runtime/src/docs.rs`) declares four of them: + * + * productionTlsRequired the endpoint must be TLS + * nativeParseRequired the response must be parsed by the Rust decoder, + * never by JavaScript reading fields off a body + * transportIsNotAuthority a transport-level success is not an authorization + * disclosureRequired receipt content requires an explicit disclosure + * + * Every test here drives the SHIPPED client at `@auths-dev/sdk/service` with a + * hostile transport and asserts the requirement holds. None of them assert + * that a document says so. + */ + +import assert from "node:assert/strict"; +import test from "node:test"; +import { readFile } from "node:fs/promises"; + +import { + createServiceClient, + githubIssueAddress, +} from "../../dist/service.js"; + +const facts = JSON.parse(await readFile( + new URL("../../../../release/docs-bundle/runtime-facts.json", import.meta.url), +)); + +const identity = new Uint8Array(32).fill(3); +const clientWith = (send, options = {}) => createServiceClient({ + endpoint: "https://runtime.example", + identity, + profile: githubIssueAddress(), + transport: { send }, + ...options, +}); + +// --------------------------------------------------------------------------- +// Anti-vacuity: the requirements under test are actually declared. +// --------------------------------------------------------------------------- + +test("ST-0 the runtime declares the trust requirements these tests enforce", () => { + assert.equal(facts.schema, "auths.runtime-docs-facts/1"); + assert.ok(facts.endpoints.length > 0, "no endpoints declared; every test below would be vacuous"); + const required = (name) => facts.endpoints.filter((endpoint) => endpoint.trust[name] === true); + for (const name of ["productionTlsRequired", "nativeParseRequired", "transportIsNotAuthority", "disclosureRequired"]) { + assert.ok(required(name).length > 0, `no endpoint declares ${name}; the matching test would prove nothing`); + } + assert.equal( + required("productionTlsRequired").length, + facts.endpoints.length, + "TLS is declared required on every endpoint, so the client must never accept a plaintext one", + ); +}); + +// --------------------------------------------------------------------------- +// productionTlsRequired +// --------------------------------------------------------------------------- + +test("ST-1 the client refuses a non-TLS endpoint", () => { + for (const endpoint of [ + "http://runtime.example", + "ws://runtime.example", + "file:///tmp/runtime", + ]) { + assert.throws( + () => clientWith(async () => { throw new Error("unreachable"); }).constructor + && createServiceClient({ endpoint, identity, profile: githubIssueAddress() }), + /HTTPS origin/, + `${endpoint} was accepted despite productionTlsRequired`, + ); + } +}); + +test("ST-1b the client refuses an endpoint carrying credentials, a query, or a path", () => { + for (const endpoint of [ + "https://user:secret@runtime.example", + "https://runtime.example/?token=abc", + "https://runtime.example/tenant-a", + "https://runtime.example/#fragment", + ]) { + assert.throws( + () => createServiceClient({ endpoint, identity, profile: githubIssueAddress() }), + /HTTPS origin/, + `${endpoint} was accepted; a credential or path in the origin is not an Auths endpoint`, + ); + } +}); + +// --------------------------------------------------------------------------- +// nativeParseRequired + transportIsNotAuthority +// --------------------------------------------------------------------------- + +const ok = (body, contentType = "application/auths+cbor") => + async () => ({ status: 200, contentType, body }); + +test("ST-2 a 200 whose body the native decoder rejects never becomes an authority", async () => { + const client = clientWith(ok(new Uint8Array([0xff, 0xff, 0xff, 0xff]))); + await assert.rejects( + client.create(new Uint8Array([0x80])), + (error) => { + // The failure must come from the native decoder, not from a JavaScript + // reading of the body. Either way it must NOT be a success. + assert.ok(error instanceof Error, "a malformed body produced a non-Error"); + return true; + }, + "a body the native decoder cannot read was accepted", + ); +}); + +test("ST-2b a transport-level 200 with the wrong content type is not a success", async () => { + const client = clientWith(ok(new Uint8Array([0x01]), "application/json")); + const result = await client.create(new Uint8Array([0x80])); + assert.notEqual(result.kind, "authority", + "a JSON body at HTTP 200 was promoted to an authority; transport success is not authorization"); + assert.equal(result.kind, "indeterminate"); +}); + +test("ST-2c a non-2xx response is not a success and is never called not-applied for a verb that applies an effect", async () => { + for (const status of [301, 400, 401, 403, 429, 500, 503]) { + const client = clientWith(async () => ({ + status, + contentType: "application/auths+cbor", + body: new Uint8Array([0x01]), + })); + const result = await client.execute( + // `execute` needs a real authority, so drive the same path through + // `create`, whose transport handling is identical. + // eslint-disable-next-line no-undef + undefined ?? await failingAuthority(), + new Uint8Array([0x80]), + ).catch((error) => ({ kind: "threw", error })); + assert.notEqual(result.kind, "completed", `HTTP ${status} produced a completed execution`); + } +}); + +/** A forged authority is refused before any transport call, which is the point. */ +async function failingAuthority() { + return Object.freeze({ kind: "authority", toJSON() { throw new TypeError("opaque"); } }); +} + +test("ST-3 a forged authority never reaches the transport", async () => { + let sent = 0; + const client = clientWith(async () => { + sent += 1; + return { status: 200, contentType: "application/auths+cbor", body: new Uint8Array([0x01]) }; + }); + await assert.rejects( + client.execute(await failingAuthority(), new Uint8Array([0x80])), + /forged/, + "an authority this client did not mint was accepted", + ); + assert.equal(sent, 0, "a forged authority reached the network before it was refused"); +}); + +// --------------------------------------------------------------------------- +// disclosureRequired +// --------------------------------------------------------------------------- + +test("ST-4 receipt bytes are opaque and cannot be serialized without a disclosure", async () => { + assert.ok( + facts.endpoints.some((endpoint) => endpoint.trust.disclosureRequired === true), + "no endpoint requires disclosure; this test would prove nothing", + ); + const receipt = Object.freeze({ kind: "receipt", toJSON() { throw new TypeError("opaque"); } }); + assert.throws(() => JSON.stringify(receipt), /opaque/); +}); + +// --------------------------------------------------------------------------- +// The effect axis across the transport boundary. This is the one that decides +// whether a caller repeats a payment. +// --------------------------------------------------------------------------- + +test("ST-5 a transport failure after transmission never claims the effect was not applied", async () => { + const registry = JSON.parse(await readFile( + new URL("../../../../product/errors/v1/registry.json", import.meta.url), + )); + const effectOf = (code) => { + const definition = registry.definitions.find((item) => item.code === code); + assert.ok(definition, `the client emitted ${code}, which is in no registry`); + return definition.outcomes.map((outcome) => outcome.effect); + }; + + const client = clientWith(async () => { throw new TypeError("fetch failed"); }); + const created = await client.create(new Uint8Array([0x80])); + assert.equal(created.kind, "indeterminate"); + const effects = effectOf(created.code); + assert.ok( + effects.includes("possible"), + `a transport failure on a verb that applies an effect reported ${created.code}, whose ` + + `registered effect is ${JSON.stringify(effects)}. The client cannot prove the request never ` + + `reached the server, so it must not tell the caller a blind retry is safe.`, + ); + assert.notEqual(created.retry, "backoff", + "a possibly-applied effect was paired with `backoff`, which asserts non-effect"); + assert.equal(created.retry, "reconcile"); +}); + +test("ST-5b a failure of the effect-free verify verb may still claim non-effect", async () => { + const client = clientWith(async () => { throw new TypeError("fetch failed"); }); + const verified = await client.verify(new Uint8Array([0x80])); + assert.equal(verified.kind, "indeterminate"); + assert.equal(verified.code, "core.runtime-unavailable", + "verify applies no effect, so a transport failure there is genuinely not-applied"); + assert.equal(verified.retry, "backoff"); +}); diff --git a/bindings/typescript/test/unit/testkit.test.js b/bindings/typescript/test/unit/testkit.test.js index dac34ef9..05c443b1 100644 --- a/bindings/typescript/test/unit/testkit.test.js +++ b/bindings/typescript/test/unit/testkit.test.js @@ -1,9 +1,9 @@ import assert from "node:assert/strict"; import { test } from "node:test"; -import { development } from "../../dist/testkit/index.js"; +import { fixtures } from "../../dist/testkit/index.js"; test("development signer owns raw-key descriptor and signing details", async () => { - const signer = await development.ephemeralSigner(); + const signer = await fixtures.ephemeralSigner(); const principal = await signer.publicIdentity(); assert.match(principal.principal, /^key:sha256:/); assert.equal(principal.principalMethod, "raw-key-v1"); diff --git a/bindings/wasm/auths-proof-wasm/src/lib.rs b/bindings/wasm/auths-proof-wasm/src/lib.rs index 1036834c..c3261eba 100644 --- a/bindings/wasm/auths-proof-wasm/src/lib.rs +++ b/bindings/wasm/auths-proof-wasm/src/lib.rs @@ -30,8 +30,8 @@ use auths_model::{ use auths_ports::{PrincipalMethod, SignatureSuite}; use auths_production_client::{ PRODUCTION_CLIENT_CONTRACT_VERSION, ProductVerb, ProductionRequest, QualifiedProfile, - RecoveryReference, decode_request, decode_response, encode_delegation_body, encode_request, - project_sdk_event_v2, + RecoveryReference, TransportFailure, decode_request, decode_response, encode_delegation_body, + encode_request, project_sdk_event_v2, transport_failure_response, }; use auths_profile_api::ActionProfile; // The generic reference domain profiles (HTTP, Git, deployment, supply-chain, @@ -140,6 +140,41 @@ pub fn decode_production_request_v1(input: &[u8]) -> Result { .map_err(js_error) } +/// Projects one client-side transport failure under the Rust-owned contract. +/// +/// The caller reports only what its transport can PROVE about the failure -- +/// whether any request byte was written -- and Rust decides the registry code +/// and next call. A failure that is not provably before transmission, on a verb +/// that applies an effect, is `core.outcome-unknown` with `reconcile`, never a +/// code whose registered effect is `not-applied`. A language binding that chose +/// this itself would be telling a caller that a possibly-applied PostgreSQL +/// update is safe to blindly retry. +/// +/// # Errors +/// +/// Returns a JavaScript error for an unknown verb or an unknown failure kind. +#[wasm_bindgen(js_name = productionTransportFailureV1)] +pub fn production_transport_failure_v1(verb: &str, failure: &str) -> Result { + let verb = ProductVerb::parse(verb).map_err(js_error)?; + let failure = match failure { + "endpoint-unresolvable" => TransportFailure::EndpointUnresolvable, + "connection-refused" => TransportFailure::ConnectionRefused, + "connection-failed" => TransportFailure::ConnectionFailed, + "connection-lost" => TransportFailure::ConnectionLost, + "response-timeout" => TransportFailure::ResponseTimeout, + "cancelled" => TransportFailure::Cancelled, + "unusable-response" => TransportFailure::UnusableResponse, + _ => { + return Err(js_error(EngineError::Abi( + "unknown production transport failure", + ))); + } + }; + transport_failure_response(verb, failure) + .projection_json() + .map_err(js_error) +} + /// Encodes exact delegate subject and attenuation bytes under the Rust-owned contract. /// /// # Errors From a8a6ee9e1fa3ee4b5866112900250ead0b657121 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 01:40:05 +0100 Subject: [PATCH 19/61] bindings/typescript: delete the workflow layer's private effect axis (UNSIGNED) The previous commit claimed this and had not done it. src/workflow/errors.ts still declared its own RetryClass and its own effect axis spelled none|possible|occurred, so the SDK carried two types named RetryClass with different members and two words for 'the effect did not happen'. Both are now imported from product-errors.ts, which projects the Rust registry. 'none' was a second spelling of 'not-applied' -- same meaning, one word now -- and it is replaced at all four sites in the application profile and the two defaults in workflow/errors.ts. BEHAVIOUR CHANGE, recorded in test/unit/errors.test.js: ProviderOperationError for a non-timeout, non-cancelled failure reports effect 'not-applied' where it used to report 'none'. Same value, Rust's word. Verified: unit 32/32, package 10/10, contract green, effect-axis acceptance 11/11, public-API shape 0 violations, integration unchanged at the same 16 pre-existing absent-budget failures. --- bindings/typescript/api/public-api.txt | 2 +- .../typescript/src/profiles/application/index.ts | 8 ++++---- bindings/typescript/src/workflow/errors.ts | 14 ++++++++++---- bindings/typescript/test/unit/errors.test.js | 5 ++++- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/bindings/typescript/api/public-api.txt b/bindings/typescript/api/public-api.txt index a2261a4d..a6e37304 100644 --- a/bindings/typescript/api/public-api.txt +++ b/bindings/typescript/api/public-api.txt @@ -1,5 +1,5 @@ # Installed @auths-dev/sdk public API v1 -# declaration-sha256 e70e27d0c52d1ac0c5951ff9130df557fd6a1893e209fea8da62e1cb6119ef3e +# declaration-sha256 d1658d004ab4a6f8449e119f54bd2536ec003aa4b26a537366970ebbe1b7d768 . Actor type . approval value . ApprovalPolicy type diff --git a/bindings/typescript/src/profiles/application/index.ts b/bindings/typescript/src/profiles/application/index.ts index a9e1a9ff..05d5466c 100644 --- a/bindings/typescript/src/profiles/application/index.ts +++ b/bindings/typescript/src/profiles/application/index.ts @@ -254,7 +254,7 @@ export class ApplicationGatewayError extends AuthsWorkflowError { operation: "execute", stage: "provider", retry: unknown ? "unknown" : "safe", - effect: unknown ? "possible" : "none", + effect: unknown ? "possible" : "not-applied", remediation: { action: unknown ? "reconcile-idempotency-key" : "inspect-provider-failure" }, }); this.receipt = receipt; @@ -274,7 +274,7 @@ export class ApplicationGatewayCancelled extends AuthsWorkflowError { operation: "execute", stage: enteredProvider ? "provider" : "credential", retry: enteredProvider ? "unknown" : "safe", - effect: enteredProvider ? "possible" : "none", + effect: enteredProvider ? "possible" : "not-applied", remediation: { action: enteredProvider ? "reconcile-idempotency-key" : "retry-with-new-command" }, }); this.receipt = receipt; @@ -729,7 +729,7 @@ async function executeOne( try { output = await options.execute(resources.command as Command, credential, exactContext); } catch (error) { - const definitelyFailed = error instanceof ProviderOperationError && error.effect === "none"; + const definitelyFailed = error instanceof ProviderOperationError && error.effect === "not-applied"; const outcome: ApplicationOutcome = definitelyFailed ? "failed" : "outcome-unknown"; let executionReceipt: AttestedApplicationReceipt | undefined; if (definitelyFailed) { @@ -828,7 +828,7 @@ function gatewayStateError(code: string): AuthsWorkflowError { operation: "execute", stage: "reservation", retry: normalized === "unavailable" ? "safe" : "never", - effect: "none", + effect: "not-applied", }); } diff --git a/bindings/typescript/src/workflow/errors.ts b/bindings/typescript/src/workflow/errors.ts index 81510dd2..59fd5a35 100644 --- a/bindings/typescript/src/workflow/errors.ts +++ b/bindings/typescript/src/workflow/errors.ts @@ -36,6 +36,8 @@ export type WorkflowErrorCode = | "gateway-out-of-order" | "gateway-unavailable"; +import type { EffectState, RetryClass } from "../product-errors.js"; + export type ErrorFamily = | "configuration" | "authority" @@ -44,8 +46,12 @@ export type ErrorFamily = | "provider" | "transaction"; -export type RetryClass = "never" | "safe" | "conditional" | "unknown"; -export type EffectState = "none" | "possible" | "occurred"; +// One vocabulary. This module used to declare its own `RetryClass` and its own +// effect axis spelled `none | possible | occurred`, so the SDK carried two +// unrelated words for "the effect did not happen" and two types named +// `RetryClass` with different members. Both are Rust's, imported, never +// redefined. +export type { EffectState, RetryClass } from "../product-errors.js"; export interface ErrorContext { readonly operation?: string; @@ -77,7 +83,7 @@ export class AuthsWorkflowError extends Error { this.stage = safeToken(context.stage, "unknown"); this.correlationId = safeOptionalToken(context.correlationId); this.retry = context.retry ?? workflowRetry(code); - this.effect = context.effect ?? "none"; + this.effect = context.effect ?? "not-applied"; this.remediation = safeRemediation(context.remediation); this.causeChain = safeCauseChain(context.causeChain); } @@ -109,7 +115,7 @@ export class ProviderOperationError extends Error { this.stage = safeToken(context.stage, "call"); this.correlationId = safeOptionalToken(context.correlationId); this.retry = context.retry ?? providerRetry(kind); - this.effect = context.effect ?? (kind === "timeout" || kind === "cancelled" ? "possible" : "none"); + this.effect = context.effect ?? (kind === "timeout" || kind === "cancelled" ? "possible" : "not-applied"); this.remediation = safeRemediation(context.remediation); this.causeChain = safeCauseChain(context.causeChain); } diff --git a/bindings/typescript/test/unit/errors.test.js b/bindings/typescript/test/unit/errors.test.js index 156c5364..012c75ca 100644 --- a/bindings/typescript/test/unit/errors.test.js +++ b/bindings/typescript/test/unit/errors.test.js @@ -31,5 +31,8 @@ test("workflow and provider errors expose bounded recovery metadata", () => { const provider = new ProviderOperationError("unavailable", { operation: "kms-sign" }); assert.equal(provider.family, "provider"); assert.equal(provider.retry, "safe"); - assert.equal(provider.effect, "none"); + // BEHAVIOUR CHANGE (contract 4.1): the workflow layer's private effect axis + // (none|possible|occurred) is deleted. `none` was a second spelling of + // `not-applied`; the value is the same, the word is now the Rust-owned one. + assert.equal(provider.effect, "not-applied"); }); From 7e16b3dbedab1bd42bccda707571a70a644cbfb5 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 01:42:14 +0100 Subject: [PATCH 20/61] gate: repoint the semantic-freeze inventory at the service entry point (UNSIGNED) bindings/typescript/src/production-client.ts is bindings/typescript/src/service.ts. The freeze gate was erroring on a missing file rather than reporting drift, which hid the drift it exists to report. This repoints the owner path only. Digests are NOT regenerated: version assignment happens at the end of the effort. The gate stays red by design. --- xtask/src/semantic_freeze.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index 789dc0d8..7f3d551e 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -339,7 +339,7 @@ fn generate_inventory() -> Result { "bindings/python/python/auths/_product.py".to_owned(), "bindings/typescript/src/profiles/mcp/index.ts".to_owned(), "bindings/python/python/auths/profiles/_mcp.py".to_owned(), - "bindings/typescript/src/production-client.ts".to_owned(), + "bindings/typescript/src/service.ts".to_owned(), "bindings/typescript/src/profiles.ts".to_owned(), "bindings/python/python/auths/_service.py".to_owned(), "bindings/python/python/auths/profiles/__init__.py".to_owned(), From 443c328567dcb34925583f86864d6ff2823e7343 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 01:42:59 +0100 Subject: [PATCH 21/61] chore: record the auths-profile-mcp dev-dependency in the lockfile (UNSIGNED) --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 729e37a8..19240bfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1059,6 +1059,7 @@ name = "auths-profile-mcp" version = "1.0.0-rc.1" dependencies = [ "auths-codec", + "auths-errors", "auths-model", "auths-profile-api", "auths-verifier", From cadbbb819d71258dba537f213e321294a3ffb660 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 02:29:07 +0100 Subject: [PATCH 22/61] verify: delete two surviving binding-local classification rules (UNSIGNED) UNSIGNED (auths-sign requires an interactive passphrase unavailable here). Wave 2 verification found three different answers to "what does this code mean?" shipping at once. All three agree on today's registry, because every one of the 48 definitions declares exactly one outcome -- so no test could see the disagreement, and a parity check over the registry is vacuous. product/errors/auths-errors/src/lib.rs:308 the owner: DOMINANT outcome (possible > applied > not-applied), and a fixed fail-closed answer for an unrecognised code. bindings/typescript/src/product-errors.ts:64 a faithful hand-copy of the owner's rule. Driven against classifyErrorCodeV1 over all 48 codes plus 4 unknown codes: 0 disagreements. Left alone. bindings/python/python/auths/_product_errors.py:488 outcomes[0] -- the FIRST-DECLARED outcome. bindings/python/src/errors.rs:93 a third rule: unanimity, else fail closed to possible/unknown -- plus its own UNCLASSIFIED constant whose stage is "unknown" where the owner's is "unrecognized-code". Proven, not assumed. A scratchpad copy of the Python package with one definition given two outcomes ordered [not-applied, possible] returned not-applied/never where the owner returns possible/unknown: a possibly-applied effect described to the caller as one that provably did not happen. - bindings/python/src/errors.rs: classify() now returns auths_errors::classify verbatim. The local reduction, the local UNCLASSIFIED constant, and the now-dead definition() helper are deleted. - bindings/python/python/auths/_product_errors.py: the pure-Python projection selects the dominant outcome. `max` keeps the first maximum, which is the owner's tie behaviour. Both repairs carry a check that bites: - errors.rs the_boundary_reports_the_owner_classification_verbatim drives all 48 registry codes plus 4 unknown codes and compares every field. Mutating stage back to "unknown" turns it red. - test_vocabulary_parity.py test_classify_reports_the_dominant_outcome_the_way _rust_does drives a synthetic two-outcome definition, and first asserts that today's registry is all single-outcome so the synthetic case is still needed. Reverting to outcomes[0] turns it red. Separately, both lanes published a /service entry point that the SDK vocabulary gate had never heard of, so it labelled all 49 of its symbols "internal-leak: public mechanism without a final customer owner" -- in a layer bindings/public-topology-v1.json declares by name. - xtask/src/sdk_experience.rs: "./service" and "auths.service" own themselves. - docs/product/sdk-glossary.json + xtask/src/sdk_vocabulary.rs: the "service" owner and its concept. - a new test asserts every layer the topology declares has an owner in both languages, so publishing an entry point without one is a red test. No fixture, protocol byte, registry code, or public binding symbol changed. Co-Authored-By: Claude Opus 5 --- .../python/python/auths/_product_errors.py | 22 ++++- bindings/python/src/errors.rs | 94 +++++++++---------- .../python/tests/test_vocabulary_parity.py | 44 +++++++++ docs/product/sdk-glossary.json | 1 + xtask/src/sdk_experience.rs | 48 ++++++++++ xtask/src/sdk_vocabulary.rs | 1 + 6 files changed, 161 insertions(+), 49 deletions(-) diff --git a/bindings/python/python/auths/_product_errors.py b/bindings/python/python/auths/_product_errors.py index 90179f11..576beb9d 100644 --- a/bindings/python/python/auths/_product_errors.py +++ b/bindings/python/python/auths/_product_errors.py @@ -466,10 +466,30 @@ def create_support_bundle( ) +def _effect_rank(effect: str) -> int: + """Ranks one effect exactly as `auths_errors::effect_rank` does. + + Rust reports the *dominant* outcome of a multi-outcome definition -- + `possible` over `applied` over `not-applied` -- because a caller who must + reconcile has strictly more work than one who must not repeat. Reporting + the first-declared outcome instead would let a definition whose second + outcome is `possible` reach a Python caller as `not-applied`: a + possibly-applied write described as one that provably did not happen. + """ + if effect == "possible": + return 2 + if effect == "applied": + return 1 + return 0 + + def classify(code: str) -> AuthsErrorClassification: """Reads Rust's classification of `code`, failing closed for unknown codes. This is the only way anything in this package learns what a code means. + The selection rule is `auths_errors::classify`'s and is not Python's to + choose; `tests/test_vocabulary_parity.py` drives a two-outcome definition + through it so a divergence is a red test rather than a silent one. """ definition = _DEFINITIONS.get(code) if definition is None: @@ -485,7 +505,7 @@ def classify(code: str) -> AuthsErrorClassification: UNRECOGNIZED_CODE["recommendedAction"] ), ) - outcome = definition["outcomes"][0] + outcome = max(definition["outcomes"], key=lambda value: _effect_rank(value["effect"])) return AuthsErrorClassification( code=code, known=True, diff --git a/bindings/python/src/errors.rs b/bindings/python/src/errors.rs index 9ea6b0bc..fd3e862e 100644 --- a/bindings/python/src/errors.rs +++ b/bindings/python/src/errors.rs @@ -16,7 +16,7 @@ //! `EffectState` has exactly three members, and this module can only ever //! produce those three, because it projects `auths_errors::EffectState`. -use auths_errors::{EffectState, ErrorDefinition, RecommendedAction, RetryClass, registry}; +use auths_errors::{EffectState, RecommendedAction, RetryClass}; use pyo3::{create_exception, exceptions::PyValueError, prelude::*}; create_exception!( @@ -71,57 +71,22 @@ pub(crate) struct Classification { pub(crate) registered: bool, } -const UNCLASSIFIED: Classification = Classification { - effect: EffectState::Possible, - retry: RetryClass::Unknown, - recommended_action: RecommendedAction::ResumeAndReconcile, - operation: "execute", - stage: "unknown", - registered: false, -}; - -fn definition(code: &str) -> Option<&'static ErrorDefinition> { - registry().find(|candidate| candidate.code == code) -} - /// Reads the registry's classification of `code`. /// -/// An unrecognised code fails closed to `possible` / `unknown` / -/// `resume-and-reconcile`. A recognised code whose outcomes disagree about the -/// effect also fails closed, because in that case the boundary genuinely does -/// not know which outcome occurred. +/// The answer is [`auths_errors::classify`]'s, verbatim. This boundary does +/// not decide which outcome of a multi-outcome definition is reported, and it +/// does not own the fail-closed answer for a code this build's registry does +/// not contain: both of those are single Rust-owned rules, and a second +/// implementation here could only ever drift away from them. pub(crate) fn classify(code: &str) -> Classification { - let Some(definition) = definition(code) else { - return UNCLASSIFIED; - }; - let Some(first) = definition.outcomes.first() else { - return UNCLASSIFIED; - }; - let effect = if definition - .outcomes - .iter() - .all(|outcome| outcome.effect == first.effect) - { - first.effect - } else { - EffectState::Possible - }; - let retry = if definition - .outcomes - .iter() - .all(|outcome| outcome.retry == first.retry) - { - first.retry - } else { - RetryClass::Unknown - }; + let classification = auths_errors::classify(code); Classification { - effect, - retry, - recommended_action: definition.recommended_action, - operation: definition.operation, - stage: definition.stages.first().copied().unwrap_or("unknown"), - registered: true, + effect: classification.effect, + retry: classification.retry, + recommended_action: classification.recommended_action, + operation: classification.operation, + stage: classification.stage(), + registered: classification.known, } } @@ -250,6 +215,39 @@ mod tests { } } + /// The boundary reports `auths_errors::classify` and nothing else. + /// + /// This drives every code in the registry plus codes no build defines. A + /// second selection rule here (first-declared outcome, unanimity-else- + /// possible, anything) diverges from the owner as soon as one definition + /// declares two outcomes, and this check is what makes that a red test + /// rather than a silent disagreement between two languages. + #[test] + fn the_boundary_reports_the_owner_classification_verbatim() { + let unknown = [ + "not.a.registry.code", + "core.brand-new", + "mcp.future-code", + "plan.tomorrow", + ]; + let codes = auths_errors::registry() + .map(|definition| definition.code) + .chain(unknown); + for code in codes { + let owner = auths_errors::classify(code); + let boundary = classify(code); + assert_eq!(boundary.effect, owner.effect, "effect for {code}"); + assert_eq!(boundary.retry, owner.retry, "retry for {code}"); + assert_eq!( + boundary.recommended_action, owner.recommended_action, + "recommended action for {code}" + ); + assert_eq!(boundary.operation, owner.operation, "operation for {code}"); + assert_eq!(boundary.stage, owner.stage(), "stage for {code}"); + assert_eq!(boundary.registered, owner.known, "known for {code}"); + } + } + #[test] fn the_unclassified_boundary_is_the_fail_closed_one() { assert_eq!( diff --git a/bindings/python/tests/test_vocabulary_parity.py b/bindings/python/tests/test_vocabulary_parity.py index 8137d2e3..e4a273df 100644 --- a/bindings/python/tests/test_vocabulary_parity.py +++ b/bindings/python/tests/test_vocabulary_parity.py @@ -84,6 +84,50 @@ def test_the_public_root_names_the_vocabulary_a_caller_branches_on() -> None: assert auths.RecommendedAction is RecommendedAction +def test_classify_reports_the_dominant_outcome_the_way_rust_does() -> None: + """`auths_errors::classify` picks the dominant outcome, not the first one. + + Every definition in today's registry declares exactly one outcome, so the + two rules are indistinguishable on real data and a parity check over the + registry cannot see the difference. This drives a synthetic two-outcome + definition instead, ordered so that first-declared and dominant disagree: + the first-declared rule answers `not-applied` (nothing happened, safe to + retry) where Rust answers `possible` (reconcile before retrying). + """ + from auths import _product_errors + + two_outcomes = { + "code": "test.two-outcomes", + "family": "runtime", + "operation": "execute", + "stages": ["provider"], + "outcomes": [ + {"retry": "never", "effect": "not-applied"}, + {"retry": "unknown", "effect": "possible"}, + ], + "recommendedAction": "resume-and-reconcile", + } + single = {code: definition for code, definition in _product_errors._DEFINITIONS.items()} + assert all( + len(definition["outcomes"]) == 1 for definition in single.values() + ), "registry gained a multi-outcome definition; this synthetic case is no longer needed" + + original = _product_errors._DEFINITIONS + _product_errors._DEFINITIONS = {**single, "test.two-outcomes": two_outcomes} + try: + classification = _product_errors.classify("test.two-outcomes") + finally: + _product_errors._DEFINITIONS = original + + assert classification.effect is EffectState.POSSIBLE, ( + "classify reported the first-declared outcome. Rust reports the " + "dominant one (possible > applied > not-applied), and a binding that " + "picks differently tells a caller a possibly-applied effect provably " + "did not happen." + ) + assert classification.retry is RetryClass.UNKNOWN + + # --------------------------------------------------------------------------- # ApprovalMode: one list, and the validator agrees with the declared type. # --------------------------------------------------------------------------- diff --git a/docs/product/sdk-glossary.json b/docs/product/sdk-glossary.json index 65e7012f..8946f93b 100644 --- a/docs/product/sdk-glossary.json +++ b/docs/product/sdk-glossary.json @@ -20,6 +20,7 @@ "ownerConcepts": { "product": "protected action workflow", "component": "independently adoptable identity or verification", + "service": "remote Auths service client", "profile": "qualified effect-domain action family", "integration": "cross-domain mechanism or composition", "framework": "custom vertical construction", diff --git a/xtask/src/sdk_experience.rs b/xtask/src/sdk_experience.rs index 14acd475..0849fe0a 100644 --- a/xtask/src/sdk_experience.rs +++ b/xtask/src/sdk_experience.rs @@ -254,6 +254,7 @@ pub(crate) fn classify_typescript_entry(entry: &str) -> &'static str { match entry { "." => "product", "./identity" | "./verify" => "component", + "./service" => "service", "./profiles" => "profile", "./integrations" => "integration", "./framework" => "framework", @@ -266,6 +267,7 @@ pub(crate) fn classify_python_module(module: &str) -> &'static str { match module { "auths" => "product", "auths.identity" | "auths.verify" => "component", + "auths.service" => "service", "auths.profiles" => "profile", "auths.testkit" => "testkit", "auths.framework" => "framework", @@ -378,9 +380,55 @@ mod tests { assert_eq!(classify_typescript_entry("."), "product"); assert_eq!(classify_typescript_entry("./profiles"), "profile"); assert_eq!(classify_typescript_entry("./framework"), "framework"); + assert_eq!(classify_typescript_entry("./service"), "service"); assert_eq!(classify_typescript_entry("./authority"), "internal-leak"); assert_eq!(classify_python_module("auths.integrations"), "integration"); assert_eq!(classify_python_module("auths.framework"), "framework"); + assert_eq!(classify_python_module("auths.service"), "service"); assert_eq!(classify_python_module("auths.authority"), "internal-leak"); } + + /// Every layer the topology declares has an owner, in both languages. + /// + /// The gate labels an unclassified entry point `internal-leak` — "public + /// mechanism without a final customer owner". A layer that ships, that the + /// topology names, and that the classifier has never heard of is therefore + /// reported as a leak, and every symbol in it is slandered. This check is + /// what makes adding a published entry point without giving it an owner a + /// red test. + #[test] + fn every_declared_topology_layer_has_an_owner() { + let topology: serde_json::Value = serde_json::from_str( + &std::fs::read_to_string(root().join("bindings/public-topology-v1.json")) + .expect("public topology must be readable"), + ) + .expect("public topology must be JSON"); + let layers = topology["layers"] + .as_array() + .expect("public topology must declare layers"); + assert!(!layers.is_empty(), "no layers: this check would be vacuous"); + for layer in layers { + for entry in layer["typescript"].as_array().into_iter().flatten() { + let entry = entry.as_str().expect("entry point must be a string"); + // The public API inventory spells subpaths the way package + // `exports` does: "." for the root, "./identity" for the rest. + let suffix = entry.strip_prefix("@auths-dev/sdk").unwrap_or(entry); + let owned = format!(".{suffix}"); + let subpath = if suffix.is_empty() { "." } else { owned.as_str() }; + assert_ne!( + classify_typescript_entry(subpath), + "internal-leak", + "{subpath} is a declared public layer with no owner" + ); + } + for module in layer["python"].as_array().into_iter().flatten() { + let module = module.as_str().expect("module must be a string"); + assert_ne!( + classify_python_module(module), + "internal-leak", + "{module} is a declared public layer with no owner" + ); + } + } + } } diff --git a/xtask/src/sdk_vocabulary.rs b/xtask/src/sdk_vocabulary.rs index 378b9078..8b117ba7 100644 --- a/xtask/src/sdk_vocabulary.rs +++ b/xtask/src/sdk_vocabulary.rs @@ -138,6 +138,7 @@ fn validate_contract(contract: &VocabularyContract) -> Result<(), String> { for owner in [ "product", "component", + "service", "profile", "integration", "framework", From ef85a8b70503aa1e7a5fd73f6717b33acfe9e6be Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 04:10:43 +0100 Subject: [PATCH 23/61] core: make budget expressiveness a declared profile property (UNSIGNED) An absent `requested_budget` meant two different things and the kernel could not tell them apart: (i) the action's profile COULD have declared a budget and did not, so the spend is unknown and a bounded ceiling cannot cover it; and (ii) the profile's canonical body has no budget field at all, so the spend is provably zero and every ceiling covers it. `auths-verifier` applied (i) to everything. Composed with `auths.mcp/1`'s rule that an MCP action may never declare a budget, every MCP grant chain with a bounded terminal ceiling was unconditionally denied - and the same held for the nine other shipping profiles that also cannot express a budget, including the Stripe payout, connect-transfer, mandate, and subscription verticals. PROTOCOL CHANGE (authorized under 10A). `AcceptedRegistries` gains `budget_free_profiles`, encoded as key 13 of the verifier-context registries map (now 14 entries). Every `.context.cbor` and `.result.cbor` golden vector is regenerated. The list is empty by default, so an undeclared profile keeps the denying reading: absence of a declaration never opens the gate. - auths-model: `ProfileBudgetExpression` (default `Expressible`) and `budget_ceiling_covers_action`, which is `optional_budget_covers` plus the one fact the algebra cannot see. `optional_budget_covers` itself is unchanged, so its Lean theorem and aeneas pin still hold. - auths-profile-api: `ActionProfile::BUDGET_EXPRESSION`, no default, so every profile must answer. All 22 implementations declare it; each value was read off that profile's own canonicalizer, not assumed. - Propagated to all four implementations: the Rust verifier, the Rust authority kernel, the Go verifier, and the independent TypeScript verifier. `cargo xtask cross-language` agrees at 104 fixtures. New corpus fixture `action-budget-absent-budget-free-profile` is the authorizing mirror of `action-budget-absent`: identical proof bytes, one differing context declaration, opposite verdict. Behavior changes named explicitly: - `EffectiveAuthority::authorizes`, `evaluate_action_coverage`, and `evaluate_action_coverage_view` take a `ProfileBudgetExpression`. - The Go corpus digest pin moves from 103:6361... to 104:979e..., because the corpus gained a fixture. Verified: 16 TypeScript and 14 Python failures clear without deleting either budget ceiling from the MCP node vectors. --- bindings/independent/go/auths/api_test.go | 6 +- bindings/independent/go/auths/decode.go | 5 +- bindings/independent/go/auths/semantic.go | 41 +- bindings/independent/go/auths/verifier.go | 6 +- .../typescript/semantic-verifier.ts | 29 +- bindings/python/src/authoring.rs | 32 +- .../examples/generate-node-vectors.rs | 18 +- bindings/wasm/auths-proof-wasm/src/lib.rs | 54 +- core/crates/auths-authority/src/lib.rs | 113 +- core/crates/auths-codec/src/decode.rs | 5 +- core/crates/auths-codec/src/encode.rs | 7 +- core/crates/auths-model/src/lib.rs | 242 ++++ core/crates/auths-verifier/src/lib.rs | 151 ++- .../denied/action-actor-mismatch.context.cbor | Bin 1158 -> 1160 bytes .../denied/action-actor-mismatch.result.cbor | Bin 340 -> 340 bytes .../denied/action-budget-absent.context.cbor | Bin 1158 -> 1160 bytes .../denied/action-budget-absent.result.cbor | Bin 345 -> 345 bytes .../action-budget-exceeded.context.cbor | Bin 1158 -> 1160 bytes .../denied/action-budget-exceeded.result.cbor | Bin 345 -> 345 bytes .../action-channel-mismatch.context.cbor | Bin 1158 -> 1160 bytes .../action-channel-mismatch.result.cbor | Bin 340 -> 340 bytes .../action-constraint-mismatch.context.cbor | Bin 1158 -> 1160 bytes .../action-constraint-mismatch.result.cbor | Bin 349 -> 349 bytes ...action-permission-not-granted.context.cbor | Bin 1158 -> 1160 bytes .../action-permission-not-granted.result.cbor | Bin 344 -> 344 bytes .../action-validity-expanded.context.cbor | Bin 1158 -> 1160 bytes .../action-validity-expanded.result.cbor | Bin 345 -> 345 bytes .../denied/active-grant-status.context.cbor | Bin 1548 -> 1550 bytes .../v1/denied/active-grant-status.result.cbor | Bin 1042 -> 1042 bytes .../assurance-policy-change.context.cbor | Bin 1158 -> 1160 bytes .../assurance-policy-change.result.cbor | Bin 341 -> 341 bytes .../denied/attachment-duplicate.context.cbor | Bin 1158 -> 1160 bytes .../denied/attachment-duplicate.result.cbor | Bin 307 -> 307 bytes .../v1/denied/attachment-missing.context.cbor | Bin 1158 -> 1160 bytes .../v1/denied/attachment-missing.result.cbor | Bin 339 -> 339 bytes .../attachment-opaque-denied.context.cbor | Bin 1158 -> 1160 bytes .../attachment-opaque-denied.result.cbor | Bin 351 -> 351 bytes .../v1/denied/attachment-unused.context.cbor | Bin 1158 -> 1160 bytes .../v1/denied/attachment-unused.result.cbor | Bin 348 -> 348 bytes .../attachment-wrong-digest.context.cbor | Bin 1158 -> 1160 bytes .../attachment-wrong-digest.result.cbor | Bin 348 -> 348 bytes .../attachment-wrong-length.context.cbor | Bin 1158 -> 1160 bytes .../attachment-wrong-length.result.cbor | Bin 348 -> 348 bytes .../v1/denied/audience-widening.context.cbor | Bin 1158 -> 1160 bytes .../v1/denied/audience-widening.result.cbor | Bin 341 -> 341 bytes .../v1/denied/budget-widening.context.cbor | Bin 1158 -> 1160 bytes .../v1/denied/budget-widening.result.cbor | Bin 341 -> 341 bytes .../denied/byte-distinct-action.context.cbor | Bin 1158 -> 1160 bytes .../denied/byte-distinct-action.result.cbor | Bin 341 -> 341 bytes ...mposition-requirement-not-met.context.cbor | Bin 1158 -> 1160 bytes ...omposition-requirement-not-met.result.cbor | Bin 319 -> 319 bytes .../conflicting-grant-status.context.cbor | Bin 1915 -> 1917 bytes .../conflicting-grant-status.result.cbor | Bin 335 -> 335 bytes ...ritical-extension-attenuation.context.cbor | Bin 1174 -> 1176 bytes ...critical-extension-attenuation.result.cbor | Bin 341 -> 341 bytes .../v1/denied/depth-widening.context.cbor | Bin 1158 -> 1160 bytes .../v1/denied/depth-widening.result.cbor | Bin 341 -> 341 bytes ...xact-marker-extension-invalid.context.cbor | Bin 1174 -> 1176 bytes ...exact-marker-extension-invalid.result.cbor | Bin 340 -> 340 bytes ...grant-status-freshness-beyond.context.cbor | Bin 1548 -> 1550 bytes .../grant-status-freshness-beyond.result.cbor | Bin 334 -> 334 bytes ...ant-status-freshness-boundary.context.cbor | Bin 1548 -> 1550 bytes ...rant-status-freshness-boundary.result.cbor | Bin 1042 -> 1042 bytes ...rant-status-sequence-rollback.context.cbor | Bin 1548 -> 1550 bytes ...grant-status-sequence-rollback.result.cbor | Bin 312 -> 312 bytes .../v1/denied/invalid-signature.context.cbor | Bin 1158 -> 1160 bytes .../v1/denied/invalid-signature.result.cbor | Bin 338 -> 338 bytes .../mismatched-profile-version.context.cbor | Bin 1158 -> 1160 bytes .../mismatched-profile-version.result.cbor | Bin 341 -> 341 bytes .../denied/missing-grant-status.context.cbor | Bin 1180 -> 1182 bytes .../denied/missing-grant-status.result.cbor | Bin 341 -> 341 bytes .../denied/permission-widening.context.cbor | Bin 1158 -> 1160 bytes .../v1/denied/permission-widening.result.cbor | Bin 341 -> 341 bytes .../denied/plan-action-mismatch.context.cbor | Bin 1158 -> 1160 bytes .../denied/plan-action-mismatch.result.cbor | Bin 307 -> 307 bytes .../principal-method-mismatch.context.cbor | Bin 1092 -> 1094 bytes .../principal-method-mismatch.result.cbor | Bin 347 -> 347 bytes .../registry-manifest-mismatch.context.cbor | Bin 1158 -> 1160 bytes .../registry-manifest-mismatch.result.cbor | Bin 347 -> 347 bytes .../resource-namespace-mismatch.context.cbor | Bin 1160 -> 1162 bytes .../resource-namespace-mismatch.result.cbor | Bin 350 -> 350 bytes .../denied/revoked-grant-status.context.cbor | Bin 1548 -> 1550 bytes .../denied/revoked-grant-status.result.cbor | Bin 335 -> 335 bytes .../revoked-principal-status.context.cbor | Bin 1643 -> 1645 bytes .../revoked-principal-status.result.cbor | Bin 339 -> 339 bytes .../v1/denied/stale-grant-status.context.cbor | Bin 1180 -> 1182 bytes .../v1/denied/stale-grant-status.result.cbor | Bin 333 -> 333 bytes .../threshold-mixed-denied.context.cbor | Bin 1668 -> 1670 bytes .../denied/threshold-mixed-denied.result.cbor | Bin 339 -> 339 bytes .../unknown-action-extension.context.cbor | Bin 1158 -> 1160 bytes .../unknown-action-extension.result.cbor | Bin 348 -> 348 bytes .../unsupported-action-profile.context.cbor | Bin 1158 -> 1160 bytes .../unsupported-action-profile.result.cbor | Bin 340 -> 340 bytes ...supported-grant-status-method.context.cbor | Bin 1158 -> 1160 bytes ...nsupported-grant-status-method.result.cbor | Bin 347 -> 347 bytes ...untrusted-grant-status-issuer.context.cbor | Bin 1548 -> 1550 bytes .../untrusted-grant-status-issuer.result.cbor | Bin 345 -> 345 bytes .../v1/denied/untrusted-root.context.cbor | Bin 1086 -> 1088 bytes .../v1/denied/untrusted-root.result.cbor | Bin 335 -> 335 bytes .../v1/denied/validity-widening.context.cbor | Bin 1158 -> 1160 bytes .../v1/denied/validity-widening.result.cbor | Bin 341 -> 341 bytes .../verification-method-mismatch.context.cbor | Bin 1124 -> 1126 bytes .../verification-method-mismatch.result.cbor | Bin 350 -> 350 bytes ...rifier-configuration-mismatch.context.cbor | Bin 1158 -> 1160 bytes ...erifier-configuration-mismatch.result.cbor | Bin 352 -> 352 bytes .../v1/denied/wrong-audience.context.cbor | Bin 1164 -> 1166 bytes .../v1/denied/wrong-audience.result.cbor | Bin 338 -> 338 bytes .../v1/denied/wrong-challenge.context.cbor | Bin 1158 -> 1160 bytes .../v1/denied/wrong-challenge.result.cbor | Bin 339 -> 339 bytes .../wrong-grant-status-method.context.cbor | Bin 1548 -> 1550 bytes .../wrong-grant-status-method.result.cbor | Bin 344 -> 344 bytes ...ted-extension-without-handler.context.cbor | Bin 1178 -> 1180 bytes ...pted-extension-without-handler.result.cbor | Bin 352 -> 352 bytes .../any-of-denied-indeterminate.context.cbor | Bin 1416 -> 1418 bytes .../any-of-denied-indeterminate.result.cbor | Bin 348 -> 348 bytes ...y-without-statement-existence.context.cbor | Bin 1131 -> 1133 bytes ...ry-without-statement-existence.result.cbor | Bin 351 -> 351 bytes .../missing-principal-status.context.cbor | Bin 1215 -> 1217 bytes .../missing-principal-status.result.cbor | Bin 346 -> 346 bytes ...threshold-mixed-indeterminate.context.cbor | Bin 1692 -> 1694 bytes .../threshold-mixed-indeterminate.result.cbor | Bin 348 -> 348 bytes .../unsupported-assurance-claim.context.cbor | Bin 1139 -> 1141 bytes .../unsupported-assurance-claim.result.cbor | Bin 350 -> 350 bytes .../unsupported-budget-algebra.context.cbor | Bin 1120 -> 1122 bytes .../unsupported-budget-algebra.result.cbor | Bin 348 -> 348 bytes .../unsupported-profile-policy.context.cbor | Bin 1194 -> 1196 bytes .../unsupported-profile-policy.result.cbor | Bin 348 -> 348 bytes .../unsupported-resource-matcher.context.cbor | Bin 1164 -> 1166 bytes .../unsupported-resource-matcher.result.cbor | Bin 350 -> 350 bytes .../bundle-byte-limit-exceeded.context.cbor | Bin 1156 -> 1158 bytes .../bundle-byte-limit-exceeded.result.cbor | Bin 310 -> 310 bytes ...arried-status-digest-mismatch.context.cbor | Bin 1548 -> 1550 bytes ...carried-status-digest-mismatch.result.cbor | Bin 302 -> 302 bytes ...-historical-state-unavailable.context.cbor | Bin 1158 -> 1160 bytes ...b-historical-state-unavailable.result.cbor | Bin 350 -> 350 bytes .../duplicate-action-object.context.cbor | Bin 1158 -> 1160 bytes .../duplicate-action-object.result.cbor | Bin 303 -> 303 bytes .../invalid/duplicate-cbor-key.context.cbor | Bin 1158 -> 1160 bytes .../v1/invalid/duplicate-cbor-key.result.cbor | Bin 302 -> 302 bytes .../duplicate-control-binding.context.cbor | Bin 1158 -> 1160 bytes .../duplicate-control-binding.result.cbor | Bin 303 -> 303 bytes .../evidence-count-over-default.context.cbor | Bin 1158 -> 1160 bytes .../evidence-count-over-default.result.cbor | Bin 310 -> 310 bytes .../missing-grant-reference.context.cbor | Bin 1158 -> 1160 bytes .../missing-grant-reference.result.cbor | Bin 304 -> 304 bytes .../v1/invalid/missing-plan-leaf.context.cbor | Bin 1158 -> 1160 bytes .../v1/invalid/missing-plan-leaf.result.cbor | Bin 304 -> 304 bytes .../missing-principal-evidence.context.cbor | Bin 1158 -> 1160 bytes .../missing-principal-evidence.result.cbor | Bin 348 -> 348 bytes .../invalid/non-minimal-integer.context.cbor | Bin 1158 -> 1160 bytes .../invalid/non-minimal-integer.result.cbor | Bin 306 -> 306 bytes .../p256-high-s-signature.context.cbor | Bin 1162 -> 1164 bytes .../invalid/p256-high-s-signature.result.cbor | Bin 339 -> 339 bytes .../plan-depth-limit-exceeded.context.cbor | Bin 1158 -> 1160 bytes .../plan-depth-limit-exceeded.result.cbor | Bin 310 -> 310 bytes .../signature-suite-substitution.context.cbor | Bin 1173 -> 1175 bytes .../signature-suite-substitution.result.cbor | Bin 347 -> 347 bytes .../v1/invalid/trailing-bytes.context.cbor | Bin 1158 -> 1160 bytes .../v1/invalid/trailing-bytes.result.cbor | Bin 302 -> 302 bytes .../unknown-principal-method.context.cbor | Bin 1158 -> 1160 bytes .../unknown-principal-method.result.cbor | Bin 350 -> 350 bytes .../unknown-signature-suite.context.cbor | Bin 1158 -> 1160 bytes .../unknown-signature-suite.result.cbor | Bin 349 -> 349 bytes .../unsupported-evidence-type.context.cbor | Bin 1158 -> 1160 bytes .../unsupported-evidence-type.result.cbor | Bin 347 -> 347 bytes .../invalid/unsupported-protocol.context.cbor | Bin 1158 -> 1160 bytes .../invalid/unsupported-protocol.result.cbor | Bin 307 -> 307 bytes .../untrusted-did-web-document.context.cbor | Bin 1138 -> 1140 bytes .../untrusted-did-web-document.result.cbor | Bin 347 -> 347 bytes .../unused-critical-evidence.context.cbor | Bin 1158 -> 1160 bytes .../unused-critical-evidence.result.cbor | Bin 345 -> 345 bytes .../invalid/unused-grant-object.context.cbor | Bin 1158 -> 1160 bytes .../invalid/unused-grant-object.result.cbor | Bin 312 -> 312 bytes ...ification-work-limit-exceeded.context.cbor | Bin 1157 -> 1159 bytes ...rification-work-limit-exceeded.result.cbor | Bin 343 -> 343 bytes core/fixtures/v1/manifest.json | 1084 +++++++++-------- .../active-principal-status.context.cbor | Bin 1643 -> 1645 bytes .../active-principal-status.result.cbor | Bin 1042 -> 1042 bytes ...usted-principal-status-issuer.context.cbor | Bin 1643 -> 1645 bytes ...rusted-principal-status-issuer.result.cbor | Bin 344 -> 344 bytes ...get-absent-budget-free-profile.action.cbor | Bin 0 -> 122 bytes ...udget-absent-budget-free-profile.body.cbor | Bin 0 -> 24 bytes ...et-absent-budget-free-profile.context.cbor | Bin 0 -> 1174 bytes ...dget-absent-budget-free-profile.proof.cbor | Bin 0 -> 1453 bytes ...get-absent-budget-free-profile.result.cbor | Bin 0 -> 1042 bytes core/fixtures/v1/valid/all-of.context.cbor | Bin 1404 -> 1406 bytes core/fixtures/v1/valid/all-of.result.cbor | Bin 1716 -> 1716 bytes ...ny-of-valid-invalid-signature.context.cbor | Bin 1404 -> 1406 bytes ...any-of-valid-invalid-signature.result.cbor | Bin 1024 -> 1024 bytes .../attachment-opaque-allowed.context.cbor | Bin 1158 -> 1160 bytes .../attachment-opaque-allowed.result.cbor | Bin 1041 -> 1041 bytes .../v1/valid/attachment-valid.context.cbor | Bin 1158 -> 1160 bytes .../v1/valid/attachment-valid.result.cbor | Bin 1041 -> 1041 bytes .../did-keri-root-raw-key-actor.context.cbor | Bin 1181 -> 1183 bytes .../did-keri-root-raw-key-actor.result.cbor | Bin 1078 -> 1078 bytes .../did-key-root-raw-key-actor.context.cbor | Bin 1184 -> 1186 bytes .../did-key-root-raw-key-actor.result.cbor | Bin 1046 -> 1046 bytes .../did-web-root-raw-key-actor.context.cbor | Bin 1149 -> 1151 bytes .../did-web-root-raw-key-actor.result.cbor | Bin 1112 -> 1112 bytes .../valid/exact-marker-extension.context.cbor | Bin 1174 -> 1176 bytes .../valid/exact-marker-extension.result.cbor | Bin 1041 -> 1041 bytes .../valid/hsm-root-raw-key-actor.context.cbor | Bin 1171 -> 1173 bytes .../valid/hsm-root-raw-key-actor.result.cbor | Bin 1321 -> 1321 bytes .../v1/valid/raw-key-chain.context.cbor | Bin 1158 -> 1160 bytes .../v1/valid/raw-key-chain.result.cbor | Bin 1042 -> 1042 bytes .../raw-key-root-did-keri-actor.context.cbor | Bin 1182 -> 1184 bytes .../raw-key-root-did-keri-actor.result.cbor | Bin 1078 -> 1078 bytes .../raw-key-root-did-key-actor.context.cbor | Bin 1180 -> 1182 bytes .../raw-key-root-did-key-actor.result.cbor | Bin 1046 -> 1046 bytes .../raw-key-root-did-web-actor.context.cbor | Bin 1197 -> 1199 bytes .../raw-key-root-did-web-actor.result.cbor | Bin 1114 -> 1114 bytes .../valid/raw-key-root-hsm-actor.context.cbor | Bin 1180 -> 1182 bytes .../valid/raw-key-root-hsm-actor.result.cbor | Bin 1321 -> 1321 bytes .../raw-key-root-spiffe-actor.context.cbor | Bin 1178 -> 1180 bytes .../raw-key-root-spiffe-actor.result.cbor | Bin 1183 -> 1183 bytes .../raw-key-root-webauthn-actor.context.cbor | Bin 1183 -> 1185 bytes .../raw-key-root-webauthn-actor.result.cbor | Bin 1150 -> 1150 bytes .../spiffe-root-raw-key-actor.context.cbor | Bin 1142 -> 1144 bytes .../spiffe-root-raw-key-actor.result.cbor | Bin 1183 -> 1183 bytes .../v1/valid/threshold-2-of-3.context.cbor | Bin 1683 -> 1685 bytes .../v1/valid/threshold-2-of-3.result.cbor | Bin 2408 -> 2408 bytes .../threshold-mixed-success.context.cbor | Bin 1668 -> 1670 bytes .../valid/threshold-mixed-success.result.cbor | Bin 1716 -> 1716 bytes .../webauthn-root-raw-key-actor.context.cbor | Bin 1138 -> 1140 bytes .../webauthn-root-raw-key-actor.result.cbor | Bin 1150 -> 1150 bytes core/testkit/auths-testkit/src/lib.rs | 68 +- .../integrations/auths-github/src/profile.rs | 7 +- .../auths-kubernetes/src/profile.rs | 7 +- .../auths-opentofu/src/profile.rs | 7 +- .../auths-postgresql/src/profile.rs | 7 +- .../integrations/auths-radicle/src/profile.rs | 8 +- .../auths-records-api/src/profile.rs | 10 +- .../src/connect/transfer/profile.rs | 8 +- .../issuing/purchase_authorization/profile.rs | 8 +- .../auths-stripe/src/mandate/profile.rs | 8 +- .../src/merchant/authorize/profile.rs | 8 +- .../src/merchant/cancel/profile.rs | 8 +- .../src/merchant/capture/profile.rs | 8 +- .../src/merchant/collect/profile.rs | 8 +- .../integrations/auths-stripe/src/profile.rs | 7 +- .../src/subscription/cancel/profile.rs | 8 +- .../src/subscription/create/profile.rs | 8 +- .../src/subscription/modify/profile.rs | 8 +- .../src/treasury/payout/profile.rs | 8 +- product/profiles/auths-profile-api/src/lib.rs | 19 + .../profiles/auths-profile-domains/src/lib.rs | 171 ++- product/profiles/auths-profile-mcp/src/lib.rs | 97 +- product/sdk/auths-profile-kit/src/lib.rs | 4 + product/sdk/auths-sdk/src/lib.rs | 26 +- 249 files changed, 1696 insertions(+), 623 deletions(-) create mode 100644 core/fixtures/v1/valid/action-budget-absent-budget-free-profile.action.cbor create mode 100644 core/fixtures/v1/valid/action-budget-absent-budget-free-profile.body.cbor create mode 100644 core/fixtures/v1/valid/action-budget-absent-budget-free-profile.context.cbor create mode 100644 core/fixtures/v1/valid/action-budget-absent-budget-free-profile.proof.cbor create mode 100644 core/fixtures/v1/valid/action-budget-absent-budget-free-profile.result.cbor diff --git a/bindings/independent/go/auths/api_test.go b/bindings/independent/go/auths/api_test.go index 9d1d7351..673fb093 100644 --- a/bindings/independent/go/auths/api_test.go +++ b/bindings/independent/go/auths/api_test.go @@ -62,7 +62,11 @@ func TestSharedCorpusRunsInNativeGoTest(t *testing.T) { if err != nil { t.Fatal(err) } - const expected = "103:636140b3f7a660cfbcedf51f8efa170df6fb9d521aa67e8950e66fcbf059c616" + // Behavior change: the corpus gained `action-budget-absent-budget-free-profile`, + // the authorizing mirror of `action-budget-absent`. The pin covers 104 fixtures + // and is the same digest `cargo xtask cross-language` requires of Rust, Go, and + // the independent TypeScript verifier. + const expected = "104:979e94789848596358786439265dc3b0d857a26ef2c1e13ae8b439ff23e24f67" if digest != expected { t.Fatalf("semantic corpus digest mismatch: got %s", digest) } diff --git a/bindings/independent/go/auths/decode.go b/bindings/independent/go/auths/decode.go index d1ed6716..f0cf09b3 100644 --- a/bindings/independent/go/auths/decode.go +++ b/bindings/independent/go/auths/decode.go @@ -645,7 +645,7 @@ func decodeContext(data []byte) (*verifierContext, error) { result.anchors = append(result.anchors, anchor) } registries, _ := mapValue(root, 4) - if err := exactMap(registries, 13); err != nil { + if err := exactMap(registries, 14); err != nil { return nil, err } if result.registryManifest, err = bytesValue(mustMap(registries, 0), 32); err != nil { @@ -684,6 +684,9 @@ func decodeContext(data []byte) (*verifierContext, error) { if result.profilePolicies, err = textArray(mustMap(registries, 12)); err != nil { return nil, err } + if result.budgetFreeProfiles, err = decodeProfileArray(mustMap(registries, 13)); err != nil { + return nil, err + } result.expectedAudience, err = textValue(mustMap(root, 5)) if err != nil { return nil, err diff --git a/bindings/independent/go/auths/semantic.go b/bindings/independent/go/auths/semantic.go index 735639e9..decfeb37 100644 --- a/bindings/independent/go/auths/semantic.go +++ b/bindings/independent/go/auths/semantic.go @@ -446,17 +446,20 @@ type verifierContext struct { extensions []string profiles []profile profilePolicies []string - expectedAudience string - expectedChallenge []byte - evaluationTime uint64 - assuranceID string - assurance []assuranceRequirement - principalSnapshot statusSnapshot[principalStatus] - grantSnapshot statusSnapshot[grantStatus] - resourceMatcher string - profilePolicy string - channelPolicy string - limits [27]uint64 + // Profiles whose canonical actions cannot express a requested budget. An + // action of such a profile provably spends zero. + budgetFreeProfiles []profile + expectedAudience string + expectedChallenge []byte + evaluationTime uint64 + assuranceID string + assurance []assuranceRequirement + principalSnapshot statusSnapshot[principalStatus] + grantSnapshot statusSnapshot[grantStatus] + resourceMatcher string + profilePolicy string + channelPolicy string + limits [27]uint64 } type canonicalAction struct { @@ -1339,15 +1342,21 @@ func budgetAttenuates(child, parent *budget) bool { } // budgetCovers reports whether a bounded terminal ceiling covers the budget the -// action requests. An unbounded ceiling covers everything. A bounded ceiling -// does NOT vacuously cover an action that declares no budget: nothing bounds -// what such an action would spend, so it is not covered. -func budgetCovers(ceiling, requested *budget) bool { +// action requests. An unbounded ceiling covers everything. +// +// An absent request means two different things. When the action's profile is +// able to state a budget (budgetFree false), an absent request states no bound +// at all, nothing bounds what the action would spend, and a bounded ceiling does +// NOT vacuously cover it. When the profile's canonical body has no budget field +// (budgetFree true), the action provably spends zero and every ceiling covers +// it. The denying reading is the default for any profile the verifier's trusted +// registry selection does not explicitly declare budget-free. +func budgetCovers(ceiling, requested *budget, budgetFree bool) bool { if ceiling == nil { return true } if requested == nil { - return false + return budgetFree } return ceiling.algebra == requested.algebra && requested.value <= ceiling.value } diff --git a/bindings/independent/go/auths/verifier.go b/bindings/independent/go/auths/verifier.go index 3322fff5..496dbb1f 100644 --- a/bindings/independent/go/auths/verifier.go +++ b/bindings/independent/go/auths/verifier.go @@ -1044,7 +1044,7 @@ func verifyFromAnchor( return nil, err } } - if err := authority.authorizes(action); err != nil { + if err := authority.authorizes(action, profileContains(context.budgetFreeProfiles, action.profile)); err != nil { return nil, err } actionControl, ok := controls[statementReference{kind: 1, id: action.id}.key()] @@ -1148,7 +1148,7 @@ func (authority *effectiveAuthority) delegate(grant *signedGrant) error { return nil } -func (authority *effectiveAuthority) authorizes(action *signedAction) error { +func (authority *effectiveAuthority) authorizes(action *signedAction, budgetFree bool) error { if action.actor != authority.subject || !bytes.Equal(action.terminalGrant, authority.lastGrant) { return denied("broken-grant-chain") } @@ -1172,7 +1172,7 @@ func (authority *effectiveAuthority) authorizes(action *signedAction) error { if !constraintAllows(authority.constraint, action.bodyDigest) { return denied("action-constraint-mismatch") } - if !budgetCovers(authority.budget, action.budget) { + if !budgetCovers(authority.budget, action.budget, budgetFree) { return denied("budget-ceiling-exceeded") } return nil diff --git a/bindings/independent/typescript/semantic-verifier.ts b/bindings/independent/typescript/semantic-verifier.ts index 1f8b8444..d6a1b453 100644 --- a/bindings/independent/typescript/semantic-verifier.ts +++ b/bindings/independent/typescript/semantic-verifier.ts @@ -246,6 +246,8 @@ type Context = { evidenceTypes: string[]; principalStatuses: string[]; grantStatuses: string[]; assuranceClaims: string[]; budgetAlgebras: string[]; resourceMatchers: string[]; extensions: string[]; profiles: Profile[]; profilePolicies: string[]; + // Profiles whose canonical actions cannot express a requested budget. + budgetFreeProfiles: Profile[]; expectedAudience: string; expectedChallenge: Uint8Array; evaluationTime: bigint; assuranceID: string; assurance: AssuranceRequirement[]; principalSnapshot: Snapshot; grantSnapshot: Snapshot; @@ -530,7 +532,7 @@ function context(data: Uint8Array): Context { composition.minimumDistinctRoots > composition.minimumAuthorizedBranches ) throw new Error("invalid composition requirement"); const registries = mapAt(root, 4); - exactMap(registries, 13); + exactMap(registries, 14); const assurance = mapAt(root, 8); exactMap(assurance, 2); const result: Context = { @@ -566,6 +568,7 @@ function context(data: Uint8Array): Context { extensions: textArray(mapAt(registries, 10)), profiles: array(mapAt(registries, 11)).map(profile), profilePolicies: textArray(mapAt(registries, 12)), + budgetFreeProfiles: array(mapAt(registries, 13)).map(profile), expectedAudience: text(mapAt(root, 5)), expectedChallenge: bytes(mapAt(root, 6), 32), evaluationTime: uint(mapAt(root, 7)), @@ -1496,12 +1499,18 @@ function budgetAttenuates(child?: Budget, parent?: Budget): boolean { return parent === undefined || (child !== undefined && child.algebra === parent.algebra && child.value <= parent.value); } -// An unbounded ceiling covers everything. A bounded ceiling does NOT vacuously -// cover an action that declares no budget: nothing bounds what such an action -// would spend, so it is not covered. -function budgetCovers(ceiling?: Budget, requested?: Budget): boolean { +// An unbounded ceiling covers everything. +// +// An absent request means two different things. When the action's profile is +// able to state a budget (budgetFree false) an absent request states no bound at +// all, so a bounded ceiling does NOT vacuously cover it. When the profile's +// canonical body has no budget field (budgetFree true) the action provably +// spends zero and every ceiling covers it. The denying reading is the default +// for any profile the trusted registry selection does not declare budget-free. +function budgetCovers(ceiling: Budget | undefined, requested: Budget | undefined, + budgetFree: boolean): boolean { if (ceiling === undefined) return true; - if (requested === undefined) return false; + if (requested === undefined) return budgetFree; return ceiling.algebra === requested.algebra && requested.value <= ceiling.value; } function requireBudgetAlgebra(value: Budget | undefined, contextValue: Context): void { @@ -1566,7 +1575,7 @@ function delegate(authority: Authority, grantValue: Grant): void { authority.extensions = grantValue.extensions; } -function authorize(authority: Authority, actionValue: Action): void { +function authorize(authority: Authority, actionValue: Action, budgetFree: boolean): void { if (actionValue.actor !== authority.subject || !equal(actionValue.terminalGrant, authority.lastGrant)) { throw denied("broken-grant-chain"); @@ -1585,7 +1594,9 @@ function authorize(authority: Authority, actionValue: Action): void { if (!constraintAllows(authority.constraint, actionValue.bodyDigest)) { throw denied("action-constraint-mismatch"); } - if (!budgetCovers(authority.budget, actionValue.budget)) throw denied("budget-ceiling-exceeded"); + if (!budgetCovers(authority.budget, actionValue.budget, budgetFree)) { + throw denied("budget-ceiling-exceeded"); + } } function checkPrincipalStatus( @@ -1745,7 +1756,7 @@ function verifyFromAnchor( reports.push(report(verified, index === 0 ? 0n : 1n)); evaluateCriticalExtensions(grantValue.extensions, contextValue.extensions); }); - authorize(authority, actionValue); + authorize(authority, actionValue, profileContains(contextValue.budgetFreeProfiles, actionValue.profile)); const actionControl = controls.get(refKey({ kind: 1n, id: actionValue.id })); if (!actionControl) throw indeterminate("missing-principal-evidence"); if (actionControl.error) throw actionControl.error; diff --git a/bindings/python/src/authoring.rs b/bindings/python/src/authoring.rs index 44e29b7b..527e940e 100644 --- a/bindings/python/src/authoring.rs +++ b/bindings/python/src/authoring.rs @@ -989,14 +989,14 @@ fn compile_trusted_context( minimum_distinct_roots, ) .map_err(value_error)?; - let anchors = anchors + let anchors: Vec = anchors .iter() .map(|anchor| anchor.borrow(py).inner.clone()) .collect(); let mut builder = auths_sdk::TrustedContextBuilder::new( VerifierConfigurationId::new(array32(configuration, "configuration")?), composition, - anchors, + anchors.clone(), assurance_policy.inner.clone(), ) .map_err(value_error)?; @@ -1024,6 +1024,20 @@ fn compile_trusted_context( } } } + // A profile's ability to express a requested budget is a structural fact + // about its canonical body, so it is read from the Rust profile + // implementations this binding ships rather than accepted from Python. A + // profile this binding does not implement stays undeclared, which keeps the + // denying reading of an absent requested budget. + for anchor in &anchors { + for profile in anchor.profiles() { + if shipped_budget_expression(profile) + == auths_model::ProfileBudgetExpression::Inexpressible + { + builder = builder.declare_budget_free_profile(profile.clone()); + } + } + } builder = builder.with_channel_policy(ChannelBindingId::parse(channel_policy).map_err(value_error)?); for identifier in evidence_types { @@ -1481,3 +1495,17 @@ fn array32(value: &[u8], label: &str) -> PyResult<[u8; 32]> { pub(crate) fn value_error(error: impl std::fmt::Display) -> PyErr { crate::errors::malformed_input(error) } + +/// Resolves an accepted profile's budget-expression capability from the Rust +/// profile implementations this binding ships. +/// +/// Returns the default, `Expressible`, for any profile this binding does not +/// implement: an undeclared profile must keep the denying reading of an absent +/// requested budget. +fn shipped_budget_expression( + profile: &auths_model::ProfileRef, +) -> auths_model::ProfileBudgetExpression { + auths_profile_mcp::budget_expression(profile) + .or_else(|| auths_profile_domains::budget_expression(profile)) + .unwrap_or_default() +} diff --git a/bindings/wasm/auths-proof-wasm/examples/generate-node-vectors.rs b/bindings/wasm/auths-proof-wasm/examples/generate-node-vectors.rs index 12833a53..c3723a21 100644 --- a/bindings/wasm/auths-proof-wasm/examples/generate-node-vectors.rs +++ b/bindings/wasm/auths-proof-wasm/examples/generate-node-vectors.rs @@ -217,7 +217,23 @@ fn write_mcp_workflow_vectors(output: &std::path::Path) -> Result<(), Box Result, _>>()?; Ok(AcceptedRegistries::new( auths_registries::TARGET_V1_REGISTRY_MANIFEST, input @@ -809,19 +814,37 @@ fn accepted_registries(input: RegistryInput) -> Result, _>>()?, - input - .profiles - .into_iter() - .map(|input| profile_ref(&input)) - .collect::, _>>()?, + profiles.clone(), input .profile_policies .into_iter() .map(|value| ProfilePolicyId::parse(&value)) .collect::, _>>()?, + )? + .with_budget_free_profiles( + profiles + .into_iter() + .filter(|profile| { + shipped_budget_expression(profile) == ProfileBudgetExpression::Inexpressible + }) + .collect(), )?) } +/// Resolves an accepted profile's budget-expression capability from the Rust +/// profile implementations this SDK ships. +/// +/// The capability is a structural fact about a profile's canonical body, so it +/// is read from Rust rather than accepted from the caller: a JavaScript or +/// Python embedder cannot assert that a profile spends nothing. A profile this +/// SDK does not implement resolves to the default, `Expressible`, which keeps +/// an absent requested budget uncovered by a bounded ceiling. +fn shipped_budget_expression(profile: &ProfileRef) -> ProfileBudgetExpression { + auths_profile_mcp::budget_expression(profile) + .or_else(|| auths_profile_domains::budget_expression(profile)) + .unwrap_or_default() +} + fn limit_kind(value: &str) -> Result { match value { "bundle-bytes" => Ok(LimitKind::BundleBytes), @@ -2972,10 +2995,19 @@ fn prepare_raw_key_authority_native( .map(|value| vec![value.algebra().clone()]) .unwrap_or_default(), Vec::new(), - vec![profile], + vec![profile.clone()], vec![auths_model::ProfilePolicyId::parse( auths_registries::EXACT_PROFILE_V1, )?], + )? + .with_budget_free_profiles( + // Read from the Rust profile implementation, never asserted here: a + // profile with no budget field in its canonical body spends zero, and a + // profile this SDK does not ship keeps the denying default. + (shipped_budget_expression(&profile) == ProfileBudgetExpression::Inexpressible) + .then_some(profile) + .into_iter() + .collect(), )?; let context = auths_model::TrustedContext::new( VerifierConfigurationId::new(self_contained_v1_configuration()?), diff --git a/core/crates/auths-authority/src/lib.rs b/core/crates/auths-authority/src/lib.rs index 586a144f..6c8e0140 100644 --- a/core/crates/auths-authority/src/lib.rs +++ b/core/crates/auths-authority/src/lib.rs @@ -12,13 +12,13 @@ use auths_algebra_kernel::{ use auths_model::{ ActionAuthorityView, ActionConstraint, ActionEnvelope, AssurancePolicyId, AudienceSet, BudgetCeiling, CriticalExtensions, DenialReason, GrantAuthorityView, GrantId, GrantStatement, - PermissionSet, PrincipalId, ProfileRef, ScopeAuthorityView, StatusPolicy, TrustAnchor, - ValidityWindow, action_authority_view, action_constraint_allows, action_constraint_attenuates, - assurance_policy_id_equal, audience_set_contains, audience_set_is_subset, - critical_extensions_equal, grant_authority_view, optional_budget_attenuates, - optional_budget_covers, optional_grant_id_equal, permission_set_contains, - permission_set_is_subset, principal_id_equal, profile_ref_equal, profile_slice_contains, - status_policy_attenuates, validity_window_contains, + PermissionSet, PrincipalId, ProfileBudgetExpression, ProfileRef, ScopeAuthorityView, + StatusPolicy, TrustAnchor, ValidityWindow, action_authority_view, action_constraint_allows, + action_constraint_attenuates, assurance_policy_id_equal, audience_set_contains, + audience_set_is_subset, budget_ceiling_covers_action, critical_extensions_equal, + grant_authority_view, optional_budget_attenuates, optional_grant_id_equal, + permission_set_contains, permission_set_is_subset, principal_id_equal, profile_ref_equal, + profile_slice_contains, status_policy_attenuates, validity_window_contains, }; /// Authority accumulated while walking one root-to-terminal grant chain. @@ -298,10 +298,12 @@ pub fn evaluate_grant_view<'grant>( pub fn evaluate_action_coverage( authority: &EffectiveAuthority, action: &ActionEnvelope, + expression: ProfileBudgetExpression, ) -> CoverageDecision { evaluate_action_coverage_view( authority_state_view(authority), action_authority_view(action), + expression, ) } @@ -311,6 +313,7 @@ pub fn evaluate_action_coverage( pub fn evaluate_action_coverage_view( authority: AuthorityStateView<'_>, action: ActionAuthorityView<'_>, + expression: ProfileBudgetExpression, ) -> CoverageDecision { // Terminal coverage is the same chain claim as a delegation edge with the // actor in the issuer position: an authority that never descended from the @@ -339,7 +342,11 @@ pub fn evaluate_action_coverage_view( if !action_constraint_allows(authority.action_constraint, action.canonical_body_digest) { return CoverageDecision::Denied(DenialReason::ActionConstraintMismatch); } - if !optional_budget_covers(authority.budget_ceiling, action.requested_budget) { + if !budget_ceiling_covers_action( + authority.budget_ceiling, + action.requested_budget, + expression, + ) { return CoverageDecision::Denied(DenialReason::BudgetCeilingExceeded); } CoverageDecision::Authorized @@ -423,8 +430,17 @@ impl EffectiveAuthority { /// # Errors /// /// Returns the first stable authority failure in protocol order. - pub fn authorizes(&self, action: &ActionEnvelope) -> Result<(), DenialReason> { - match evaluate_action_coverage(self, action) { + /// `expression` states whether the action's profile is able to declare a + /// requested budget at all; the caller resolves it from its trusted + /// registry selection. [`ProfileBudgetExpression::Expressible`] — the + /// default — keeps an absent request unknown and therefore uncovered by a + /// bounded ceiling. + pub fn authorizes( + &self, + action: &ActionEnvelope, + expression: ProfileBudgetExpression, + ) -> Result<(), DenialReason> { + match evaluate_action_coverage(self, action, expression) { CoverageDecision::Authorized => Ok(()), CoverageDecision::Denied(reason) => Err(reason), } @@ -652,7 +668,7 @@ mod tests { extensions: None, }; assert_eq!( - evaluate_action_coverage_view(unrooted, action), + evaluate_action_coverage_view(unrooted, action, ProfileBudgetExpression::Expressible), CoverageDecision::Denied(DenialReason::BrokenGrantChain) ); } @@ -693,7 +709,11 @@ mod tests { terminal_grant: None, }; assert_eq!( - evaluate_action_coverage_view(authority_state_view(&authority), action), + evaluate_action_coverage_view( + authority_state_view(&authority), + action, + ProfileBudgetExpression::Expressible + ), CoverageDecision::Denied(DenialReason::BudgetCeilingExceeded), "a bounded ceiling must not authorize an unbounded (absent) request" ); @@ -730,11 +750,76 @@ mod tests { ..action }; assert_eq!( - evaluate_action_coverage_view(authority_state_view(&authority), bounded), + evaluate_action_coverage_view( + authority_state_view(&authority), + bounded, + ProfileBudgetExpression::Expressible + ), CoverageDecision::Authorized ); assert_eq!( - evaluate_action_coverage_view(authority_state_view(&authority), action), + evaluate_action_coverage_view( + authority_state_view(&authority), + action, + ProfileBudgetExpression::Expressible + ), + CoverageDecision::Denied(DenialReason::BudgetCeilingExceeded) + ); + } + + /// The mirror of the two tests above: the *only* thing that changes is what + /// the caller says about the action's profile, and the verdict flips. + /// + /// An action whose profile cannot express a budget provably spends zero, so + /// every ceiling covers it. Without this the whole class of budget-free + /// profiles — `auths.mcp/1` among them — is unconstructible under any + /// bounded grant chain. + #[test] + fn terminal_coverage_authorizes_an_absent_request_for_a_budget_free_profile() { + let anchor = anchor_with_budget(Some(numeric_budget(10))); + let authority = EffectiveAuthority::from_anchor(&anchor); + let actor = PrincipalId::parse("did:key:root").expect("root"); + let selected = profile("profile-a"); + let permission = auths_model::Permission::new( + CapabilityId::parse("deploy").expect("capability"), + ResourceId::parse("cluster://production").expect("resource"), + ); + let audience = Audience::parse("cluster://production").expect("audience"); + let action = ActionAuthorityView { + profile: &selected, + canonical_body_digest: auths_model::Digest::new([0; 32]), + permission: &permission, + requested_budget: None, + audience: &audience, + validity: anchor.validity(), + actor: &actor, + terminal_grant: None, + }; + for ceiling in [0, 10, u64::MAX] { + let anchor = anchor_with_budget(Some(numeric_budget(ceiling))); + let authority = EffectiveAuthority::from_anchor(&anchor); + assert_eq!( + evaluate_action_coverage_view( + authority_state_view(&authority), + action, + ProfileBudgetExpression::Inexpressible + ), + CoverageDecision::Authorized, + "zero spend is within ceiling {ceiling}" + ); + } + // The capability only reclassifies an *absent* request. A declared + // request is still compared against the ceiling by the algebra. + let over = numeric_budget(11); + assert_eq!( + evaluate_action_coverage_view( + authority_state_view(&authority), + ActionAuthorityView { + requested_budget: Some(&over), + ..action + }, + ProfileBudgetExpression::Inexpressible + ), CoverageDecision::Denied(DenialReason::BudgetCeilingExceeded) ); } diff --git a/core/crates/auths-codec/src/decode.rs b/core/crates/auths-codec/src/decode.rs index f6f03965..d238b949 100644 --- a/core/crates/auths-codec/src/decode.rs +++ b/core/crates/auths-codec/src/decode.rs @@ -948,7 +948,7 @@ fn registries( limits: &VerifierLimits, ) -> Result { let maximum = limits.get(LimitKind::RegistryEntries); - map(decoder, 13)?; + map(decoder, 14)?; key(decoder, 0)?; let manifest_id = digest_id!(decoder, RegistryManifestId)?; key(decoder, 1)?; @@ -975,6 +975,8 @@ fn registries( let profiles = profile_refs(decoder, maximum)?; key(decoder, 12)?; let profile_policies = parsed_texts(decoder, maximum, ProfilePolicyId::parse)?; + key(decoder, 13)?; + let budget_free_profiles = profile_refs(decoder, maximum)?; AcceptedRegistries::new( manifest_id, principal_methods, @@ -990,6 +992,7 @@ fn registries( profiles, profile_policies, ) + .and_then(|registries| registries.with_budget_free_profiles(budget_free_profiles)) .map_err(CodecError::from) } diff --git a/core/crates/auths-codec/src/encode.rs b/core/crates/auths-codec/src/encode.rs index efbf86f0..346cd413 100644 --- a/core/crates/auths-codec/src/encode.rs +++ b/core/crates/auths-codec/src/encode.rs @@ -1255,7 +1255,7 @@ fn encode_registries( encoder: &mut V1Encoder, registries: &AcceptedRegistries, ) -> Result<(), CodecError> { - map(encoder, 13)?; + map(encoder, 14)?; key(encoder, 0)?; bytes(encoder, registries.manifest_id().as_bytes())?; key(encoder, 1)?; @@ -1289,6 +1289,11 @@ fn encode_registries( } key(encoder, 12)?; encode_registry_ids(encoder, registries.profile_policies(), |id| id.as_str())?; + key(encoder, 13)?; + array(encoder, registries.budget_free_profiles().len())?; + for profile in registries.budget_free_profiles() { + encode_profile_ref(encoder, profile)?; + } Ok(()) } diff --git a/core/crates/auths-model/src/lib.rs b/core/crates/auths-model/src/lib.rs index 24478e1b..d13e82ec 100644 --- a/core/crates/auths-model/src/lib.rs +++ b/core/crates/auths-model/src/lib.rs @@ -945,6 +945,54 @@ pub fn optional_budget_covers( } } +/// Whether a profile's canonical actions can express a requested budget. +/// +/// An absent `requested_budget` means two different things, and only the +/// profile knows which: +/// +/// * [`Expressible`](Self::Expressible) — the profile's canonical actions +/// *could* have carried a budget and this one did not. The spend is +/// **unknown**, so a bounded ceiling has nothing to bound and cannot cover +/// the action. +/// * [`Inexpressible`](Self::Inexpressible) — the profile's canonical body has +/// no budget field, so no action of this profile can ever declare one. The +/// spend is **provably zero**, and zero is within every ceiling. +/// +/// The default is [`Expressible`](Self::Expressible): a profile whose +/// capability was never declared is treated as the denying case. Absence of a +/// declaration must never open the gate. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub enum ProfileBudgetExpression { + /// Canonical actions of this profile can carry a requested budget. + #[default] + Expressible, + /// Canonical actions of this profile cannot carry a requested budget. + Inexpressible, +} + +/// Applies target-V1 terminal coverage to one action of a known profile. +/// +/// This is [`optional_budget_covers`] — the pure ceiling algebra — plus the one +/// fact the algebra cannot see: whether the action's profile is *able* to state +/// a budget at all. When it is not, the action provably spends zero and every +/// ceiling covers it. When it is, an absent request states no bound and a +/// bounded ceiling denies, exactly as before. +/// +/// The profile capability only ever reclassifies an **absent** request. A +/// declared request is always compared against the ceiling by the algebra. +#[must_use] +pub fn budget_ceiling_covers_action( + ceiling: Option<&BudgetCeiling>, + requested: Option<&BudgetCeiling>, + expression: ProfileBudgetExpression, +) -> bool { + match (requested, expression) { + // Zero spend, and zero is within every ceiling including an absent one. + (None, ProfileBudgetExpression::Inexpressible) => true, + _ => optional_budget_covers(ceiling, requested), + } +} + /// Non-zero maximum age for a required status observation. #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct FreshnessLimit(u64); @@ -3622,6 +3670,7 @@ pub struct AcceptedRegistries { critical_extensions: Vec, profiles: Vec, profile_policies: Vec, + budget_free_profiles: Vec, } impl AcceptedRegistries { @@ -3672,9 +3721,45 @@ impl AcceptedRegistries { critical_extensions, profiles, profile_policies: canonical_registry_ids(profile_policies, true)?, + // Safe default: no accepted profile is declared budget-free, so + // every absent request is treated as an unknown spend and denied + // under a bounded ceiling. + budget_free_profiles: Vec::new(), }) } + /// Declares which accepted profiles cannot express a requested budget. + /// + /// A profile listed here has no budget field in its canonical body, so any + /// of its actions provably spends zero and is covered by every terminal + /// ceiling. Every other accepted profile keeps the denying reading of an + /// absent request. + /// + /// # Errors + /// + /// Returns [`ModelError::InvalidRegistrySelection`] when the list exceeds + /// [`HARD_MAX_REGISTRY_ENTRIES`] or names a profile this registry does not + /// accept. A declaration about a profile the verifier will refuse anyway is + /// a configuration error, not a silently ignored entry. + pub fn with_budget_free_profiles( + mut self, + mut budget_free_profiles: Vec, + ) -> Result { + if budget_free_profiles.len() > HARD_MAX_REGISTRY_ENTRIES { + return Err(ModelError::InvalidRegistrySelection); + } + budget_free_profiles.sort(); + budget_free_profiles.dedup(); + if budget_free_profiles + .iter() + .any(|profile| !self.accepts_profile(profile)) + { + return Err(ModelError::InvalidRegistrySelection); + } + self.budget_free_profiles = budget_free_profiles; + Ok(self) + } + /// Returns the pinned registry-manifest identifier. #[must_use] pub const fn manifest_id(&self) -> RegistryManifestId { @@ -3829,6 +3914,27 @@ impl AcceptedRegistries { self.profile_policies.binary_search(policy).is_ok() } + /// Returns profiles declared unable to express a requested budget. + #[must_use] + pub fn budget_free_profiles(&self) -> &[ProfileRef] { + &self.budget_free_profiles + } + + /// Resolves what an absent requested budget means for one exact profile. + /// + /// Returns [`ProfileBudgetExpression::Inexpressible`] only for a profile + /// this registry explicitly declares budget-free. Every other profile — + /// including one this registry has never heard of — resolves to + /// [`ProfileBudgetExpression::Expressible`], the denying reading. + #[must_use] + pub fn profile_budget_expression(&self, profile: &ProfileRef) -> ProfileBudgetExpression { + if self.budget_free_profiles.binary_search(profile).is_ok() { + ProfileBudgetExpression::Inexpressible + } else { + ProfileBudgetExpression::Expressible + } + } + /// Returns the largest accepted registry collection. #[must_use] pub fn maximum_entry_count(&self) -> usize { @@ -3845,6 +3951,7 @@ impl AcceptedRegistries { self.critical_extensions.len(), self.profiles.len(), self.profile_policies.len(), + self.budget_free_profiles.len(), ] .into_iter() .max() @@ -4899,6 +5006,141 @@ mod tests { } } + /// The profile capability decides what an *absent* request means, and + /// nothing else. + /// + /// Both readings are exercised on the same inputs so neither arm can be a + /// constant: only the third argument differs between the two loops. + #[test] + fn only_a_budget_free_profile_makes_an_absent_request_a_zero_spend() { + let requested = numeric_budget(5); + let ceilings = [ + numeric_budget(0), + numeric_budget(5), + numeric_budget(u64::MAX), + ]; + + for ceiling in &ceilings { + // (i) the profile could have declared a budget and did not: the + // spend is unknown, so a bounded ceiling cannot cover it. + assert!( + !budget_ceiling_covers_action( + Some(ceiling), + None, + ProfileBudgetExpression::Expressible + ), + "unknown spend must stay denied under ceiling {}", + ceiling.value() + ); + // (ii) the profile cannot declare a budget at all: the spend is + // provably zero and zero is within every ceiling. + assert!( + budget_ceiling_covers_action( + Some(ceiling), + None, + ProfileBudgetExpression::Inexpressible + ), + "zero spend must be covered by ceiling {}", + ceiling.value() + ); + } + + // A declared request is never reclassified: both readings agree with + // the ceiling algebra, in both directions. + for expression in [ + ProfileBudgetExpression::Expressible, + ProfileBudgetExpression::Inexpressible, + ] { + assert!(budget_ceiling_covers_action( + Some(&numeric_budget(5)), + Some(&requested), + expression + )); + assert!(!budget_ceiling_covers_action( + Some(&numeric_budget(4)), + Some(&requested), + expression + )); + // An unbounded ceiling covers everything under either reading. + assert!(budget_ceiling_covers_action(None, None, expression)); + assert!(budget_ceiling_covers_action( + None, + Some(&requested), + expression + )); + } + } + + /// A budget-free declaration reaches exactly the profiles it names. + #[test] + fn budget_free_declaration_is_exact_and_must_name_an_accepted_profile() { + let declared = ProfileRef::new(ProfileId::parse("auths.mcp").unwrap(), 1).unwrap(); + let other_version = ProfileRef::new(ProfileId::parse("auths.mcp").unwrap(), 2).unwrap(); + let other_id = ProfileRef::new(ProfileId::parse("auths.records").unwrap(), 1).unwrap(); + let unaccepted = ProfileRef::new(ProfileId::parse("auths.absent").unwrap(), 1).unwrap(); + let registries = AcceptedRegistries::new( + RegistryManifestId::new([0x11; 32]), + vec![PrincipalMethodId::parse("raw-key-v1").unwrap()], + vec![SignatureSuiteId::parse("ed25519-v1").unwrap()], + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + vec![ResourceMatcherId::parse("uri-namespace-v1").unwrap()], + Vec::new(), + Vec::new(), + vec![declared.clone(), other_version.clone(), other_id.clone()], + vec![ProfilePolicyId::parse("exact-v1").unwrap()], + ) + .expect("registries"); + + // Undeclared: every accepted profile keeps the denying reading. + for profile in [&declared, &other_version, &other_id, &unaccepted] { + assert_eq!( + registries.profile_budget_expression(profile), + ProfileBudgetExpression::Expressible + ); + } + + let registries = registries + .with_budget_free_profiles(vec![declared.clone()]) + .expect("declaration"); + assert_eq!( + registries.profile_budget_expression(&declared), + ProfileBudgetExpression::Inexpressible + ); + // A different version of the same identifier is a different profile. + for profile in [&other_version, &other_id, &unaccepted] { + assert_eq!( + registries.profile_budget_expression(profile), + ProfileBudgetExpression::Expressible, + "declaration must not leak to {profile:?}" + ); + } + + // Declaring a profile the registry does not accept is a configuration + // error, not a silently ignored entry. + assert_eq!( + registries.with_budget_free_profiles(vec![unaccepted]), + Err(ModelError::InvalidRegistrySelection) + ); + } + + /// The undeclared profile must land on the denying reading. + #[test] + fn the_default_profile_budget_expression_denies_an_absent_request() { + assert_eq!( + ProfileBudgetExpression::default(), + ProfileBudgetExpression::Expressible + ); + assert!(!budget_ceiling_covers_action( + Some(&numeric_budget(10)), + None, + ProfileBudgetExpression::default() + )); + } + #[test] fn action_constraint_constructor_matrix_is_a_preorder() { let any = ActionConstraint::AnyBody; diff --git a/core/crates/auths-verifier/src/lib.rs b/core/crates/auths-verifier/src/lib.rs index d0216e6a..a736686f 100644 --- a/core/crates/auths-verifier/src/lib.rs +++ b/core/crates/auths-verifier/src/lib.rs @@ -24,10 +24,10 @@ use auths_composition::{ use auths_model::{ ActionId, AssuranceSatisfaction, CanonicalAction, ContextDigest, DenialReason, Digest, EvidenceObject, GrantId, GrantStatusId, ParticipantAssurance, ParticipantRole, PlanId, - PortableVerificationResult, PrincipalId, PrincipalStatusId, ProofBundle, ProofRef, Requirement, - SignatureEnvelope, SignedAction, SignedGrant, StatementRef, StatusPolicy, Timestamp, - TrustAnchor, TrustedContext, VerificationCode, VerificationDecision, VerificationResources, - VerificationStage, VerifierConfigurationId, + PortableVerificationResult, PrincipalId, PrincipalStatusId, ProfileBudgetExpression, + ProofBundle, ProofRef, Requirement, SignatureEnvelope, SignedAction, SignedGrant, StatementRef, + StatusPolicy, Timestamp, TrustAnchor, TrustedContext, VerificationCode, VerificationDecision, + VerificationResources, VerificationStage, VerifierConfigurationId, }; use auths_ports::{ ControlEvidence, ControlPurpose, PrincipalControlError, PrincipalControlInput, ProfileDecision, @@ -2248,7 +2248,12 @@ fn verify_branch_from_anchor( evaluate_extensions(grant.statement().extensions(), context, registries, meter)?; } authority - .authorizes(action.envelope()) + .authorizes( + action.envelope(), + context + .accepted_registries() + .profile_budget_expression(action.envelope().profile()), + ) .map_err(VerificationFailure::Denied)?; let action_control = control_for(controlled, StatementRef::Action(action_id))?; reports.push(participant_report( @@ -2541,16 +2546,23 @@ fn validate_budget_constraints( parent = child; } if let Some(ceiling) = parent { - // A bounded terminal authority requires a bounded request. An action - // that declares no budget is NOT vacuously covered: it would spend an - // unbounded amount under a ceiling that the verifier could never - // compare against, so it is denied. - let requested = action - .envelope() - .requested_budget() - .ok_or(VerificationFailure::Denied( - DenialReason::BudgetCeilingExceeded, - ))?; + // A bounded terminal authority requires a bounded request *when the + // action's profile is able to state one*. An action of a profile that + // could have declared a budget and did not states no bound at all, so + // there is nothing for the ceiling to bound and it is denied. An action + // of a profile whose canonical body has no budget field provably spends + // zero, and zero is within every ceiling. + let expression = context + .accepted_registries() + .profile_budget_expression(action.envelope().profile()); + let Some(requested) = action.envelope().requested_budget() else { + return match expression { + ProfileBudgetExpression::Inexpressible => Ok(()), + ProfileBudgetExpression::Expressible => Err(VerificationFailure::Denied( + DenialReason::BudgetCeilingExceeded, + )), + }; + }; let algebra = registries .budget_algebra(context.accepted_registries(), ceiling.algebra()) .ok_or(VerificationFailure::Indeterminate( @@ -2958,7 +2970,13 @@ mod tests { .expect("fixture anchor"); let authority = EffectiveAuthority::from_anchor(anchor); assert_eq!( - authority.authorizes(fixture.action.envelope()), + authority.authorizes( + fixture.action.envelope(), + fixture + .context + .accepted_registries() + .profile_budget_expression(fixture.action.envelope().profile()) + ), Err(DenialReason::BudgetCeilingExceeded), "the kernel alone must deny; the Wave 1 guard is defense in depth" ); @@ -2976,7 +2994,13 @@ mod tests { .first() .expect("fixture anchor"); assert_eq!( - EffectiveAuthority::from_anchor(inside_anchor).authorizes(inside.action.envelope()), + EffectiveAuthority::from_anchor(inside_anchor).authorizes( + inside.action.envelope(), + inside + .context + .accepted_registries() + .profile_budget_expression(inside.action.envelope().profile()) + ), Ok(()) ); } @@ -3010,7 +3034,13 @@ mod tests { .first() .expect("fixture anchor"); assert_eq!( - EffectiveAuthority::from_anchor(anchor).authorizes(fixture.action.envelope()), + EffectiveAuthority::from_anchor(anchor).authorizes( + fixture.action.envelope(), + fixture + .context + .accepted_registries() + .profile_budget_expression(fixture.action.envelope().profile()) + ), Ok(()) ); @@ -3032,6 +3062,91 @@ mod tests { ); } + /// Rebuilds a context that declares exactly the given profiles budget-free. + /// + /// Everything else — anchors, registries, policies, limits — is carried + /// across unchanged, so a verdict that differs between the original and the + /// rebuilt context can only be caused by this one declaration. + fn declaring_budget_free( + context: &TrustedContext, + profiles: Vec, + ) -> TrustedContext { + TrustedContext::new( + context.configuration(), + context.composition(), + context.trust_anchors().to_vec(), + context + .accepted_registries() + .clone() + .with_budget_free_profiles(profiles) + .expect("budget-free declaration"), + context.expected_audience().clone(), + context.expected_challenge().clone(), + context.evaluation_time(), + context.assurance_policy().clone(), + context.principal_status_snapshot().clone(), + context.grant_status_snapshot().clone(), + context.resource_matcher().clone(), + context.profile_policy().clone(), + context.channel_policy().clone(), + context.limits().clone(), + ) + .expect("rebuilt context") + } + + /// The v1.0 blocker: a profile whose canonical body has no budget field is + /// unusable under any bounded grant chain unless the verifier is told so. + /// + /// `bounded_ceiling_denies_an_action_that_requests_no_budget` above proves + /// the denial for a profile that *can* express a budget, and must stay + /// green. This drives byte-identical proof material through a context that + /// differs in exactly one declaration, and the verdict flips. + #[test] + fn a_budget_free_profile_authorizes_under_a_bounded_ceiling() { + let fixture = target_fixture_with_budget(false, Some(numeric_ceiling(10_000)), None); + let method = RawKeyMethod::new().unwrap(); + let suite = Ed25519Suite::new().unwrap(); + let methods: [&dyn auths_ports::PrincipalMethod; 1] = [&method]; + let suites: [&dyn auths_ports::SignatureSuite; 1] = [&suite]; + let registries = ImmutableRegistries::new(&methods, &suites).unwrap(); + + // Undeclared: the action states no bound at all, so it is denied. + assert_eq!( + verify( + &fixture.bytes, + &fixture.canonical, + &fixture.context, + ®istries, + ), + VerificationOutcome::Denied(DenialReason::BudgetCeilingExceeded) + ); + + // Declared budget-free: the same action provably spends zero. + let declared = declaring_budget_free( + &fixture.context, + vec![fixture.action.envelope().profile().clone()], + ); + let outcome = verify(&fixture.bytes, &fixture.canonical, &declared, ®istries); + assert!( + matches!(outcome, VerificationOutcome::Authorized(_)), + "a budget-free profile spends zero, which every ceiling covers: {outcome:?}" + ); + + // The kernel must reach the same answer with the verifier's own + // `validate_budget_constraints` guard bypassed, so correctness cannot + // rest on which of the two runs first. + let anchor = declared.trust_anchors().first().expect("fixture anchor"); + assert_eq!( + EffectiveAuthority::from_anchor(anchor).authorizes( + fixture.action.envelope(), + declared + .accepted_registries() + .profile_budget_expression(fixture.action.envelope().profile()) + ), + Ok(()) + ); + } + /// An unbounded authority is still the top scope: it does not require the /// action to declare a budget. #[test] diff --git a/core/fixtures/v1/denied/action-actor-mismatch.context.cbor b/core/fixtures/v1/denied/action-actor-mismatch.context.cbor index 2b5aedeba1b3f12a9e05eafc2f8e662f741e3d54..01cb4609661ca5be6e2c8d70a32791151e379dd3 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/action-actor-mismatch.result.cbor b/core/fixtures/v1/denied/action-actor-mismatch.result.cbor index 783eb648448d2cfdc0e6eb817b614677ac29ee7e..e9aad2b9bdb5093da34c48184cbf6aa416e8dd73 100644 GIT binary patch delta 97 zcmV-n0G|KU0@MPKW+0+%4RXHyTlW$-E|qKGa^zsy3;KL-*q!8BpGGDV^-__BCNqEK z>1V1_!P!`C8>zIpbVL^}2<8#1$tbHDhFQ;i8Tbf*34jWK3#R}X1;YUtkOCP5h>_oQ D*e@#h delta 97 zcmV-n0G|KU0@MPKW*|~XM(nzh$Kltwc{f9mU4!>E-V55N&DcIT!8vt>kdt2f-+w_Vo5u1wF*u4uC3D^jL34jWK3#R}X1+4)XdIA{)h>_%V D1(7Tc delta 97 zcmV-n0G|KZ0@(tPYamicM(nzh$Kltwc{f9mU4!>E-V55N&DcIT!8vt34jWK3#R}X1+4)XdIA{)hLPlT DjfyBJ diff --git a/core/fixtures/v1/denied/action-budget-exceeded.context.cbor b/core/fixtures/v1/denied/action-budget-exceeded.context.cbor index 2b5aedeba1b3f12a9e05eafc2f8e662f741e3d54..01cb4609661ca5be6e2c8d70a32791151e379dd3 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/action-budget-exceeded.result.cbor b/core/fixtures/v1/denied/action-budget-exceeded.result.cbor index 6d7069ee0174fc9850b9fb1d1bcd2ce70ad173a9..de574642a52f12011d34f67aafaa927acb8b5ac4 100644 GIT binary patch delta 97 zcmV-n0G|KZ0@(tPYapU+4RXHyTlW$-E|qKGa^zsy3;KL-*q!8BpGGDV^-__GCNr;w z{Ql_$7JH;-g*)e=bgz{>FTvLke~kkd;w6AHt?LMY34jWK3#R}X1;YUtkOCP5h>_%V D@Zu|b delta 97 zcmV-n0G|KZ0@(tPYamicM(nzh$Kltwc{f9mU4!>E-V55N&DcIT!8vt_rQ Dniwg{ delta 97 zcmV-n0G|KU0@MPKXCP8YM(nzh$Kltwc{f9mU4!>E-V55N&DcIT!8vtHAn!T|Av;33j9H(!dEPre>h6V@S`_!_@Z DTcR#j delta 97 zcmV-n0G|Kd0^I_TZy-`gM(nzh$Kltwc{f9mU4!>E-V55N&DcIT!8vtkh3JxMnjis+Ip=T41MV^-xgqCe)tH034jWK3#R}X1;haukpdY6hLPxX DAV@3+ diff --git a/core/fixtures/v1/denied/action-permission-not-granted.context.cbor b/core/fixtures/v1/denied/action-permission-not-granted.context.cbor index 2b5aedeba1b3f12a9e05eafc2f8e662f741e3d54..01cb4609661ca5be6e2c8d70a32791151e379dd3 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/action-permission-not-granted.result.cbor b/core/fixtures/v1/denied/action-permission-not-granted.result.cbor index 37460fad17b36afaaf04135d0c1f48eb23494826..9d2a12ef25c94aa3b9502429f2f6faa7ea41e52f 100644 GIT binary patch delta 114 zcmV-&0FD3H0@wnOY9OL*4RXHyTlW$-E|qKGa^zsy3;KL-*q!8BpGGDV^-__FCO~>B zIfup)(4SB9+c#;JfLSwE;rd+x`MiY3@>$L^b|VOY34jWK3#R}X1;qgvk^&h7hyw-$ U0R;gD838p6SRj${ACnIONxpq8Qvd(} delta 114 zcmV-&0FD3H0@wnOY9LZbM(nzh$Kltwc{f9mU4!>E-V55N&DcIT!8vt!vx_<<+`!|Keh;f34jWK3#R}X1;POskOCP5h>_%V D1PLsK delta 97 zcmV-n0G|KZ0@(tPYamicM(nzh$Kltwc{f9mU4!>E-V55N&DcIT!8vtEqe3pOJCh<^zmd7@2q*HkUBZU;+SM@CVKS delta 19 bcmeC<>EYS1pOJCx<^zmd7&n(O&tL)oOBDw| diff --git a/core/fixtures/v1/denied/active-grant-status.result.cbor b/core/fixtures/v1/denied/active-grant-status.result.cbor index 27ba1a9bdc14908e70193a4c57f9157c67a4aa95..3202a318153fe621524f22309acb5c53fb2d2449 100644 GIT binary patch delta 95 zcmV-l0HFVp2$BepULfv4Bs@<%#d7&QiXrtiaHtT0y_xh{*=~>J4S=fnt)vU0{%2-p?=sz6_Dbm5??ZiXJm)M>;Id%!84KCNqOS zIUgUoz%Igo{oAYYNG@I)B3E)LxNtIYimam$!CMG`34jWK3#R}X1-}6pkOCP5h>_rR D$t5Pq delta 97 zcmV-n0G|KV0@VVLXCRk4hCy|tf`YE5_fcKx;pE$Sx5+?tadzqKf20|lHdc{`CNt`P zm@>xEIVzw6^favyq{2IpX;Voxo2$Vt%MHgOgP#b134jWK3#R}X1-}6pkOCP5hLPZP DDpo7e diff --git a/core/fixtures/v1/denied/attachment-duplicate.context.cbor b/core/fixtures/v1/denied/attachment-duplicate.context.cbor index 8e55e2758e17eb32836bc67e753a1b628cc2efed..b4e64474959c542f2da23dde315c75027d3dc3ff 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/attachment-duplicate.result.cbor b/core/fixtures/v1/denied/attachment-duplicate.result.cbor index afb1244430abc0e5bb0540f698e49a9e7a575a0d..1b1407e9c50637c38a7b62ce3ca37791f110d47f 100644 GIT binary patch delta 98 zcmV-o0G9zqeQgo6yRICQ}2Us9x z<||%uej_QuR4oePkcI<1bgatBF+dY$j%xFuRG?`HfC+#KfD5Mp83dC77|;S41c;Hq EbE3*76aWAK delta 98 zcmV-o0GhFRVRs`}B|rCbrnbuKUc^?5D{f=>qa2UsAO z?d;{@y(4KPD~}5^Tjr>(&feqbNxLnw4F(eEgiL`5fC+#KfD5Mp83dC77|;S41cs5o Ea|atMp8x;= diff --git a/core/fixtures/v1/denied/attachment-missing.context.cbor b/core/fixtures/v1/denied/attachment-missing.context.cbor index 8e55e2758e17eb32836bc67e753a1b628cc2efed..b4e64474959c542f2da23dde315c75027d3dc3ff 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/attachment-missing.result.cbor b/core/fixtures/v1/denied/attachment-missing.result.cbor index 7398096768e7db706f9cc3c1f02794a39dfb975e..d93509dafeb2a8c6f5bfbe227a7ec353f6e828ad 100644 GIT binary patch delta 97 zcmV-n0G|KT0@DJJW*}H@3!ynm_EQjlm51;HUq9zqeQgo6yRIHJPCNt4F zSFg+ZI>N`{vBZ23U{Y17ES4|Pd}kJay-i~Y4tEHE34jWK3#R}X1K9x>kOCP5h>_oP D@Y5%G delta 97 zcmV-n0G|KT0@DJJW*`jQ(}`ja!EJt>hFRVRs`}B|rCbrnbuKUc^?5D{f=`i#CNr8N z3qBKbdT{=WTmPRVmpNeQN=;>kOCP5hLPWN Dps*@o diff --git a/core/fixtures/v1/denied/attachment-opaque-denied.context.cbor b/core/fixtures/v1/denied/attachment-opaque-denied.context.cbor index 8e55e2758e17eb32836bc67e753a1b628cc2efed..b4e64474959c542f2da23dde315c75027d3dc3ff 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/attachment-opaque-denied.result.cbor b/core/fixtures/v1/denied/attachment-opaque-denied.result.cbor index f88bc6de4efb52dae7ad2c5c423b7971cbc90edb..164ad4247824a1d37bb88fdd1e5c9ba34f3b9fbd 100644 GIT binary patch delta 97 zcmV-n0G|Kf0^b6Vav)f43!ynm_EQjlm51;HUq9zqeQgo6yRIHJbCNmA+ zAQ4YSg!Nasp`T+jA+~gf(?8Nh{VvXiuVF>!P=pA834jWK3#R}X1K9x>&;l6*h>`1b D*`z2j delta 97 zcmV-n0G|Kf0^b6Vav%)c(}`ja!EJt>hFRVRs`}B|rCbrnbuKUc^?5D{f=`i>CNr*x za>GKj-nxP?l|VEK(NdPu_!-ItPDl&4$>EjmZiong34jWK3#R}X1K9x>&;l6*hLP)Z D=6Ngi diff --git a/core/fixtures/v1/denied/attachment-unused.context.cbor b/core/fixtures/v1/denied/attachment-unused.context.cbor index 8e55e2758e17eb32836bc67e753a1b628cc2efed..b4e64474959c542f2da23dde315c75027d3dc3ff 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/attachment-unused.result.cbor b/core/fixtures/v1/denied/attachment-unused.result.cbor index e97cd5fcef68cfe64becadf320f32b3a8d3aa29f..02a9aaf9135fa570efb30f625f2c88abbf34dafe 100644 GIT binary patch delta 97 zcmV-n0G|Kc0^99zqeQgo6yRIHJYCNq%? zStB9tL!=GyNU0FsrDAq#R_>5B5?y!*mQUqUKy3(s34jWK3#R}X10Mkx&;l6*h>_@Y Dj?^V$ delta 97 zcmV-n0G|Kc0^9hFRVRs`}B|rCbrnbuKUc^?5D{f=`i;CNtV4 zj~;^DkyJq&2c#?DI)jXmkb1dOWD>rOkbhkCsY(cd34jWK3#R}X10Mkx&;l6*hLPxW DlKCl% diff --git a/core/fixtures/v1/denied/attachment-wrong-digest.context.cbor b/core/fixtures/v1/denied/attachment-wrong-digest.context.cbor index 8e55e2758e17eb32836bc67e753a1b628cc2efed..b4e64474959c542f2da23dde315c75027d3dc3ff 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/attachment-wrong-digest.result.cbor b/core/fixtures/v1/denied/attachment-wrong-digest.result.cbor index a44d3ca395706343b46601de4d0950f272acb715..eb832686fb18dfdf2fde24c17c1cc21bdf858029 100644 GIT binary patch delta 97 zcmV-n0G|Kc0^99zqeQgo6yRIHJYCNos9 z#VfCcUDdw|R#qFRClrNJU>?a6-34jWK3#R}X1K9x>&;l6*h>_@Y DhFRVRs`}B|rCbrnbuKUc^?5D{f=`i;CNn00 zRd+ExH?Y1)4q(y(?L2ds(zZx?=;DIXdJN-SN*)M+34jWK3#R}X1K9x>&;l6*hLPxW DkC!SM diff --git a/core/fixtures/v1/denied/attachment-wrong-length.context.cbor b/core/fixtures/v1/denied/attachment-wrong-length.context.cbor index 8e55e2758e17eb32836bc67e753a1b628cc2efed..b4e64474959c542f2da23dde315c75027d3dc3ff 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/attachment-wrong-length.result.cbor b/core/fixtures/v1/denied/attachment-wrong-length.result.cbor index e28d17aaef449281b39656363988b238a81119bf..6869356970c468e9b6be36ac6eef7bfbf844ee01 100644 GIT binary patch delta 97 zcmV-n0G|Kc0^99zqeQgo6yRIHJYCNpbw zAOsnf6^Q6BFt%O%i8F1~o>tk?tGoL8mFhOxfBOi434jWK3#R}X1K9x>&;l6*h>_@Y D1bHee delta 97 zcmV-n0G|Kc0^9hFRVRs`}B|rCbrnbuKUc^?5D{f=`i;CNp*{ zpjbB5>zBfVy===PW^DXwssuv;20YCVynf;JH4q4Z34jWK3#R}X1K9x>&;l6*hLPxW DwRkFG diff --git a/core/fixtures/v1/denied/audience-widening.context.cbor b/core/fixtures/v1/denied/audience-widening.context.cbor index d38af67d6c7fc59679605429a0a04d2866e94611..af542dd574f40ee1408b250a08632faf5fa727a6 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/audience-widening.result.cbor b/core/fixtures/v1/denied/audience-widening.result.cbor index 6a9306c41cfe4b23c41fc91b265fe1318bed7683..fdaed6dd82433a56950545d0b759febafb08faec 100644 GIT binary patch delta 97 zcmV-n0G|KV0@VVLXCMx8xcxVQl>{%2-p?=sz6_Dbm5??ZiXJm)M>;Id%!84KCNshn zCC*&$R)2@^dV;lo+9UIWwJ2!Lwv4DK9VHGFr4tB%34jWK3#R}X1<3&zkOCP5h>_rR D1ZXF( delta 97 zcmV-n0G|KV0@VVLXCRk4hCy|tf`YE5_fcKx;pE$Sx5+?tadzqKf20|lHdc{`CNt{p zx7?1Y839ndCqtA!+e&A_bY3OIp{%2-p?=sz6_Dbm5??ZiXJm)M>;Id%!84KCNut@ zR#MM=$_rR DI1nhv delta 97 zcmV-n0G|KV0@VVLXCRk4hCy|tf`YE5_fcKx;pE$Sx5+?tadzqKf20|lHdc{`CNufK ztI-#!gaTdhd5t1&1*{Eb{O434jWK3#R}X1;YUtkOCP5hLPZP DQkyLB diff --git a/core/fixtures/v1/denied/byte-distinct-action.context.cbor b/core/fixtures/v1/denied/byte-distinct-action.context.cbor index ea4eaf2b7bd2d3a5720233cab962d3adfe68133d..88ee214e4e23a38aa59523927da1fe0c2f93eb1b 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/byte-distinct-action.result.cbor b/core/fixtures/v1/denied/byte-distinct-action.result.cbor index 2cee485a1e642275ac0a4efc3b70b2faf5368d6f..6d121a486b61fc4967911bb2eb4e9938615f2ab5 100644 GIT binary patch delta 97 zcmV-n0G|KV0@VVLXdro;yKU$GQENbG$HHF4$E70k$yw>WMBww+O-KM&HyV+MCNnoq z644~y8!ITA11E*f3VJBS#Xp1fkxGA4sXt3f@Tv%a34jWK3#R}X1;YUtkpdY6h>_uR D#*QiD delta 97 zcmV-n0G|KV0@VVLXduE=A^RIt#rZ`JkBAizX6G6}VI->B=@cfvjfHe|2Xv8$CNuct z2-~JBvQP5n1onnuz|?d^Z;geRe%&(+QD(j#E7Ayn34jWK3#R}X1;YUtkpdY6hLPcP DPBtlP diff --git a/core/fixtures/v1/denied/composition-requirement-not-met.context.cbor b/core/fixtures/v1/denied/composition-requirement-not-met.context.cbor index 4655d84c74be7a749e4392accf0187a118befef8..8ce6d8de86c748732c8196209cfa18e9d3074743 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/composition-requirement-not-met.result.cbor b/core/fixtures/v1/denied/composition-requirement-not-met.result.cbor index dc405a27bf4167fce959a5530ef840833f8497f7..a2e650422c2c2cc794c651053cd4f1502e0cb597 100644 GIT binary patch delta 98 zcmV-o0G1)~bXek>QM8C`6{dQT%MQq){bV`4vUz97fYhL=zyJUM delta 98 zcmV-o0G1)~bXbt4Jv`GUact%+kogtW1anX3V${KRfuP_gcbakZI+X_Y2UsAc zv4n9-Gtz(`pr<5izcojLzXVo4_+D|@6u!4PoZV-hnJ1nyRM8y(WyGjUv34jWK3#R}X2z&t;kOCP5n33Rh DJj*Q4 delta 97 zcmV-n0G|KV0@VVLXCRTc5|w;bwapPiQHTO}20zCW7=3i|8OFKER~L!ttX`3aCNtDw zOeY0Vlf}{0K6XCuVf}ZRs!2qGSkw34jWK3#R}X2z&t;kOCP5mXY9f D{!J)p diff --git a/core/fixtures/v1/denied/depth-widening.context.cbor b/core/fixtures/v1/denied/depth-widening.context.cbor index d38af67d6c7fc59679605429a0a04d2866e94611..af542dd574f40ee1408b250a08632faf5fa727a6 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/depth-widening.result.cbor b/core/fixtures/v1/denied/depth-widening.result.cbor index 256f4c7789294871f769821df0da835382f66d32..877f52a6d565bb00fbbacc580a92aa199db3d730 100644 GIT binary patch delta 97 zcmV-n0G|KV0@VVLXCMx8xcxVQl>{%2-p?=sz6_Dbm5??ZiXJm)M>;Id%!84KCNrNH zX*kz8t3VCr06ikyYlQsr*cQ{f_rR D05~X} delta 97 zcmV-n0G|KV0@VVLXCRk4hCy|tf`YE5_fcKx;pE$Sx5+?tadzqKf20|lHdc{`CNoLu z7-9g5MGByQ@Z5sg9*2Ekt0h|D-06a6LHaTgT>DrTNHTlmBk2v34jWK3#R}X12h2`kOCP5n33Rg Dyly5n delta 97 zcmV-n0G|KU0@MPKXCMubpKiWKp8eEsG!*3qwhKjkmh4iQtPAs`3bc34jWK3#R}X12h2`kOCP5mXY9e Dhfyk9 diff --git a/core/fixtures/v1/denied/grant-status-freshness-beyond.context.cbor b/core/fixtures/v1/denied/grant-status-freshness-beyond.context.cbor index 87e5f528abe3f4eeda8d9e792f93c3372f026be8..5a29d45fd0848fd97bfbbd9892aeda6c5b6a855e 100644 GIT binary patch delta 22 ecmeC->Eqe3pOJCh<^zmd7@2q*HkUBZU;+SM@CVKS delta 19 bcmeC<>EYS1pOJCx<^zmd7&n(O&tL)oOBDw| diff --git a/core/fixtures/v1/denied/grant-status-freshness-beyond.result.cbor b/core/fixtures/v1/denied/grant-status-freshness-beyond.result.cbor index 3b81079525da9fb4ac236b6a49550f1e15252089..19e7efd3597a841487cac058b2f33de7697bf7ad 100644 GIT binary patch delta 97 zcmV-n0G|KO0?q=EU?42Wy9kJsYAyCBpIA*vfJ&lk$K;Itr$(cLj&f^fQ$3M@CNp*c zkF3VwL`*?HYzk~#2+uB)V404W_2yX9Btos1w$ccI34jWK3#R}X1}Fg-kOCP74w2b) DO|>YG delta 97 zcmV-n0G|KO0?q=EU?Ap>TdmI9(2h%8xo83XFGCv)*i0n^q@7uSLz6kEJr&@=%_4m>i@ykf34jWK3#R}X1}Fg-kOCP743XJ& D%+o1_ diff --git a/core/fixtures/v1/denied/grant-status-freshness-boundary.context.cbor b/core/fixtures/v1/denied/grant-status-freshness-boundary.context.cbor index 14197dc365a3b81dd9aa2613a968480aadd072fc..85a7a927fd7548fc14ac94c7bf6c52323c1b2804 100644 GIT binary patch delta 22 ecmeC->Eqe3pOJCh<^zmd7@2q*HkUBZU;+SM@CVKS delta 19 bcmeC<>EYS1pOJCx<^zmd7&n(O&tL)oOBDw| diff --git a/core/fixtures/v1/denied/grant-status-freshness-boundary.result.cbor b/core/fixtures/v1/denied/grant-status-freshness-boundary.result.cbor index cb7a30e8d88e168e682083b27801acaca9694615..b6a8e95fa2b12091cbf2e7d2728ee4ea2a4a5b76 100644 GIT binary patch delta 95 zcmV-l0HFVp2$BepULaRxiB-9K&}9OhijB@$MDvD=2H(1DN+p36YLntGr*x5iCL{tV zzVh0u3=?;MD}w&c6BCK4;`JW@*;Jcm!F~vKzFr7{SRj$n7m?ryvFEh{0S=R$19kTr BDmVZD delta 95 zcmV-l0HFVp2$BepULb-0k{E$sPLqx9pzr%$<9mUdeG!7oI*nC<8w`uh*%6U`CL}7S z?w(27(ye2~qy&Rb1apl&@!Q}scH{s@B{?Pf`1uHdSRj$n7m?ryvFEh{0SuF!19hLi BEeZet diff --git a/core/fixtures/v1/denied/grant-status-sequence-rollback.context.cbor b/core/fixtures/v1/denied/grant-status-sequence-rollback.context.cbor index 0e78d950e9d6184d357f43f4f1cc287c85c6f327..a634b76ef31e30b799873def42f0689dced86ab2 100644 GIT binary patch delta 22 ecmeC->Eqe3pOJCh<^zmd7@2q*HkUBZU;+SM@CVKS delta 19 bcmeC<>EYS1pOJCx<^zmd7&n(O&tL)oOBDw| diff --git a/core/fixtures/v1/denied/grant-status-sequence-rollback.result.cbor b/core/fixtures/v1/denied/grant-status-sequence-rollback.result.cbor index 94254c73f086d324ee65e8d22eda4c824b0719d0..9655f5e524bb10a43a17712b40e03cffcb289d01 100644 GIT binary patch delta 112 zcmV-$0FVE;0=NQ@ZDRo3<2uqV&fqI&u4`l&JUXhJB_wP)S5RK+;$blM&Ep342UsBS z!$tuhM delta 112 zcmV-$0FVE;0=NQ@ZDaLi;E$#R<1F2YF;zi0fCx+P+QK68VBg?syogDxl4J(<2UsBH zCxS3NL83vyQS`_}v#d5V2k)w0ms@w_gkjHazM=&PfC+#KfD5Mp83#lG7?1)P1`GoT S1OWvB1^^6LAd%4@k?%=D4J@7j diff --git a/core/fixtures/v1/denied/invalid-signature.context.cbor b/core/fixtures/v1/denied/invalid-signature.context.cbor index b260567010a67fd9cf385a44af9eea1ec8614bae..836e56acd8e8b4ca41fd40dafa32143e0abec038 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/invalid-signature.result.cbor b/core/fixtures/v1/denied/invalid-signature.result.cbor index 66161bc58c7021d3c0f4b37092f75b5d59f0d87f..623951652a3b1c33e5570f00a5ad5ab35bc0a85d 100644 GIT binary patch delta 97 zcmV-n0G|KS0@4DIWgvv8!Vbo#*$8b)>$224r!luUy?7UUdBt8eoo{Ifl5UZOCNtm0 zC>ib~|I>7ZwY3VLuRIB*`f$EO9CT(;I&)+Zm@Wu_34jWK3#R}X10MkxkOCP5h>_lO DgH$Mb delta 97 zcmV-n0G|KS0@4DIWgv$8y-i^4bD&ftA#Dz+)|WMBww+O-KM&HyV+MCNl`d zs2qek*k%Dpimux8ZsFd_3IfLK;2#k)3`iAIjDHA#34jWK3#R}X1;YUtkOCP5h>_uR D55X$i delta 97 zcmV-n0G|KV0@VVLXduE=A^RIt#rZ`JkBAizX6G6}VI->B=@cfvjfHe|2Xv8$CNtgf z8?)>M>W8!j{F0t}w9RBaR#fWOmAo%d0!y(*nnehJ34jWK3#R}X1;YUtkOCP5hLPcP DP+2K& diff --git a/core/fixtures/v1/denied/missing-grant-status.context.cbor b/core/fixtures/v1/denied/missing-grant-status.context.cbor index 5ed415ec0fe734ec3aa5f13f28ff36db3d996fb1..47bcce847e4ecd84e5bbf4257641d86d45779397 100644 GIT binary patch delta 22 ecmbQkIgfM0en!T1n-4H6A`kOCP5o{`~n Dka8vP delta 97 zcmV-n0G|KV0@VVLXdqPAZ%$LvA1*27OX}Sb`o=NwUqz9KCNnw# zFr+y<4mW1y2~tcj%Vq6#5`rQ!jk36flT3kX(bfon34jWK3#R}X1>6A`kOCP5oRQ&l D_Jb#{ diff --git a/core/fixtures/v1/denied/permission-widening.context.cbor b/core/fixtures/v1/denied/permission-widening.context.cbor index d38af67d6c7fc59679605429a0a04d2866e94611..af542dd574f40ee1408b250a08632faf5fa727a6 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/permission-widening.result.cbor b/core/fixtures/v1/denied/permission-widening.result.cbor index f4c8e4fe9bb04faf7d38ea51261a5c380f8474d1..f9aa73197d41f3b092ffa09a883d9b55ae7ca68d 100644 GIT binary patch delta 97 zcmV-n0G|KV0@VVLXCMx8xcxVQl>{%2-p?=sz6_Dbm5??ZiXJm)M>;Id%!84KCNo1G z5Ha+A5ox?I5x}^aLLUX8jeC2Lxjzq!vT4V{I`;^G34jWK3#R}X1;qgvkOCP5h>_rR DqK77f delta 97 zcmV-n0G|KV0@VVLXCRk4hCy|tf`YE5_fcKx;pE$Sx5+?tadzqKf20|lHdc{`CNsyd z4sO}DuuMic1mGbSk=fE0?)x2#E3@A$uV_n@b_)oA34jWK3#R}X1;qgvkOCP5hLPZP DFs3W~ diff --git a/core/fixtures/v1/denied/plan-action-mismatch.context.cbor b/core/fixtures/v1/denied/plan-action-mismatch.context.cbor index 5bfbcb0d26b6b31569ab418427222599eceb6e3b..b526e93352abf844803ff8e93315a8a57705883d 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/plan-action-mismatch.result.cbor b/core/fixtures/v1/denied/plan-action-mismatch.result.cbor index 2e8177bef4d7787e84fb940333e0b98a2163646d..de9ba5d4f0d94097fb393813e60c50c0ceaef9d2 100644 GIT binary patch delta 98 zcmV-o0G$icqh%x4Y(1yyE|QWI`H9By+^h9B*p5y^}QfC+#KfD5Mp83P{y7?1)P1c;Hq EbFTj>IRF3v delta 98 zcmV-o0GSU8Pb$CslyM(OPmJw2Us8w zjchiryI$QnEOax1C($A)+?oY=!YNh0mZX0FcjWJQ38_7UL4B`JVd`)twhn#hrkCNt4b zbQx$xG0g}KEn#}YEzMH#|D40+K2ZtvG=FM!y+{av34jWK3#R}X0{;ORkOCP5Mv>=q DGX5+z delta 97 zcmV-n0G|Kb0^0(RZXn-0R9`PBdicYnD66L(t{QcT^Oe<26o=BPmXTFO*%W7%r`s(YHm@!hfC)pU`MCNo>i z89X*?h{(&!E93j@R%Wrl)f+IgYd~e|v;|+V6kZ5`34jWK3#R}X1;YUtkOCP5h>_@X D0|qZ! delta 97 zcmV-n0G|Kb0^0(RZy>%`wU^l9k0Xp0xc(oS&|&W#ZRGkQe0-eu#7g!f2!D}}CNo-g zUy14Vx2~H5dW(5eyFi delta 34 ncmeC;?BLvRkdb9ALxjTQi;N_`a Dr$8yj diff --git a/core/fixtures/v1/denied/revoked-grant-status.context.cbor b/core/fixtures/v1/denied/revoked-grant-status.context.cbor index 2680cfd6c135974be41daf442988223ffb25fc06..b27235e01eab4694f239fa9f88660bfe64858316 100644 GIT binary patch delta 22 ecmeC->Eqe3pOJCh<^zmd7@2q*HkUBZU;+SM@CVKS delta 19 bcmeC<>EYS1pOJCx<^zmd7&n(O&tL)oOBDw| diff --git a/core/fixtures/v1/denied/revoked-grant-status.result.cbor b/core/fixtures/v1/denied/revoked-grant-status.result.cbor index f6437705ca184aa7b6adf8ad87238ed64098eca5..7baadfe147e1ad6abcc018d8da3281c04b15c543 100644 GIT binary patch delta 97 zcmV-n0G|KP0?z`FVIbXa=8ZB&_8uylN+hajjJLI$hUHb|Cp-Z*KH0f6oobPRCNrd} zn(}$6VSf6veg!Ygj581OVa4c|%Q56xOTd`D?jQ(&34jWK3#R}X2SfoFkOCP74w2e* D%HJyp delta 97 zcmV-n0G|KP0?z`FVIWWpF8{|RQ+ee>4lT{x%})BIkjk7ya#vs2zIu^($`O%)CNt7j zFRaR*5+(QaoKWp=D;2lq*sW;^4Vkp`#rU#sRI~_y34jWK3#R}X2SfoFkOCP743XM( D=JYGT diff --git a/core/fixtures/v1/denied/revoked-principal-status.context.cbor b/core/fixtures/v1/denied/revoked-principal-status.context.cbor index 6ab03e1a8e3655779f0dbb3141a9bec2eb671191..3ff3515c19e6af950f72569cb69aa3f44b319ae9 100644 GIT binary patch delta 22 ecmaFO^Ok4BT}H-ro9{91WMtxP*gT#23KIZxA_*)2 delta 19 bcmaFM^O|SFT}H;Wo9{91WZXQH`3e&NTuKO@ diff --git a/core/fixtures/v1/denied/revoked-principal-status.result.cbor b/core/fixtures/v1/denied/revoked-principal-status.result.cbor index a3c2a4b87ea8f0da2dfeba9196edeaa5dc5af5ec..2e4d35a289c24e3cc6fe725f8da83eb75605c1dc 100644 GIT binary patch delta 97 zcmV-n0G|KT0@DJJWgr=atFJX-3D}pUW+MqT9NjRgS)k8CScMOq^byz{Wqy%`CNoAS zz{v0c+|+Yu9>rHAAEy1EMZE``s?Z-L-J-XFI5P-<34jWK3#R}X2VwyjkOCP7ZIRw} D>WU|T delta 97 zcmV-n0G|KT0@DJJWguN;9v-lz4@A;A#2pD;otZ^l>>8=Wlj_N6plDVVJk61XCNtqe zsOe3LIu50+NQ+IQ++Bk(Q#9CURA12;3%`7R3;YOx34jWK3#R}X2VwyjkOCP7Ymwe{ D{01k6 diff --git a/core/fixtures/v1/denied/stale-grant-status.context.cbor b/core/fixtures/v1/denied/stale-grant-status.context.cbor index 8f10ae0ab5b811d95162677c5025bd039036fa81..78547ffa28523f6007310536c873e233b8bd023f 100644 GIT binary patch delta 22 ecmbQkIgfM0en!T1n-4H)(v;M&ArP-fs-o}X{b;Q70eWcs%MdaCNns` z6}gql16oDOdQhSP+lntqm^Z*%@@=-{EVDF`>sAPW34jWK3#R}X1>6A`kOCP5o{`yf Dc1tPG delta 97 zcmV-n0G|KN0?h)DU?2mtGXQ`12*l*`pQ0*;{+>%#f=JsZW8=Z5ibfA`=kSq$CNn+R zZPBcXYX1khUhV>y_4laExKf9~B-d0|TU%vj6A`kOCP5oRQgd DA}}p@ diff --git a/core/fixtures/v1/denied/threshold-mixed-denied.context.cbor b/core/fixtures/v1/denied/threshold-mixed-denied.context.cbor index 8be61394401fc0f8f77bf9e7d901de96cbd2b0fb..c9d74738f4a66282ae7897e52454ad45c90ee816 100644 GIT binary patch delta 22 ecmZqSZR6c=o|$po<_pa87@2q*Hs`U5F#-Tx+XqJg delta 19 bcmZqUZQ6A`kOCP5h>_@Y DV8$rQ delta 97 zcmV-n0G|Kc0^9E-V55N&DcIT!8vt34jWK3#R}X1>6A`kOCP5hLPxW Dq;n|c diff --git a/core/fixtures/v1/denied/unsupported-action-profile.context.cbor b/core/fixtures/v1/denied/unsupported-action-profile.context.cbor index 2b5aedeba1b3f12a9e05eafc2f8e662f741e3d54..01cb4609661ca5be6e2c8d70a32791151e379dd3 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/unsupported-action-profile.result.cbor b/core/fixtures/v1/denied/unsupported-action-profile.result.cbor index bfd1d5855df75cda203b841ccd8ae8b32f3068c9..d4d755822f0377e83348f3ce78aea0d900465019 100644 GIT binary patch delta 97 zcmV-n0G|KU0@MPKXCR_&4RXHyTlW$-E|qKGa^zsy3;KL-*q!8BpGGDV^-__CCNtrE zMUJv#>mcw&wpPA1(+F28l}us)2BpHCxnA1DZb34jWK3#R}X1;+sxlmZz9h>_rQ Dl`JUD delta 97 zcmV-n0G|KU0@MPKXCP8YM(nzh$Kltwc{f9mU4!>E-V55N&DcIT!8vt_=X Di-{_` delta 97 zcmV-n0G|Kb0^0(RZXnBLRVXR}wMZZ4?*A`MqY*w3Tq3Jz2;!qwf_$=U<}#6vCNtt@ z)v_E7O>ddSBQ-RELEO!5pYf=tncXVfzZjQUT%8Dj34jWK3#R}X1=s-?kOCP5hLPuV D9X2UV diff --git a/core/fixtures/v1/denied/untrusted-grant-status-issuer.context.cbor b/core/fixtures/v1/denied/untrusted-grant-status-issuer.context.cbor index a0c1812ac6c4dce07d9b4b44629fba3ebe58741a..eda01a001ff2c5bb15d7c45e2c96b1e3af942a1c 100644 GIT binary patch delta 22 ecmeC->Eqe3pOJCh<^zmd7@2q*HkUBZU;+SM@CVKS delta 19 bcmeC<>EYS1pOJCx<^zmd7&n(O&tL)oOBDw| diff --git a/core/fixtures/v1/denied/untrusted-grant-status-issuer.result.cbor b/core/fixtures/v1/denied/untrusted-grant-status-issuer.result.cbor index 41edad8331c8491dd653ae700d488f7f6072880b..db7bdd7945829b8bfaa495394ad7c2ff2cdae2ea 100644 GIT binary patch delta 97 zcmV-n0G|KZ0@(tPYal$745#RP32+8rY8xgwi-{F^ufv%*4Ep^DWXjmz%$<>oCNqO! zuV%tT3J9-|0OC6L-w-uD0`JgLBx%c00l}HM=c)*R34jWK3#R}X1}Fg-kOCP74w2+_ D0bM7m delta 97 zcmV-n0G|KZ0@(tPYardG!PqD>)1$Or`(u~KOSm4Ht6fQyj_k9k82ByXRo#(`CNl~N z?!$S}ATvH@nj}=PHXCNqz& zP(?oKx0F7Y|u#Gg-?1?X4+-@cK8CNl?a z3H3taF!hIWZxJwbOY(xlidTes@qV~J$H3<6w diff --git a/core/fixtures/v1/denied/validity-widening.context.cbor b/core/fixtures/v1/denied/validity-widening.context.cbor index d38af67d6c7fc59679605429a0a04d2866e94611..af542dd574f40ee1408b250a08632faf5fa727a6 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/validity-widening.result.cbor b/core/fixtures/v1/denied/validity-widening.result.cbor index 7a98ac2fc1a86962dd1321f7c68b240d5f4673fc..373861fa5d313914d8afe2b6945a1b3a1cfa1475 100644 GIT binary patch delta 97 zcmV-n0G|KV0@VVLXCMx8xcxVQl>{%2-p?=sz6_Dbm5??ZiXJm)M>;Id%!84KCNo@E zrif>-taz|fV+hNIlq&8D8}CT&QhFQ6RxH?YPL>FO34jWK3#R}X1;YUtkOCP5h>_rR D>Gda* delta 97 zcmV-n0G|KV0@VVLXCRk4hCy|tf`YE5_fcKx;pE$Sx5+?tadzqKf20|lHdc{`CNsg8 zD|=jjBZg8kII=c+-ix?ijNJ40%6(3iBB~HOKa&W634jWK3#R}X1;YUtkOCP5hLPZP DEQ~B7 diff --git a/core/fixtures/v1/denied/verification-method-mismatch.context.cbor b/core/fixtures/v1/denied/verification-method-mismatch.context.cbor index 57bd1f14f989e8956d83920ff211d0a1bafd6413..1b8a0e32380ac4354419fe795d5f019d71912772 100644 GIT binary patch delta 22 ecmaFD@r+}`Tt>!qo98jkV`SoO*lfeB!3Y3mNe6QP delta 19 bcmaFH@q}Z;Tt>#Vo98jkW87@VticEXQG^EN diff --git a/core/fixtures/v1/denied/verification-method-mismatch.result.cbor b/core/fixtures/v1/denied/verification-method-mismatch.result.cbor index 9719cbf3d9c9e708193dd60ccbc0c8545588f4af..ce7bd0c21d4722a4f98ce82b6c093f21311777d5 100644 GIT binary patch delta 97 zcmV-n0G|Ke0^S0UaUiDU;kFt{QeZSmmZo}l?N{$B96r(}Y`sYSM%#>!V#A2&Rd?eZn?R&@w~34jWK3#R}X0}25ckOCP5W|8W2 DaN#JM delta 97 zcmV-n0G|Ke0^S0UaUj|ptFLw{Ylu_PyHDfkrHffYaBzn8?z8kO;Sr))$AFQMCNp>g zE{eS-58lOON!=p;<1kaZiuIAYGGv@dFm7Wkujy=4I0?uOb1c(uV32=G~o4;2W234jWK3#R}X1;YUtkOCP5h>`7c Dmt86$ delta 97 zcmV-n0G|Kg0^kCWbRa@NkTS}}u7OSV$WW2#nD}*hPOMk^9AXH}?OC_=fk=^*CNmc* zGU?h7e)W%|+0{*vR;R5s6vptYE1Ov&Vb6VOoEcVNazNm diff --git a/core/fixtures/v1/denied/wrong-audience.result.cbor b/core/fixtures/v1/denied/wrong-audience.result.cbor index 7b9b472ff89e1944bc72363161eae87a7f10069e..13d2cd36d11fad379fb7aaf843d1c8f88ee8df84 100644 GIT binary patch delta 97 zcmV-n0G|KS0@4DIWgv^{kzL>^_zug7w_Ig0{YT~SuO-ze!+`yu34jWK3#R}X1;YUtkOCP5jFH}S DNA4}G diff --git a/core/fixtures/v1/denied/wrong-challenge.context.cbor b/core/fixtures/v1/denied/wrong-challenge.context.cbor index fe538146c16fc443bfa0cd8f8eddca6f101f7818..7d1e7b99a2bebfd62d1e9df74571392bbf4b12ca 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/denied/wrong-challenge.result.cbor b/core/fixtures/v1/denied/wrong-challenge.result.cbor index 9a5415c71a9cf651a52be87df22d71e43182b3d4..e9e2fd37f36cb25ea183c9502ce35e90d5a324ab 100644 GIT binary patch delta 97 zcmV-n0G|KT0@DJJW*}CRG0<5tSJhvY2gPYpW}Q%;MLxM<*02%u{S$*lc_oP D-cKya delta 97 zcmV-n0G|KT0@DJJW+3qO3s4y}|FnwRlQ8mMZ=o;O&$Z*LblDc=ykaI+O16=PCNu6X zbEQmgHgG1Mf++21XHKy4{Oim)BvMMDnQHQpt;q<034jWK3#R}X1;YUtkOCP5hLPWN DK`AXv diff --git a/core/fixtures/v1/denied/wrong-grant-status-method.context.cbor b/core/fixtures/v1/denied/wrong-grant-status-method.context.cbor index 292de63692dd8c1a739c7c3e98a6205d1d85b8fa..d6510b95f078c341bc4f79e569b0049daac1f08b 100644 GIT binary patch delta 22 ecmeC->Eqe3pOJCh<^zmd7@2q*HkUBZU;+SM@CVKS delta 19 bcmeC<>EYS1pOJCx<^zmd7&n(O&tL)oOBDw| diff --git a/core/fixtures/v1/denied/wrong-grant-status-method.result.cbor b/core/fixtures/v1/denied/wrong-grant-status-method.result.cbor index ae3bb45ce651997cf2b42e56dd27d96da1effab4..c0a26d3bce00715730df778de089bca463b089ee 100644 GIT binary patch delta 114 zcmV-&0FD3H0@wnOY9Mw;gks$7D8SSLCMoZmuzQY>uN_zfb?4hm*(_7Rn-h_WCO~=$ zm0!E)aS&!v@mKX6)xb`KEBHRf>mNIH%dF>q-@?D!TphnCO{@y z#iY>yMZ@jPhtnM^R)u@&uhv-qhGb+6=~J`TNXH0(34jWK3#R}X1}Fg-kOCP736A`kOCP5oRREw DbZ{#E delta 97 zcmV-n0G|Kg0^kCWb0F8~D|rCwkq5sy0QW^zi(x8J5T(+1b^sy|_m~_?1e}qRCNmy2 z5V7Wi#1AnU89dnJrV%0K?mVgA2KN7X_T%4zVsZ$834jWK3#R}X1>6A`kOCP5nvv{u D|AmWgrg=5Wd5cRn8Y@rMJykwPg5=8Belh0yA>~Wks6HfHZfKE?CNutE zB!igak3VmcXdEO^!EK6ma=zN#Nxpdt_wRK57-$H934jWK3#R}X2pItwkOCP6ijn7a DMJ_3B delta 97 zcmV-n0G|Kc0^9GO-AN34jWK3#R}X2pItwkOCP6h>_=Y Dy>liw diff --git a/core/fixtures/v1/indeterminate/did-web-history-without-statement-existence.context.cbor b/core/fixtures/v1/indeterminate/did-web-history-without-statement-existence.context.cbor index 6c06c310d56f7f9131b82bb35f3c88e86246e835..c37adf21606bd0f9ca65decfbfee5dfffe313fdb 100644 GIT binary patch delta 22 ecmaFO@s?x5Tt>!qo98jEV`SoO*zChB&j#Vo98jEW8Cb=EYAo4QyvEj diff --git a/core/fixtures/v1/indeterminate/did-web-history-without-statement-existence.result.cbor b/core/fixtures/v1/indeterminate/did-web-history-without-statement-existence.result.cbor index eed3746c0af6ab03b1dfe65ed003f25d3d5d362d..bdeb6afc39c0fe1c04a5b20b878a74e3072ce17c 100644 GIT binary patch delta 97 zcmV-n0G|Kf0^b6Vav*pV}|4itD|+#Zdl0l^!qIWM~4}kzr+7#7PK%34jWK3#R}X1o;6NkOCP5Ymw`8 DZ$m0) diff --git a/core/fixtures/v1/indeterminate/missing-principal-status.context.cbor b/core/fixtures/v1/indeterminate/missing-principal-status.context.cbor index ed0f415721ffdb5e820c27e0f32cf7932c2d27dd..d24b93e018ed1c0a50231aa8487336a59dbb6569 100644 GIT binary patch delta 22 ecmdnbd609%T}H-ro9{91WMtxP*gTzCgAo8~83-W& delta 19 bcmX@exu0{xT}H;Wo9{91WZXQHS%VP(R9gp= diff --git a/core/fixtures/v1/indeterminate/missing-principal-status.result.cbor b/core/fixtures/v1/indeterminate/missing-principal-status.result.cbor index aa0341e5de22b41945a54304c6506a1a7e88745b..501a2bbeb8657b95223d3f441ad914ee40f23131 100644 GIT binary patch delta 97 zcmV-n0G|Ka0@?zQZ6GHQQ_Fw4UA?^M@GB0_+%B8R(F+t`DMMc8-iOZHkVcV>CNnkr zs2;G*JzIB2Cm?qT#cAv~IOk*9yN@3MtS8kqn{^0)34jWK3#R}X1;YUtkOCP5!I9>4 DoiZvK delta 97 zcmV-n0G|Ka0@?zQZ6Kp1DHa^=ikoFa8yydiMkhnjE8)pF;KpXt&Xxd5t1*#{CNoBw zh5lK(M>mz{A{u5#*A->@g9n;YU<3ec7YES* delta 19 bcmey$@tI@8en!T%n-4H{Gj8@})?fqxR%Hi6 diff --git a/core/fixtures/v1/indeterminate/unsupported-assurance-claim.result.cbor b/core/fixtures/v1/indeterminate/unsupported-assurance-claim.result.cbor index aea0c2f7f8ea9ef58875e5b9f399953adbffd760..454cec80181309a1eec2a0acbfe87b7689d67e02 100644 GIT binary patch delta 97 zcmV-n0G|Ke0^S0Ua3Gt%kKmG@zbF1b8Qw^jlSaj}@*HpCfp{bULl3pRS=Nz|CNl&L z9@#yC^ma1NmfyS0O`-~e&;N3bE8TJ9-uc@Sd?^Tk34jWK3#R}X1;YUtkOCP5b&=_H D76>e( delta 97 zcmV-n0G|Ke0^S0Ua3D@a0Vs$vZv76F`%;y*za%VL=z~F7{ta|)#mZ^Dj=hnPCNrWu zYxAdc-Rjcr4r@Ps^>5?U!}gv$XgAo9PhX|hl diff --git a/core/fixtures/v1/indeterminate/unsupported-budget-algebra.result.cbor b/core/fixtures/v1/indeterminate/unsupported-budget-algebra.result.cbor index e41f309be81387bf969907d4955b7f70574401ca..000e5be60f217551259031aa5f2d4c8f4412cda8 100644 GIT binary patch delta 97 zcmV-n0G|Kc0^9y)z)-gWAHf$8kbH~*v|-n34jWK3#R}X10Dewj{+G4Vv*={ Dt=cKl delta 97 zcmV-n0G|Kc0^9PxI^5WEOsm(MT32RwhHWEL8!L6_&wK!*`tw23rvoZ2Jg+34jWK3#R}X10Dewj{+G4V3Fu_ DzY!}# diff --git a/core/fixtures/v1/indeterminate/unsupported-profile-policy.context.cbor b/core/fixtures/v1/indeterminate/unsupported-profile-policy.context.cbor index 927cbedc276963ac9f03cb3fffe35d14ab710fd2..d9efb7aafcc82dd42f6ef9854bf171afb334d34e 100644 GIT binary patch delta 22 ecmZ3*xrTGYen!T1n-4HNdpF^ diff --git a/core/fixtures/v1/indeterminate/unsupported-resource-matcher.result.cbor b/core/fixtures/v1/indeterminate/unsupported-resource-matcher.result.cbor index 478a0d27eeb40c38489284c7cde6ddef8ba6c7d1..7ae7807052662f07dc275428091d5e285412a4a3 100644 GIT binary patch delta 97 zcmV-n0G|Ke0^S0UaUcOTaKs#|FZmvfm3{qteyKCxo%pxt=T*=ya*wA-HH(pvCNmh7 zfHKc)1m7g2X}Tag-FJ!SNC8{gcxqX2-KIu9y>bYE34jWK3#R}X1;YUtkOCP5j*;qg Dwihaz delta 97 zcmV-n0G|Ke0^S0UaUd<|J^kuQKNN~zc!oD?rHLj5SX;A?Wky~a?kC{C1T2w}CNu0C z4a;=7C~IuBH{zx{OmEliyL;!5R?HPFbS+!jAbtpd34jWK3#R}X1;YUtkOCP5jFIYe D4y7nQ diff --git a/core/fixtures/v1/invalid/bundle-byte-limit-exceeded.context.cbor b/core/fixtures/v1/invalid/bundle-byte-limit-exceeded.context.cbor index 45d2e713a56a32ec85688abac088342e71fd7fc4..7c6686d45e0df3312fd1578772793d50cbde44a1 100644 GIT binary patch delta 22 ecmZqSY~$RpmyvPZ=6#Iw7@2q*Hpeh)FaiKt(FYX( delta 19 bcmZqUY~kFnmyvPp=6#Iw7&pf;YcK);M}`K6 diff --git a/core/fixtures/v1/invalid/bundle-byte-limit-exceeded.result.cbor b/core/fixtures/v1/invalid/bundle-byte-limit-exceeded.result.cbor index 0c9cf95fb620220d167bae387cf60b029ef2bc42..de1073d8410210366ab8c39a284b433387282873 100644 GIT binary patch delta 112 zcmV-$0FVE+0=5E>YhzPwV(SO2w>&lO{J(l?8qICmmP9*>J@Ceik?TuJzb!)m delta 112 zcmV-$0FVE+0=5E>Yh&?aIo^*#IQyvMQQQ?2-qk^?8S-kM^Y3%)sfU+0h=K<82UsAH z24bP-EFuoqI?G3vCcfOZpil|iU#@I~d$_-=wUDz2fC+#KfD5Mp83n@u7?1)P1cUk?Tv*m@i)d diff --git a/core/fixtures/v1/invalid/carried-status-digest-mismatch.context.cbor b/core/fixtures/v1/invalid/carried-status-digest-mismatch.context.cbor index 2680cfd6c135974be41daf442988223ffb25fc06..b27235e01eab4694f239fa9f88660bfe64858316 100644 GIT binary patch delta 22 ecmeC->Eqe3pOJCh<^zmd7@2q*HkUBZU;+SM@CVKS delta 19 bcmeC<>EYS1pOJCx<^zmd7&n(O&tL)oOBDw| diff --git a/core/fixtures/v1/invalid/carried-status-digest-mismatch.result.cbor b/core/fixtures/v1/invalid/carried-status-digest-mismatch.result.cbor index cb2646fb75982b4cc81a77c7c4663641a4e079cf..4e7f89eef59c3b0a60b2c6a78ae18e5f5ec743cc 100644 GIT binary patch delta 98 zcmV-o0G~8~^|S delta 98 zcmV-o0G4lT{x%})BIkjk7ya#vs2zIu^($`Jtg7@2q*Hpeh)F#-Tt`3DOC delta 19 bcmeC+Y~$RpmXUGo=5>tg7&pf;YcT=O&5`oCNt>_ z3A$CiYEA(~aaF95TVbsp`r(^DF}HYo^z34jWK3#R}X1xf)JkOCP5h>_}a Du-qlw delta 97 zcmV-n0G|Ke0^S0UaUdNf%ATg^aEn(V4S!~c$Qj2C)$UD*RPt9{fxY|FDm#&pCNoq5 zYC04GHCL0?lJ6aOOUOp{vCcM(&<+E5B$4s7&o~Hx34jWK3#R}X1xf)JkOCP5hLP%Y DLc=JL diff --git a/core/fixtures/v1/invalid/duplicate-action-object.context.cbor b/core/fixtures/v1/invalid/duplicate-action-object.context.cbor index ea4eaf2b7bd2d3a5720233cab962d3adfe68133d..88ee214e4e23a38aa59523927da1fe0c2f93eb1b 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/invalid/duplicate-action-object.result.cbor b/core/fixtures/v1/invalid/duplicate-action-object.result.cbor index 86aeb81dabae224e4439ba55dac14dc59c258c18..d756764d34eed7fe8b7c64e2798ac4ab7b70ed03 100644 GIT binary patch delta 98 zcmV-o0GWMBww+O-KM&HyQ@^2UsAV zLt2k!txWF~?M)?~Q@7O3z=4@|${!_az=etnHisGrfC+#KfD5Mp83)(_7?1)P1c;Hm Eb2*$Va{vGU delta 98 zcmV-o0GB=@cfvjfHe|2XqGZ2Us8y zr6-{=kuLeV834u*n~lY(QFgh{1JD$qdWf8%jP+XxfC+#KfD5Mp83)(_7?1)P1cs5k EbNgK=3;+NC diff --git a/core/fixtures/v1/invalid/duplicate-cbor-key.context.cbor b/core/fixtures/v1/invalid/duplicate-cbor-key.context.cbor index ea4eaf2b7bd2d3a5720233cab962d3adfe68133d..88ee214e4e23a38aa59523927da1fe0c2f93eb1b 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/invalid/duplicate-cbor-key.result.cbor b/core/fixtures/v1/invalid/duplicate-cbor-key.result.cbor index a9dbb9678d9e8e036a71c6fbd049489fd03b3346..3c64569bf75d96197c777e2ab0929055a4e1bb28 100644 GIT binary patch delta 98 zcmV-o0GWMBww+O-KM&HyQ@^2UsBK zel0(tstgjo4W?~4MYy9~mBCHEWUe*;iw0dDi4<@MfC+#KfD5Mp83n}w7?1)P1c;Hl Ea{!hq8UO$Q delta 98 zcmV-o0GB=@cfvjfHe|2XqGZ2UsAd zqu;#2Eu3YK@>Rqmg`)Z$z^SvWbXxo-S;L_kJuff_fC+#KfD5Mp83n}w7?1)P1cs5j Eb6*uHQ~&?~ diff --git a/core/fixtures/v1/invalid/duplicate-control-binding.context.cbor b/core/fixtures/v1/invalid/duplicate-control-binding.context.cbor index ea4eaf2b7bd2d3a5720233cab962d3adfe68133d..88ee214e4e23a38aa59523927da1fe0c2f93eb1b 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/invalid/duplicate-control-binding.result.cbor b/core/fixtures/v1/invalid/duplicate-control-binding.result.cbor index dbabdbdfb998a9dea628c59414fd12485cd4df40..479abd6421312f4b73eb85b4cc20b7a1739bd0a3 100644 GIT binary patch delta 98 zcmV-o0GWMBww+O-KM&HyQ@^2Us8$ z7jd(jk>lAZE-V%v*ASlT(c8O_k~yqoe>`4QosI+ufC+#KfD5Mp83qpl7?1)P1c;Hm Ea}P->DF6Tf delta 98 zcmV-o0GB=@cfvjfHe|2XqGZ2UsAr z;B|@UrC0r7j05+lrbKP4u2#GSdGfC6W-4g)Rega7fC+#KfD5Mp83qpl7?1)P1cs5k Eb8*}$wEzGB diff --git a/core/fixtures/v1/invalid/evidence-count-over-default.context.cbor b/core/fixtures/v1/invalid/evidence-count-over-default.context.cbor index ea4eaf2b7bd2d3a5720233cab962d3adfe68133d..88ee214e4e23a38aa59523927da1fe0c2f93eb1b 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/invalid/evidence-count-over-default.result.cbor b/core/fixtures/v1/invalid/evidence-count-over-default.result.cbor index 32cf918cdd10ac70a8379024342b3fd517ba0704..f1e4730752e412230c846d60d636d356b97eb67b 100644 GIT binary patch delta 112 zcmV-$0FVE+0=5E>Yh!tvyKU$GQENbG$HHF4$E70k$yw>WMBww+O-KM&HyQ@^2Us98 zf~9bg6o-u;geRGLpeN?)Qa>Hk?TuF#w-Z{ delta 112 zcmV-$0FVE+0=5E>Yh%JxA^RIt#rZ`JkBAizX6G6}VI->B=@cfvjfHe|2XqGZ2UsA0 zc@Azu0LnzYfqz!TD(IuFRsxu3e0!`|ac%VhK1x;yfC+#KfD5Mp85TSN7?1)P1cn0u S1ONp91^^6LAd%4>k?Tw0$cC-7e?N6N#5@XiMI2UsA8 zr4{lw>tcdvdD}=>GK|5tQ%X;~Xs3$p+N6Nfmw8DDfC+#KfD5Mp83RB87?1)P1cs5l EbF^qHCIA2c diff --git a/core/fixtures/v1/invalid/missing-plan-leaf.context.cbor b/core/fixtures/v1/invalid/missing-plan-leaf.context.cbor index 60c56399e22d5a9086b0f1d96f8db4116814d661..cf6ec85f05ff20bb7e7bcc805a4a0e9f96fa9c29 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/invalid/missing-plan-leaf.result.cbor b/core/fixtures/v1/invalid/missing-plan-leaf.result.cbor index e3efd1c247b789e4fd620d15605cde5101238158..3ed2a5fed929ac4d2fb6fc166bb3eb50d3dce76a 100644 GIT binary patch delta 98 zcmV-o0G%2A$`*c-VkZXn2Us9T z&*5u|(A4+69lMybek!n)^bt`;*$|U@byh&wk)aa^fC+#KfD5Mp83RfI7?1)P1c;Hn EbG!~J2mk;8 delta 98 zcmV-o0GT`V!Qb#j*9N8nN1xcF9K@TNeu?}2Us8z zB(pUsM+y`yoigCPy7qwBv$yRJIjB5u_+07-lw0-)fC+#KfD5Mp83RfI7?1)P1cs5l Eb1X9`4*&oF diff --git a/core/fixtures/v1/invalid/missing-principal-evidence.context.cbor b/core/fixtures/v1/invalid/missing-principal-evidence.context.cbor index ea4eaf2b7bd2d3a5720233cab962d3adfe68133d..88ee214e4e23a38aa59523927da1fe0c2f93eb1b 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/invalid/missing-principal-evidence.result.cbor b/core/fixtures/v1/invalid/missing-principal-evidence.result.cbor index d312f588270d5e240934c51971a262e23bafee7c..393e6113e4d7989e6be4f911e99d72af4cad460d 100644 GIT binary patch delta 97 zcmV-n0G|Kc0^9WMBww+O-KM&HyV+TCNnEy zi+zge3cq9{A^_YQk{a<(3ZI0p*YRHj*lJ>9wb2NG34jWK3#R}X1RDVukOCP5h>_@Y D0Bb6( delta 97 zcmV-n0G|Kc0^9@{A^RIt#rZ`JkBAizX6G6}VI->B=@cfvjfHe|2Xv8-CNrtK z22&Q?`YQVyEI(eOETAo1j80M4V%2F_eAp812f_$|34jWK3#R}X1RDVukOCP5hLPxW D;pryK diff --git a/core/fixtures/v1/invalid/non-minimal-integer.context.cbor b/core/fixtures/v1/invalid/non-minimal-integer.context.cbor index ea4eaf2b7bd2d3a5720233cab962d3adfe68133d..88ee214e4e23a38aa59523927da1fe0c2f93eb1b 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/invalid/non-minimal-integer.result.cbor b/core/fixtures/v1/invalid/non-minimal-integer.result.cbor index a3407b589e31766d61c4e5a2bcc9b8168b121f0a..60d467216b6e2e6a053b06bbc6dc533de60fc26e 100644 GIT binary patch delta 98 zcmV-o0G-XIOcgyKU$GQENbG$HHF4$E70k$yw>WMBww+O-KM&HyQ@^2Us9J z{4Z7)``04x`Yh%$oSf5b;`=4&3!f11Q+JO&QC++UfC+#KfD5Mp83n`v7?1)P1c;Hp Eb9f~!9RL6T delta 98 zcmV-o0G-XIR2iA^RIt#rZ`JkBAizX6G6}VI->B=@cfvjfHe|2XqGZ2UsAb zIQ^i&;ef$7@ZZkYhx^R(g};2Xp*sE$get%L4Fuc^Sjhr(~O<*WFTuQrJV-$2Us8& z-x~-!x`F0o3XA2+Px7$=y*eqNm&S@_tDaWJ4A?XXfC+#KfD5Mp83n@u7?1)P1c(Cw S1ONp91^^6LAd%4>k?TwL1T1y{ delta 112 zcmV-$0FVE+0=5E>YhxUYp?@*YPLrTAB+s)i_fqs9gRQkk?Ttv1uU8X diff --git a/core/fixtures/v1/invalid/signature-suite-substitution.context.cbor b/core/fixtures/v1/invalid/signature-suite-substitution.context.cbor index 718d50e28459547b0367065e9217ad4f27a902d6..6ce0d22591a4411ded59184224caa118f085c54e 100644 GIT binary patch delta 22 ecmbQrIh}LEen!T1n-4IqWn|)Q*qqC(!3Y3euLoNI delta 19 bcmbQvIhAw6en!T%n-4IqW!#+4ticEXO2r1v diff --git a/core/fixtures/v1/invalid/signature-suite-substitution.result.cbor b/core/fixtures/v1/invalid/signature-suite-substitution.result.cbor index 120c561e7f305fea3be01ade8f2c2e5a09b71623..4dea14f3ee85f10e63bfbd557db786face5ebff1 100644 GIT binary patch delta 97 zcmV-n0G|Kb0^0(RZ6Fu(Z)GdIrLx3@r0qBG8wxrKVx^G@0bR89C(M#|g+-B#CNl>o zxp|IHmMcdF`Bt>A1VERHF{WMBww+O-KM&HyQ@^2Us8z zU@Hm6jJ!*fKgj00Rd2kV{pxXdDo1z4JVni!if2U#fC+#KfD5Mp83n`v7?1)P1c;Hl Eb345&`~Uy| delta 98 zcmV-o0GB=@cfvjfHe|2XqGZ2Us9X zixr_e!w+i-cBVJNfN#K&_cWx}vXvj0ch2r}p1iaOfC+#KfD5Mp83n`v7?1)P1cs5j Ea~Ucr6aWAK diff --git a/core/fixtures/v1/invalid/unknown-principal-method.context.cbor b/core/fixtures/v1/invalid/unknown-principal-method.context.cbor index fc667fd9606a3ff167d839d85dcbfb72b221869b..cca7124b0e7a257c2bc4b31c9e68913df1831e75 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/invalid/unknown-principal-method.result.cbor b/core/fixtures/v1/invalid/unknown-principal-method.result.cbor index 8795e18f521906769720622127adb938434fed8c..3ca4ba88027253ab503009ee2d69146acefb2679 100644 GIT binary patch delta 97 zcmV-n0G|Ke0^S0UaUgnlX#$(fpOkbb*)(-oXsKW8{o^eBJZvl-6#lVou>p~hCNq;e zf>q#s(oq3{+z4+agAtb`jVsmKyXgR#@jj_rbUz4y34jWK3#R}X1113&kOCP5h>_}a DXs#&6 delta 97 zcmV-n0G|Ke0^S0UaUj!|lzFY{GqI@w9?OFJiTo~$(6ZtntOSPV>0+3|Hs6tvCNl(k z@VfJyG4yu*!3`$fj?dPU`2zbxSXQc~51)gt}KL)m4wHTzz2|3x)-9`b9Y~}CEo~u34jWK3#R}X10?|%kOCP5h>_`Z D;ukAe delta 97 zcmV-n0G|Kd0^I_Ta3Gm4MMi3JFqGZX1e@FtX5B{drr-{~3ix(@a3nBicNdY6CNqOd zP;K&rl!w%6zKcm`7C^2#QaL-yY2Jvp-lx2O04oT934jWK3#R}X10?|%kOCP5hLP!X DKYl1( diff --git a/core/fixtures/v1/invalid/unsupported-evidence-type.context.cbor b/core/fixtures/v1/invalid/unsupported-evidence-type.context.cbor index ea4eaf2b7bd2d3a5720233cab962d3adfe68133d..88ee214e4e23a38aa59523927da1fe0c2f93eb1b 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/invalid/unsupported-evidence-type.result.cbor b/core/fixtures/v1/invalid/unsupported-evidence-type.result.cbor index 96ea124ece3f934366775c8d677e622a74991aef..92d18850db744eed3192d5493e80823760909170 100644 GIT binary patch delta 97 zcmV-n0G|Kb0^0(RZXkJ^yKU$GQENbG$HHF4$E70k$yw>WMBww+O-KM&HyV+SCNsa( zR-i&brLV$V8QCC0oUwix`P2(KgqGBY#+=v_;qnN834jWK3#R}X1o#0MkOCP5h>_=X DYu7FD delta 97 zcmV-n0G|Kb0^0(RZXm)`A^RIt#rZ`JkBAizX6G6}VI->B=@cfvjfHe|2Xv8+CNo}9 zka3{s+vI%##OcF{!MiELxcYXK4-!DO5`}-kBu@x{34jWK3#R}X1o#0MkOCP5hLPuV DS`jJM diff --git a/core/fixtures/v1/invalid/unsupported-protocol.context.cbor b/core/fixtures/v1/invalid/unsupported-protocol.context.cbor index ea4eaf2b7bd2d3a5720233cab962d3adfe68133d..88ee214e4e23a38aa59523927da1fe0c2f93eb1b 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/invalid/unsupported-protocol.result.cbor b/core/fixtures/v1/invalid/unsupported-protocol.result.cbor index 4eb049affa3483b49e161b2ee7b8aa58e9a961c1..17e45b1c146b9d8e440a61583f530a1a7623100e 100644 GIT binary patch delta 98 zcmV-o0GWMBww+O-KM&HyQ@^2Us9i z!Czt=pEGQXC5i!02WT)U2x)~31oXm5=tIG)usC7}fC+#KfD5Mp83n@u7?1)P1c;Hq EbFt7Ui2wiq delta 98 zcmV-o0GB=@cfvjfHe|2XqGZ2UsA! zIF5<7P5gs(Fz-1gXr1D9PvK>e)>ZQ*n9#_z)?+RRfC+#KfD5Mp83n@u7?1)P1cs5o Eb6G7Z{{R30 diff --git a/core/fixtures/v1/invalid/untrusted-did-web-document.context.cbor b/core/fixtures/v1/invalid/untrusted-did-web-document.context.cbor index e4efc654c0b420b73ea1c5c27726909495171764..6d651bbb6b1f77851b5f6b443dc010ab66f23bed 100644 GIT binary patch delta 33 pcmeyw@r7fbEkOCP5bdl$D DHk&P; delta 97 zcmV-n0G|Kb0^0(RZXlHfN<#ci@RwQgg&JX4sd^6nWF^0TquX~;n5JhA$a0a6CNrw= zeudb9#tgq0!{=ZES(L1jTLj$d99?|+(ob)CZsiDo34jWK3#R}X1n>bEkOCP5a*^kB D(SIx1 diff --git a/core/fixtures/v1/invalid/unused-critical-evidence.context.cbor b/core/fixtures/v1/invalid/unused-critical-evidence.context.cbor index ea4eaf2b7bd2d3a5720233cab962d3adfe68133d..88ee214e4e23a38aa59523927da1fe0c2f93eb1b 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/invalid/unused-critical-evidence.result.cbor b/core/fixtures/v1/invalid/unused-critical-evidence.result.cbor index 68f3d1f4110d110ccc21407534fe1e737e99af9a..0ed08cb95bc66147c22a47143c85936413a2122c 100644 GIT binary patch delta 97 zcmV-n0G|KZ0@(tPZ6JA@yKU$GQENbG$HHF4$E70k$yw>WMBww+O-KM&HyV+RCNut7 zk+D(umKyYbF>QBw#;?hlr*(?NhSsjEQ|bNGl)ean34jWK3#R}X21@}LkOCP5h>_-V D!JaS5 delta 97 zcmV-n0G|KZ0@(tPZ6Lx_A^RIt#rZ`JkBAizX6G6}VI->B=@cfvjfHe|2Xv8*CNtzu z17c(JQLGjI1OJ&)Z>fc^Gxam>vcJVfU2xJnPVNYR34jWK3#R}X21@}LkOCP5hLPrT DHUB9? diff --git a/core/fixtures/v1/invalid/unused-grant-object.context.cbor b/core/fixtures/v1/invalid/unused-grant-object.context.cbor index ea4eaf2b7bd2d3a5720233cab962d3adfe68133d..88ee214e4e23a38aa59523927da1fe0c2f93eb1b 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/invalid/unused-grant-object.result.cbor b/core/fixtures/v1/invalid/unused-grant-object.result.cbor index 4c8c72f57e14cdccfc525a26bcf9429ab171d30b..014e5726722c3c1b96d3618e1f49454d5231bc19 100644 GIT binary patch delta 112 zcmV-$0FVE;0=NQ@ZDVWMBww+O-KM&HyQ@^2UsBb z%Wyva3)C7#z^*B3iob2+QkhJb7GCK!H|ODES=zz~fC+#KfD5Mp83#E57?1)P1c(C% S1OWvB1^^6LAd%4@k?%>`YA(|N delta 112 zcmV-$0FVE;0=NQ@ZDYbzA^RIt#rZ`JkBAizX6G6}VI->B=@cfvjfHe|2XqGZ2Us9? zWkLfJj9O{3wm6g&O-!uUaGZY34jWK3#R}X1;YUtkOCP5g#!iz S0R;gD01Q|lk?|gr4FO7Vhb(OX diff --git a/core/fixtures/v1/manifest.json b/core/fixtures/v1/manifest.json index 7d106c69..f7f5c5b2 100644 --- a/core/fixtures/v1/manifest.json +++ b/core/fixtures/v1/manifest.json @@ -144,7 +144,7 @@ }, "context": { "path": "valid/raw-key-chain.context.cbor", - "sha256": "eec5bbbd76e306873c88f2b521b6797e9c9740b0816c35efa906099356e3605a" + "sha256": "70303b0766a62c85f9b5b1b751fa008727abad8f82e7b3757b89d537a24a7f44" }, "canonical_action": { "path": "valid/raw-key-chain.action.cbor", @@ -165,15 +165,15 @@ }, "expected_result": { "path": "valid/raw-key-chain.result.cbor", - "sha256": "0b9aa7c0694bfb30b051c30de3ce26a621047d0354f38b985f29a55ebd637309", + "sha256": "8f0396d6d102c00a78a10fc958c3c067aa7cdba4efa8eade13d92f737cfa71c3", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "c25421fb1b54c5f9450e8f88151066e71a406124aadae91426bf8d8574750774", + "context_digest": "799bbb6de7fe516b4068c7c25ec5c7a522f3c959e9bd44e0f3d74d480057371a", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "db3ca5da5a64740dcc564a387853ad84e7b4a66db8fd79c8df0a47d987cd2c3f", + "result_digest": "16a6054d56804dbb6d4681f60ed92292911c65020142b5d3cbd6736b46fa2dbf", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101" ], @@ -181,7 +181,7 @@ "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -201,7 +201,7 @@ }, "context": { "path": "valid/did-key-root-raw-key-actor.context.cbor", - "sha256": "b358ae91d70a59a9b270bdb4601f0a6e61ce6626ab23282a6cd80174d2508b55" + "sha256": "c60a831e5289491bcfd33bd0c94b90dcf631a3313f07dad6669426fa574a3fb8" }, "canonical_action": { "path": "valid/did-key-root-raw-key-actor.action.cbor", @@ -222,15 +222,15 @@ }, "expected_result": { "path": "valid/did-key-root-raw-key-actor.result.cbor", - "sha256": "5bd2d6c9d05dcbedcd2ff98546dd32fec85a17dcbec1a7fe5692c85b06f38e06", + "sha256": "6742e8a60acd51a578bdc975e8efbbb53f10c293394b5f8d78711b74b85d882b", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "a8576edb58de0c7cbb6ab676dfd9516a772d6bb23fd61530f0e24f0bb180e1fb", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "701e83e53dd576ef74edd61e9338141adc49219f58eea877aa0f820bac61483c", + "context_digest": "61e9189a645bccc3569edd70ec39a958c459c675729f535e6e6b09243ce60daf", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "2ac71f7783b1091084ecdace5e03c2908f2ff7593c4f82fbebb364b2c3ee34bc", + "result_digest": "31da389fa3c89390acaf579dfe6d21af69543b7fbe8ddd7a0b0c89269f154006", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101" ], @@ -238,7 +238,7 @@ "resources": { "proof_bytes": 1543, "action_bytes": 144, - "context_bytes": 1184, + "context_bytes": 1186, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -258,7 +258,7 @@ }, "context": { "path": "valid/raw-key-root-did-key-actor.context.cbor", - "sha256": "1b1696f1698f82a78ba260370f19c8997b57eec8cddda8cf38db5f5f421b9c30" + "sha256": "b1dd8acec92250961e9deb545d0bbe5fd8108e091513abd7245c3e76acaedd93" }, "canonical_action": { "path": "valid/raw-key-root-did-key-actor.action.cbor", @@ -279,15 +279,15 @@ }, "expected_result": { "path": "valid/raw-key-root-did-key-actor.result.cbor", - "sha256": "77cfa29e603762918a391ecf87fffce7db2535daf64818b8779f5df6f931406a", + "sha256": "f2e9733ac0d5627b84703046a2b4bc03a8111e124887eae2bb51b062af0b5198", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "efec3bbe9e9edc37d64e581b199e394e2b0f2a67f34995bfbb8626acd355ea7d", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "480d200f1a24396e7d1a0780fae104c8f77ba46a2323a43166059d1e3b03a382", + "context_digest": "2db8c5ef2605a17562c88c6e23f46f9e6cc3fce360fda724c48ae6a142fdb1a2", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "58aa1a05f1cdd39edfdc6e5859d23aca242441aab8c121a568b77f904347262a", + "result_digest": "986efa435259853dcbecb62a4670343f961fb8e39d773634dcc960f7cb7c9388", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101" ], @@ -295,7 +295,7 @@ "resources": { "proof_bytes": 1545, "action_bytes": 144, - "context_bytes": 1180, + "context_bytes": 1182, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -315,7 +315,7 @@ }, "context": { "path": "valid/did-keri-root-raw-key-actor.context.cbor", - "sha256": "a6fd5c4e3bc1ca76d078f3f3d95a08217284d86aebb7421c4ddec79536ed6a01" + "sha256": "0b47ddd4ef670486ed3361e85ed395f2361827b581acf9e4703146a6b469bb41" }, "canonical_action": { "path": "valid/did-keri-root-raw-key-actor.action.cbor", @@ -336,15 +336,15 @@ }, "expected_result": { "path": "valid/did-keri-root-raw-key-actor.result.cbor", - "sha256": "12be560927163358d88e9cffcdbf5580dbe0bb359712e93c332583a7db7a22d8", + "sha256": "29f48ea0fab483cb3f37bed700b167c54d073bcddf6b520a4b9d149f3d133d00", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "d02248cf037534ca078853f37128f76a64eb23e18bd61c885a503789f88742de", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "0d1076acdad08b176445dee65eede1a9c1844498079762e5b6e2da6dbeeeed37", + "context_digest": "c16b12ce136f13c0e50836baf758b6d09e461eb7dc577efb289ac995a8487859", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "55f538b2dae03a6bf30e445cf3097d6e1b3bdf983d1a6f68b3a3e26bd876a3f2", + "result_digest": "6727b0a4865bdbb703cc660a23f47358d401c438e3d295466834c8b5055a4683", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101" ], @@ -352,7 +352,7 @@ "resources": { "proof_bytes": 2308, "action_bytes": 144, - "context_bytes": 1181, + "context_bytes": 1183, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -372,7 +372,7 @@ }, "context": { "path": "valid/raw-key-root-did-keri-actor.context.cbor", - "sha256": "1e9f9ba03ff97baae7a72bb3a6293b5b361f08d0435c8ee636fb78e6c1562e43" + "sha256": "dc3511d92e8ba45bfc370fea443df1445f4b474ac674e494bf35488cd588e81b" }, "canonical_action": { "path": "valid/raw-key-root-did-keri-actor.action.cbor", @@ -393,15 +393,15 @@ }, "expected_result": { "path": "valid/raw-key-root-did-keri-actor.result.cbor", - "sha256": "3289246b4e1fdbf8fc6e3cc9cb7881b0bc659b5db74dfa12ebd785c48f4f3e3f", + "sha256": "8fabd104c837511ba190991357776a473e0d3972f762c43bd28e87119d20fec7", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "b0db6ddaa9976d0af6cba0f5a6a7c8fc5aab4b80504b17e58ace4aff21c0ad8e", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "cbcbca03739b5e61350ff010f8f32368a6f9c0952bee5ccbdc51141464758473", + "context_digest": "c870cb247a3f0f2c4aa9a17de9e1f79d50e4da1a33fc17a0f8be84b8601494d3", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "ce4a6fe311d5e92fb0fe2a77034ca7eff99e899dc59a55b4310df7f5be524f20", + "result_digest": "b44a37e16f3919c02cb14079d3232677569f259c6ad134e7721cc50903441c41", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101" ], @@ -409,7 +409,7 @@ "resources": { "proof_bytes": 2307, "action_bytes": 144, - "context_bytes": 1182, + "context_bytes": 1184, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -429,7 +429,7 @@ }, "context": { "path": "valid/spiffe-root-raw-key-actor.context.cbor", - "sha256": "99745ee5289686d56252eeaf02c8b8b2a438f298ee60355db6be2ecdd3af6c4f" + "sha256": "359fad5c5421a0f4aa1c464390efdea8915cf84a371e9aa68ef98a316c6c498a" }, "canonical_action": { "path": "valid/spiffe-root-raw-key-actor.action.cbor", @@ -450,15 +450,15 @@ }, "expected_result": { "path": "valid/spiffe-root-raw-key-actor.result.cbor", - "sha256": "846e7a6174dda8b1ef247f25257aef8e5e8da525178d6bc89ea32aaa7596973d", + "sha256": "9050975c15a009ca61394859e8b5de5cf6cad183fc94eb2a433560696ada8318", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "f70cbef8c272b9bfc96d0c99f79ab17a390f11c865c1c09a50f878c4102345bb", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "e0a6d6301e029e9094b9e35eee616ee54efb21bdd0edd061c76e3103f0f0ca7d", + "context_digest": "3f0ab8f40f4b6bdf5f720017b8fc50940357cbdf9a5aad1570870aa55d39445e", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "f5e54852e9a5393db4f12da8e7055cedc4e5bc7ac32dbafa7cf36b9e2d65e374", + "result_digest": "dbc78b944ac0b30b6883409b5a8992a7555239017c510b0e71200048d5cae861", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101" ], @@ -466,7 +466,7 @@ "resources": { "proof_bytes": 1783, "action_bytes": 144, - "context_bytes": 1142, + "context_bytes": 1144, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -486,7 +486,7 @@ }, "context": { "path": "valid/raw-key-root-spiffe-actor.context.cbor", - "sha256": "9185ff7f4edcd848cefdde04f8761eedd0505550dfede814ebd7abf079d5a247" + "sha256": "0721d9b572b7be73b2fa3d287fad16bada78ec0747e5ade4eb63a51820577028" }, "canonical_action": { "path": "valid/raw-key-root-spiffe-actor.action.cbor", @@ -507,15 +507,15 @@ }, "expected_result": { "path": "valid/raw-key-root-spiffe-actor.result.cbor", - "sha256": "3baf2437d5979c2b014d9a558c227fcf47822941ff7a84ba12ce9e08c8049e32", + "sha256": "4dbbf77b46742deb8b26220186e8854ad12c1f64d26445ffed6270c55297bc85", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "b2013ac0ec56fdcf5666d5f1b5f511490dd1c53804cf694a2becb4c444caf2f6", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "123e57563341ce967ae4eb74818c8f3220240a3f6443c6e98d2d300d6077c78f", + "context_digest": "97c3e6ef80402e78335513a292013ece7aa384bc750dbaf41041a483a051584a", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "b5ece0c4e06e190f9e3771c55c66a42d85b1b390ab56b3331bd795927bc77ba3", + "result_digest": "81ecd06ed1b45d4c116ccdd9b28a2b17ff69e2bb54187aea19a807687649b691", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101" ], @@ -523,7 +523,7 @@ "resources": { "proof_bytes": 1763, "action_bytes": 144, - "context_bytes": 1178, + "context_bytes": 1180, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -543,7 +543,7 @@ }, "context": { "path": "valid/did-web-root-raw-key-actor.context.cbor", - "sha256": "0c7f22d1467f2bc7076c031a3265584a6f6da8508ae5ddda3076149683be6001" + "sha256": "8a2b9f74de12fca8b6571f3c65b0009285fea4953956be2599b4f0bb131cbdd8" }, "canonical_action": { "path": "valid/did-web-root-raw-key-actor.action.cbor", @@ -564,15 +564,15 @@ }, "expected_result": { "path": "valid/did-web-root-raw-key-actor.result.cbor", - "sha256": "bace0747bf741d4b84e6cba4c4e97adfe4da6faad68f99f1923ca2826dc837e5", + "sha256": "f7d216718d89d4a7b8a03c6ff0acedf2159f6d50a15526283ccd3e0d0073de9d", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "3bf827ead9f446364ab992c342f6a91cdb1784ba46c5cafdf256d85480a346fa", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "5273f1503c4281584f69115bd80c85bd135120d7f30a9e49dd65fead51b54c93", + "context_digest": "a9673fbf7f2dfdccf6b5c39724a7d097f4f9c052c7cf0fcf495bc21792e9276e", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "8f79441ea8bdf6d867324efb68c17d46d0e1403e659b0c979614ed7be53e4bf3", + "result_digest": "f4b3d5f6b07127760271c3d9f11dcdf2d43ceb948c3eb1bd82cdc45b7fc81a93", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101" ], @@ -580,7 +580,7 @@ "resources": { "proof_bytes": 1895, "action_bytes": 144, - "context_bytes": 1149, + "context_bytes": 1151, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -600,7 +600,7 @@ }, "context": { "path": "valid/raw-key-root-did-web-actor.context.cbor", - "sha256": "ac781aa2d483dbba2f96b517e954faaade0e8d3459392e5baacd001e2551a498" + "sha256": "15cf880750aa240ac75fbdb89f027030ccd828095ae746e2f03b3ef8d4b3fbb6" }, "canonical_action": { "path": "valid/raw-key-root-did-web-actor.action.cbor", @@ -621,15 +621,15 @@ }, "expected_result": { "path": "valid/raw-key-root-did-web-actor.result.cbor", - "sha256": "173dba3189e37ea7aa6cb60705d8db45caa50d3de361590467eb3c8c53d89676", + "sha256": "30249821803487d80047b7caec04d7d8f6c2bdafd57eada79372f12a4321aa2e", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "a75540d193eb7df7c906d1384fbdbfc1cd9421bf5e7630efe576a574fcd6aee7", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "de4380b3e8f37dcb033d68a8c827a679906b22a5e0e420f7de2dcc286dae1cef", + "context_digest": "3d16be3b832d29dca01b9f4b6ae5d7babbb1e9f40d334e1dbcdd40922d9f3d83", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "694f4c710524c81e69a7cd2c953fbc2c5e8cb705d8a1797f376301e1c4ab4ad4", + "result_digest": "ac69f57e7a965abde846e8916f601b10ccf73012f724b885e54f0ffcd3568ffd", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101" ], @@ -637,7 +637,7 @@ "resources": { "proof_bytes": 1877, "action_bytes": 144, - "context_bytes": 1197, + "context_bytes": 1199, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -657,7 +657,7 @@ }, "context": { "path": "valid/webauthn-root-raw-key-actor.context.cbor", - "sha256": "b47eff6fa1b4836c7c1512968082a834fc014c969f28934a465e6d8da5c93c85" + "sha256": "d8f13dfb75aa0bbccfa547ecfd5319897ac7e8e05f756e584ac65961d0248d61" }, "canonical_action": { "path": "valid/webauthn-root-raw-key-actor.action.cbor", @@ -678,15 +678,15 @@ }, "expected_result": { "path": "valid/webauthn-root-raw-key-actor.result.cbor", - "sha256": "718fe0d8966cbd38d149dab4cc6f2edeadbb73279ce0286a4b68597d8b70ca28", + "sha256": "27b4cb9fc8ee4323dfb8910f63b4c3438fdc355b7c3d824e7fd4e06c678e4677", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "9244bb48c023c7e65b1a37eb6c8a67edbeefd8e635241e22dff18793e19201bb", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "03e8b786949b16d70938aa8dbf952d0183e3d8cad2d95fc62ff32f09a39a0fdc", + "context_digest": "8aec77d06c4f56478b6617c3fde54a39d0d5d2e1a487c9acc5a0acab6039a8dc", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "314c95254b95d72e906e035c6228c8924707d7b0adc91e771e6a095b472fe902", + "result_digest": "5fabe2e6e5698ebfa0ecf1bed8f5e3231456e16ed515a893ee38018fb36cc2e7", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101" ], @@ -694,7 +694,7 @@ "resources": { "proof_bytes": 1618, "action_bytes": 144, - "context_bytes": 1138, + "context_bytes": 1140, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -714,7 +714,7 @@ }, "context": { "path": "valid/raw-key-root-webauthn-actor.context.cbor", - "sha256": "229a5e2c4308570fe2df9fa836e28dbda2c1c451a0dc94dc2aacdc9d9b86beb2" + "sha256": "0b1ff5d45a6dafa6744f06984898a814bc438cf1417bd2488c58f014b26f1a34" }, "canonical_action": { "path": "valid/raw-key-root-webauthn-actor.action.cbor", @@ -735,15 +735,15 @@ }, "expected_result": { "path": "valid/raw-key-root-webauthn-actor.result.cbor", - "sha256": "f3bb4e30faeff697b934c40765ca156f1bcdc682c31e26af2d91261d17a2b1ec", + "sha256": "cc73f0281c13337fa7ded6ae9c1053dd410fabf85b8993ff170fa35284e5ca68", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "5e0b615f46971db588c25e4f5f441b1e6c517ae7f39c834bf121c29343b4d58d", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "080eb581c48bbb1ea16a1cd58c9d533d9d879013ff844358e85aa21bd28382cb", + "context_digest": "e39344ce71296f17f0754722f9457f287e3b9d69aa9457bec5355823dac5768e", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "8eaab4a32efbf4b31a4b9033da1f46a1b191c0d29e8067b5e861ff78b33fc593", + "result_digest": "6d8c9a21e72eb6402630204d16f34dfc2d2a5e2a9a85db07e73347ecdb7404a8", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101" ], @@ -751,7 +751,7 @@ "resources": { "proof_bytes": 1595, "action_bytes": 144, - "context_bytes": 1183, + "context_bytes": 1185, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -771,7 +771,7 @@ }, "context": { "path": "valid/hsm-root-raw-key-actor.context.cbor", - "sha256": "021246165a43e0167f4123d3bf868d891b582cbed62487490012d4a533023c54" + "sha256": "9effb07100d4d3d07cc55108f96b2d4636676ef17a6d1f3cd0d0a08093bdf0ab" }, "canonical_action": { "path": "valid/hsm-root-raw-key-actor.action.cbor", @@ -792,15 +792,15 @@ }, "expected_result": { "path": "valid/hsm-root-raw-key-actor.result.cbor", - "sha256": "1b217f10d72ea8f3f1e807e4dc5651ed3a5e844301df9100ae65dc2b2a5b83a5", + "sha256": "ea881cc86da379ebd95382afd718de503c586aeffe88e57f210bf09a52aa73c2", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "f7d00c677283527ea8bb645e4c40292feffb64f56500915d2ae667aae1db6b3b", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "d313dafce4b3a6d71472f2d7f41a21ae64d7a973d484b2bb71c1607e4888436e", + "context_digest": "df2e7a6c8279b14911fdc992091c5be358a8b3ddb682b2b8c524175adf0ee847", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "9ae4c38b6062816fbe135b341c6e25e911dcc863c54bd04557d307fcbce836ca", + "result_digest": "597b8db8d139f4cba2db8b876b9787ad1ea6e865fe77bffdf47f50815536c8dc", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101" ], @@ -808,7 +808,7 @@ "resources": { "proof_bytes": 1587, "action_bytes": 144, - "context_bytes": 1171, + "context_bytes": 1173, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -828,7 +828,7 @@ }, "context": { "path": "valid/raw-key-root-hsm-actor.context.cbor", - "sha256": "8be462ab4f1e1a978b6dd7463fccf07bf63894d07876442f9759fbc01bdd4953" + "sha256": "6ea51338020e7633f515a14a39da828b715745b08deffd0b51b68274cd38ba20" }, "canonical_action": { "path": "valid/raw-key-root-hsm-actor.action.cbor", @@ -849,15 +849,15 @@ }, "expected_result": { "path": "valid/raw-key-root-hsm-actor.result.cbor", - "sha256": "3b2aeff90ce7a7de69f975999411c81e32a42524c303789fcd91d1feaf8ed8fa", + "sha256": "bfafc869afaa85a70b167b31252e4ee35b58ec9b77288674003787cb272b8477", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "44fc7b3b76f93d239784583c7043785695be1b8d8a867024a8423885d7b2eb8b", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "5fd6fc2129f94803a34ec577e2f8b921f049585b1e70a3babd9cced0d1925d25", + "context_digest": "875c531cc0038196fd1946e9180b48cb2fc50bf9b31ece53db58f8d2a709d482", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "f21262079084071cf377948d1cc90f3caf964f99793802ac5159946c9521a98f", + "result_digest": "630def706ce16db3e02e320bb8f928e1337f72020f6ef3e263386d9185622c14", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101" ], @@ -865,7 +865,7 @@ "resources": { "proof_bytes": 1580, "action_bytes": 144, - "context_bytes": 1180, + "context_bytes": 1182, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -885,7 +885,7 @@ }, "context": { "path": "invalid/untrusted-did-web-document.context.cbor", - "sha256": "dc597db64821f2cd31c37e92d48f2fd2e25ef299926405563c4bdedfbb5a761a" + "sha256": "98881dbd55221ab33f311826d4c332597ed8f8ae68c9fdaec7a6380753fcc10b" }, "canonical_action": { "path": "invalid/untrusted-did-web-document.action.cbor", @@ -906,21 +906,21 @@ }, "expected_result": { "path": "invalid/untrusted-did-web-document.result.cbor", - "sha256": "acc60bbffa74518c50a9b0c5e6a919cd843b6d0ee82e63fe984972b43e1da975", + "sha256": "049354b4311fb85fbef15990ff9e1cbc164489257db27beebdeab5f50e416199", "stage": "authority", "decision": "indeterminate", "code": "external-fact-unavailable", "proof_digest": "0588665ea92e708760502f53004a75e8529e1fe577b1308d51567a2b193ab41f", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "95054a42fc4df09759f2851a6158a97a0efe6425bf7ea3db775198a6670ec872", + "context_digest": "bb9907d0c067b80a0907bc7ca2c048c6f602c02e223bb7f1d1913efa12d7f287", "plan_digest": "3cb070f838d7a15dcab170696403a376679cac7036d2c1bf5704864f71ced7b8", - "result_digest": "aaf07e85d881c60cbf18c3e760025994ac925b04dcea1c5d7cfad24f6f7a6ee5", + "result_digest": "4d68e1fc32a487fdeefee5aa430686c4fa1f7f2ffd7242c8331bfb325dc34df6", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1264, "action_bytes": 144, - "context_bytes": 1138, + "context_bytes": 1140, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -940,7 +940,7 @@ }, "context": { "path": "indeterminate/did-web-history-without-statement-existence.context.cbor", - "sha256": "9725540736f851fd34325d19b6a3098eee67e25b24eda3887956581ec1619c04" + "sha256": "9b788733cc2076e1fbf93dda61eaac481f2c39624b99f346da63f6f853dda24c" }, "canonical_action": { "path": "indeterminate/did-web-history-without-statement-existence.action.cbor", @@ -961,21 +961,21 @@ }, "expected_result": { "path": "indeterminate/did-web-history-without-statement-existence.result.cbor", - "sha256": "355b27704ba27b26eee8a578f2a6f8e3fcdb0770df2f6ef23fcf3c7936a720b2", + "sha256": "43f70ec2800ecba0f685f3e96960c82ccee36910d71968e21482dcde60fd959d", "stage": "authority", "decision": "indeterminate", "code": "assurance-requirement-not-met", "proof_digest": "6e406db76634d641a046c7d9bf5ad0fda401428947abf4fa2ce29d5c91d713c2", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "da248367495254a9b5cd2f465b8a2c0f862b6902c284e003c8a1b8730b4f988d", + "context_digest": "dffec258bbb6dceb1e73ed0ebea88b5df8b1c5ddd5ae6bb657fb64cd890fd75b", "plan_digest": "606286e4a8dee2ba799637e4b7dbd10d975422a9dfdf183aa239e13959088f8d", - "result_digest": "e95927e69af19fda2faf8aebbdbcc550fe40951e2fd9646802d991616559c449", + "result_digest": "aa38a27d089d118d705119ea8f3980dd50716c365bbc05a883aa407453931ea7", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1273, "action_bytes": 144, - "context_bytes": 1131, + "context_bytes": 1133, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -995,7 +995,7 @@ }, "context": { "path": "invalid/did-web-historical-state-unavailable.context.cbor", - "sha256": "91cfa272d541bcb0e31ff69fe459e6e62f56b44f1ec6fc0240cf7461b1fb60b1" + "sha256": "001aa5d03cf216f4f12dec4e1daa52c71230822d8dd910b9379a51b67d3308a5" }, "canonical_action": { "path": "invalid/did-web-historical-state-unavailable.action.cbor", @@ -1016,21 +1016,21 @@ }, "expected_result": { "path": "invalid/did-web-historical-state-unavailable.result.cbor", - "sha256": "8b45005c4c280d1689906078fc14fd7c3daa7a364fce97683e8b13de723b6d04", + "sha256": "68c5f25a46c1bc5899d002aec407498b168a6a00c52a0355c2c2f2ca8a60ed95", "stage": "authority", "decision": "indeterminate", "code": "historical-state-unavailable", "proof_digest": "eaa06a65b7e65e7f3a3b13999e8319d0cf70999fa9b3e773da16d4f56af96ac9", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "1d25ca9ea6e8708b57210d7f6688c819c70dd5ee4d8854f2575d81bdfbd32a3b", + "context_digest": "b20808ced31028b76c502ca326e0639257de0fd9d8904a202536d80153c84d17", "plan_digest": "4284e3307911b84a2d8820360fb3ab2b8a8d9b0520ab7ce94028bd4365ef6963", - "result_digest": "54026a3a1402355793d692ef1d784bc846f6b1ce368cd00e03782491f1b4cf38", + "result_digest": "e90c09ba55bd6a4e01457155ac915b61ad1efae19b3fe545b0d4003724a23629", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1354, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -1050,7 +1050,7 @@ }, "context": { "path": "invalid/unsupported-protocol.context.cbor", - "sha256": "eec5bbbd76e306873c88f2b521b6797e9c9740b0816c35efa906099356e3605a" + "sha256": "70303b0766a62c85f9b5b1b751fa008727abad8f82e7b3757b89d537a24a7f44" }, "canonical_action": { "path": "invalid/unsupported-protocol.action.cbor", @@ -1071,21 +1071,21 @@ }, "expected_result": { "path": "invalid/unsupported-protocol.result.cbor", - "sha256": "88b3a25e927279e50056d9ec6aa070f766dbf787ae2bebc3094951c43464a8a3", + "sha256": "210010323dd72ac12436495176dc349568fd36cef168d10370934f63732e12b2", "stage": "decode", "decision": "indeterminate", "code": "unsupported-protocol", "proof_digest": "22b2de278f35449ea540c3f2a7176cf3af1dd3eb68069f1693f862c3fb45bf41", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "c25421fb1b54c5f9450e8f88151066e71a406124aadae91426bf8d8574750774", + "context_digest": "799bbb6de7fe516b4068c7c25ec5c7a522f3c959e9bd44e0f3d74d480057371a", "plan_digest": null, - "result_digest": "be388e89b64dfc837530ef3926689de2754fe16591d655f32598d0c8b5d6632e", + "result_digest": "57c15f621c9f336c8c258a014f076830290869850c04f4c249e843c1abb03862", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 0, "plan_leaves": 0, "plan_depth": 0, @@ -1105,7 +1105,7 @@ }, "context": { "path": "valid/all-of.context.cbor", - "sha256": "3dfe79287964085dac143cdb9a3421eaf3dc087897802d298adde88769c85059" + "sha256": "a3a837ba4bc2ba4216bb8b20e9e64a1340f8f952a63189c8ebd52329532832e6" }, "canonical_action": { "path": "valid/all-of.action.cbor", @@ -1126,15 +1126,15 @@ }, "expected_result": { "path": "valid/all-of.result.cbor", - "sha256": "3b76b3beeb4fe06b162e1d7a0062f6c427cacd9fba93484b9509b24afb9c11b5", + "sha256": "193e305a652cb85ca2fa8f90e35a305553fb7e93050a403ac83b0b5df2f54f86", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "49bb0c0b51c1a385381fcfe3af818cb012fe726d33206d797209361f8b6ae3ed", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "7caba47204bcd29e0e768fb897cb4582ceace8ab39b52e29de13b1868723d484", + "context_digest": "b273600b9ade79c4e33aec14438a1084c0b26068ca7d678412fd7367042e8d1b", "plan_digest": "aeb840d03bd782b2eb94c65193383c255fcb672f6f1468cafc40bb95737ad2ca", - "result_digest": "7670280fe49cfa117357aec95af12972e7bf8af3e09c3e196c1c6f0bff81d7ab", + "result_digest": "838882b2b7e82910b768cc01a1e63b3bac489947dfb520e41032f2e4764b9448", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101", "0202020202020202020202020202020202020202020202020202020202020202" @@ -1143,7 +1143,7 @@ "resources": { "proof_bytes": 1554, "action_bytes": 144, - "context_bytes": 1404, + "context_bytes": 1406, "object_count": 6, "plan_leaves": 2, "plan_depth": 2, @@ -1163,7 +1163,7 @@ }, "context": { "path": "valid/any-of-valid-invalid-signature.context.cbor", - "sha256": "012c9e96ae29e0a489939969595938d5fcdf4b0cbc881f14817f4ee28d85a4d8" + "sha256": "0047cc834354d6a883fa1e73e3e6dec8c802e3981cc85a2cdda2cbdeb0e36f96" }, "canonical_action": { "path": "valid/any-of-valid-invalid-signature.action.cbor", @@ -1184,15 +1184,15 @@ }, "expected_result": { "path": "valid/any-of-valid-invalid-signature.result.cbor", - "sha256": "b7d0038ea05cf47eda824eaa2ca8b3f6dd9a533560dadbf6d8ef5ac52a9ad4e8", + "sha256": "99158feb4b8cfda8a546fcff192e0acf5f9534b5cbcd9236d20488d508139449", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "d764aacfd898d3329c30f38e8719360fa29e4889f8048da25bf0b6ef4609935d", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "a8858ae20e6d8df3d7ac2a21191abf249af956667ebe920ec27e7008d0d18222", + "context_digest": "8eda68ce119eda4dbc6f104e04543fa9694a24a02d4e07ac7b427e2ad648a1f9", "plan_digest": "28d680c940ee1d6f79bcaa313f77aa1dada7b4533198f29b28b1e38b36b5af7d", - "result_digest": "7b584038724071866f3a30572462a053d6c1846b4082bda6f29b998ecc6ad2fa", + "result_digest": "68ee834a515e265bd625d737f1b763d462d581d70f27084fa4d72c10f6583753", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101" ], @@ -1200,7 +1200,7 @@ "resources": { "proof_bytes": 1554, "action_bytes": 144, - "context_bytes": 1404, + "context_bytes": 1406, "object_count": 6, "plan_leaves": 2, "plan_depth": 2, @@ -1220,7 +1220,7 @@ }, "context": { "path": "indeterminate/any-of-denied-indeterminate.context.cbor", - "sha256": "b7b045dea4a7048bad0497361546db168e7df0742396508af9682ac385a2f49e" + "sha256": "bbf006b573288eaac85fef8779b59dc54dd2443f638d5ee2044f9d8ae927dca3" }, "canonical_action": { "path": "indeterminate/any-of-denied-indeterminate.action.cbor", @@ -1241,21 +1241,21 @@ }, "expected_result": { "path": "indeterminate/any-of-denied-indeterminate.result.cbor", - "sha256": "7327466cf5501d39465e94ef8c94ed258ec057c56a3f6ee6da93f8bd4506d40c", + "sha256": "a1161e5204f2c5a7a994d2edbea9529f805b8b8283faae7578496313f4a3e049", "stage": "authority", "decision": "indeterminate", "code": "external-fact-unavailable", "proof_digest": "f47075316fb57b36bef5b5d3866eb90602863d58e3b163bd7261ac4a39d94f28", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "7c07bb2c424e47484f4bbf80224e6a0125c6dfd9ce6101ce3694db7da2c364c2", + "context_digest": "8c05798b4b361a2b4fd63d553e4882e4cad87e31e6fa21e54bdba83e245b6e68", "plan_digest": "28d680c940ee1d6f79bcaa313f77aa1dada7b4533198f29b28b1e38b36b5af7d", - "result_digest": "b49f75a31319a6011100fe925352dc237a45f1521b3cfe1682a113a341a832b1", + "result_digest": "fe60248398e38f3f6f92681c2450c16d8a7672bedadd49be790bf7ef74fd1868", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 2073, "action_bytes": 144, - "context_bytes": 1416, + "context_bytes": 1418, "object_count": 6, "plan_leaves": 2, "plan_depth": 2, @@ -1275,7 +1275,7 @@ }, "context": { "path": "valid/threshold-2-of-3.context.cbor", - "sha256": "0b5fd91c3007f202a7c7d138c574d40ad6dc4092f7bfb816121c11294b04caa8" + "sha256": "529ccaefbcca305aef20b493ee77c06179294b9fa7bb2cf3658d12573d59dae5" }, "canonical_action": { "path": "valid/threshold-2-of-3.action.cbor", @@ -1296,15 +1296,15 @@ }, "expected_result": { "path": "valid/threshold-2-of-3.result.cbor", - "sha256": "dd5296c20008b44243c7854a0b9158f5e1a0d68e836392fdd70599b1030149b5", + "sha256": "91eb5aeb19761e8ebcaf8a118759a19f5118e21d4b351aef91f696767e5e0b3f", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "3694462e2423350dc5a3156ebf57d2ceba87f6537c11d5c6e1128cd5201943fa", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "0a873b98bb6a8ea54b3a107329ed1bf7e206854c33640ec360e1d8f4816a5a8c", + "context_digest": "0abe116e2424b1712d141e45e6b0c5dc353f6ed8e2cd15e7fca4c161a2d6c89b", "plan_digest": "cad107fc97848d4e075d4548e79ae7242a6147a7faadc9d9a6d2abf58c6e6b68", - "result_digest": "e8a1c56d512243ef9c96aa84710856e9bebc59f55f6e12d26de241ddf1184485", + "result_digest": "4b4742051ec4fdcd16e6d7e4ab133d8ff797f9f5043ba28213a945887a9df45a", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101", "0202020202020202020202020202020202020202020202020202020202020202", @@ -1314,7 +1314,7 @@ "resources": { "proof_bytes": 2311, "action_bytes": 144, - "context_bytes": 1683, + "context_bytes": 1685, "object_count": 9, "plan_leaves": 3, "plan_depth": 2, @@ -1334,7 +1334,7 @@ }, "context": { "path": "valid/threshold-mixed-success.context.cbor", - "sha256": "1ea26c8714419f12fdf75a2a134c1c36440e921d9b19ca0e8be996bbcfc507da" + "sha256": "6e1234bcfe18222ab3df682232fe961d8272c84c243d9db2c6e1efa560c27ddb" }, "canonical_action": { "path": "valid/threshold-mixed-success.action.cbor", @@ -1355,15 +1355,15 @@ }, "expected_result": { "path": "valid/threshold-mixed-success.result.cbor", - "sha256": "b1140297b9569998e0205b015309d09eaedc3bc2de180de5ec9beb0702dff478", + "sha256": "6b205445ac1076572de0287dcd4e54e5920be8b364a9c55e2d6d6ac17c84c5dd", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "81ad6e22a3fbad683250659e768453cc9a259b0e20313e81887062c6d06626da", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "2a24644b94f9e51d111131e6225794c73709e9f4fc89fcd15facb4597b5a58b6", + "context_digest": "2fb2008e377809c4ccb80676911f3948670d6fb22b1c219a91eb209d7e108137", "plan_digest": "cad107fc97848d4e075d4548e79ae7242a6147a7faadc9d9a6d2abf58c6e6b68", - "result_digest": "3e1ca2ed3362cdc7ecca361cae30ac608204f02625943a415fd9716c8d6d0fb8", + "result_digest": "96ca8c5f9fc4eda11e6f87162a26940a7a847b00b239cedd8c508d235bdd7744", "authorized_branches": [ "0101010101010101010101010101010101010101010101010101010101010101", "0202020202020202020202020202020202020202020202020202020202020202" @@ -1372,7 +1372,7 @@ "resources": { "proof_bytes": 2306, "action_bytes": 144, - "context_bytes": 1668, + "context_bytes": 1670, "object_count": 9, "plan_leaves": 3, "plan_depth": 2, @@ -1392,7 +1392,7 @@ }, "context": { "path": "indeterminate/threshold-mixed-indeterminate.context.cbor", - "sha256": "1650befe2743d788a13bbb3817f7a3b8a05e063ea87764909094de829d4a44c9" + "sha256": "3dfdeee296637f65d7f50e5630a53edb03c0634216197ebf5e5a3996072f17c3" }, "canonical_action": { "path": "indeterminate/threshold-mixed-indeterminate.action.cbor", @@ -1413,21 +1413,21 @@ }, "expected_result": { "path": "indeterminate/threshold-mixed-indeterminate.result.cbor", - "sha256": "7530a9fffec67b37db1296ef0f63355daeac6af4c218c70e57cdda6dcd5e70f1", + "sha256": "8aee0c0117de87c99a136c9591ebdd78394638f08780ec7945278dc2c5b58914", "stage": "authority", "decision": "indeterminate", "code": "external-fact-unavailable", "proof_digest": "87902509e6992380e5842688def9c12bb664fd89a558bc08260cf23d04041569", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "d0b9c0bb437c4d08e0071ebde94a0f4505330744ee1e04fcae4c6a353d410916", + "context_digest": "7ed11a3a48945344d3a6f103df1b94f69e6abedc94b0e3eec8cd428ad7c8557b", "plan_digest": "cad107fc97848d4e075d4548e79ae7242a6147a7faadc9d9a6d2abf58c6e6b68", - "result_digest": "a9b4a78f9f5b846e9b53d9fdf70fb1f29f7ce01fdbedf0520473121ee2569611", + "result_digest": "ea813a0c91e607b1cd2e8d146cc44cc04a4e7a9afe12e7e8648881dfc0a1e56d", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 2879, "action_bytes": 144, - "context_bytes": 1692, + "context_bytes": 1694, "object_count": 9, "plan_leaves": 3, "plan_depth": 2, @@ -1447,7 +1447,7 @@ }, "context": { "path": "denied/threshold-mixed-denied.context.cbor", - "sha256": "d77eb2f86ccd4983fe7aaa5d9c3afcfb085d8f4f0500fdb4a76d0f55211a0513" + "sha256": "a9c7778485594fc2d9d03ed955149853ccacd07810cb0bd470502251d92f1999" }, "canonical_action": { "path": "denied/threshold-mixed-denied.action.cbor", @@ -1468,21 +1468,21 @@ }, "expected_result": { "path": "denied/threshold-mixed-denied.result.cbor", - "sha256": "59de69a590fec0c6ceee34c90122bb478910a50f088b72f26d8d3d1ae4ffc288", + "sha256": "b5326727311448275add62d6fefd75f472ea2a9fa210f24ebe2de1fa47bae096", "stage": "authority", "decision": "denied", "code": "invalid-signature", "proof_digest": "496faf70e77fbe13785d0c40a508faa68f0a8de9556b701c056d5c8f02e2a74c", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "b59158b5ae41a3aa0ced9cb29980360dcd297bcd70c91981258a7a709ea8e056", + "context_digest": "27e583450162af09b6833a0effa2f0af0ef294ee66a12049de91b10bc266cf7f", "plan_digest": "cad107fc97848d4e075d4548e79ae7242a6147a7faadc9d9a6d2abf58c6e6b68", - "result_digest": "f8aae8c5771782e1c3614640f523c8bb27066463049b792aa96cf92e2224ae89", + "result_digest": "2727d7e79aa214c739758b224040c70f16bbcfc1a50e4e9a4d7dc8fb7eca5bb0", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 2306, "action_bytes": 144, - "context_bytes": 1668, + "context_bytes": 1670, "object_count": 9, "plan_leaves": 3, "plan_depth": 2, @@ -1502,7 +1502,7 @@ }, "context": { "path": "denied/permission-widening.context.cbor", - "sha256": "620e11d65f479c7ad559f2ae116ff9efd6805e2e73d2244b37e51fb49606cd71" + "sha256": "d98dd435ddb11879d3a01383b5c99edd6cde5d2801dd2af93a121819c10e7fcd" }, "canonical_action": { "path": "denied/permission-widening.action.cbor", @@ -1523,21 +1523,21 @@ }, "expected_result": { "path": "denied/permission-widening.result.cbor", - "sha256": "fe8dd07f949ea3dea7e4871a4b7416ddd81f15c75ece069a41ec0884e3df17cc", + "sha256": "951c61e0dc83fe79b03950df84b9374bfa2dd7a7823e327344abf2cb194017a3", "stage": "authority", "decision": "denied", "code": "delegation-expanded", "proof_digest": "12fc8b2617e8aec8eb9534a97e04b2ce363553e80eac6bdaa52f816e03fa85a6", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "973a864175a48282aea6f7515de9e1e4db79b7c940757176e9ec7fa4199c3656", + "context_digest": "0e72b8fd378195042f8edecf3002be0c91c89590361c8a1e3368473a2ccecc83", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "c7b00e6ed9b6b04c463804e0211691d9d217eefb1d8c2bb3df2caf684b94760b", + "result_digest": "431e1031f47e1169bc3011c0b899421f05a18d7b7b90b93f0f8bb269c7c23af7", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1477, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -1557,7 +1557,7 @@ }, "context": { "path": "denied/validity-widening.context.cbor", - "sha256": "620e11d65f479c7ad559f2ae116ff9efd6805e2e73d2244b37e51fb49606cd71" + "sha256": "d98dd435ddb11879d3a01383b5c99edd6cde5d2801dd2af93a121819c10e7fcd" }, "canonical_action": { "path": "denied/validity-widening.action.cbor", @@ -1578,21 +1578,21 @@ }, "expected_result": { "path": "denied/validity-widening.result.cbor", - "sha256": "4ea2f8e4239dac4323c5f515764a712489e072ce0b5656f7eb38723bc8d3a8d0", + "sha256": "de193f176a1590675c97a7496d2a2a26f5eea299d0dae953d85f61e95ccaf992", "stage": "authority", "decision": "denied", "code": "delegation-expanded", "proof_digest": "95becaa8c42bd49cc367b9f24a861701dc414213c18f244fbd12f0e6d2f829a2", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "973a864175a48282aea6f7515de9e1e4db79b7c940757176e9ec7fa4199c3656", + "context_digest": "0e72b8fd378195042f8edecf3002be0c91c89590361c8a1e3368473a2ccecc83", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "c1972b7b5c7f2386523238b2367ade8bb85e8cdcf3f7ca7d4e9522aa103b3f93", + "result_digest": "5c58a68867b0ac78b0536308cb85942aee0a1bef48ee527a1bc8562cd8714e96", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -1612,7 +1612,7 @@ }, "context": { "path": "denied/audience-widening.context.cbor", - "sha256": "620e11d65f479c7ad559f2ae116ff9efd6805e2e73d2244b37e51fb49606cd71" + "sha256": "d98dd435ddb11879d3a01383b5c99edd6cde5d2801dd2af93a121819c10e7fcd" }, "canonical_action": { "path": "denied/audience-widening.action.cbor", @@ -1633,21 +1633,21 @@ }, "expected_result": { "path": "denied/audience-widening.result.cbor", - "sha256": "b58a4f9d6f19d6fbd4986c50064d2684f921b73d0c8c7f9e99772312605747aa", + "sha256": "c7f305dc41aaee476bacc512a7e57134a3107488d49aafb445b848d7a0dc51ca", "stage": "authority", "decision": "denied", "code": "delegation-expanded", "proof_digest": "06e09d209c155c1e6cc2761c1c0068cfa532c8cba4e573ae1e2d7158a39a7715", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "973a864175a48282aea6f7515de9e1e4db79b7c940757176e9ec7fa4199c3656", + "context_digest": "0e72b8fd378195042f8edecf3002be0c91c89590361c8a1e3368473a2ccecc83", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "eaeeb7dc8ea9190150bc2743943fdb4a67c1745e25c4a15ed18d94257fc40582", + "result_digest": "c21525ce5cef567f87f07a82b57fda23f383b52868ceb68ca8281d250e14a513", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1481, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -1667,7 +1667,7 @@ }, "context": { "path": "denied/budget-widening.context.cbor", - "sha256": "620e11d65f479c7ad559f2ae116ff9efd6805e2e73d2244b37e51fb49606cd71" + "sha256": "d98dd435ddb11879d3a01383b5c99edd6cde5d2801dd2af93a121819c10e7fcd" }, "canonical_action": { "path": "denied/budget-widening.action.cbor", @@ -1688,21 +1688,21 @@ }, "expected_result": { "path": "denied/budget-widening.result.cbor", - "sha256": "0530d1992d1a43b7dbdb648503becc2b21ecfab105bb1bc6c7a01755e00595f5", + "sha256": "ea2978489e69c21f027dc5723a94dd5afeb623c9877e5a7cd5037d3615e89bff", "stage": "authority", "decision": "denied", "code": "delegation-expanded", "proof_digest": "d3b8de22baa2fa40a26d0556615feda77065345d91a1af69357583bb4dcd7fa1", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "973a864175a48282aea6f7515de9e1e4db79b7c940757176e9ec7fa4199c3656", + "context_digest": "0e72b8fd378195042f8edecf3002be0c91c89590361c8a1e3368473a2ccecc83", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "f9c0abd117a984025df1798d226e05ac0d66feb32947f52fb7ec993108f09590", + "result_digest": "fe9f5652cf7cc9ceebdd01e36fee973f9f1e0ef0902d9940cf226f4c8d1a3189", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -1722,7 +1722,7 @@ }, "context": { "path": "denied/depth-widening.context.cbor", - "sha256": "620e11d65f479c7ad559f2ae116ff9efd6805e2e73d2244b37e51fb49606cd71" + "sha256": "d98dd435ddb11879d3a01383b5c99edd6cde5d2801dd2af93a121819c10e7fcd" }, "canonical_action": { "path": "denied/depth-widening.action.cbor", @@ -1743,21 +1743,21 @@ }, "expected_result": { "path": "denied/depth-widening.result.cbor", - "sha256": "58366521aa7d35a1a9d06587aadfbb8b3dcc4825e52ff32d59b56b9d14eb81ca", + "sha256": "a03e4295a4e21a0397d83b582670035ee596404252571bc7eab0fb2b49976ec4", "stage": "authority", "decision": "denied", "code": "delegation-expanded", "proof_digest": "466062d8c8b078801d32bc59b0e81a2cbe2fe4351f44b77c74d42189cbbac834", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "973a864175a48282aea6f7515de9e1e4db79b7c940757176e9ec7fa4199c3656", + "context_digest": "0e72b8fd378195042f8edecf3002be0c91c89590361c8a1e3368473a2ccecc83", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "49ea1862008a450aa07ff0dc82d91e879105b4405c3ebd1f100b8911164d9334", + "result_digest": "9f186938d739ab400de6003d22dc6b84fcf2d816d3bbe3e8f91098488abf2a38", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -1777,7 +1777,7 @@ }, "context": { "path": "denied/assurance-policy-change.context.cbor", - "sha256": "620e11d65f479c7ad559f2ae116ff9efd6805e2e73d2244b37e51fb49606cd71" + "sha256": "d98dd435ddb11879d3a01383b5c99edd6cde5d2801dd2af93a121819c10e7fcd" }, "canonical_action": { "path": "denied/assurance-policy-change.action.cbor", @@ -1798,21 +1798,21 @@ }, "expected_result": { "path": "denied/assurance-policy-change.result.cbor", - "sha256": "382fbd26856e5e0260b0a062fc414e5d5a126189089d8be9b72ef247a5ebd5b8", + "sha256": "dcf8104172ede50a16c738b7963aadb35f343d889ca1e71201e0b7a2502d5b29", "stage": "authority", "decision": "denied", "code": "delegation-expanded", "proof_digest": "61725f5bd3173e2ea542b71e2bb54183f1f63fca9697723ffbe773c210a2d37e", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "973a864175a48282aea6f7515de9e1e4db79b7c940757176e9ec7fa4199c3656", + "context_digest": "0e72b8fd378195042f8edecf3002be0c91c89590361c8a1e3368473a2ccecc83", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "ea7f9832c6d1392aa002f434ad10a4c23b90695349359babc12ecb0dc722839f", + "result_digest": "833f391f1fbac02ec280fddbabf1482e5e1a22577229b87032718aaca310c15b", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1471, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -1832,7 +1832,7 @@ }, "context": { "path": "denied/critical-extension-attenuation.context.cbor", - "sha256": "7eece123bfe15dc52b3b3bf4c09f9b1c769a2c76f75a68792bd1b741121f6278" + "sha256": "c8f101157c76dd7b3224684a18c5641f6662df336a553d6f3238bcc95287fe0f" }, "canonical_action": { "path": "denied/critical-extension-attenuation.action.cbor", @@ -1853,21 +1853,21 @@ }, "expected_result": { "path": "denied/critical-extension-attenuation.result.cbor", - "sha256": "0cd691235b0bc7261dbfb1c9f14a02f67dfdef241ef96c6f64f5fa19af6d58b8", + "sha256": "0b396f738097c355f2949a42961d83d1cd106a2f9d64cb6ece7aa8536c8c4b3a", "stage": "authority", "decision": "denied", "code": "delegation-expanded", "proof_digest": "cbc09ab5675a2767415874f5b504dd7212c7b1cfefeda06b39892b0a06782e33", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "91b712957c56b5cd114251880277063fc713187d74f219c6b9c9571789e9ac5e", + "context_digest": "65452ac4d8a9fd51efaac5944c994039ca143e2412df2897dc12c49aaadddeae", "plan_digest": "6a1d4506227ad7faaede8df4449b7eb54c9c1294a96fba4e85815b8b12b68f2d", - "result_digest": "d4614c27057b51a91bb11c6248e7ea829af5fc083294276674fb6de92fd0a264", + "result_digest": "a1bbea9397edf85e71d914beb7399cdd679e99279e3254d1212cdfae5041bb4a", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 2172, "action_bytes": 144, - "context_bytes": 1174, + "context_bytes": 1176, "object_count": 9, "plan_leaves": 1, "plan_depth": 1, @@ -1887,7 +1887,7 @@ }, "context": { "path": "denied/active-grant-status.context.cbor", - "sha256": "9f302ac8eaee0a39b26976dad365406b582b14dbb6d66c702e94cc93951144af" + "sha256": "d0dc4eaa86a2bbc170fc19f5d61ad3df9639c7d5a3d712e7fe21790e1a3e80d4" }, "canonical_action": { "path": "denied/active-grant-status.action.cbor", @@ -1908,15 +1908,15 @@ }, "expected_result": { "path": "denied/active-grant-status.result.cbor", - "sha256": "3bea930e300f47fd4adf04703f410a2df90deefa9865a953adda3a3097562062", + "sha256": "96a2a830dced0645e20523473d42e4962a0bc1142cf9d22a34ebf20041fab610", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "e87e2cd6c744420782a78e746bff4a7f4b657d010fa1ac3e1e146c516ff8c348", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "b3fb782de08028fea37ddc1490f60490aeed6cdfe1caece096e41e9cc3b28364", + "context_digest": "ee41243c4f3cc572f93d8a21f53570a81081bd99f45ad96d49e13869f5b08904", "plan_digest": "70696130dac7917ae925b3a7223ebc028fdcd1dc5009a641d2c892eb8de3fd81", - "result_digest": "ed16785e65816e103076c86b83a6c3d00efee3750020f7c832c121fb73fb97ae", + "result_digest": "8a6e46dce2453b4683ce9a805cfb02b0e09bf3301b567eaf85b373d55a894a13", "authorized_branches": [ "9191919191919191919191919191919191919191919191919191919191919191" ], @@ -1924,7 +1924,7 @@ "resources": { "proof_bytes": 1576, "action_bytes": 144, - "context_bytes": 1548, + "context_bytes": 1550, "object_count": 7, "plan_leaves": 1, "plan_depth": 1, @@ -1944,7 +1944,7 @@ }, "context": { "path": "denied/missing-grant-status.context.cbor", - "sha256": "a8d3fe72259d91540a82de536ccfb09b0461d7ef1c9d6f74638325243339b511" + "sha256": "8f65c01e7d3d840dd7ab9c77b4d104afc5b225b1853e41789035f0c872fd0cd1" }, "canonical_action": { "path": "denied/missing-grant-status.action.cbor", @@ -1965,21 +1965,21 @@ }, "expected_result": { "path": "denied/missing-grant-status.result.cbor", - "sha256": "0c613b44da1dcb0033047cf053c4f247e32e081b721c35a71fde6eaa77a6e7b0", + "sha256": "7a524fb701755e012b42110dbb5dd8081f6bb07d05cc876bf97eae54b5b55980", "stage": "authority", "decision": "indeterminate", "code": "missing-grant-status", "proof_digest": "a4716b360f77fb2d8ff5e15acabbc9a003e35681a8d2d488341bd33fb13adfe0", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "54d76f4e53e235c11f55d02b7e04c7749db41f2e29e54beadd11fac631f15f45", + "context_digest": "61f84e6e4cb66e31ed3b13a31e3fbd531a5959167f4c185bcc8296df90544f87", "plan_digest": "70696130dac7917ae925b3a7223ebc028fdcd1dc5009a641d2c892eb8de3fd81", - "result_digest": "3a0130a4393b0e3766e509524c30cb65ed75128222328db2b885934c816ad1d6", + "result_digest": "a8dc632658098fc41d30744b727b6526712359239a633bd7df72259683db4f69", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1500, "action_bytes": 144, - "context_bytes": 1180, + "context_bytes": 1182, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -1999,7 +1999,7 @@ }, "context": { "path": "denied/stale-grant-status.context.cbor", - "sha256": "4692af909440570ac9a69a6662f53124741d84df88b451decb50a4c4620e5ea4" + "sha256": "8f24a32db4e9dcbc4d06e0504b807c56508a3d3ed5d9c1e6916f8244b22027e6" }, "canonical_action": { "path": "denied/stale-grant-status.action.cbor", @@ -2020,21 +2020,21 @@ }, "expected_result": { "path": "denied/stale-grant-status.result.cbor", - "sha256": "6a63b468e2d5599f4498c60b7260b252f9a4eb6d8ed2144ab85f379146203af0", + "sha256": "bfb6cae479a808d267ce9915df4b1923bcb63a3847c75acf084a2056507c03cc", "stage": "authority", "decision": "indeterminate", "code": "stale-status", "proof_digest": "a4716b360f77fb2d8ff5e15acabbc9a003e35681a8d2d488341bd33fb13adfe0", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "03b333007ff808c4e4f39fa22a86fe9e4b568248db2763e3c1a68a460f70e7f0", + "context_digest": "ae916557a058e7c65b96d60d77bacdbdcb4581932b1369a8500d15cc1485aa67", "plan_digest": "70696130dac7917ae925b3a7223ebc028fdcd1dc5009a641d2c892eb8de3fd81", - "result_digest": "3dd96dd1ac8a6aff07b95eee0297f5f7a8ccb85287c124d754575b5b6566e3b2", + "result_digest": "38be15b995ca035a45ca7a50a201db8a2f499837c05bf26db6e42cb33491eb56", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1500, "action_bytes": 144, - "context_bytes": 1180, + "context_bytes": 1182, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -2054,7 +2054,7 @@ }, "context": { "path": "denied/revoked-grant-status.context.cbor", - "sha256": "5bb2874362972c1a7d7d9c81ee16cefe1c6a75518ff2a1458aab0f92e7c48989" + "sha256": "e9ba61988ed335da400419e325db3168ad08ee5f9670814c065ee9cc8f9844e5" }, "canonical_action": { "path": "denied/revoked-grant-status.action.cbor", @@ -2075,21 +2075,21 @@ }, "expected_result": { "path": "denied/revoked-grant-status.result.cbor", - "sha256": "3338c075b9806b299d764078bce94d1ba3e03efa636496224f6ba0816417728b", + "sha256": "4e2358adfef573f289f073e58a885ade574041fb74ac18efbce63a85b26486b5", "stage": "authority", "decision": "denied", "code": "grant-revoked", "proof_digest": "b6b6df0ace7e2c5336a2077031b8d883ac46133968b3f1c678856b9262d11a8f", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "500c2effc7255379e5430e2dcddccd4efaa590ca9c4472575fd8be7a9178ca11", + "context_digest": "dd6fe68d3246f61e2a9a4a24aa698cb7b59b86e555e5273c01363ed9b9349d6a", "plan_digest": "70696130dac7917ae925b3a7223ebc028fdcd1dc5009a641d2c892eb8de3fd81", - "result_digest": "d2562facca9e1225f7f49c50ed6f2b15b7e6d8ad69090d99b4f4c5f8b26f54b4", + "result_digest": "a4aa9af279a9617efab37e052fcc8c330ff361c5e897cb31e4594bc098bdee20", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1860, "action_bytes": 144, - "context_bytes": 1548, + "context_bytes": 1550, "object_count": 8, "plan_leaves": 1, "plan_depth": 1, @@ -2109,7 +2109,7 @@ }, "context": { "path": "denied/grant-status-sequence-rollback.context.cbor", - "sha256": "f85c37ec1d1a24fa8decc8f8f2ed85f936901488ce487941b99b880676a51b8b" + "sha256": "2fb04dc7d94a2807d8054db0827305c478463f76c66a1a36d1bb3b0ca0df0044" }, "canonical_action": { "path": "denied/grant-status-sequence-rollback.action.cbor", @@ -2130,21 +2130,21 @@ }, "expected_result": { "path": "denied/grant-status-sequence-rollback.result.cbor", - "sha256": "c93453d9cda0336767b4989c61fbc8cebddb68ddc6821de104885e23f86cf659", + "sha256": "2404d9dc66bb455c858a33207a4f62d5f791c7cfb553a835135bf9aaffb890b2", "stage": "resolve", "decision": "denied", "code": "status-sequence-rollback", "proof_digest": "75965a1fe56cc8c222088697fac2c504a57d91e0ab2710e4516efadddfb5a84e", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "f565e08fa604e32cdd893155413880084befdac222f260dfe06abc8849ac9264", + "context_digest": "00dce33ad22ecee02b67ae6b64193c3aaa9c25246c3957505eeae26130f6cde3", "plan_digest": null, - "result_digest": "e62782303d41a241c151f4c844b3ac363307efaa5e975b77e48461cf6ebea205", + "result_digest": "f1e4459af9fe2e30781bf779ded01852e741284be343fa42189a17079e1146a2", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1860, "action_bytes": 144, - "context_bytes": 1548, + "context_bytes": 1550, "object_count": 8, "plan_leaves": 1, "plan_depth": 1, @@ -2164,7 +2164,7 @@ }, "context": { "path": "denied/wrong-grant-status-method.context.cbor", - "sha256": "ddb73f0ea8322b018d0e735225c43ab655c9dd92501bce356f983d1f8d3a270e" + "sha256": "4b9b4d180141a696dab701dc6edeb73b300fc77c2a6f57ca5233d1c3209521f0" }, "canonical_action": { "path": "denied/wrong-grant-status-method.action.cbor", @@ -2185,21 +2185,21 @@ }, "expected_result": { "path": "denied/wrong-grant-status-method.result.cbor", - "sha256": "1a031a26ec4cffa9e1afdfb94ec844f23c25e15e1f074b01d0932c50497c7b9c", + "sha256": "77e79a921c27d4f0ab697913ac361ab7cf38bc91234aac4e4d23ac4ec5e70c90", "stage": "authority", "decision": "denied", "code": "status-method-mismatch", "proof_digest": "0e965660dad98b1aed8419f459ce8cf6a3870199e8b111459164e0a0dfd7f137", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "97ebd4e7c4bcb4306a27152ee0d63ebecafd8255622d374a3be3a46cde1dc1fd", + "context_digest": "76478462dced28c0d4022629ef9bb07b8e90af1d580475e7db4cd92c53c19b13", "plan_digest": "70696130dac7917ae925b3a7223ebc028fdcd1dc5009a641d2c892eb8de3fd81", - "result_digest": "265ac5a4d10045c3edcc87d31d2b56857beaafd658ff8664640ce953b3d648c7", + "result_digest": "7a0a955fcf7458fdeb926f66472a7a90fbe0b72fa322d049b4beca4983482b8d", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1576, "action_bytes": 144, - "context_bytes": 1548, + "context_bytes": 1550, "object_count": 7, "plan_leaves": 1, "plan_depth": 1, @@ -2219,7 +2219,7 @@ }, "context": { "path": "denied/untrusted-grant-status-issuer.context.cbor", - "sha256": "17a70f0ec03240c246e4518b67004077c17e0b0155ceee844b17759c79866e4a" + "sha256": "958ffa74e11b924a684af7cc20df44c9b6d78704b970a0f8edb9da47ce885c23" }, "canonical_action": { "path": "denied/untrusted-grant-status-issuer.action.cbor", @@ -2240,21 +2240,21 @@ }, "expected_result": { "path": "denied/untrusted-grant-status-issuer.result.cbor", - "sha256": "05f2b683a739ab6c6e908c3839d3786d9221bf5398f320cf828466553690c9a2", + "sha256": "363f39b333cf0e222c2c3464a31f5fb02bb6f9cf9d6357f496a68ba21e4ed412", "stage": "authority", "decision": "denied", "code": "status-issuer-untrusted", "proof_digest": "9ecf1f256d31eaee030d74c7f96a17bc9565018ffacd9a8e6f44ca32a67a87a5", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "dda6c1d82833d3a3b45efb6397c74bb81e99ab5d49948eecb3a918f82de255dd", + "context_digest": "3c940ca7e87c097006606a1b26398b891579afc399380cfafd0864cad8dfcc9d", "plan_digest": "70696130dac7917ae925b3a7223ebc028fdcd1dc5009a641d2c892eb8de3fd81", - "result_digest": "0a09eec379d120333e669a2454b03622261167c79b26e8303267dae091349ace", + "result_digest": "8361af66c2440a08af9000e23af7df10353d02efd0522469cb5101c199b9e7aa", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1576, "action_bytes": 144, - "context_bytes": 1548, + "context_bytes": 1550, "object_count": 7, "plan_leaves": 1, "plan_depth": 1, @@ -2274,7 +2274,7 @@ }, "context": { "path": "denied/grant-status-freshness-boundary.context.cbor", - "sha256": "7377f5ff8fd21f27ea21420e12a5ba99d6fdf5714e9c6049d5051b8cf63b8aeb" + "sha256": "4fc153d8dd9c6dc6076bce0f9fd26c94d459fb249504f59d2c22689b322a3bf0" }, "canonical_action": { "path": "denied/grant-status-freshness-boundary.action.cbor", @@ -2295,15 +2295,15 @@ }, "expected_result": { "path": "denied/grant-status-freshness-boundary.result.cbor", - "sha256": "1269da02028bb21be0ce21cebb8c696d5dd3eac90069111a7fe76a22e29b74e9", + "sha256": "80fffd55039782941ced6be2927ff602ba6ca303adb0304036ec020b83d196b2", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "343298dcd33fd4fba3e7ccde4b7133a79bcea158a2128e6112e1bc409f23a7d2", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "81ff9218815f4e938deea0effb5ee37b819b7d1182cb3a8d55811b0c8bcdd911", + "context_digest": "57658955b97bd065029d8a8dce5944f3868b06dfba6c4a2581156a93e22fa774", "plan_digest": "70696130dac7917ae925b3a7223ebc028fdcd1dc5009a641d2c892eb8de3fd81", - "result_digest": "2aa7ee9e49dad2ad63c5a404834e04738d3df1dbe03276e40047253925faf8f9", + "result_digest": "0228bef2daab0c13777f2b82fece131389a9e2f51f00d9549b66c17e0876be5e", "authorized_branches": [ "9191919191919191919191919191919191919191919191919191919191919191" ], @@ -2311,7 +2311,7 @@ "resources": { "proof_bytes": 1576, "action_bytes": 144, - "context_bytes": 1548, + "context_bytes": 1550, "object_count": 7, "plan_leaves": 1, "plan_depth": 1, @@ -2331,7 +2331,7 @@ }, "context": { "path": "denied/grant-status-freshness-beyond.context.cbor", - "sha256": "8ae4fcf74e7a7fa5a627b6abaa82632ff75bdae279defd85af88b37d4990e3bd" + "sha256": "d8dcb52dc89eba4eb6a12a23c15bbcd5c670ee88d18af5ecc53cc7f0d025f1d1" }, "canonical_action": { "path": "denied/grant-status-freshness-beyond.action.cbor", @@ -2352,21 +2352,21 @@ }, "expected_result": { "path": "denied/grant-status-freshness-beyond.result.cbor", - "sha256": "0e62b99cd42ee949fdfa984ebda5e8440585f5bf5a2f8b734d7be4102725bf36", + "sha256": "77aecac27401965536260b24f450c93e2151fddcbe58fea6d080d7c6732d2ca4", "stage": "authority", "decision": "indeterminate", "code": "stale-status", "proof_digest": "3f46840a7b84905592140281f6bddd2733de09d9de21bcad302043a54c6a7227", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "e68e5badcedbd08e4b5db96801fd2f432038d787babe3c50d6f5a737927b0bfd", + "context_digest": "2cc8bb0888946a2df6279f584d49804aa26bc7e48cfda746a3838e726b67533d", "plan_digest": "70696130dac7917ae925b3a7223ebc028fdcd1dc5009a641d2c892eb8de3fd81", - "result_digest": "558659afbd1169c89879d84c2504a49d5980439339a83d15e0c1cd227d338bbf", + "result_digest": "76018facc6e1444c413f6c0a6c5c08cf2e9360998e97f5e658d32442ad97b6d2", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1576, "action_bytes": 144, - "context_bytes": 1548, + "context_bytes": 1550, "object_count": 7, "plan_leaves": 1, "plan_depth": 1, @@ -2386,7 +2386,7 @@ }, "context": { "path": "denied/conflicting-grant-status.context.cbor", - "sha256": "a7bcb9ff53dd598fbfe9d089d901213f6b8980c199d5920e3fa8e82cfe327eb5" + "sha256": "1d7060a9db2127d0f331ec3d01654ea863116c6af85ab2357623618a3343c3b9" }, "canonical_action": { "path": "denied/conflicting-grant-status.action.cbor", @@ -2407,21 +2407,21 @@ }, "expected_result": { "path": "denied/conflicting-grant-status.result.cbor", - "sha256": "4c9013fcd070c0338f573511c4600864d1c46c7b56b9f84be82579a2281e505a", + "sha256": "caf653694ae218ae030d0056149556ca682b81097eac174c9d9b846345c5b005", "stage": "authority", "decision": "denied", "code": "grant-revoked", "proof_digest": "74ba5c733988f18aaa23884ed9ea2b323320bbf8208790f04a54a2386245f70a", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "689c3ec90b82fbb50e59ffcea02208f5d503d91663068b015e2028c27f848951", + "context_digest": "73acd0ef4abe78190aa482af374849e3f8b57dc6d8470160c1f703bcf169f69e", "plan_digest": "70696130dac7917ae925b3a7223ebc028fdcd1dc5009a641d2c892eb8de3fd81", - "result_digest": "0697ce7933ac516158e60349e4bc4cb74c07682453847238a2167fe861804802", + "result_digest": "0ab405b47f90a9374c424021b1e6d39e142e00ebbec14e36b83be42365a1021d", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1652, "action_bytes": 144, - "context_bytes": 1915, + "context_bytes": 1917, "object_count": 8, "plan_leaves": 1, "plan_depth": 1, @@ -2441,7 +2441,7 @@ }, "context": { "path": "denied/unsupported-grant-status-method.context.cbor", - "sha256": "0079c3092e55d9405de9ced7d9163be58c40aac9ce35641c2210a63773f59206" + "sha256": "7331d739ec07d175e63b79ee8a2c3f903e63cca1f365c5d2eeac9818e22b11c6" }, "canonical_action": { "path": "denied/unsupported-grant-status-method.action.cbor", @@ -2462,21 +2462,21 @@ }, "expected_result": { "path": "denied/unsupported-grant-status-method.result.cbor", - "sha256": "473126bd9f14b7d76410c17aaaa14955b5e89c5f051b426b01beace21718bfcb", + "sha256": "c11727fd5f2d1c14924a0840746185dd6d4abecce1964cc335dbf7a5d8572562", "stage": "authority", "decision": "indeterminate", "code": "unsupported-status-method", "proof_digest": "3c4ecfc8975b87122eb0264dfde19fd83cdfb252530acc9d7e5b621a04530ac8", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "cb6555282a01b5481fe6eeff2f4da3113e105c22ab6808e2a356827cb26ce632", + "context_digest": "4fcc18525452e1f39383d50aca2703ee70919c0d1766c4286162bfb4cc5b8d1c", "plan_digest": "70696130dac7917ae925b3a7223ebc028fdcd1dc5009a641d2c892eb8de3fd81", - "result_digest": "e266d5b21c0d4d6f99c52335348141dccd6e9ff1a8a799dd2adcbf1897595c9d", + "result_digest": "47f82e6e5be7938f44d9fc6307b2e50ef151d2b6ab1b036ccca56251d286908a", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1496, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -2496,7 +2496,7 @@ }, "context": { "path": "status/active-principal-status.context.cbor", - "sha256": "7b63730f3f530ba2a5d68609725ec8a4bb2585055e54e5b0612f329d33ba88aa" + "sha256": "72414ea85705ed4a33465b72398812b4eba9a5b0f311e1397330147457b0596e" }, "canonical_action": { "path": "status/active-principal-status.action.cbor", @@ -2517,15 +2517,15 @@ }, "expected_result": { "path": "status/active-principal-status.result.cbor", - "sha256": "205ebfd760d8540e2fc2586ad814022e6e5024552d9a1e0df8a108b487f71566", + "sha256": "5187751123d23baf467204d3240167abb2f1754c9a166cc1f34b8265da892750", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "1d29879a6318794bced798ff0d646d79ebf295168f9f719f5f26fd1259a935e3", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "3b8a3d9c27c6083ef38f8fd0762d0e43f79be0530dee47262670475619be9606", + "context_digest": "9506b57c6235fee7e0fecec0b7ee5394f0706afa5c7b93d70f98a8918b24e727", "plan_digest": "a2bef15eacbd3ec60c7be5e5f34e07dfcb9c10decf5260a12a048c5ec6741583", - "result_digest": "68761d713fc828cde4ab4bb9b4e216899ed1733ba7aface0f5d11a63654902fd", + "result_digest": "f921b26d97c0c943e2a238d82dffa766a7a275424c8a18dbf56463d5b963ddcc", "authorized_branches": [ "9696969696969696969696969696969696969696969696969696969696969696" ], @@ -2533,7 +2533,7 @@ "resources": { "proof_bytes": 875, "action_bytes": 144, - "context_bytes": 1643, + "context_bytes": 1645, "object_count": 4, "plan_leaves": 1, "plan_depth": 1, @@ -2553,7 +2553,7 @@ }, "context": { "path": "status/untrusted-principal-status-issuer.context.cbor", - "sha256": "947c7fa7a8b6e7aa5e932d599268cdaffe9b7ae08649ac509708790434dfd676" + "sha256": "0f9163f7a8fc0d83a18ba176915a88ac0f0984ddda6d36e08cfe018a3ff6c554" }, "canonical_action": { "path": "status/untrusted-principal-status-issuer.action.cbor", @@ -2574,21 +2574,21 @@ }, "expected_result": { "path": "status/untrusted-principal-status-issuer.result.cbor", - "sha256": "e92ce352dc8bb72264695542e720f4d048b222ef6a69530b2b1793e0d05c4821", + "sha256": "bd782fed61390881ae2be0740b639d22a8be2c4e62ca49988b0072c0ef920ee2", "stage": "authority", "decision": "denied", "code": "status-issuer-untrusted", "proof_digest": "3b49f30841fd885afacd8e2cf1749af8591ae64909b56c359286affbd8505b5d", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "41a253e60082d513dc7e08a24298985c32ae67a6eb5c9ab440bf747fabeb49e9", + "context_digest": "39f493bc27c6cd2302ce5ad97bc397ff438f724879d7b334bafe99c6e784520c", "plan_digest": "a2bef15eacbd3ec60c7be5e5f34e07dfcb9c10decf5260a12a048c5ec6741583", - "result_digest": "826f214db4c385f99b4c08012ce8b9c22c1a918c03505e92eabc9012339bc773", + "result_digest": "9ecc45014c33d086d581dff87451d1b80cadef9ae52c3df449424e46f70682a8", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1011, "action_bytes": 144, - "context_bytes": 1643, + "context_bytes": 1645, "object_count": 5, "plan_leaves": 1, "plan_depth": 1, @@ -2608,7 +2608,7 @@ }, "context": { "path": "denied/revoked-principal-status.context.cbor", - "sha256": "3782fcef2a68eb0e3106ec6adb828a6993892c3b1936ae74b2f2848eb563691a" + "sha256": "2e8b25356062e1bbb655e6854db9ce332312003a02b23be08ddec464d7796ba3" }, "canonical_action": { "path": "denied/revoked-principal-status.action.cbor", @@ -2629,21 +2629,21 @@ }, "expected_result": { "path": "denied/revoked-principal-status.result.cbor", - "sha256": "04ac91084e26176298d510ba98a09af1515a19371392c1835d86e39dc36ed378", + "sha256": "80720c72eb88fa3ee258c2dd980bf5f20b1824a8c2bd2b8ecb6eedecd7c35c3b", "stage": "authority", "decision": "denied", "code": "principal-revoked", "proof_digest": "2da3aecd8ba8d55f7a08f3732b6df3ba9468bacc552cd362f5e0521bd11a68ac", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "5d651e1eb0a50f44d239c41d095d9d99455dec1aa9c493eac968a06856153ccd", + "context_digest": "1985abaf356109d897a4662309351cdd30aa59a0cf4258850f9cf411d81d657e", "plan_digest": "70696130dac7917ae925b3a7223ebc028fdcd1dc5009a641d2c892eb8de3fd81", - "result_digest": "e142a8e94d8a3a0ea5ad488b4da3dc5d83305334d868545fd1180bbf7c7e0bfc", + "result_digest": "4627c0c8f001dcd473681ec557241fa6fd9f45bd079baad01f25dda2b7813833", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1890, "action_bytes": 144, - "context_bytes": 1643, + "context_bytes": 1645, "object_count": 8, "plan_leaves": 1, "plan_depth": 1, @@ -2663,7 +2663,7 @@ }, "context": { "path": "indeterminate/missing-principal-status.context.cbor", - "sha256": "8246ba97ac886f2470d1c557a1b03bfe89b36d34ae7dc46fb56453685172063f" + "sha256": "f2f749f5a77fd033b6b72b08d9c3f87f205d285c1366bc17757507cbf6c220d6" }, "canonical_action": { "path": "indeterminate/missing-principal-status.action.cbor", @@ -2684,21 +2684,21 @@ }, "expected_result": { "path": "indeterminate/missing-principal-status.result.cbor", - "sha256": "e21f5afaca44395e66099bb94441fa18f35685eb549b31d91ef7aa12f3d91a2b", + "sha256": "586cb559a151f7280f8f078a3d89cd651c953face495c6db83afe4c6db6568db", "stage": "authority", "decision": "indeterminate", "code": "missing-principal-status", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "a32529161cee8a9b65431b1d0f8f462743d12be1c938e0c666d3ce96004aab31", + "context_digest": "271053cb7fba5dbdbce8f02b0ecfdc2e9bc9d10b145f29435ee7de87cedb9046", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "469a85fe59ba473795e7221a6647d71565fa830a36329b1bf0664e491e0c6b80", + "result_digest": "35fba81eb0cd3d5b774727207709c569ec3838e763dabb8f1f01ac27d5359b75", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1215, + "context_bytes": 1217, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -2718,7 +2718,7 @@ }, "context": { "path": "denied/action-permission-not-granted.context.cbor", - "sha256": "bba4e01d24b8031b68b728355c163d4ea5baf033bbc7e647666738492947166a" + "sha256": "6dad8cfcbe63c45c350378540da1ef7a369045c09da5414d76007259a8770b9c" }, "canonical_action": { "path": "denied/action-permission-not-granted.action.cbor", @@ -2739,21 +2739,21 @@ }, "expected_result": { "path": "denied/action-permission-not-granted.result.cbor", - "sha256": "5c1dbcaf785b2f0e7ec73588ca3c93473f5748d452229a0870bd9a02f519bf3f", + "sha256": "e099d3cb4c46aaf5f7f2a29d425ee3f23a899537d8f06707121054e4275d66a8", "stage": "authority", "decision": "denied", "code": "permission-not-granted", "proof_digest": "29be07d6a5ceb8550ad53cd2d9e775569fbd27a2c896ee0f94d970f5125084b5", "action_digest": "dfb498fcb31c7de1e85a8642d13826f8479a9bdbd3229f2178de63337e8dba92", - "context_digest": "524846ecba92c7e1d7b8793743915d83f735de0bdaa7cdd83e38c13975837671", + "context_digest": "a26d0d72befd5bf712372e956be072e460d90bfa7c6fd89de45a9f462613f552", "plan_digest": "2af75ddb207db5bcdec335d9cc1ee40da5747e49fa927ea28dd1002d6aa9726f", - "result_digest": "1f53000a55febab5075940a734e6732505cc672886a13cab5bc007e40a94417d", + "result_digest": "7a2a3987c612d09f4ff3db37699680593356e1fa5d01f9bc84c7f259ce337623", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1477, "action_bytes": 146, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -2773,7 +2773,7 @@ }, "context": { "path": "denied/action-constraint-mismatch.context.cbor", - "sha256": "bba4e01d24b8031b68b728355c163d4ea5baf033bbc7e647666738492947166a" + "sha256": "6dad8cfcbe63c45c350378540da1ef7a369045c09da5414d76007259a8770b9c" }, "canonical_action": { "path": "denied/action-constraint-mismatch.action.cbor", @@ -2794,21 +2794,21 @@ }, "expected_result": { "path": "denied/action-constraint-mismatch.result.cbor", - "sha256": "46947130956c58897d412d5a48e737df629e5ff3237db9acd02b6ebbedd68c8c", + "sha256": "0ec3d8929f8eaed8cc4b2cac650807547696979bb922c8d49fc7652775ea7777", "stage": "authority", "decision": "denied", "code": "action-constraint-mismatch", "proof_digest": "4f4de706f72a0db8589a3582f2a6912f547c109a1ddc82782fd8400e363ce7c1", "action_digest": "9122e8057f45fe8f9e2c2eff0a6fe3e91d56b48244c52e2685f748c8c7472c7d", - "context_digest": "524846ecba92c7e1d7b8793743915d83f735de0bdaa7cdd83e38c13975837671", + "context_digest": "a26d0d72befd5bf712372e956be072e460d90bfa7c6fd89de45a9f462613f552", "plan_digest": "2af75ddb207db5bcdec335d9cc1ee40da5747e49fa927ea28dd1002d6aa9726f", - "result_digest": "fce43f36c64de132697890b324d24643976ada7aad1a0c7de930df1660607ef8", + "result_digest": "13dc62dec5bc65c2af63ab24e3e4bdf8e8c56e8b9129bcf39e98597449d03b6e", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1476, "action_bytes": 145, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -2828,7 +2828,7 @@ }, "context": { "path": "denied/action-budget-exceeded.context.cbor", - "sha256": "bba4e01d24b8031b68b728355c163d4ea5baf033bbc7e647666738492947166a" + "sha256": "6dad8cfcbe63c45c350378540da1ef7a369045c09da5414d76007259a8770b9c" }, "canonical_action": { "path": "denied/action-budget-exceeded.action.cbor", @@ -2849,21 +2849,21 @@ }, "expected_result": { "path": "denied/action-budget-exceeded.result.cbor", - "sha256": "4508c7d1689e1fe7574801e4fbd0e880ab58e51024f1b0419fe529bae99a1acc", + "sha256": "61c644494c40260ee986b6723bc8cf09d684280f637960463259d23ac990f1fd", "stage": "authority", "decision": "denied", "code": "budget-ceiling-exceeded", "proof_digest": "94313a09ed10dbf6ad24ee0b18d2dcc5d94933e4d0f32f8dcb9cd091d91f73c7", "action_digest": "2e943e8272aa6dc43efa1009d4c60bbe1cf63f75c40dd66139b3faf83af250e4", - "context_digest": "524846ecba92c7e1d7b8793743915d83f735de0bdaa7cdd83e38c13975837671", + "context_digest": "a26d0d72befd5bf712372e956be072e460d90bfa7c6fd89de45a9f462613f552", "plan_digest": "2af75ddb207db5bcdec335d9cc1ee40da5747e49fa927ea28dd1002d6aa9726f", - "result_digest": "bced220778a73da417a15e811a23dbf1adf964d73469e77ba854199db5f29850", + "result_digest": "af86fcfee905167ba466853be7a174af953c2fc1d7107f8d0317e2258033adeb", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -2883,7 +2883,7 @@ }, "context": { "path": "denied/action-budget-absent.context.cbor", - "sha256": "bba4e01d24b8031b68b728355c163d4ea5baf033bbc7e647666738492947166a" + "sha256": "6dad8cfcbe63c45c350378540da1ef7a369045c09da5414d76007259a8770b9c" }, "canonical_action": { "path": "denied/action-budget-absent.action.cbor", @@ -2901,21 +2901,21 @@ }, "expected_result": { "path": "denied/action-budget-absent.result.cbor", - "sha256": "73f83d1f9d6911976bc05bcdc1f680e3e919012169395e12a1dc6f6f02e3d983", + "sha256": "eef86092570efe99dc2d1d37892c42638175c3ab78b95956768e27047194911e", "stage": "authority", "decision": "denied", "code": "budget-ceiling-exceeded", "proof_digest": "b83cd035292a01f14c987032beed5134059ed35db6d34346ef40fab2a85932a1", "action_digest": "2689e2076637a9d685e83f1118ebda241a62727b84752fca1d867ddfa974efdd", - "context_digest": "524846ecba92c7e1d7b8793743915d83f735de0bdaa7cdd83e38c13975837671", + "context_digest": "a26d0d72befd5bf712372e956be072e460d90bfa7c6fd89de45a9f462613f552", "plan_digest": "2af75ddb207db5bcdec335d9cc1ee40da5747e49fa927ea28dd1002d6aa9726f", - "result_digest": "133920946e8c2e9a9a3e77728381c874b214826839c2644ca01bbe2e04343637", + "result_digest": "494452cfe15b87eeea75d663cb287b5be238dbf48a2c119b8ad7d8bd0b1509d8", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1453, "action_bytes": 122, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -2926,6 +2926,60 @@ "expected_decision": "denied", "expected_code": "budget-ceiling-exceeded" }, + { + "name": "action-budget-absent-budget-free-profile", + "class": "valid", + "proof": { + "path": "valid/action-budget-absent-budget-free-profile.proof.cbor", + "sha256": "b83cd035292a01f14c987032beed5134059ed35db6d34346ef40fab2a85932a1" + }, + "context": { + "path": "valid/action-budget-absent-budget-free-profile.context.cbor", + "sha256": "c3866caa41c631d8998f34c39bc210f156714a2ceba6c874e07299c730358bdd" + }, + "canonical_action": { + "path": "valid/action-budget-absent-budget-free-profile.action.cbor", + "sha256": "2689e2076637a9d685e83f1118ebda241a62727b84752fca1d867ddfa974efdd", + "profile": "auths.mcp", + "profile_version": 1, + "media_type": "application/vnd.auths.mcp-call.v1+cbor", + "capability": "tools/call", + "resource": "mcp://reports/read", + "requested_budget": null + }, + "canonical_body": { + "path": "valid/action-budget-absent-budget-free-profile.body.cbor", + "sha256": "5c63cdd40e91c52d8e1e01d76e968cb2cd262c6c0ca0e9674a0975a1118f0a2d" + }, + "expected_result": { + "path": "valid/action-budget-absent-budget-free-profile.result.cbor", + "sha256": "571f2e8b14c87691ba47b6af5239a8f374298f9b67121ed496347d7ac9f7687f", + "stage": "complete", + "decision": "authorized", + "code": "authorized", + "proof_digest": "b83cd035292a01f14c987032beed5134059ed35db6d34346ef40fab2a85932a1", + "action_digest": "2689e2076637a9d685e83f1118ebda241a62727b84752fca1d867ddfa974efdd", + "context_digest": "92f06ffc8808ff712bba19f06c6c40668a0c327252bab5653bda1cfcd955fc3c", + "plan_digest": "2af75ddb207db5bcdec335d9cc1ee40da5747e49fa927ea28dd1002d6aa9726f", + "result_digest": "97240bdb6968755b75f9753d4ac4452d7515c918e100b1f00eb71a2962d83dd1", + "authorized_branches": [ + "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1" + ], + "assurance_satisfactions": 2, + "resources": { + "proof_bytes": 1453, + "action_bytes": 122, + "context_bytes": 1174, + "object_count": 6, + "plan_leaves": 1, + "plan_depth": 1, + "work_units": 311 + }, + "registry_manifest": "3333333333333333333333333333333333333333333333333333333333333333" + }, + "expected_decision": "authorized", + "expected_code": "authorized" + }, { "name": "action-validity-expanded", "class": "denied", @@ -2935,7 +2989,7 @@ }, "context": { "path": "denied/action-validity-expanded.context.cbor", - "sha256": "bba4e01d24b8031b68b728355c163d4ea5baf033bbc7e647666738492947166a" + "sha256": "6dad8cfcbe63c45c350378540da1ef7a369045c09da5414d76007259a8770b9c" }, "canonical_action": { "path": "denied/action-validity-expanded.action.cbor", @@ -2956,21 +3010,21 @@ }, "expected_result": { "path": "denied/action-validity-expanded.result.cbor", - "sha256": "9810dc5d719e3aed2f6fae71b917caf64dc31c2b25a8e0e0c642b20b5ff925b5", + "sha256": "de22a851eef780cae307a6cd204732ae9b0ed17fd3331cf697080ea35d247cc4", "stage": "authority", "decision": "denied", "code": "action-outside-validity", "proof_digest": "9ee777b1c0d19c82c99680194d72a11831f614710b9690f27873fbc6546b9dd4", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "524846ecba92c7e1d7b8793743915d83f735de0bdaa7cdd83e38c13975837671", + "context_digest": "a26d0d72befd5bf712372e956be072e460d90bfa7c6fd89de45a9f462613f552", "plan_digest": "2af75ddb207db5bcdec335d9cc1ee40da5747e49fa927ea28dd1002d6aa9726f", - "result_digest": "687bd5b4f842c2a53fb0393a20321b71323c39cc5827e94c5418652744b3a65b", + "result_digest": "71bdfc7df01f0eea89814a31fd890940d6a1b97e3e2ac304e14ae5bab1ff3fb6", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1474, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -2990,7 +3044,7 @@ }, "context": { "path": "denied/action-actor-mismatch.context.cbor", - "sha256": "bba4e01d24b8031b68b728355c163d4ea5baf033bbc7e647666738492947166a" + "sha256": "6dad8cfcbe63c45c350378540da1ef7a369045c09da5414d76007259a8770b9c" }, "canonical_action": { "path": "denied/action-actor-mismatch.action.cbor", @@ -3011,21 +3065,21 @@ }, "expected_result": { "path": "denied/action-actor-mismatch.result.cbor", - "sha256": "8df651a2edb16d8f6441edabd5f493e848b9103e415b7e32ede05bebae7415e8", + "sha256": "686bead7926eb2240144e7749939c7bab3bdde6d6dc87c75c3c6070fc7e9d6eb", "stage": "authority", "decision": "denied", "code": "broken-grant-chain", "proof_digest": "9c54629985d174205dcff51c661bb587f5ed0561986e71fadece50a391da5249", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "524846ecba92c7e1d7b8793743915d83f735de0bdaa7cdd83e38c13975837671", + "context_digest": "a26d0d72befd5bf712372e956be072e460d90bfa7c6fd89de45a9f462613f552", "plan_digest": "2af75ddb207db5bcdec335d9cc1ee40da5747e49fa927ea28dd1002d6aa9726f", - "result_digest": "b7cec06153825b124c34e7e31c172f2058fbf9e23c0edcbdc5d56b42e3eba603", + "result_digest": "7fe5e967aa53c1d9586d1ba9b4b97444172e08e611abc928ab1a8659cf7c19f8", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -3045,7 +3099,7 @@ }, "context": { "path": "denied/unsupported-action-profile.context.cbor", - "sha256": "bba4e01d24b8031b68b728355c163d4ea5baf033bbc7e647666738492947166a" + "sha256": "6dad8cfcbe63c45c350378540da1ef7a369045c09da5414d76007259a8770b9c" }, "canonical_action": { "path": "denied/unsupported-action-profile.action.cbor", @@ -3066,21 +3120,21 @@ }, "expected_result": { "path": "denied/unsupported-action-profile.result.cbor", - "sha256": "b4bc40812ca0b4410f209855981776df4582a90da00757a2179af9a6fd162310", + "sha256": "519e00a9e91e5d2b26e801153e64ff03e8236b0ff161222298293823777457d7", "stage": "authority", "decision": "indeterminate", "code": "unsupported-profile", "proof_digest": "53b5aaa54d59519d5dab200436e91afa138f80742795de412edbe84be82502ee", "action_digest": "4e51c0b47dbbd1c702d94fba30c9267f757ff9425f4f366f8c535d7273d2d2f4", - "context_digest": "524846ecba92c7e1d7b8793743915d83f735de0bdaa7cdd83e38c13975837671", + "context_digest": "a26d0d72befd5bf712372e956be072e460d90bfa7c6fd89de45a9f462613f552", "plan_digest": "2af75ddb207db5bcdec335d9cc1ee40da5747e49fa927ea28dd1002d6aa9726f", - "result_digest": "e50bdcbc5b8e9402491e9eb2d39cbd4205e456dcfa5427ef47842be4cb1684b5", + "result_digest": "e17d458eb263eb20f045b656be35d308572a954c620008e8bb099f4f23741f28", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1479, "action_bytes": 148, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -3100,7 +3154,7 @@ }, "context": { "path": "denied/unknown-action-extension.context.cbor", - "sha256": "bba4e01d24b8031b68b728355c163d4ea5baf033bbc7e647666738492947166a" + "sha256": "6dad8cfcbe63c45c350378540da1ef7a369045c09da5414d76007259a8770b9c" }, "canonical_action": { "path": "denied/unknown-action-extension.action.cbor", @@ -3121,21 +3175,21 @@ }, "expected_result": { "path": "denied/unknown-action-extension.result.cbor", - "sha256": "1e78a187ab413258bb9a9a10c1c6588b1ceddcdd085b181a364b8de39c9306d9", + "sha256": "833cb7b7f9428ebf234d7c01e58d0b7efdb39301412c900cfc6388d7ca6c5fe9", "stage": "authority", "decision": "denied", "code": "critical-extension-unknown", "proof_digest": "70c94fbfc7eb0748414ce93b7653b088c03c68c8c010633d2f86531c7673d361", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "524846ecba92c7e1d7b8793743915d83f735de0bdaa7cdd83e38c13975837671", + "context_digest": "a26d0d72befd5bf712372e956be072e460d90bfa7c6fd89de45a9f462613f552", "plan_digest": "2af75ddb207db5bcdec335d9cc1ee40da5747e49fa927ea28dd1002d6aa9726f", - "result_digest": "1c5d27e04ff704455b147b9e37f1358445dedf80234bf0396792232003e90414", + "result_digest": "2908531b14c34704eba4da4089743b6f04e35fc366541e11ad2a5a77c6a6cd84", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1500, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -3155,7 +3209,7 @@ }, "context": { "path": "denied/action-channel-mismatch.context.cbor", - "sha256": "bba4e01d24b8031b68b728355c163d4ea5baf033bbc7e647666738492947166a" + "sha256": "6dad8cfcbe63c45c350378540da1ef7a369045c09da5414d76007259a8770b9c" }, "canonical_action": { "path": "denied/action-channel-mismatch.action.cbor", @@ -3176,21 +3230,21 @@ }, "expected_result": { "path": "denied/action-channel-mismatch.result.cbor", - "sha256": "4632fe85744780bd58e4760ceb4293fbce20a96fbba0abf8f0d7267bbb03c5fd", + "sha256": "f36626c4ddc881eec47615417c6da8522e8d77bd7bf3fa172e70426fa5f60123", "stage": "authority", "decision": "denied", "code": "local-policy-denied", "proof_digest": "27ac565af707c3ecc8a1d1f9ca57c8ddd1e6e1c2e568fbc6c7b39ad4d753a8da", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "524846ecba92c7e1d7b8793743915d83f735de0bdaa7cdd83e38c13975837671", + "context_digest": "a26d0d72befd5bf712372e956be072e460d90bfa7c6fd89de45a9f462613f552", "plan_digest": "2af75ddb207db5bcdec335d9cc1ee40da5747e49fa927ea28dd1002d6aa9726f", - "result_digest": "d9e10ad3a68c20ef3a08213b42b98c599fb879de3b3feaee85985a14f7e2602b", + "result_digest": "a9eacb19ba74af4789274212a6590a46f92619154706b4aea660dcbe4e54b2c0", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1486, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -3210,7 +3264,7 @@ }, "context": { "path": "denied/invalid-signature.context.cbor", - "sha256": "21d5ca2d3a4f6d4f158bec4403485a135148bb81c2d09d761b463f593d88cc2b" + "sha256": "721a6b6d2d1d52e8792f337f92c01a2310a7f43606c005e71d003f468ec2fe80" }, "canonical_action": { "path": "denied/invalid-signature.action.cbor", @@ -3231,21 +3285,21 @@ }, "expected_result": { "path": "denied/invalid-signature.result.cbor", - "sha256": "77c49eb37e500593cc0642b0152ef210e4b355b17fc839462e98034abfac892d", + "sha256": "28f450aa38e2e24d01bb71976adff19cdaa3d8c05738d615932e793ac5249cfb", "stage": "authority", "decision": "denied", "code": "invalid-signature", "proof_digest": "986c2f87cec14290c52434573b30b7d2a74297069d3e030e68f1ede6527fad5b", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "86fbbd4d60ed73a05424216d0ea9d69bbb021ce959a80e849049ed052b0fadcf", + "context_digest": "84a8c20ec6a7d9086d4aebb2d43ba731b738bd78177b79c55e359d6f6908926e", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "4fe4f6b737418e13ca172d64c53144f55bf6f360d7b13f871915121142697215", + "result_digest": "dfc62819ee24ffd37485b5b50a9faf3c09a5fa70be431c7466513a736411982e", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 799, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -3265,7 +3319,7 @@ }, "context": { "path": "invalid/missing-grant-reference.context.cbor", - "sha256": "f35fb9dff0a6d272a7a04154e5d23c4bc3cca661b82449884d0f105ef50b83b0" + "sha256": "319cb8c20f916985f9f2a808d63aeeb4dcb0ebd611e8277a5e4c81ad476df9f4" }, "canonical_action": { "path": "invalid/missing-grant-reference.action.cbor", @@ -3286,21 +3340,21 @@ }, "expected_result": { "path": "invalid/missing-grant-reference.result.cbor", - "sha256": "324a33154350ee78cfc4bf793669211e97b4bbfa4d30f0bbfa48bded8384ff61", + "sha256": "b81e4433d45e0e9d4ba97040e7b0b295fad3c77d1e04da067fc70156cd19dbbd", "stage": "resolve", "decision": "denied", "code": "missing-reference", "proof_digest": "b8becc181566911dd78c793666ec297c5def7cbdf36c35c3873cd203afd025c7", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "266080403c0e402ad45b1551cb39b7e4cef3ffca288827f05f2a47cac384f0ce", + "context_digest": "fd301dca23f4dbfb95044d08843e52131265c0d1c1957fe085d6fcaa8a0d62f6", "plan_digest": null, - "result_digest": "88a515f237eb62826879db4858328cc1b6534a4fbc68a78aeddaa480d3977949", + "result_digest": "e0f966e8ff5f3ce2671a56a6ed9a500cbf35b83f106232a76b0ea9eeef52aaa6", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 832, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -3320,7 +3374,7 @@ }, "context": { "path": "invalid/missing-plan-leaf.context.cbor", - "sha256": "7624f5dd4a7faae33759880d21bbfff0469cd323457d19c6e49198850d131d8c" + "sha256": "54ded6faf19a5047a2d5a4c416155b18204791e910b47f22e4fc6fe1226feb96" }, "canonical_action": { "path": "invalid/missing-plan-leaf.action.cbor", @@ -3341,21 +3395,21 @@ }, "expected_result": { "path": "invalid/missing-plan-leaf.result.cbor", - "sha256": "3f6695600e753ae12fd87c4ac336954feb6420c45e66bbdf2c3de7d2a2bfecaf", + "sha256": "52a2c9f3dd118fff031ccdbb8e0bb412768521d4b972eaf60835d62357166f0e", "stage": "resolve", "decision": "denied", "code": "missing-reference", "proof_digest": "0c0f9e321659bcfea9b142ec45273c39c1ba6924387ebbb1bb4d9665feef89f2", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "e005cdb74dec2c1bc6362f5cc1ad62bbf9278e8aeeaa994d1d222f026ad6490d", + "context_digest": "cb25f95472db61e2de872316b2be8d26f806099554094a55e683ca167e926227", "plan_digest": null, - "result_digest": "1324b33529470a142c9d32e0bdbaf680d8b3b7ed0f39a83c6ff85cea07945bf6", + "result_digest": "48cfe16b8ad0d4f7bd1dbb98b37e2ab095f4115146d910937a755640d891a113", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 842, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 3, "plan_leaves": 2, "plan_depth": 2, @@ -3375,7 +3429,7 @@ }, "context": { "path": "invalid/signature-suite-substitution.context.cbor", - "sha256": "89eb1630b4df907e86471b568c4242ef181085cde8b23624058a8721ee448e62" + "sha256": "5238641ae9b29e201f5a2b1fbd93eddae0b4782725a936743fc0a2d4ceb45fdb" }, "canonical_action": { "path": "invalid/signature-suite-substitution.action.cbor", @@ -3396,21 +3450,21 @@ }, "expected_result": { "path": "invalid/signature-suite-substitution.result.cbor", - "sha256": "6889946bbc5413fb04b20286b8ece5558c3c765f04ace7912bdb1d47675c91f4", + "sha256": "a15f710a4a455bef177f75195df37dae41532a79a3fe95e4dc593bf0e602e911", "stage": "authority", "decision": "denied", "code": "signature-suite-mismatch", "proof_digest": "e83191c867ff68fc6b86d9f60e507ee0de57790c99a2de21d773f84ae04d74eb", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "0c3d2bd9cbd7e56f3c21d47b02737544228b2b61db4f7e48201bfa32f8942da2", + "context_digest": "17f36f652bbca5b2c485a4ed37f01b0a3a0a62a59109015db4f527cc92768545", "plan_digest": "f34e855fb60f63db073a45dcf6393b6d778ee53156cafd0d36098520f3003642", - "result_digest": "421cf9187c59e863e515e0b41f3d4e132a8ea3024111c9febcda01ea4fda8543", + "result_digest": "0727b9798e4f962b4706f956b4af0440978a31aae99e54527759b32e4ee75b40", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 803, "action_bytes": 144, - "context_bytes": 1173, + "context_bytes": 1175, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -3430,7 +3484,7 @@ }, "context": { "path": "invalid/unknown-signature-suite.context.cbor", - "sha256": "29b9c552f4aae7328ad71011da61b1622489f5314b46c42168b25ef1d618e125" + "sha256": "a3ab44a52751920b45efc0002b95ea53e0d456c0b02a232f1bc2b03b2527332b" }, "canonical_action": { "path": "invalid/unknown-signature-suite.action.cbor", @@ -3451,21 +3505,21 @@ }, "expected_result": { "path": "invalid/unknown-signature-suite.result.cbor", - "sha256": "4c0882c7809425229326bcc6898432b530a7faab9974126de8871ae7be7e8fb6", + "sha256": "78ea364ac2bb9ae1b725791f350c3a0976cad27cc7857f0641f73a2491e071fe", "stage": "authority", "decision": "indeterminate", "code": "unsupported-signature-suite", "proof_digest": "cd4bdb7859c64cdb058c5b6c4c10a2019cfc9313bea54407a53c530eac005269", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "992f45466a723094ddd3049bdc1066dd46f1a6e00ebe0af8767e702430677717", + "context_digest": "08329e8fec480225fb82ec8f7ddd439ac3ea0335e6bfe907859c22cd5cdf17e1", "plan_digest": "d8c5617c488955741d7236985174af45a783a75915e66cb3d0f443aa0a33b4da", - "result_digest": "834a506df2849487d46abe8b49671640ae3a52393bca69de88b7dea7bc7f002b", + "result_digest": "2b20a6b84271e989ed73ae2c848f9584c790c0079055ba17a2db73775fb325df", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 805, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -3485,7 +3539,7 @@ }, "context": { "path": "invalid/unknown-principal-method.context.cbor", - "sha256": "06ee88eed5fbcad87c56939cbbef9d6da96ff9f16896ea39ac86cccd41e9f866" + "sha256": "befd86d64330444d4bb7e4f4acb296041387efb38e79687c047ad657f406a0b0" }, "canonical_action": { "path": "invalid/unknown-principal-method.action.cbor", @@ -3506,21 +3560,21 @@ }, "expected_result": { "path": "invalid/unknown-principal-method.result.cbor", - "sha256": "32636fb2b30b80262fe84ebfe4803fde9e2a8aa089b3c944a02f3020d3a447fb", + "sha256": "af478ae9a3a9fe6021b62501d333538b3342f98145a52da49f31eef74b863d62", "stage": "authority", "decision": "indeterminate", "code": "unsupported-principal-method", "proof_digest": "97ea0b571b2ad961baf0f66ecec59f0c8786b8c719f17cd3c81e66417c6065fc", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "d3979479adea33b1a9011ecb82fb89fc2e8bd0b2e220ac0486e7e96298c236df", + "context_digest": "7a7869029bcc9f947427d934755968a95febfde32cfc3c6c2c1d14feb16db101", "plan_digest": "a2c206fa942601eb9a2decf4ac0afc644f42c8c0969ee73a74c49e1309881329", - "result_digest": "047bf0baf39c31f476fdc10d26de8ecfd693f902fb435856aaa5e4ced584cef7", + "result_digest": "933b8255e07dd2510181dc086f25831197248d2bd5dabbe90099f13ea95b743f", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 806, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -3540,7 +3594,7 @@ }, "context": { "path": "invalid/unsupported-evidence-type.context.cbor", - "sha256": "eec5bbbd76e306873c88f2b521b6797e9c9740b0816c35efa906099356e3605a" + "sha256": "70303b0766a62c85f9b5b1b751fa008727abad8f82e7b3757b89d537a24a7f44" }, "canonical_action": { "path": "invalid/unsupported-evidence-type.action.cbor", @@ -3561,21 +3615,21 @@ }, "expected_result": { "path": "invalid/unsupported-evidence-type.result.cbor", - "sha256": "7255d545982c6787c31944289a833cf701ce90b53d62eeafd61938684b2146cb", + "sha256": "2d6bd6b50bec9465d417bcde3d4a09c644b7e9b4fa60d6bb511a090163eeca79", "stage": "authority", "decision": "indeterminate", "code": "unsupported-evidence-type", "proof_digest": "50685d44122298b98d8c0c14bc8b27c15cfc4ea4773ae500b1cb5c57790eb2eb", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "c25421fb1b54c5f9450e8f88151066e71a406124aadae91426bf8d8574750774", + "context_digest": "799bbb6de7fe516b4068c7c25ec5c7a522f3c959e9bd44e0f3d74d480057371a", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "5e519071a0e8dbe47d01c4e9c389c1bb29c3b8fa76940f1240b612857fc1244f", + "result_digest": "bfd456a04241a5afc25c19d920429cb17e19f9d40b3a8496d487c69cd813e1f2", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1272, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 4, "plan_leaves": 1, "plan_depth": 1, @@ -3595,7 +3649,7 @@ }, "context": { "path": "denied/principal-method-mismatch.context.cbor", - "sha256": "38e341176fd02096e7a2ca0391bde4a1bc199e7964344d8779d7a38c8990962f" + "sha256": "183106db71e64b6a04eb5a48ace19b19bebf24f0086edaac8fb5d5ef337143f1" }, "canonical_action": { "path": "denied/principal-method-mismatch.action.cbor", @@ -3616,21 +3670,21 @@ }, "expected_result": { "path": "denied/principal-method-mismatch.result.cbor", - "sha256": "6f9131b164f8e03cb95c42801ddce93ba3b9f22fa6c1b6406231914ed9233899", + "sha256": "10d5a09c32b3c94659385d8020567d94f2ec656d4d54c47f8b95d33acb197234", "stage": "authority", "decision": "denied", "code": "principal-method-mismatch", "proof_digest": "8dfdb543c227df4a1eeb2cadc94252980ddbf9d945bae87bcc7bf8d1c3b51b04", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "df3c545f2f287af8c3a428aba71dae1a7589f395d54d1487d2da249f2fb7b51c", + "context_digest": "414cfc238321eaede1bd66aecef364458088f611e0c525298058fb6cd1f09ac8", "plan_digest": "9e624d4b8fe4eb623428d404b5ea8516146c4d804efebc774ac306c0bcf82151", - "result_digest": "d5527fe7f9b997365af0dbfeb8bd90ae035809832bad6fc599a6c3e5532adf9e", + "result_digest": "d14f7419684531cd080d2d617ac32dcd52f1ff9cc3e53e5109f5347f6a75bd48", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 767, "action_bytes": 144, - "context_bytes": 1092, + "context_bytes": 1094, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -3650,7 +3704,7 @@ }, "context": { "path": "denied/verification-method-mismatch.context.cbor", - "sha256": "3c4e1158ae6268bd64d77a42ccdc06d7feeb7a9e92dd80cb3a55c268ba65786f" + "sha256": "4bfbc908353ad8c3a7173813caae395c58cadfbcc4cb560ec498b6d45238332c" }, "canonical_action": { "path": "denied/verification-method-mismatch.action.cbor", @@ -3671,21 +3725,21 @@ }, "expected_result": { "path": "denied/verification-method-mismatch.result.cbor", - "sha256": "7e231221adea206aee1e5d532e0fa3578fb6f08f97b8cbbb2ba8346fbf8ad9aa", + "sha256": "8f53451be90ce40572593b590d1a75312b939acd9de27eb9cd106cf4e7e04106", "stage": "authority", "decision": "denied", "code": "verification-method-mismatch", "proof_digest": "cb402d720a641c67adce3ea235fb81cfd3af9b22ba41d9906a29aa42d267e5ad", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "da1cabaf762a6b8853d1bb4fe3e9a58b5942707086f5eeb3f42be111a259c780", + "context_digest": "a6e5e1b61a495260344996a67a77ed57ef2b1c3ed2266cbf63337464bd77ffd4", "plan_digest": "9e624d4b8fe4eb623428d404b5ea8516146c4d804efebc774ac306c0bcf82151", - "result_digest": "78032e8abd260fdec56449dd22fee33053bb8af591ba32649c49306e70a5877a", + "result_digest": "9c895fd5310dba6a487944220e2ca945b69bc6ca57561f373c94edf225f05675", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 778, "action_bytes": 144, - "context_bytes": 1124, + "context_bytes": 1126, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -3705,7 +3759,7 @@ }, "context": { "path": "denied/plan-action-mismatch.context.cbor", - "sha256": "419db7e1b8dc456ac8d53cfb739a2abe2b29d0056720c5502d1ff21019254e6a" + "sha256": "5299a44d09017daa61c2dc7f8d62be675b8a59a8055df5d9604e7d02fcf29be3" }, "canonical_action": { "path": "denied/plan-action-mismatch.action.cbor", @@ -3726,21 +3780,21 @@ }, "expected_result": { "path": "denied/plan-action-mismatch.result.cbor", - "sha256": "4176ec604c684fbf69c1d84979ea3cda287c4b4bc0890ed5c1e011af8c70d0ce", + "sha256": "bced9f7e0f18a0a3418cc455619727c199092e6abdf32715969bdae27473a031", "stage": "resolve", "decision": "denied", "code": "plan-action-mismatch", "proof_digest": "649d99f12b5962f2d288859b49b2e301b6a3c2c648e29116bc49a7701cde35b3", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "55fe8b92ebdbc2693208ba91777d413e45dac745972419d29436a9c30fc64b9c", + "context_digest": "c0dadbc74c7fc5310c947653880f7887b34ba9a332b8d59bab0b6b1ef8150f92", "plan_digest": null, - "result_digest": "108d6c36afbb5edd392c74338227d12229dc9a0578c2295feed886393c432ce4", + "result_digest": "39ebb781b98831e681d086ca96e44a626b52136e3d1c6e7350861fed9f11c98c", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 799, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -3760,7 +3814,7 @@ }, "context": { "path": "invalid/carried-status-digest-mismatch.context.cbor", - "sha256": "5bb2874362972c1a7d7d9c81ee16cefe1c6a75518ff2a1458aab0f92e7c48989" + "sha256": "e9ba61988ed335da400419e325db3168ad08ee5f9670814c065ee9cc8f9844e5" }, "canonical_action": { "path": "invalid/carried-status-digest-mismatch.action.cbor", @@ -3781,21 +3835,21 @@ }, "expected_result": { "path": "invalid/carried-status-digest-mismatch.result.cbor", - "sha256": "3227d60a1db6a2a405028e8dd9d5e3e6dc878dc665f2e375c1181590ee7f7ed9", + "sha256": "b9c85ac2d1379dfb6e8e2f50ee9ed151b076dda51ee9d6ef29963e8289a17dc1", "stage": "resolve", "decision": "denied", "code": "digest-mismatch", "proof_digest": "1831f42a9d1776137f438d4878df1619af3a025d0dac8e1113abb91f9efff7c2", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "500c2effc7255379e5430e2dcddccd4efaa590ca9c4472575fd8be7a9178ca11", + "context_digest": "dd6fe68d3246f61e2a9a4a24aa698cb7b59b86e555e5273c01363ed9b9349d6a", "plan_digest": null, - "result_digest": "810d43ce00dd99e3034048f67cfee8bdeab56043eedd942efaaf717077fde947", + "result_digest": "e1c3743a34ca2299639a5910137e283f76b30e441e51e8066a168de114ca4ae9", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1860, "action_bytes": 144, - "context_bytes": 1548, + "context_bytes": 1550, "object_count": 8, "plan_leaves": 1, "plan_depth": 1, @@ -3815,7 +3869,7 @@ }, "context": { "path": "invalid/p256-high-s-signature.context.cbor", - "sha256": "503a59d0ab6ba697a5cdd6b2577586baba37090fe421be1eac1c4dd00b2e39f9" + "sha256": "f4747210cb47b6f23a6aff2be4b4510b12b433771c12d5808aef1ab3cc8eb693" }, "canonical_action": { "path": "invalid/p256-high-s-signature.action.cbor", @@ -3836,21 +3890,21 @@ }, "expected_result": { "path": "invalid/p256-high-s-signature.result.cbor", - "sha256": "8de1de101a66d536d7479bcc7e986be9d58764bd277e11aea3774277489da3cd", + "sha256": "c4a403ae5349b164f990927db74e8338a5013ca3b3bb84380602a5c66b1f881c", "stage": "authority", "decision": "denied", "code": "invalid-signature", "proof_digest": "42ed4153f22473789f7bdbb98082498425ecdca28ac42da3b06911a80e450e20", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "4dc044790c870ff30fb787cc1a8abbb114d8e2ee09494ac7cee0c28934f7691f", + "context_digest": "fca412742d3d1bff1fd28e342265bf628dd2fdac24646c97f83873d6352cde84", "plan_digest": "167417cf9d3638ebb8142571e0751f075062a353cd63234ecdcb46923ed3b20b", - "result_digest": "1033ac008cc45a34f7f59288b7e3df2a043c80089bb18b0b81d5e131011a3d3a", + "result_digest": "e30cde5391f9f5e7903fb5f2b71f6daf9f4d4709bdc5c76b4da291d2781f54af", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 804, "action_bytes": 144, - "context_bytes": 1162, + "context_bytes": 1164, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -3870,7 +3924,7 @@ }, "context": { "path": "invalid/bundle-byte-limit-exceeded.context.cbor", - "sha256": "857e7d70a9f9cf52d10c9dca7a8c9f4dc11f8b5a1d96b5d708ccb62114f5c7f0" + "sha256": "b341f027199fac4d3b40a2f5e9d38d40554670e2472cc1a5eacfe16f169f2862" }, "canonical_action": { "path": "invalid/bundle-byte-limit-exceeded.action.cbor", @@ -3891,21 +3945,21 @@ }, "expected_result": { "path": "invalid/bundle-byte-limit-exceeded.result.cbor", - "sha256": "760d161332d83d637ba0d188181d55a68f40b09be65b0955c0537299ff06b35f", + "sha256": "d7428ccd5730a03ca166a356c45379bbefc1ec984498124911c94ef0afb7fa9f", "stage": "decode", "decision": "denied", "code": "resource-limit-exceeded", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "f16339de8f4238fba8e351dc1514ded541ab19f26a9ff3ef73eca98797378882", + "context_digest": "536d62eb07acb73c35eefcbf7a6a1acd6dda96443b8a3df0c68ce55b0f04268d", "plan_digest": null, - "result_digest": "910662a1e72c220ed73acb479626bedcb6a05009dc5fae6c847bb8bfabb590b3", + "result_digest": "9582fc8c2280ca9e73c1ecdd64dc6e6fd923f557d38fc73c4451ec0f3539521a", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1156, + "context_bytes": 1158, "object_count": 0, "plan_leaves": 0, "plan_depth": 0, @@ -3925,7 +3979,7 @@ }, "context": { "path": "invalid/verification-work-limit-exceeded.context.cbor", - "sha256": "3c1b0b50108060ebc3069ffdcef8abef92328227fcb11d7b8e50c14dfc677723" + "sha256": "c62478b2457a94b842978507ebf772ae86214dd1c0abf11a323ceecf68cc6d20" }, "canonical_action": { "path": "invalid/verification-work-limit-exceeded.action.cbor", @@ -3946,21 +4000,21 @@ }, "expected_result": { "path": "invalid/verification-work-limit-exceeded.result.cbor", - "sha256": "78b10528eac7a320188cfa7911ac9a94023a214646cdaeff096433d643f6d56a", + "sha256": "9f0ed2737c25a2319094f2d81f0c9e3a5ec95e5a336ec2bf2be1958cc8a0f586", "stage": "principalcontrol", "decision": "denied", "code": "resource-limit-exceeded", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "e53bcea1c95f37b6f171b207fac40d951273cdff8b844d4c6d7a06f44d629929", + "context_digest": "53a2735c00f089095edc6830d564e390696ab2dd862fb9398d06241c99384f8e", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "e989eb1d63d54d4cacd7e4e719e693142476a07de5541285052a45006ad5eaae", + "result_digest": "5c50807c892ea1104fea84d9c064704db5144c0af2417121d2e40eeb83422be0", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1157, + "context_bytes": 1159, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -3980,7 +4034,7 @@ }, "context": { "path": "invalid/plan-depth-limit-exceeded.context.cbor", - "sha256": "105b9d92de7574aad72dcd40543bc74bf31fa2f58d311d68847d05700b66f0c0" + "sha256": "9462bdb8e9afd0e77cf2c5a79a80ad82fe1d8e4b589ead37990f2ad09de2b9c9" }, "canonical_action": { "path": "invalid/plan-depth-limit-exceeded.action.cbor", @@ -4001,21 +4055,21 @@ }, "expected_result": { "path": "invalid/plan-depth-limit-exceeded.result.cbor", - "sha256": "1b24f915eae04fd9e8a82900d0c1c45e4cb44c7d1613f59fb9e589922914c301", + "sha256": "372fb5b099a27c3c13141f70dc646f6b52e517fdebcde33aeb058432fe801481", "stage": "decode", "decision": "denied", "code": "resource-limit-exceeded", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "1c8ca17f31cf4e93a03224cfb32ff752f41f83adb562ab9eb71beffb1e0f8d6a", + "context_digest": "2c75d2098b9a6892b161c8af3a8e417e184ef3bbd45bd38c9df164206b2aa59d", "plan_digest": null, - "result_digest": "6c3dc3f4b7590f59d8f93996b6c0b18f9d7d75127a4eb1b4de700fc0891e021a", + "result_digest": "18df1b083bba81e6640a8be5ca4ff2b101bd3a29a097c68a66ab9e56c70cd834", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 0, "plan_leaves": 0, "plan_depth": 0, @@ -4035,7 +4089,7 @@ }, "context": { "path": "invalid/missing-principal-evidence.context.cbor", - "sha256": "eec5bbbd76e306873c88f2b521b6797e9c9740b0816c35efa906099356e3605a" + "sha256": "70303b0766a62c85f9b5b1b751fa008727abad8f82e7b3757b89d537a24a7f44" }, "canonical_action": { "path": "invalid/missing-principal-evidence.action.cbor", @@ -4056,21 +4110,21 @@ }, "expected_result": { "path": "invalid/missing-principal-evidence.result.cbor", - "sha256": "74bafcac3b8004273106a1b18cff5f3c6912b7579c9b9a6a9e0b42791a0fc1dc", + "sha256": "4dd1d176ae1dce3571ec7ff8a2384a6bf2a5e0b593aab56f864e96ac2dcbad78", "stage": "authority", "decision": "indeterminate", "code": "missing-principal-evidence", "proof_digest": "4ce3eb9cf4db4c245607f16ec5d03da3f963d2fc9082557bd27ae4d570d87e52", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "c25421fb1b54c5f9450e8f88151066e71a406124aadae91426bf8d8574750774", + "context_digest": "799bbb6de7fe516b4068c7c25ec5c7a522f3c959e9bd44e0f3d74d480057371a", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "a9bb065316dcfa2afb1b2c3f5ea32ca02d5c8c4e51d762d569587cd812ee07c2", + "result_digest": "2b628b7d8ae90abf64232200dc1b921af14f0a9f84afd7f15f04d86a6263b5d1", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1051, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 2, "plan_leaves": 1, "plan_depth": 1, @@ -4090,7 +4144,7 @@ }, "context": { "path": "invalid/unused-critical-evidence.context.cbor", - "sha256": "eec5bbbd76e306873c88f2b521b6797e9c9740b0816c35efa906099356e3605a" + "sha256": "70303b0766a62c85f9b5b1b751fa008727abad8f82e7b3757b89d537a24a7f44" }, "canonical_action": { "path": "invalid/unused-critical-evidence.action.cbor", @@ -4111,21 +4165,21 @@ }, "expected_result": { "path": "invalid/unused-critical-evidence.result.cbor", - "sha256": "593ab78e0c989c7061d3b80621f8c061c717ca468593451c46466c1aa04869b6", + "sha256": "2e7fdb6553965b0b236236f23f647e7787cead88fba4174ee65824818f078ef6", "stage": "principalcontrol", "decision": "denied", "code": "unused-critical-evidence", "proof_digest": "7513d942d3172c348fe89aae8b782d979f4956d228014940ddce376c289382f9", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "c25421fb1b54c5f9450e8f88151066e71a406124aadae91426bf8d8574750774", + "context_digest": "799bbb6de7fe516b4068c7c25ec5c7a522f3c959e9bd44e0f3d74d480057371a", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "e44f036263f451ac15fe03ff99526fa985af33f533eeb2bfc5465d70d23b4eee", + "result_digest": "fe5991b151f8961af47f316d7779c6afc999a7758ac486d6aeac53e9fdd494be", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1611, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 7, "plan_leaves": 1, "plan_depth": 1, @@ -4145,7 +4199,7 @@ }, "context": { "path": "invalid/duplicate-action-object.context.cbor", - "sha256": "eec5bbbd76e306873c88f2b521b6797e9c9740b0816c35efa906099356e3605a" + "sha256": "70303b0766a62c85f9b5b1b751fa008727abad8f82e7b3757b89d537a24a7f44" }, "canonical_action": { "path": "invalid/duplicate-action-object.action.cbor", @@ -4166,21 +4220,21 @@ }, "expected_result": { "path": "invalid/duplicate-action-object.result.cbor", - "sha256": "f6303a0f8d214d4724e7e1a7802be2fbf7bfabd619bfc288264d2abedd60e5b8", + "sha256": "e8daffdec111b7d2189b8745ce98deb8a2fbf80278af517ad67ee13f820b4038", "stage": "resolve", "decision": "denied", "code": "duplicate-object", "proof_digest": "7beea0a0d999aa6d69f86cbbe0f7d8f924e8fdf5f2cd746dfdf9f278d0a9083b", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "c25421fb1b54c5f9450e8f88151066e71a406124aadae91426bf8d8574750774", + "context_digest": "799bbb6de7fe516b4068c7c25ec5c7a522f3c959e9bd44e0f3d74d480057371a", "plan_digest": null, - "result_digest": "12a527a131912ef9ba1900c6109b8dc5a95176b9cf03d014a17a889ca18cf55b", + "result_digest": "9f435a8f66ad4cef15ed4d259e53b7d4cdc0819976ca1f256ac0858a0b36871a", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 2008, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 7, "plan_leaves": 1, "plan_depth": 1, @@ -4200,7 +4254,7 @@ }, "context": { "path": "invalid/duplicate-control-binding.context.cbor", - "sha256": "eec5bbbd76e306873c88f2b521b6797e9c9740b0816c35efa906099356e3605a" + "sha256": "70303b0766a62c85f9b5b1b751fa008727abad8f82e7b3757b89d537a24a7f44" }, "canonical_action": { "path": "invalid/duplicate-control-binding.action.cbor", @@ -4221,21 +4275,21 @@ }, "expected_result": { "path": "invalid/duplicate-control-binding.result.cbor", - "sha256": "f355a19b3f1e91d1d12217464e6c12717c5787fc6dd9ed8dae3d3bc14f143746", + "sha256": "bf9c6ed3c7ca4c8349c45583ba08996fd91cd4b63fe03908737d994c13ea288d", "stage": "resolve", "decision": "denied", "code": "duplicate-object", "proof_digest": "74e47e25de8213fd4ad0f6d414afb0160d416db1b5ea3312325cb2d53f719bf0", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "c25421fb1b54c5f9450e8f88151066e71a406124aadae91426bf8d8574750774", + "context_digest": "799bbb6de7fe516b4068c7c25ec5c7a522f3c959e9bd44e0f3d74d480057371a", "plan_digest": null, - "result_digest": "b5e07589e8a557fd618c03f7a7a6446dabae56bc0579f2aeea662a68f5557d81", + "result_digest": "161771b39b91e3d9292e2c161ed7109eebd1dbbb909239ac647f3c5e559d8e04", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1551, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 7, "plan_leaves": 1, "plan_depth": 1, @@ -4255,7 +4309,7 @@ }, "context": { "path": "invalid/unused-grant-object.context.cbor", - "sha256": "eec5bbbd76e306873c88f2b521b6797e9c9740b0816c35efa906099356e3605a" + "sha256": "70303b0766a62c85f9b5b1b751fa008727abad8f82e7b3757b89d537a24a7f44" }, "canonical_action": { "path": "invalid/unused-grant-object.action.cbor", @@ -4276,21 +4330,21 @@ }, "expected_result": { "path": "invalid/unused-grant-object.result.cbor", - "sha256": "f6997aaaa7c5847beb9f4e845ae8821bd9facfff05472e37640978ce7d0bf54d", + "sha256": "30e131938cd9f94b46e107f42cc3203a24ad2cdb1912fc31d7380752b1719e2e", "stage": "resolve", "decision": "denied", "code": "unused-critical-evidence", "proof_digest": "3f9ac54170dcdc0376dbd6f199ffd799b508223c4a328b8e8b060a8e2e3e0661", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "c25421fb1b54c5f9450e8f88151066e71a406124aadae91426bf8d8574750774", + "context_digest": "799bbb6de7fe516b4068c7c25ec5c7a522f3c959e9bd44e0f3d74d480057371a", "plan_digest": null, - "result_digest": "77654203138c5a69b2b72768827211f73b2a0b87161899204c6c0e9090073ce5", + "result_digest": "facb703efe0bd41a45c0ae296a8abf6de352994c97165ee93537e7e16259dac2", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1849, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 7, "plan_leaves": 1, "plan_depth": 1, @@ -4310,7 +4364,7 @@ }, "context": { "path": "invalid/evidence-count-over-default.context.cbor", - "sha256": "eec5bbbd76e306873c88f2b521b6797e9c9740b0816c35efa906099356e3605a" + "sha256": "70303b0766a62c85f9b5b1b751fa008727abad8f82e7b3757b89d537a24a7f44" }, "canonical_action": { "path": "invalid/evidence-count-over-default.action.cbor", @@ -4331,21 +4385,21 @@ }, "expected_result": { "path": "invalid/evidence-count-over-default.result.cbor", - "sha256": "178bf2fdd17d72806a9c0f73d93dc632c1adacc70c3a2414054b3832c02078a8", + "sha256": "20901d4dfc482530f7e96521d0cf9821ac84d8f910460a4c9edfa14767c4eb9d", "stage": "decode", "decision": "denied", "code": "resource-limit-exceeded", "proof_digest": "e0976609b1ccf0a65738decf7f52c1c89206289ec47a69e0f37043c542374916", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "c25421fb1b54c5f9450e8f88151066e71a406124aadae91426bf8d8574750774", + "context_digest": "799bbb6de7fe516b4068c7c25ec5c7a522f3c959e9bd44e0f3d74d480057371a", "plan_digest": null, - "result_digest": "80790e6e4200ca44bd817f56c52ae8a3ad560298677c7bac58716df5013e4a56", + "result_digest": "3382a5709114878d1f8427997aa027e6ea523f1def8d4674bd1be82f0c1d69da", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 5692, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 0, "plan_leaves": 0, "plan_depth": 0, @@ -4365,7 +4419,7 @@ }, "context": { "path": "invalid/duplicate-cbor-key.context.cbor", - "sha256": "eec5bbbd76e306873c88f2b521b6797e9c9740b0816c35efa906099356e3605a" + "sha256": "70303b0766a62c85f9b5b1b751fa008727abad8f82e7b3757b89d537a24a7f44" }, "canonical_action": { "path": "invalid/duplicate-cbor-key.action.cbor", @@ -4386,21 +4440,21 @@ }, "expected_result": { "path": "invalid/duplicate-cbor-key.result.cbor", - "sha256": "92d2796bd02dbf972c82a31f560d09a5f8b335c1b31bab22bcf723ec6e231e17", + "sha256": "b73a46308a089cdbfa8b0af86f9bb5b0f7b98f36b4044afccec0ff8d674bcf93", "stage": "decode", "decision": "denied", "code": "malformed-proof", "proof_digest": "301851af044049a64972f9353eec991fff3a7e72bba74868165f1eb6ed7fb851", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "c25421fb1b54c5f9450e8f88151066e71a406124aadae91426bf8d8574750774", + "context_digest": "799bbb6de7fe516b4068c7c25ec5c7a522f3c959e9bd44e0f3d74d480057371a", "plan_digest": null, - "result_digest": "a7a3dfbcc12d9c658ff255c42385a2fa1dc0a9b3ac745afc2659c3a11a3d2f30", + "result_digest": "e97e2d3fa1aa0c12be0da66d3845b8a35d95c14dbe64ae35ff8b065d1e891470", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1477, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 0, "plan_leaves": 0, "plan_depth": 0, @@ -4420,7 +4474,7 @@ }, "context": { "path": "invalid/non-minimal-integer.context.cbor", - "sha256": "eec5bbbd76e306873c88f2b521b6797e9c9740b0816c35efa906099356e3605a" + "sha256": "70303b0766a62c85f9b5b1b751fa008727abad8f82e7b3757b89d537a24a7f44" }, "canonical_action": { "path": "invalid/non-minimal-integer.action.cbor", @@ -4441,21 +4495,21 @@ }, "expected_result": { "path": "invalid/non-minimal-integer.result.cbor", - "sha256": "1236650430589b1dbec81be3ffe512a34dd34d40dfb7570de9c1a40913796d54", + "sha256": "79ba978c29e1013d0969552df4cc634437ca59f4c978903fe6cf79f403154bbe", "stage": "decode", "decision": "denied", "code": "non-canonical-proof", "proof_digest": "dc25984798befe24c508b4f74c462fe0c3cf6b4d9a20d71be1274a53effc7e8a", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "c25421fb1b54c5f9450e8f88151066e71a406124aadae91426bf8d8574750774", + "context_digest": "799bbb6de7fe516b4068c7c25ec5c7a522f3c959e9bd44e0f3d74d480057371a", "plan_digest": null, - "result_digest": "a538fda0c0e18438fecec0b87f5158958225bf17a19fe708c6c1e4f4a00723dd", + "result_digest": "3efc2f5617fbd722effa2ce6329c9cd36de2fb25e80b9f10f053778f3d515dbc", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1476, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 0, "plan_leaves": 0, "plan_depth": 0, @@ -4475,7 +4529,7 @@ }, "context": { "path": "invalid/trailing-bytes.context.cbor", - "sha256": "eec5bbbd76e306873c88f2b521b6797e9c9740b0816c35efa906099356e3605a" + "sha256": "70303b0766a62c85f9b5b1b751fa008727abad8f82e7b3757b89d537a24a7f44" }, "canonical_action": { "path": "invalid/trailing-bytes.action.cbor", @@ -4496,21 +4550,21 @@ }, "expected_result": { "path": "invalid/trailing-bytes.result.cbor", - "sha256": "4ae1dd27df6692abc7abbf819797ddb2c58f6eb42bec51d5122eeb672406f6d0", + "sha256": "7957fffda1576725d4148e895de03529ba5135252ad83536445315d541d21119", "stage": "decode", "decision": "denied", "code": "malformed-proof", "proof_digest": "a3acef185e126403afb9f546528497991487b997a763e670754baf549270ce85", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "c25421fb1b54c5f9450e8f88151066e71a406124aadae91426bf8d8574750774", + "context_digest": "799bbb6de7fe516b4068c7c25ec5c7a522f3c959e9bd44e0f3d74d480057371a", "plan_digest": null, - "result_digest": "4c8b15a13bc30f6b0976a637c2806fc091f734a4d7b2951f9877ceee739ebcb4", + "result_digest": "13602b09c68cbc4b943fc8e6bb556fbc9dfdea71772a4777c53c45cd998a6745", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1476, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 0, "plan_leaves": 0, "plan_depth": 0, @@ -4530,7 +4584,7 @@ }, "context": { "path": "denied/mismatched-profile-version.context.cbor", - "sha256": "eec5bbbd76e306873c88f2b521b6797e9c9740b0816c35efa906099356e3605a" + "sha256": "70303b0766a62c85f9b5b1b751fa008727abad8f82e7b3757b89d537a24a7f44" }, "canonical_action": { "path": "denied/mismatched-profile-version.action.cbor", @@ -4551,21 +4605,21 @@ }, "expected_result": { "path": "denied/mismatched-profile-version.result.cbor", - "sha256": "f3d9b7a37a098660952e0bf6ce565d0cb4d65b4e487a0419d29017e3128883ee", + "sha256": "4832723089d77fdff54e2cd601d3d17228c56b81d25bc6001a8e05a0a8d2536c", "stage": "authority", "decision": "denied", "code": "action-body-mismatch", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "9c389cd2f55d47089c0c4a2984db71cd594d14b1f8764ca5161cff918d3bf812", - "context_digest": "c25421fb1b54c5f9450e8f88151066e71a406124aadae91426bf8d8574750774", + "context_digest": "799bbb6de7fe516b4068c7c25ec5c7a522f3c959e9bd44e0f3d74d480057371a", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "ddf11bb3ec05ea87b406fc929e7ab4cd643d5654ead795bc2f50024bb1469a45", + "result_digest": "08c5a81c843ad86601488aaedaf36ee1deca0a02c6ebe01f11330c4815538c7f", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -4585,7 +4639,7 @@ }, "context": { "path": "denied/byte-distinct-action.context.cbor", - "sha256": "eec5bbbd76e306873c88f2b521b6797e9c9740b0816c35efa906099356e3605a" + "sha256": "70303b0766a62c85f9b5b1b751fa008727abad8f82e7b3757b89d537a24a7f44" }, "canonical_action": { "path": "denied/byte-distinct-action.action.cbor", @@ -4606,21 +4660,21 @@ }, "expected_result": { "path": "denied/byte-distinct-action.result.cbor", - "sha256": "49940c2617da8696f2aad25f403617f56d780d67979a9f8604d4c3242fffec9d", + "sha256": "28f3f8ddc53f535649d762cc830d9e29444d43445cc9f2c4d918961304723d0d", "stage": "authority", "decision": "denied", "code": "action-body-mismatch", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "9122e8057f45fe8f9e2c2eff0a6fe3e91d56b48244c52e2685f748c8c7472c7d", - "context_digest": "c25421fb1b54c5f9450e8f88151066e71a406124aadae91426bf8d8574750774", + "context_digest": "799bbb6de7fe516b4068c7c25ec5c7a522f3c959e9bd44e0f3d74d480057371a", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "f8e408dba62bb24ff2e604f68661c0d474446f8d85987edd330c5166be1e2bd2", + "result_digest": "374e12d124de1b2b289b032785ce0a7a28c4c53f83f5914a7f53a93f4b4af0aa", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 145, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -4640,7 +4694,7 @@ }, "context": { "path": "denied/wrong-audience.context.cbor", - "sha256": "05a0ca3b9b17e5c624c74488824d568bf1dd72c884f99e26fcb02c11798338ce" + "sha256": "5bdd5beb6853fa8599f87e392da0e898ef68261f50785299fe758b2abf254aa0" }, "canonical_action": { "path": "denied/wrong-audience.action.cbor", @@ -4661,21 +4715,21 @@ }, "expected_result": { "path": "denied/wrong-audience.result.cbor", - "sha256": "43b62c3fe391059ab119d30a067f66691c54c990ba80bcd21d1652eca5ae9eb3", + "sha256": "b4c5534ade2fd8801c05af0fc6265030336a0efed2fa91677af19a8f8e922898", "stage": "authority", "decision": "denied", "code": "audience-mismatch", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "ed1ab768fbac17bf1ed2883be5eb14a6e67ea3057ac861257c99ff40c0cc813c", + "context_digest": "8bea915de029f80ecb89b75c6531fd47e5f0af25d528c3803620875746c64a61", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "5dd63bfd0b66dc8735ab542337e7ffcefb6cae2cdd48f037e7acb805ba588ecb", + "result_digest": "dba2881f761b09c450662f5c2623d2affe5d95d04451c62892e97a7135fe8b1f", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1164, + "context_bytes": 1166, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -4695,7 +4749,7 @@ }, "context": { "path": "denied/wrong-challenge.context.cbor", - "sha256": "61550d3f1be25ba96ee2bd29fe40080a0449f9277c0982169de9e78b73eb281b" + "sha256": "f4fcf33db3119a000b14ff9b41212fc4f9274d4c5055d8641cc208bb8b1a4f0c" }, "canonical_action": { "path": "denied/wrong-challenge.action.cbor", @@ -4716,21 +4770,21 @@ }, "expected_result": { "path": "denied/wrong-challenge.result.cbor", - "sha256": "7d1b347d15dc5b08a421eecc662cb6c9ec55b00bf3b5d9632836d8615025572e", + "sha256": "4d618cdb534d06ba844ad332fe52f4fde59cd096e025d687f35899cce835f2e5", "stage": "authority", "decision": "denied", "code": "challenge-mismatch", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "f0f50b501934ffb48adb9330f25f6fa12fd7cfb5e3ab74d916e5bc6226564ab6", + "context_digest": "569331d0593157d55f9507c56952669d509e453eb961d6b011f4fd13834578ec", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "ee2e73a54c6f3670269e8228ed68674eb0f2fcebcc3924524aa1996af291adc9", + "result_digest": "a9cef17aaca66d8e51a5ff40ad4d5dff02fbef4489f12fbdb73a841b70c9d9d4", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -4750,7 +4804,7 @@ }, "context": { "path": "denied/composition-requirement-not-met.context.cbor", - "sha256": "58a997d75f1bea806c3314fa01fdaca1682905091afa9b5bc55da3dab47e360e" + "sha256": "48884e973833f272ce1742f721bf29c3b69372052da5501c9f03adde0934225f" }, "canonical_action": { "path": "denied/composition-requirement-not-met.action.cbor", @@ -4771,21 +4825,21 @@ }, "expected_result": { "path": "denied/composition-requirement-not-met.result.cbor", - "sha256": "b7c00bc5103b9a1887e499572ef1c2cca36d7fccd1d0170aab8f1ec9cb0ab3e2", + "sha256": "610c7dcea39ec14c37d84246580b4923a5b3d99300e697b4338d64dc63b7355a", "stage": "resolve", "decision": "denied", "code": "composition-requirement-not-met", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "90a63d3cd326716ce62c90f915d504734fe662d4c17781a0e027779a71f43a95", + "context_digest": "e29b51b4886c15a67a91cb0ec8a1fd6439bdb27967c880d4b1c2c0772ad94b3d", "plan_digest": null, - "result_digest": "a6b184714a33d2801fa0a7246abf354783c080213c652dbfefe62e6c3a6b4936", + "result_digest": "c713938b1af22cfc5646c7402be756aefae733a13c9366dc381bc3519eb4ef0d", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -4805,7 +4859,7 @@ }, "context": { "path": "denied/verifier-configuration-mismatch.context.cbor", - "sha256": "a9df3db0359b9fad6a890c810fdccbbc8aaafbad57b7e3b4ed31ef152b451867" + "sha256": "f8d65228602acfcf8f7899b2ad78bf6d26e2873cb960f128ff5d51140d9c33f5" }, "canonical_action": { "path": "denied/verifier-configuration-mismatch.action.cbor", @@ -4826,21 +4880,21 @@ }, "expected_result": { "path": "denied/verifier-configuration-mismatch.result.cbor", - "sha256": "701c14a9c8759652c9aa0523328fa0e9a1589399faec5da2bd0a61d72e71d649", + "sha256": "03f4aa67caa7c86f561afd7a816c5626c08e68b6a6a583672cce8d17ec998767", "stage": "principalcontrol", "decision": "denied", "code": "verifier-configuration-mismatch", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "42409032cac5ae814df6c85091e998f875794eac57fb1c6208cced59b7f58148", + "context_digest": "a6d527591d9607b7674b40a9145c01ecd9d6f5c067f02545afb3383ac4bd0df1", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "172a32e9da107ef58fa3d9d54d9156a7ad3514c6f0ab2b9b592261cf7d69e410", + "result_digest": "73ea4bb5de45152d7511ea8be27ebd6500dcee86da0678b5c11308f0598a0f15", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -4860,7 +4914,7 @@ }, "context": { "path": "denied/registry-manifest-mismatch.context.cbor", - "sha256": "03170d49b6338b8727db437f450c0ec752dfec7851fb78cb842c2cc07d2cec44" + "sha256": "310cd9153ac6d6feb57d96d5c34c06f5d33e42365f5d72c3c506830a34014cde" }, "canonical_action": { "path": "denied/registry-manifest-mismatch.action.cbor", @@ -4881,21 +4935,21 @@ }, "expected_result": { "path": "denied/registry-manifest-mismatch.result.cbor", - "sha256": "119440a175b4454b2273fe8ec01a27d5fd18e07bb8157a8f48d15e40619b34d1", + "sha256": "f475ace3e278c75c6cc3e09b15ba28e0f2d44a671d280e9f7a1aaccf69433081", "stage": "principalcontrol", "decision": "denied", "code": "registry-manifest-mismatch", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "be57b597d8e28f238c16b8fe1f9ad061ef1d6de4fa227c7c9cf7c44af623087f", + "context_digest": "e065c5cbdc2ac7cfdba4188941e85aca547263d96afdaa7b9ac4f1ddb1dfd574", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "5a765f8a00f9726caf4d0a09968240483195db479210fe2fdcc53f32c78e9faa", + "result_digest": "5bcc193c366b88c8cbcb2be3fbec5666b1c0d51b30b36b4065ebb4055fb0145e", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -4915,7 +4969,7 @@ }, "context": { "path": "indeterminate/unsupported-resource-matcher.context.cbor", - "sha256": "7034b15950de4f2f97b5152376fe0664333e7a9827df95d858ab8e28a901f116" + "sha256": "f7ff4055912ba05eb4f514af1c66c0d2e59d018070c2718a880ed903f1df4cbb" }, "canonical_action": { "path": "indeterminate/unsupported-resource-matcher.action.cbor", @@ -4936,21 +4990,21 @@ }, "expected_result": { "path": "indeterminate/unsupported-resource-matcher.result.cbor", - "sha256": "67b961b174919c00833eb4d9235b6eea62bf4bf0b803565c65bfd181cce5af99", + "sha256": "bc84644f565b398bb7cb8db90c68ad93b26c89a4f922be347db385dd9153b2f5", "stage": "authority", "decision": "indeterminate", "code": "unsupported-resource-matcher", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "2de83dfdea493f148a5f7886376ba5892605585bb38f65465e1aee27e0bf042c", + "context_digest": "013570c41cab2ff91e8c957dfd7b7ea933df9df8b7e8e755d02e728fa748358b", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "ec1b0dcb74b9286b6cb537e2a63b4c6fd7edbb7be79056cc152c742d5bda207e", + "result_digest": "18958032cf6c04df24a469ba203bdd7789e748015bda786a5970dda6463ebd72", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1164, + "context_bytes": 1166, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -4970,7 +5024,7 @@ }, "context": { "path": "indeterminate/unsupported-profile-policy.context.cbor", - "sha256": "7d37e287d2b73ca7f9e50f0a529497f35369d6635d72c83d3e820f485f2a2818" + "sha256": "7b48cc2aa4e0bb9c436bd66109dd19e3c284e7eb90d2809a397921871dce5bd4" }, "canonical_action": { "path": "indeterminate/unsupported-profile-policy.action.cbor", @@ -4991,21 +5045,21 @@ }, "expected_result": { "path": "indeterminate/unsupported-profile-policy.result.cbor", - "sha256": "5e0a88031f275825ac2efd6e782a8a59ae1f48001409d8c8e3afc6a5ecf851b8", + "sha256": "c428ac0028673318b573ca94e264ed6379d88e54a587bc60239a30c806b3d7e6", "stage": "authority", "decision": "indeterminate", "code": "unsupported-profile-policy", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "939726eab5e893f017f94bcc7b3528d49f6190fe8ac1c8c9c9cc7f7601c1f09b", + "context_digest": "4120bcc3f4e65ada06ddd421e0b8019ddac9678b10629995457c0839e3a51dd2", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "b1de100476f9f8b8ffa2387a0894ca480cd0565ae677d4d3e6e1723cee963498", + "result_digest": "789d82e27df860e65ba1819f9c7a140600885b001af92096f406e7a2a09364d9", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1194, + "context_bytes": 1196, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -5025,7 +5079,7 @@ }, "context": { "path": "valid/exact-marker-extension.context.cbor", - "sha256": "9a25a35fe82a64d6b964e11c8233dd30cd60f61b8609e0228550c8d372f8626f" + "sha256": "70395b2b849abd3b85d1c0e48f03c55e16bca205add380b12905a69aa27c293b" }, "canonical_action": { "path": "valid/exact-marker-extension.action.cbor", @@ -5046,15 +5100,15 @@ }, "expected_result": { "path": "valid/exact-marker-extension.result.cbor", - "sha256": "f845b8fa14282f5735485aa1a7f68180d42e24f7e489cc005326c2241d29f81c", + "sha256": "ada1e0d220bf134d9ccc862d7d05717a885f3eaf9a4dbe77edd68f4e463c575e", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "0c8415d23656e982d47639f1d26cfce43c2b3014d536ca47a522f7fb03c01b37", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "0d909f6ebe479efdd46f3414e506b61118d7d512cefe142e74412cd680d0a58a", + "context_digest": "6e2714f8b96462b7d7082e4d78cdaa8736555d320fcd66c010df640293a29678", "plan_digest": "80aefd002ef3b73778a24c25b0942cb0e86f43f7f9d83513370f546fc89f0a3f", - "result_digest": "1e1b49c4432e23ed5a00ccb562528145c5e85c4e2b88091fc9b1c57988672e03", + "result_digest": "eae49dd505e831680b95b090f318257a2fd0d2e5c5d085f464221fae7122bd23", "authorized_branches": [ "adadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadad" ], @@ -5062,7 +5116,7 @@ "resources": { "proof_bytes": 820, "action_bytes": 144, - "context_bytes": 1174, + "context_bytes": 1176, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -5082,7 +5136,7 @@ }, "context": { "path": "denied/exact-marker-extension-invalid.context.cbor", - "sha256": "9a25a35fe82a64d6b964e11c8233dd30cd60f61b8609e0228550c8d372f8626f" + "sha256": "70395b2b849abd3b85d1c0e48f03c55e16bca205add380b12905a69aa27c293b" }, "canonical_action": { "path": "denied/exact-marker-extension-invalid.action.cbor", @@ -5103,21 +5157,21 @@ }, "expected_result": { "path": "denied/exact-marker-extension-invalid.result.cbor", - "sha256": "57a87e58d49988aa39c18b032f7a8470f20731f03eb2fc776eea404284bf8ca2", + "sha256": "fab1cdf9dd209c42fd55b406c25920253f85967b35c9b5fe79fe1b03ec73f1d2", "stage": "authority", "decision": "denied", "code": "local-policy-denied", "proof_digest": "42407ee914b3414fd8a3ff783e54bd8801bdfbc2850b41556fb37cd8bb69b6af", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "0d909f6ebe479efdd46f3414e506b61118d7d512cefe142e74412cd680d0a58a", + "context_digest": "6e2714f8b96462b7d7082e4d78cdaa8736555d320fcd66c010df640293a29678", "plan_digest": "80aefd002ef3b73778a24c25b0942cb0e86f43f7f9d83513370f546fc89f0a3f", - "result_digest": "3ed5a735ae36e21a518c44c50eeba00b00957c9cd9768db7baae89e08b14aaf2", + "result_digest": "ec03255a0af55e88ce0391234b522b0c12da1371d17183c5cf1c5b1478f195c5", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 820, "action_bytes": 144, - "context_bytes": 1174, + "context_bytes": 1176, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -5137,7 +5191,7 @@ }, "context": { "path": "indeterminate/accepted-extension-without-handler.context.cbor", - "sha256": "7fc1a171097360957b298545bee64142c11482baf09fd75dab0d4cd2f3eda2c2" + "sha256": "2a0c05b61aa3b7fcc297c432702d67bb646d8300411f49321c107d7194c5a742" }, "canonical_action": { "path": "indeterminate/accepted-extension-without-handler.action.cbor", @@ -5158,21 +5212,21 @@ }, "expected_result": { "path": "indeterminate/accepted-extension-without-handler.result.cbor", - "sha256": "d93f34a223b44f09aaf2ddf906a96f6839a36d2d8878b8f866000a3325321006", + "sha256": "ef24f6630f77329a128fc65fb671093cfd2be4bce264a533be8fc04d7f223f85", "stage": "authority", "decision": "indeterminate", "code": "unsupported-critical-extension", "proof_digest": "70c94fbfc7eb0748414ce93b7653b088c03c68c8c010633d2f86531c7673d361", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "d7e82b7900ea9107bf3900f745558b612a5110a5d2787600220ff7981c4a049c", + "context_digest": "58a773e76403a24c2c06f579814c5ec020c594259e8430fb1bb0cfafe9033c38", "plan_digest": "2af75ddb207db5bcdec335d9cc1ee40da5747e49fa927ea28dd1002d6aa9726f", - "result_digest": "1e3510b1e683c40f3119193cd8e4a61121e5ee3ca9df06f6ff79f6e3df826272", + "result_digest": "acd372a7f15b159cdb2e52263ba82e2d005535a5e49bb009c9eeb5fbb99dd9df", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1500, "action_bytes": 144, - "context_bytes": 1178, + "context_bytes": 1180, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -5192,7 +5246,7 @@ }, "context": { "path": "denied/resource-namespace-mismatch.context.cbor", - "sha256": "de49c43a9a67b5e76cb0be6d4a351382b68b26dfefc49e553bbdd70ff392cfad" + "sha256": "3c2f4c66c15c9fea163ef77293c2ca90332ea63983e496c62fa6fe6179c02b80" }, "canonical_action": { "path": "denied/resource-namespace-mismatch.action.cbor", @@ -5213,21 +5267,21 @@ }, "expected_result": { "path": "denied/resource-namespace-mismatch.result.cbor", - "sha256": "550478df4aac5cb126435cf57922f2eef2212e98ac74112947938988816b4dd5", + "sha256": "477b991c0ab9d37e8d52657c762a7c7daff2ef62cb7c640e38981390842db140", "stage": "authority", "decision": "denied", "code": "resource-namespace-mismatch", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "228aa4cad306c9af8bb7dab536b0a2df5c3d7bf294235f42c09fd424675022f2", + "context_digest": "aa5d73cbff32a1f8fd55b9867fd6e33121ef06171b2803b3d9305597bd718dbb", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "388246774b0a6e290ed2d30e7e3e3dd93b7edd2e1b40d3da1c82fe712608f8e6", + "result_digest": "0a8a2386705539afd4b08cff8f805885053634680afb64c57dd642fc954c14a3", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1160, + "context_bytes": 1162, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -5247,7 +5301,7 @@ }, "context": { "path": "denied/untrusted-root.context.cbor", - "sha256": "78af97370cb7c357c5141b38a3a7cdba07ae25d5811451c3b539d5fb9d42f3ae" + "sha256": "337f9fe20c51242117499e9351f1d69e8d1ed538163df354ff8d1ac250f61ed7" }, "canonical_action": { "path": "denied/untrusted-root.action.cbor", @@ -5268,21 +5322,21 @@ }, "expected_result": { "path": "denied/untrusted-root.result.cbor", - "sha256": "4dc8d80098a8f40d6646377473b56f54e1958fc989c091d380b095a1c5393535", + "sha256": "13e9e9bf2cf00a2f4c8dca3e83dab42ca4dbb7d8d13d2e4b0385bdaa08dbe43b", "stage": "authority", "decision": "denied", "code": "untrusted-root", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "8edbc5c2afd44d34b11902a726125533994c85706f7dc49f4f9805e85800dfbe", + "context_digest": "eb2dfe7ab65c1b8cb1ad5e546f777fca5bcae2493465f009d792ceba0af0acdf", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "076e09f542e230f587726f1130744bf282c38a57847af17eb83fc7c0e72e957a", + "result_digest": "8fae50453eeab7943ebea57a0467bb5a32488a91e52e2a994fcf4ebb418ebf64", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1086, + "context_bytes": 1088, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -5302,7 +5356,7 @@ }, "context": { "path": "indeterminate/unsupported-assurance-claim.context.cbor", - "sha256": "95edff3e614dc02b130a740556d3f0007ce2eda03c69f74f07e2a3b47c5d2676" + "sha256": "eb3e09754e96361c3a42eed28abaa817c18e65c614085d27d28ae2370c3fb917" }, "canonical_action": { "path": "indeterminate/unsupported-assurance-claim.action.cbor", @@ -5323,21 +5377,21 @@ }, "expected_result": { "path": "indeterminate/unsupported-assurance-claim.result.cbor", - "sha256": "2448b67f13bc38573a6c1cc3292ea9ca3bc100703860c0d8cccfecc09276c048", + "sha256": "0a65e155a5b431122f23464512413796ea76e2bf5a972d39c04a88488c1c4d8f", "stage": "authority", "decision": "indeterminate", "code": "unsupported-assurance-claim", "proof_digest": "50afa552a70a32a9fff3d0401cb97e7a494927bf90535be8c50eb52a3b65e911", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "4e45012888326efd0e94fb5295b6bf242c5ae8834159fe0d746ec5ca69bd8ebd", + "context_digest": "9bbf8fe0929fbf27fe3f19de48979346c5b3f21c6fe281782400430fb5bd59d6", "plan_digest": "9c11759c469c230f7d56808c3db31ebb44d368ba173af197ff55015432c8658c", - "result_digest": "a23c6bf3a774ddead2ee0e6b3f7df56fe3d4c3f69e4528292471470d3cb48e1c", + "result_digest": "040d1ed93d81f47632ce96dfbbcd4da20a83cfff728d2bdd71e3def9db127c29", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1475, "action_bytes": 144, - "context_bytes": 1139, + "context_bytes": 1141, "object_count": 6, "plan_leaves": 1, "plan_depth": 1, @@ -5357,7 +5411,7 @@ }, "context": { "path": "indeterminate/unsupported-budget-algebra.context.cbor", - "sha256": "83f1215fb9d54b4ee95d9bfc5b4bed324240a343113e5ce15bfb8a7a53792f08" + "sha256": "9fff3f37c282b1038c914d49b959e105bc85cc769ded38487e83e883819ba04d" }, "canonical_action": { "path": "indeterminate/unsupported-budget-algebra.action.cbor", @@ -5378,21 +5432,21 @@ }, "expected_result": { "path": "indeterminate/unsupported-budget-algebra.result.cbor", - "sha256": "733beb69355ef6660c920fea9d3aca3ff380e4de9d7518c1d0aa9793fe9097ec", + "sha256": "64b24371ca55b2a8780bf4d50fb341a941d225a957f93d325e2b832d99589577", "stage": "authority", "decision": "indeterminate", "code": "unsupported-budget-algebra", "proof_digest": "c7eb44a976b374eb185fde9aa88a4993b7cc3b5c8a4b2aeaec9b79d859ae54ee", "action_digest": "3f1c71fb00567eabef0ea106524b5f3f10de27d50ff158d0a272ffe661ba702c", - "context_digest": "215d286660d4b843dc9c2c762c1f45837d7fb60aec6041a8b6baf83dd785d9a3", + "context_digest": "3699baa8f63bdeb46514a1dc640a9245f47ad7621bdd074264c6010c9bd96f6e", "plan_digest": "c9fc0a0063cdf5db4ae5274dcfb130584dad255a88b7f33c821ca7e671c9ad76", - "result_digest": "0234a4f3c9654bf394ea719c13b8cdd386a589c9df6619f691fe065b11156cfb", + "result_digest": "21caff5280b4c0a62001ee6bcb36c2530cc4d5d6745e63f039081a974e54d8cf", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 798, "action_bytes": 143, - "context_bytes": 1120, + "context_bytes": 1122, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -5412,7 +5466,7 @@ }, "context": { "path": "valid/attachment-valid.context.cbor", - "sha256": "37b9b7628f5b9ea81dc675b456c0734763320f2c98824faae6782be763f3f9b3" + "sha256": "d12a6231a64d9788a5c9b4389f96e2e15b7748a9de39b5483d09454ce0015454" }, "canonical_action": { "path": "valid/attachment-valid.action.cbor", @@ -5433,15 +5487,15 @@ }, "expected_result": { "path": "valid/attachment-valid.result.cbor", - "sha256": "227c6f67a40e28ee656dd4ca402180693cfc2917ddf34e5c8daec9e4fc0e7002", + "sha256": "5f5cda38cdf8b107f438b7dfcc977bceb5e7608d26258437f662c0f4c4bd0b02", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "fc32a0110aec81c2e182c37184c89d7fcf674274e49bb04f6eefa4037af10a7c", "action_digest": "2581413121d68359183a2f2428ca7d7f800aa0e3d9480da65f1faeac80b47dc7", - "context_digest": "0cdcd389620fc16d7e9d8659de07aafad1daa55c11c8752e2ffdf5792e09824f", + "context_digest": "586e0ba1394af653107f9587f0035f46ef68e9162714e9b601315274923854ac", "plan_digest": "9dcbca93688318444c01100dfd6b2ae9357794177c98e87584f2ad35e20436f7", - "result_digest": "190cc1056cb0c05988cba2aeb9205f0c6712816722956fa597f5cc1e6ffbbc2c", + "result_digest": "367b08b63e11a1c4dc4794833b6a8b731538ce634a6c0fb7c6fb08255b42716d", "authorized_branches": [ "a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8" ], @@ -5449,7 +5503,7 @@ "resources": { "proof_bytes": 985, "action_bytes": 208, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 4, "plan_leaves": 1, "plan_depth": 1, @@ -5469,7 +5523,7 @@ }, "context": { "path": "denied/attachment-missing.context.cbor", - "sha256": "37b9b7628f5b9ea81dc675b456c0734763320f2c98824faae6782be763f3f9b3" + "sha256": "d12a6231a64d9788a5c9b4389f96e2e15b7748a9de39b5483d09454ce0015454" }, "canonical_action": { "path": "denied/attachment-missing.action.cbor", @@ -5490,21 +5544,21 @@ }, "expected_result": { "path": "denied/attachment-missing.result.cbor", - "sha256": "611af3c9b274044dd8df0fa9bd20a31b395a5498989ea38992fc7f85bc165abc", + "sha256": "52e8d44dfab98ed6c101d0b40230197aee3702c52e6bd7157af1a936b2779481", "stage": "authority", "decision": "denied", "code": "attachment-missing", "proof_digest": "fc32a0110aec81c2e182c37184c89d7fcf674274e49bb04f6eefa4037af10a7c", "action_digest": "409417c365cae4d8dd06b6106f954bc35a564355503f6d304efa1e1d00065297", - "context_digest": "0cdcd389620fc16d7e9d8659de07aafad1daa55c11c8752e2ffdf5792e09824f", + "context_digest": "586e0ba1394af653107f9587f0035f46ef68e9162714e9b601315274923854ac", "plan_digest": "9dcbca93688318444c01100dfd6b2ae9357794177c98e87584f2ad35e20436f7", - "result_digest": "9a230b3e1373e3ad5932febefd744ec973524909a6a6e2cc35813159d261cd50", + "result_digest": "d13957afcbfa3ac2c7e1b1c47c10605255a92c962fd17c67167fbd4d63090e77", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 985, "action_bytes": 144, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 4, "plan_leaves": 1, "plan_depth": 1, @@ -5524,7 +5578,7 @@ }, "context": { "path": "denied/attachment-wrong-digest.context.cbor", - "sha256": "37b9b7628f5b9ea81dc675b456c0734763320f2c98824faae6782be763f3f9b3" + "sha256": "d12a6231a64d9788a5c9b4389f96e2e15b7748a9de39b5483d09454ce0015454" }, "canonical_action": { "path": "denied/attachment-wrong-digest.action.cbor", @@ -5545,21 +5599,21 @@ }, "expected_result": { "path": "denied/attachment-wrong-digest.result.cbor", - "sha256": "9c3774afbec6d986f457b93b5004fbba464ddf0fccf9ee6979ffeb7fbb51740f", + "sha256": "cd8d0509973e91fc0ebf07d6a5e326d818bb28a1f4fa59e64f5699e4c54140b5", "stage": "authority", "decision": "denied", "code": "attachment-digest-mismatch", "proof_digest": "d5d3d4072ba72d364c5f4f908c225b6cddc7691086b5286dd7b7c8a8a781780b", "action_digest": "846ae055f4bdda77ffb9a47e53ffc91feb45b22de59e9e34230b63286e226f0b", - "context_digest": "0cdcd389620fc16d7e9d8659de07aafad1daa55c11c8752e2ffdf5792e09824f", + "context_digest": "586e0ba1394af653107f9587f0035f46ef68e9162714e9b601315274923854ac", "plan_digest": "9dcbca93688318444c01100dfd6b2ae9357794177c98e87584f2ad35e20436f7", - "result_digest": "26805577313d37b0be480e60d202ed3c7398d2b6487ae8e282d27a0ce35c4a1e", + "result_digest": "54b0c52baf856009fc1667d1184c7271f76f29e6b357c5eda00d942f931cec27", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 985, "action_bytes": 208, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 4, "plan_leaves": 1, "plan_depth": 1, @@ -5579,7 +5633,7 @@ }, "context": { "path": "denied/attachment-wrong-length.context.cbor", - "sha256": "37b9b7628f5b9ea81dc675b456c0734763320f2c98824faae6782be763f3f9b3" + "sha256": "d12a6231a64d9788a5c9b4389f96e2e15b7748a9de39b5483d09454ce0015454" }, "canonical_action": { "path": "denied/attachment-wrong-length.action.cbor", @@ -5600,21 +5654,21 @@ }, "expected_result": { "path": "denied/attachment-wrong-length.result.cbor", - "sha256": "0a8d51962d3002fdaf236ea4069c8c5ee9807de15e413688bac1280dcd006474", + "sha256": "e1b002cf23d1b0a02959e0d66230c17787812142cfa8c64a57b0ef47b3fea0cf", "stage": "authority", "decision": "denied", "code": "attachment-length-mismatch", "proof_digest": "c00467e2997d31f57d35d42c089fc14efe95ad1dccfe33d7c6cad549f3ccbb14", "action_digest": "2581413121d68359183a2f2428ca7d7f800aa0e3d9480da65f1faeac80b47dc7", - "context_digest": "0cdcd389620fc16d7e9d8659de07aafad1daa55c11c8752e2ffdf5792e09824f", + "context_digest": "586e0ba1394af653107f9587f0035f46ef68e9162714e9b601315274923854ac", "plan_digest": "9dcbca93688318444c01100dfd6b2ae9357794177c98e87584f2ad35e20436f7", - "result_digest": "762ca05836d5eb97c283bd6ccb23666cfc6aaa044301063ccd0fbc7ee1f53510", + "result_digest": "6b75200419961588e82f30b65dfc89336dd49e56d9d2abbbfafa95ea36d97ffb", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 985, "action_bytes": 208, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 4, "plan_leaves": 1, "plan_depth": 1, @@ -5634,7 +5688,7 @@ }, "context": { "path": "denied/attachment-duplicate.context.cbor", - "sha256": "37b9b7628f5b9ea81dc675b456c0734763320f2c98824faae6782be763f3f9b3" + "sha256": "d12a6231a64d9788a5c9b4389f96e2e15b7748a9de39b5483d09454ce0015454" }, "canonical_action": { "path": "denied/attachment-duplicate.action.cbor", @@ -5655,21 +5709,21 @@ }, "expected_result": { "path": "denied/attachment-duplicate.result.cbor", - "sha256": "2ecf888df7b3e330c31f2a6fae2ea804a710e8a22a76bea007701fe162df75c3", + "sha256": "147055ecad1b0bb7f2581d5daa468ec7cafca91d849f9d6d18120f205b2e1507", "stage": "resolve", "decision": "denied", "code": "duplicate-attachment", "proof_digest": "e8cee33ffce5ae54fac45fae8b8924b6bd7fc82a220911020df0a7b84f1fedc3", "action_digest": "2581413121d68359183a2f2428ca7d7f800aa0e3d9480da65f1faeac80b47dc7", - "context_digest": "0cdcd389620fc16d7e9d8659de07aafad1daa55c11c8752e2ffdf5792e09824f", + "context_digest": "586e0ba1394af653107f9587f0035f46ef68e9162714e9b601315274923854ac", "plan_digest": null, - "result_digest": "98edece5e1bd2369242b8f0b335be6a8adcedee3e849bb2db20d0612e8844c81", + "result_digest": "66e62b5e727e2329c2542d0ae29086033c74accac9314013668e6af3a054a069", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 1171, "action_bytes": 208, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 5, "plan_leaves": 1, "plan_depth": 1, @@ -5689,7 +5743,7 @@ }, "context": { "path": "denied/attachment-unused.context.cbor", - "sha256": "37b9b7628f5b9ea81dc675b456c0734763320f2c98824faae6782be763f3f9b3" + "sha256": "d12a6231a64d9788a5c9b4389f96e2e15b7748a9de39b5483d09454ce0015454" }, "canonical_action": { "path": "denied/attachment-unused.action.cbor", @@ -5710,21 +5764,21 @@ }, "expected_result": { "path": "denied/attachment-unused.result.cbor", - "sha256": "c2f13ee348b1c5e9bcd8875faf1679b02ca87ef9dcc3df040a5115ae53719f77", + "sha256": "6cdedfa5a8d02e9aa1d62b5a07e588387e10c6f12e1c66a6e25e1b6719b850f4", "stage": "authority", "decision": "denied", "code": "unused-critical-attachment", "proof_digest": "4df2724e5bcb3cbb8d944c3f0150b0c423132b339d8337d2f39c9ec4768a4942", "action_digest": "2581413121d68359183a2f2428ca7d7f800aa0e3d9480da65f1faeac80b47dc7", - "context_digest": "0cdcd389620fc16d7e9d8659de07aafad1daa55c11c8752e2ffdf5792e09824f", + "context_digest": "586e0ba1394af653107f9587f0035f46ef68e9162714e9b601315274923854ac", "plan_digest": "9dcbca93688318444c01100dfd6b2ae9357794177c98e87584f2ad35e20436f7", - "result_digest": "da258f1e82dc9154411b07a42be03a838c90907ab9536412be8d907f5cf4a94a", + "result_digest": "910c592321ee43a40df148a910dea562766b56ee9035125d7808964fe552406d", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 799, "action_bytes": 208, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 3, "plan_leaves": 1, "plan_depth": 1, @@ -5744,7 +5798,7 @@ }, "context": { "path": "valid/attachment-opaque-allowed.context.cbor", - "sha256": "37b9b7628f5b9ea81dc675b456c0734763320f2c98824faae6782be763f3f9b3" + "sha256": "d12a6231a64d9788a5c9b4389f96e2e15b7748a9de39b5483d09454ce0015454" }, "canonical_action": { "path": "valid/attachment-opaque-allowed.action.cbor", @@ -5765,15 +5819,15 @@ }, "expected_result": { "path": "valid/attachment-opaque-allowed.result.cbor", - "sha256": "b978b9280d0d94dd03f8fae2e32d637b3beb2a9eb37952c18fc96dc75c63590c", + "sha256": "036002b41b126dfd47a9f04a0d35fdf8a983c32cb5163cc2155050770daa6d72", "stage": "complete", "decision": "authorized", "code": "authorized", "proof_digest": "9b2b98d1713dc22821abac5f8b9f5ff0e20e13e919a7a9b48d765d27d3fb2ea7", "action_digest": "2581413121d68359183a2f2428ca7d7f800aa0e3d9480da65f1faeac80b47dc7", - "context_digest": "0cdcd389620fc16d7e9d8659de07aafad1daa55c11c8752e2ffdf5792e09824f", + "context_digest": "586e0ba1394af653107f9587f0035f46ef68e9162714e9b601315274923854ac", "plan_digest": "9dcbca93688318444c01100dfd6b2ae9357794177c98e87584f2ad35e20436f7", - "result_digest": "cb39ca0937da9ac85ed0242f582a53acedef6c0fae5089d7f9fba9b92c390319", + "result_digest": "673892f8128e7b422447569a45b3de378680b27ffc699e60c49c34c4ba0cf769", "authorized_branches": [ "a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8" ], @@ -5781,7 +5835,7 @@ "resources": { "proof_bytes": 985, "action_bytes": 208, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 4, "plan_leaves": 1, "plan_depth": 1, @@ -5801,7 +5855,7 @@ }, "context": { "path": "denied/attachment-opaque-denied.context.cbor", - "sha256": "37b9b7628f5b9ea81dc675b456c0734763320f2c98824faae6782be763f3f9b3" + "sha256": "d12a6231a64d9788a5c9b4389f96e2e15b7748a9de39b5483d09454ce0015454" }, "canonical_action": { "path": "denied/attachment-opaque-denied.action.cbor", @@ -5822,21 +5876,21 @@ }, "expected_result": { "path": "denied/attachment-opaque-denied.result.cbor", - "sha256": "78dca7c84dfc2ae3fe96974aa1ceee6c44f0821bb217b16519a4eed790b15d94", + "sha256": "48036203cafcd0f9466f89009452c17226cf2204f11407c9e77539fe2b8a521a", "stage": "authority", "decision": "denied", "code": "opaque-attachment-not-allowed", "proof_digest": "df24f9a0e5b8d2d9ec5038a8af25231baffbc05d71f009f3f5c8cb3b1bedc13d", "action_digest": "2581413121d68359183a2f2428ca7d7f800aa0e3d9480da65f1faeac80b47dc7", - "context_digest": "0cdcd389620fc16d7e9d8659de07aafad1daa55c11c8752e2ffdf5792e09824f", + "context_digest": "586e0ba1394af653107f9587f0035f46ef68e9162714e9b601315274923854ac", "plan_digest": "9dcbca93688318444c01100dfd6b2ae9357794177c98e87584f2ad35e20436f7", - "result_digest": "ae8872c342b4deba82309540340ad15296d2f819ca054e480bb7c9e195ee6e88", + "result_digest": "0ddf20114f4684f557b9a19f633321b67487d33fd245fd2ece86af6145e75084", "authorized_branches": [], "assurance_satisfactions": 0, "resources": { "proof_bytes": 985, "action_bytes": 208, - "context_bytes": 1158, + "context_bytes": 1160, "object_count": 4, "plan_leaves": 1, "plan_depth": 1, diff --git a/core/fixtures/v1/status/active-principal-status.context.cbor b/core/fixtures/v1/status/active-principal-status.context.cbor index 0cecbb2180c3a8c383827f860ac5ed9ebb80e08a..b3af300cc523271c10d48a53eccc8085f8393df0 100644 GIT binary patch delta 22 ecmaFO^Ok4BT}H-ro9{91WMtxP*gT#23KIZxA_*)2 delta 19 bcmaFM^O|SFT}H;Wo9{91WZXQH`3e&NTuKO@ diff --git a/core/fixtures/v1/status/active-principal-status.result.cbor b/core/fixtures/v1/status/active-principal-status.result.cbor index 0c9dd361bea46e1f5a0842d21514c7b4fa8ed4cc..1ff5be67c823686c6810ba0ae2fc9989e8848341 100644 GIT binary patch delta 95 zcmV-l0HFVp2$BepULchQwR~bV{^#KS&cL_sQYVCM5YG zvTc{Z$wT6zIM^-!r)H<3bwW&v7~Az^W7WB1-OLDqSRj$n7m?ryvFEh{0d13=19f-= BFkGkOCP7Z36`a T0R;gD81@WUAd&GOlMew&Ew3-R delta 113 zcmV-%0FM9I0@wnOYal_QQ|17I)f3!)2%kGkOCP7YXb!Y T0R;gD81@WUAd&GOlMew&HUBJk diff --git a/core/fixtures/v1/valid/action-budget-absent-budget-free-profile.action.cbor b/core/fixtures/v1/valid/action-budget-absent-budget-free-profile.action.cbor new file mode 100644 index 0000000000000000000000000000000000000000..8354f34e8d4062404a4b1c287390e435b7a2a4cf GIT binary patch literal 122 zcmZ3+u!tcuv9u(kST8rZfRT~0LM^eNASW|9u_QA;ProcLMGr2ko1B=FqgQ6Aot%_k z#1tX1h#{pYH8F)TU%x1|Ait=jSijI%uOKCjc@aZaNq&A#u|7xxV-e6`D}A^ckQSD2 GtPKDxwJOa3 literal 0 HcmV?d00001 diff --git a/core/fixtures/v1/valid/action-budget-absent-budget-free-profile.body.cbor b/core/fixtures/v1/valid/action-budget-absent-budget-free-profile.body.cbor new file mode 100644 index 0000000000000000000000000000000000000000..3edfb10557fc6a4f3652c4b35c07d99bafcf284e GIT binary patch literal 24 fcmZ3)kW!SIn8KK^UzA#qUsO`8UudjXkdg)fYQYGM literal 0 HcmV?d00001 diff --git a/core/fixtures/v1/valid/action-budget-absent-budget-free-profile.context.cbor b/core/fixtures/v1/valid/action-budget-absent-budget-free-profile.context.cbor new file mode 100644 index 0000000000000000000000000000000000000000..466eb16c755229c908f6eacc6e6e0463ce5b72da GIT binary patch literal 1174 zcmbVLO=}ZD7@paDe^jHbI!RF%kHQ+0lG64Rsa8u7YN@3bL?*kF>?XTA>Fi{a94v{R zdlCPDsh0L&4}#!9@X&)7K|FX6K`$P>D-k?7nD>)J%9Df+x*6B z=^cdf>VOI6GbYKst2D?}5WY(@pP=ffUe zBPBE!#Yh-8icN1Sh9)Qp48j^Gdo(SNB54RZ#8!z?Wz;32np-tTDA^+<(CP-aiQ_0- zXT;H|sVNrWt98mMCdD4|o~}?U+^*J`q1f0WP8ExeZYI+H!{tO+i^U^fJ}fb`F~kxj8u7Y7&w64HDv aZ)Kuh{`CR2FF$r*tnpvtD%sTAp3D#7PkE>S literal 0 HcmV?d00001 diff --git a/core/fixtures/v1/valid/action-budget-absent-budget-free-profile.proof.cbor b/core/fixtures/v1/valid/action-budget-absent-budget-free-profile.proof.cbor new file mode 100644 index 0000000000000000000000000000000000000000..8bfd7d20e2a9bf33105c6dcba840f84a634d5f93 GIT binary patch literal 1453 zcmZ3*u!wVV@Qf;d4WN0g@0N~VZLXmQLu+@ykDSsieH7N ziC4C(uV=A)V6Ks$flD@XW@2ecMzLOQasdk?E6^2LCHeU|#rnyKIXR3)Kv64w{i4)@ z{GyU#Af1@PCc-Wez|okCBEt#tLxe(1^4TkV6OZcl$uVBfo7S`GteQ>^&w`ifUYw;1 z1^c;lxfU@L<(1~97G);uCZ}fRWag#omKic~aWn9I<6X$Wz*kU|Sgs2U0Ntd-;#8n? zDt`mxB8J5bSuk;+Y7~!>5<*$2DMqHIhL#{JBOIbPpT26LZrGG6H{a}lyxF|>6LZAM zcV@CBi#R{f)K5?@3pe$86DKKK%To4}DgFIfQUB)qIf@4p=BHd15AaUqW&#G)W}vST z!N_?EoLG{XpQm4zm!by`TwPFr>XjL4Cnx0>u@DX`R-|}f!x9hd-#F0X zAVNWj0N|3);FhrAK?y+;k_vBxg8BdFdS|XKU-RCscP7_O$BUP|-+2bl%8V)T&fhF^ z>1q^Tgo4)h*xL%VTld^MY`(cAp!}21m`oMCV={Gr&me$_w+Vp`OT3&czH^6cU02ma+ZIyc;0W(ys@~Bf68;F zNAJG}yE4?*OFe9jTw=azdX(bDd7BxINZl__;AURLzyM711bncNrD+L6ghFq@F{4dx z`=>Wc3%K-tv8~QHKC$xii#x|YeB|{zFxSbS5h;Z;RVZLjMbJzDOhn8PMvkE&9>Kan zj^VoAu92V;M8RasO%}tj)4AulUvp`CZV#O^g;)D@?!KPjm7AF=9xrzTxnt%xg^q7W zYJVsA%s+GZUHG%P1`k$zTrDVj49&Seq6B%RErn5lT&rjf7ncG7D7pxIXOdS+GgOVp&@IP5x7?Q|6mF xzYus-D0g<<^xbAEXNo{(5sC%22KEMy2F?hHMGPsRvW_tyUi1|j>lLJ=0RZW0F7N;V literal 0 HcmV?d00001 diff --git a/core/fixtures/v1/valid/action-budget-absent-budget-free-profile.result.cbor b/core/fixtures/v1/valid/action-budget-absent-budget-free-profile.result.cbor new file mode 100644 index 0000000000000000000000000000000000000000..9205d1170dfb4ce932c78e98809d4484fa515f98 GIT binary patch literal 1042 zcmdnMz`)4DvxK%h>FfuSTEnz6iPfN?m%uCfROD)Pw%S=qlNo8zc`o^49lvu6{G+4LH za0x?&RB>ufnr?DxQAuW6WoBNwZe~hq9*~`xT7<5e8R+bp-xNB&9jX1D;4}Zs;dkNB z<{CU$A$Db(!$byau8rODIxI-0vofN2!7(Jov%J6{x57Uyr7+(!)F{|PH{LJMJjJiV z)5I&=)z`DwJuuhE&%h;{k%?wr=q)&Aw8?G%^k!)Rm)6{&8W0m6cx>?-!B3@7=s9ewk-cUV!iCs}76L^AT3U&jbKz C^O!dP literal 0 HcmV?d00001 diff --git a/core/fixtures/v1/valid/all-of.context.cbor b/core/fixtures/v1/valid/all-of.context.cbor index f8670fbf5fad5e33647adc38961eab7445f74764..2b5f1af50f859da1941f50ba742b46834cbaa135 100644 GIT binary patch delta 33 pcmeyv^^a@AaVC~^3=s;GFEU9@e!#@G`888DBNK1K<}?;DMgZU#3yc5& delta 30 mcmeyz^@nT2aVD0v3=s;GFEU9@e!#@G`888D=Z+a5QM<8U}(yHXM_^{b7urDjT@1ECM1K1 zg0i>hDG;}4%mJb1J3FjMnMdEXAmk7-^5k|)lt>7ISRj$n7m?ryvFBF>0e+J|26ZZ0 BDzN|n delta 95 zcmV-l0HFW04YUoAULbs{q;dqj(w+`>kGPl1MS{+(=&L!kE-BsQDRSq(iu2%{J{fErZwvo{*Q*GESRj$n7m?ryvFBF>0eq7`26eKn BEkXbQ diff --git a/core/fixtures/v1/valid/any-of-valid-invalid-signature.context.cbor b/core/fixtures/v1/valid/any-of-valid-invalid-signature.context.cbor index 7704ab232e1155e63d0ff92bb0d49f699ffd3602..5e76b52c5f727eee6b65602e79e352d84bd17260 100644 GIT binary patch delta 33 pcmeyv^^a@AaVC~^3=s;GFEU9@e!#@G`888DBNK1K<}?;DMgZU#3yc5& delta 30 mcmeyz^@nT2aVD0v3=s;GFEU9@e!#@G`888D BD_H;l diff --git a/core/fixtures/v1/valid/attachment-opaque-allowed.context.cbor b/core/fixtures/v1/valid/attachment-opaque-allowed.context.cbor index 8e55e2758e17eb32836bc67e753a1b628cc2efed..b4e64474959c542f2da23dde315c75027d3dc3ff 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/valid/attachment-opaque-allowed.result.cbor b/core/fixtures/v1/valid/attachment-opaque-allowed.result.cbor index e6be491c2110534aef6ecf1d2d9b4a8207993c3a..27ff64311425e92c08bd411e4116c361374405b2 100644 GIT binary patch delta 95 zcmV-l0HFVo2$2YoULaU*3!ynm_EQjlm51;HUq9zqeQgo6yRIHJHCM0J# zlK2vidqN~fR+>e#-ZzGTvVZ((o?yhBG{m|L_h|@$SRj$n7m?ryvFEh{0f>{G19W%O BDTM$4 delta 95 zcmV-l0HFVo2$2YoULXwI(}`ja!EJt>hFRVRs`}B|rCbrnbuKUc^?5D{f=`itCM3%_ z$_Y2xn#f+zBrjMhQ>^XpY!9wbiP!o2sktmU0~rW`SRj$n7m?ryvFEh{0fv*E19Vr& BED`_! diff --git a/core/fixtures/v1/valid/attachment-valid.context.cbor b/core/fixtures/v1/valid/attachment-valid.context.cbor index 8e55e2758e17eb32836bc67e753a1b628cc2efed..b4e64474959c542f2da23dde315c75027d3dc3ff 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/valid/attachment-valid.result.cbor b/core/fixtures/v1/valid/attachment-valid.result.cbor index f292e6f3b48917612285010d4f1ef46d5b8d3d4c..ce8a22ceb9a8e4d4d9752ab93929251ed211dcb0 100644 GIT binary patch delta 95 zcmV-l0HFVo2$2YoULaU*3!ynm_EQjlm51;HUq9zqeQgo6yRIHJHCL}g{ z2(~^Ep~T!rl!H5Ji*pq?&SOe!54Xnq2qjxWacu~JSRj$n7m?ryvFEh{0f>{G19T|+ BCgK19 delta 95 zcmV-l0HFVo2$2YoULXwI(}`ja!EJt>hFRVRs`}B|rCbrnbuKUc^?5D{f=`itCL|dQ z!3Auvz*&gPqOQ3hUkqmwfoCF>Z>5*@%pPz1yetTTSRj$n7m?ryvFEh{0fv*E19T$L BD>VQB diff --git a/core/fixtures/v1/valid/did-keri-root-raw-key-actor.context.cbor b/core/fixtures/v1/valid/did-keri-root-raw-key-actor.context.cbor index 125b14036fe59b0ef8d97c71409b136844922dc9..addb2e2aaf6434e66e5b34e78c48ed5d83a3b712 100644 GIT binary patch delta 22 ecmbQsIiGXGK1RlMoA)zrV`SoO*j&o2!3Y3gTnBmp delta 19 bcmbQwIhS+8K1Rm1oA)zrW87TMticEXOf3fN diff --git a/core/fixtures/v1/valid/did-keri-root-raw-key-actor.result.cbor b/core/fixtures/v1/valid/did-keri-root-raw-key-actor.result.cbor index fbaf728680557bf66b9d6baa47dbdaded9cf3632..15fe59305a5e224548d5f893df82d1615a1aaa73 100644 GIT binary patch delta 95 zcmV-l0HFW22(}22ULe7163!EE6TsyNHoEs%w$PqN9=F_Ae)}kz$(5)`cv+EtCM0Jk zu%w1t+qVPEW(p(pb6C^?#5m*9l}2bZ$h8GpMuP}}SRj$n7m?ryvFF(W0iTn>19eof BEGGZ} delta 95 zcmV-l0HFW22(}22ULXw+cC6aaix*@?-sWEI;i?KhErCL~q$ zII`N{I&1R|L|pR;eQp~&-7 delta 30 mcmZ3)xqx%SVMdm<3=s;GFEWZve!$4S`3d6`#?4jC8jJv{KniXE diff --git a/core/fixtures/v1/valid/did-key-root-raw-key-actor.result.cbor b/core/fixtures/v1/valid/did-key-root-raw-key-actor.result.cbor index 876d5c5feed04963dcb30358715ee5f79651ac5c..57a9e3bb8d20fa41229f1c4a25c26bbc46ca48b3 100644 GIT binary patch delta 95 zcmV-l0HFVt2$l$tULaxV7@A~T%)?fm-EizVsaV8W#&vR^Q(kUs2_!t`4X=@YCL}T1 zIG>})laQ>hSDpTCA+KpvJAb~7-FgcQi6);FKn4hbSRj$n7m?ryvFEu00iu(k19df+ BD!Tvx delta 95 zcmV-l0HFVt2$l$tULbHDgXKNdcJFlU)*h2M6dK$~A)i?8sCTLlf(xu+NIa2#CL}7y zA9sVX2@r(r+Rk1B!jO+I_gOqog8S>UWU|BVG`t9bSRj$n7m?ryvFEu00icti19cJJ BEFAy< diff --git a/core/fixtures/v1/valid/did-web-root-raw-key-actor.context.cbor b/core/fixtures/v1/valid/did-web-root-raw-key-actor.context.cbor index fbc6bf6b4a95c74615cc1d32f6b823aa176edb49..5bcb5e67788d89458ab7f1f8f4186aa87f741500 100644 GIT binary patch delta 22 ecmey%@ty(T>vAu%L#9M#J8j}ctSRj$n7m?ryvFG~&4u1m%1OWvB S1{nce3|JtO@dF=|4+Kdjt~AyF delta 112 zcmV-$0FVFJ2-paaULaC)@lZTMfmlyz5nI>{g}oC|AlLH>o=M$h{;g59Op}p*CM1t} zL>{QU_Sk1KPWx!VeMZpXKt5%g440M^?R(`uOY;bUSRj$n7m?ryvFG~&4t)a#1OWvB S1{nce3|JtO@dF=|4+KdKwJuEn diff --git a/core/fixtures/v1/valid/exact-marker-extension.context.cbor b/core/fixtures/v1/valid/exact-marker-extension.context.cbor index db40cce0fb8d4dfb8fea0db697e0c6d7adfe6795..38432339e96f10a6b9ff22e5e40dd1d038dab832 100644 GIT binary patch delta 22 ecmbQnIfHYC$lzQD-7`4;1BMkd~d&85t8i~zy!3a9`8 delta 30 mcmbQrIhk|AHb$1U3=s;G4>C$lzQD-7`4;1B#?9r-a*P0^4GJ0n diff --git a/core/fixtures/v1/valid/hsm-root-raw-key-actor.result.cbor b/core/fixtures/v1/valid/hsm-root-raw-key-actor.result.cbor index 8f8b62b8fe80bf11d0294d155433f70cceedc366..09a889585f6af4cb2b9569669b00d96897b47ee7 100644 GIT binary patch delta 95 zcmV-l0HFV=3aJW^ULfBtdTfGuu}KmA$&v{iTjN-$v)#6Wvbe=07h2yA=tq%$CL~#V zjkwV{^vj~#i-&8Mhpir_=w<$Qzy0)oP=Qr8$lM5lSRj$n7m?ryvFFSK0hN=q1a;`F BFg5@H delta 95 zcmV-l0HFV=3aJW^ULeyG+Wh3Rrq>j5^4IhlA+BWCsdLnXvb%A?V17u5LvE3NCM25V z!;4^Ifp5MOTQnSQCFv2|$YaGz&_!3%2mHL~Hp&QrSRj$n7m?ryvFFSK0h5!o1a&uk BFR1_k diff --git a/core/fixtures/v1/valid/raw-key-chain.context.cbor b/core/fixtures/v1/valid/raw-key-chain.context.cbor index ea4eaf2b7bd2d3a5720233cab962d3adfe68133d..88ee214e4e23a38aa59523927da1fe0c2f93eb1b 100644 GIT binary patch delta 22 ecmZqU?BLw6pOJCh<^zoL7@2q*Hpek*FaiKuX9phu delta 19 bcmeC+Y~$RppOJCx<^zoL7&j*{YcK);NCXCv diff --git a/core/fixtures/v1/valid/raw-key-chain.result.cbor b/core/fixtures/v1/valid/raw-key-chain.result.cbor index 9a8aefd8896f9f5b2d654bf67d62aecf916d1086..3fbf1e3a49f37723c249bba6bb2c8ce593e2af3f 100644 GIT binary patch delta 95 zcmV-l0HFVp2$BepULbj!yKU$GQENbG$HHF4$E70k$yw>WMBww+O-KM&HyV+CCL|W7 z1x;3fO}lMIf%XpBB9f6DWdZ?0wbRSib8ANWEx!nXSRj$n7m?ryvFEh{0f>{G19f{} BEAIdR delta 95 zcmV-l0HFVp2$BepULe9$A^RIt#rZ`JkBAizX6G6}VI->B=@cfvjfHe|2Xv8sCM4TD zrP^9#bPdc_N;r5^t%T>arfsE zZ{rcw=`XPUDt7}+r|<-&jBX?JZ=P(!{NrH#rzFIR=AlCUv7(WFCM1|{ z`a@D#g+0scwkk$&G(VOfxZ|C7HZlZXg`SRj$n7m?ryvFEu00iKhg19b#L BFOUEL delta 95 zcmV-l0HFVt2$l$tULZ&fAP*WOIc|L#2Y~wF1jzS$q-rB0q%md%ogO;_qk@rsCL~y@ z8U^vq)1KelZdh5;I?5y@L8`dHA*E=ye~?2*CMpPlSRj$n7m?ryvFEu00i2Ve19jxX BCjS5c diff --git a/core/fixtures/v1/valid/raw-key-root-did-web-actor.context.cbor b/core/fixtures/v1/valid/raw-key-root-did-web-actor.context.cbor index a821fdf59a4767c170a66f41f7e938d8209a7e10..0840c857dc26738ddcdf9ccc46fcc42895ec9844 100644 GIT binary patch delta 22 ecmZ3>xt?>wen!T1n-4G^VPxWM*xbUb$p`>r;|IC` delta 19 bcmZ3_xt4Roen!T%n-4G^VcguttjP!fPb3E{ diff --git a/core/fixtures/v1/valid/raw-key-root-did-web-actor.result.cbor b/core/fixtures/v1/valid/raw-key-root-did-web-actor.result.cbor index 599000beed6468f6d40a84210d2a53591243f1a9..3e304c24641904edc15fcbdec3f1f876b9c845e3 100644 GIT binary patch delta 95 zcmV-l0HFWc2-*mcULZXdzB_|0DcqnNpG#`x*Sfp0>GTaVP9416K$0z=J%f>cCM2wB z^?rJmTD|B-=#g(=8xYL*FcSA9xP|3U5B$?skNpUNSRj$n7m?ryvFH5)0k4zg19gT3 BE@%J% delta 95 zcmV-l0HFWc2-*mcULf8>fV1fHeaizqXsF01rg@NSBBkKuAot!a%qVTH9Pg2SCM0Q3 zOmPJy$R25@%`BBayewXfw*}atd4D%!0pY}}O4JB}SRj$n7m?ryvFH5)0j-ne19hb0 BEmQyi diff --git a/core/fixtures/v1/valid/raw-key-root-hsm-actor.context.cbor b/core/fixtures/v1/valid/raw-key-root-hsm-actor.context.cbor index 4e46fd0f4852ffca1dc251be28ca325b784d106e..d2bf2e51c235119cb0aeab0c8f48283f539fd888 100644 GIT binary patch delta 22 ecmbQkIgfM0en!T1n-4JVV`SoO*j&df#|QvmuLpqu delta 19 bcmbQoIfrw@en!T%n-4JVW8B=pEXN1{OlSu4 diff --git a/core/fixtures/v1/valid/raw-key-root-hsm-actor.result.cbor b/core/fixtures/v1/valid/raw-key-root-hsm-actor.result.cbor index 8455fccedc943051792fb077cc2e0cc006766273..903448b775a202df1e0b7f03279e242e45f8861b 100644 GIT binary patch delta 95 zcmV-l0HFV=3aJW^ULc2DQyjnpftLLlM(G#}NXsw93;DAi&Qsf1_|m5d)Pj+ICM07G z?{IA4ZL{DmG7Gr*DB&}Iasm%-^WtMTZIOjyEEEWVSRj$n7m?ryvFFSK0iKhz1adL delta 19 bcmbQkIg4|{en!T%n-4JVVccBDEXN1{ObQ0; diff --git a/core/fixtures/v1/valid/raw-key-root-spiffe-actor.result.cbor b/core/fixtures/v1/valid/raw-key-root-spiffe-actor.result.cbor index c08348be35baa9b1cf176f0df89c0cea5753d4e9..49a5d4f4905bc040ed24d4af2b4bfb62ab093e41 100644 GIT binary patch delta 95 zcmV-l0HFV$37-j&ULcpl=I?+&E_gFl6QYsXd2WWOlwvh;dSRj$n7m?ryvFAbq0i2U61a&et BD(L_K delta 95 zcmV-l0HFV$37-j&ULX=aS5`AY&X#)Q>vVyPk1`-63O{5+#_5eMFb!aL$B&VICM31& z;Kbl=84sQ}am8F_q%DQ9vyiJ+vojmlm6ChMd!q<}SRj$n7m?ryvFAbq0h*I41a-Mn BDe?dS diff --git a/core/fixtures/v1/valid/raw-key-root-webauthn-actor.context.cbor b/core/fixtures/v1/valid/raw-key-root-webauthn-actor.context.cbor index a4b8e0a8c8a9e01f5354d375588e6e39cffb19d5..39e7baae1862581eeafae57f517f8edbbae0dec4 100644 GIT binary patch delta 22 ecmbQwxsY?ien!T1n-4G^Wn|)Q*xbr2#RvdlhX;`W delta 19 bcmZ3;IiGXGen!T%n-4G^W!&7(EX4=_O&JFR diff --git a/core/fixtures/v1/valid/raw-key-root-webauthn-actor.result.cbor b/core/fixtures/v1/valid/raw-key-root-webauthn-actor.result.cbor index 4911a8007d5ad80c1b1a9990c8810b8c3e4ba1e2..07e383725a587f3570d5d3958c8f394f288cf58e 100644 GIT binary patch delta 95 zcmV-l0HFW=2>u9=ULfO>M9y(3Zx`@&MlSc8-yLCM0c) znjz;dwm>E@AWatYP5dn?UMiY}+Xv?}N9@~l1gHpsSRj$n7m?ryvF9NJ0ilx#1a%U( BDO&&l delta 95 zcmV-l0HFW=2>u9=ULXh#wSmNoyB?ux9Mz1SQ$3xBkQ4ueLs;lqq8rkKg3FP9CM1rk zw4*Nj^s^dEkTcpJMxn8hz|x+8XSL{I|9G=M#ghnuSRj$n7m?ryvF9NJ0iTlz1a-W@ BE(QPq diff --git a/core/fixtures/v1/valid/spiffe-root-raw-key-actor.context.cbor b/core/fixtures/v1/valid/spiffe-root-raw-key-actor.context.cbor index 3259a24073544911a44aa592b2ef7f9d3baf9be3..1860cdff03ecf4e40d71884e0462618a939caaab 100644 GIT binary patch delta 29 lcmeyy@q=T-Y(|!K3=s;G7c$Ci-pe?fk%_lqa|E*-BLJl{36TH* delta 26 icmeyt@r`4{Y(|!~3=s;G7c$Ci-pe?fadQ;293udVLkR-_ diff --git a/core/fixtures/v1/valid/spiffe-root-raw-key-actor.result.cbor b/core/fixtures/v1/valid/spiffe-root-raw-key-actor.result.cbor index 4e6af011f7df6e901f23bd2546b17365e142d313..c672cf8e5c6d8ff0d186822259f70421e40325e4 100644 GIT binary patch delta 95 zcmV-l0HFV$37-j&ULZdTxbzQ8Yu{gT02jFYP?Q5#%io$>trc*G3Z-2+L|&18CM4U( ziVWQT^n~{DdBuhs^ z1s=rx%@*d@Cii=L-l9l>-R`0|f#G R83JSsSRj)D2_KUm2}yk5E|~xT delta 111 zcmV-#0FeLa66g|;Umu4%n7e9@rAs;xb1Cf`_u>YHOfzH-!(idq^nq$xjFEmOBAt*K^Cii=L-l9lLH9^0|f#G R83JSsSRj)D2_KUm2}w4=Es+2K diff --git a/core/fixtures/v1/valid/threshold-mixed-success.context.cbor b/core/fixtures/v1/valid/threshold-mixed-success.context.cbor index 2970757a89fe6338c58afd4b8ddfc85466326831..595a31354024004d48e0146f5ebb2f4911a089fa 100644 GIT binary patch delta 22 ecmZqSZR6c=o|$po<_pa87@2q*Hs`U5F#-Tx+XqJg delta 19 bcmZqUZQZ?Y>KA)1lvAf0{?fj5zUCM1^1 zj9;I`?V%oThZZU(lnQ!;djPUI&fSbqjU!v#cSHz+SRj$n7m?ryvFBF>0fv)526ZU_ BCb|Fs delta 95 zcmV-l0HFW04YUoAULYzYWJ{F!Gb@G{Lx>mv{`#vShkUVCL}%_ zqU|$c&ByG@HXN=ntYCr!@FpdcIzeCAacqrk54Z?|SRj$n7m?ryvFBF>0fdu326Y;^ BDp3Fc diff --git a/core/fixtures/v1/valid/webauthn-root-raw-key-actor.context.cbor b/core/fixtures/v1/valid/webauthn-root-raw-key-actor.context.cbor index 84b8e01e6be46fb63b911339405706e1d163d203..14ba644ac786f3f93a5c054fca30357fa0ae3b7f 100644 GIT binary patch delta 37 scmeyw@r7f2>u9=ULT6=chGE4R!56w7sLJKN;%Ng(&40s$*jeotgB!-sF8gqBwwrI z=H+RQzo6{#zS#BSBNSHQZq*g2lkPYHkF#vT=LmsVAd%4*k>Cii=OF_Dbdw1Lb+)=O Ay#N3J delta 94 zcmV-k0HOc>2>u9=ULOPKw}zCP7S{Cii=OF_Da+3)Jb@MAH A@Bjb+ diff --git a/core/testkit/auths-testkit/src/lib.rs b/core/testkit/auths-testkit/src/lib.rs index 1fab40d2..0d1f105e 100644 --- a/core/testkit/auths-testkit/src/lib.rs +++ b/core/testkit/auths-testkit/src/lib.rs @@ -754,6 +754,34 @@ fn anchor(identity: &Identity, depth: u16) -> TrustAnchor { anchor_with_status(identity, depth, StatusPolicy::ExpiryOnly) } +/// Rebuilds a context that declares exactly the given profiles budget-free. +/// +/// Every other field is carried across unchanged, so a fixture pair built from +/// the same proof bytes differs in exactly this one declaration. +fn declaring_budget_free(source: &TrustedContext, budget_free: Vec) -> TrustedContext { + TrustedContext::new( + source.configuration(), + source.composition(), + source.trust_anchors().to_vec(), + source + .accepted_registries() + .clone() + .with_budget_free_profiles(budget_free) + .expect("budget-free declaration"), + source.expected_audience().clone(), + source.expected_challenge().clone(), + source.evaluation_time(), + source.assurance_policy().clone(), + source.principal_status_snapshot().clone(), + source.grant_status_snapshot().clone(), + source.resource_matcher().clone(), + source.profile_policy().clone(), + source.channel_policy().clone(), + source.limits().clone(), + ) + .expect("context") +} + fn context(identities: &[Identity], anchors: Vec) -> TrustedContext { context_with_assurance(identities, anchors, assurance_policy(identities)) } @@ -2526,6 +2554,11 @@ enum ActionVariation { Constraint, Budget, BudgetAbsent, + /// Byte-identical to [`ActionVariation::BudgetAbsent`] except that the + /// verifier context declares the action's profile unable to express a + /// requested budget. The absent request is then a provable zero spend, so + /// the same bounded ceiling authorizes instead of denying. + BudgetAbsentBudgetFreeProfile, Validity, Actor, UnsupportedProfile, @@ -2562,7 +2595,7 @@ fn action_authority_fixture(name: &'static str, variation: ActionVariation) -> C // against is not vacuously satisfied; the verifier must deny. let action_budget = match variation { ActionVariation::Budget => Some(11), - ActionVariation::BudgetAbsent => None, + ActionVariation::BudgetAbsent | ActionVariation::BudgetAbsentBudgetFreeProfile => None, _ => Some(5), }; let action_profile = if matches!(variation, ActionVariation::UnsupportedProfile) { @@ -2664,6 +2697,11 @@ fn action_authority_fixture(name: &'static str, variation: ActionVariation) -> C .expect("action binding"), ]; let verifier_context = context(&identities, vec![anchor(&root, 1)]); + let verifier_context = if matches!(variation, ActionVariation::BudgetAbsentBudgetFreeProfile) { + declaring_budget_free(&verifier_context, vec![profile()]) + } else { + verifier_context + }; let bundle = ProofBundle::new( BundleHeader::v1(), vec![grant], @@ -2678,6 +2716,7 @@ fn action_authority_fixture(name: &'static str, variation: ActionVariation) -> C ) .expect("action authority proof"); let expected = match variation { + ActionVariation::BudgetAbsentBudgetFreeProfile => Expected::Authorized, ActionVariation::Permission => Expected::Denied(DenialReason::PermissionNotGranted), ActionVariation::Constraint => Expected::Denied(DenialReason::ActionConstraintMismatch), ActionVariation::Budget | ActionVariation::BudgetAbsent => { @@ -2693,14 +2732,12 @@ fn action_authority_fixture(name: &'static str, variation: ActionVariation) -> C } ActionVariation::Channel => Expected::Denied(DenialReason::LocalPolicyDenied), }; - fixture( - name, - "denied", - &bundle, - &verifier_context, - canonical, - expected, - ) + let class = if matches!(expected, Expected::Authorized) { + "valid" + } else { + "denied" + }; + fixture(name, class, &bundle, &verifier_context, canonical, expected) } macro_rules! action_fixture { @@ -2742,6 +2779,18 @@ action_fixture!( "action-budget-absent", BudgetAbsent ); +action_fixture!( + /// Signed action declares no budget while terminal authority is bounded, + /// and the verifier context declares that profile unable to express one. + /// + /// This is the authorizing mirror of `action-budget-absent`: identical + /// proof bytes, one differing declaration in the trusted context, opposite + /// verdict. Without both fixtures the branch is only exercised on one side + /// and independent implementations can disagree unnoticed. + action_budget_absent_budget_free_profile, + "action-budget-absent-budget-free-profile", + BudgetAbsentBudgetFreeProfile +); action_fixture!( /// Signed action validity exceeds the grant window. action_validity_expanded, @@ -4675,6 +4724,7 @@ pub fn corpus() -> Vec { action_constraint_mismatch(), action_budget_exceeded(), action_budget_absent(), + action_budget_absent_budget_free_profile(), action_validity_expanded(), action_actor_mismatch(), unsupported_action_profile(), diff --git a/product/integrations/auths-github/src/profile.rs b/product/integrations/auths-github/src/profile.rs index 12690fbb..676127df 100644 --- a/product/integrations/auths-github/src/profile.rs +++ b/product/integrations/auths-github/src/profile.rs @@ -4,7 +4,9 @@ use auths_model::{ BudgetAlgebraId, BudgetCeiling, CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use crate::types::{ @@ -35,6 +37,9 @@ pub struct GitHubIssueProfile; impl ActionProfile for GitHubIssueProfile { type Command = GitHubCommand; + /// `canonical_action` always declares one `numeric-ceiling-v1` unit. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Expressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-kubernetes/src/profile.rs b/product/integrations/auths-kubernetes/src/profile.rs index 8abc80af..514cc4fc 100644 --- a/product/integrations/auths-kubernetes/src/profile.rs +++ b/product/integrations/auths-kubernetes/src/profile.rs @@ -4,7 +4,9 @@ use auths_model::{ BudgetAlgebraId, BudgetCeiling, CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -36,6 +38,9 @@ pub struct KubernetesRolloutProfile; impl ActionProfile for KubernetesRolloutProfile { type Command = KubernetesRolloutCommand; + /// `canonical_action` always declares one `numeric-ceiling-v1` unit. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Expressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-opentofu/src/profile.rs b/product/integrations/auths-opentofu/src/profile.rs index 69560c38..b58dc186 100644 --- a/product/integrations/auths-opentofu/src/profile.rs +++ b/product/integrations/auths-opentofu/src/profile.rs @@ -4,7 +4,9 @@ use auths_model::{ BudgetAlgebraId, BudgetCeiling, CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -37,6 +39,9 @@ pub struct OpenTofuSavedPlanProfile; impl ActionProfile for OpenTofuSavedPlanProfile { type Command = OpenTofuApplyCommand; + /// `canonical_action` always declares one `numeric-ceiling-v1` unit. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Expressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-postgresql/src/profile.rs b/product/integrations/auths-postgresql/src/profile.rs index 8d66a47f..2e64ffc9 100644 --- a/product/integrations/auths-postgresql/src/profile.rs +++ b/product/integrations/auths-postgresql/src/profile.rs @@ -4,7 +4,9 @@ use auths_model::{ BudgetAlgebraId, BudgetCeiling, CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -39,6 +41,9 @@ pub struct PostgresBoundedUpdateProfile; impl ActionProfile for PostgresBoundedUpdateProfile { type Command = PostgresUpdateCommand; + /// `canonical_action` always declares one `numeric-ceiling-v1` unit. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Expressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-radicle/src/profile.rs b/product/integrations/auths-radicle/src/profile.rs index 4d0ec8c2..3d090948 100644 --- a/product/integrations/auths-radicle/src/profile.rs +++ b/product/integrations/auths-radicle/src/profile.rs @@ -4,7 +4,9 @@ use auths_model::{ BudgetAlgebraId, BudgetCeiling, CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -36,6 +38,10 @@ pub struct RadiclePatchProfile; impl ActionProfile for RadiclePatchProfile { type Command = RadiclePatchCommand; + /// `canonical_action` derives a `numeric-ceiling-v1` request from the + /// publication budget ordinal. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Expressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-records-api/src/profile.rs b/product/integrations/auths-records-api/src/profile.rs index 8764492b..728a6f59 100644 --- a/product/integrations/auths-records-api/src/profile.rs +++ b/product/integrations/auths-records-api/src/profile.rs @@ -4,7 +4,9 @@ use auths_model::{ BudgetAlgebraId, BudgetCeiling, CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use crate::{ @@ -42,6 +44,9 @@ pub struct CreateRecordProfile; impl ActionProfile for CreateRecordProfile { type Command = VerifiedCreateRecordCommand; + /// `canonical_create` always declares one `numeric-ceiling-v1` unit. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Expressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { let action = CreateRecordV1::from_canonical_bytes(untrusted).map_err(ProfileContractError::from)?; @@ -86,6 +91,9 @@ pub struct ReadRecordProfile; impl ActionProfile for ReadRecordProfile { type Command = VerifiedReadRecordCommand; + /// `canonical_read` always declares one `numeric-ceiling-v1` unit. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Expressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { let action = ReadRecordV1::from_canonical_bytes(untrusted).map_err(ProfileContractError::from)?; diff --git a/product/integrations/auths-stripe/src/connect/transfer/profile.rs b/product/integrations/auths-stripe/src/connect/transfer/profile.rs index ca065dd5..98c0955c 100644 --- a/product/integrations/auths-stripe/src/connect/transfer/profile.rs +++ b/product/integrations/auths-stripe/src/connect/transfer/profile.rs @@ -8,7 +8,9 @@ use auths_model::{ CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -35,6 +37,10 @@ pub struct StripeConnectTransferProfile; impl ActionProfile for StripeConnectTransferProfile { type Command = StripeConnectTransferCommand; + /// `canonical_action` passes `None` and `validate_canonical` rejects any + /// requested budget. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Inexpressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-stripe/src/issuing/purchase_authorization/profile.rs b/product/integrations/auths-stripe/src/issuing/purchase_authorization/profile.rs index 464b756f..bf7af590 100644 --- a/product/integrations/auths-stripe/src/issuing/purchase_authorization/profile.rs +++ b/product/integrations/auths-stripe/src/issuing/purchase_authorization/profile.rs @@ -8,7 +8,9 @@ use auths_model::{ CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -35,6 +37,10 @@ pub struct StripePurchaseAuthorizationProfile; impl ActionProfile for StripePurchaseAuthorizationProfile { type Command = StripePurchaseAuthorizationCommand; + /// `canonical_action` passes `None` and `validate_canonical` rejects any + /// requested budget. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Inexpressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-stripe/src/mandate/profile.rs b/product/integrations/auths-stripe/src/mandate/profile.rs index bf15b8db..cb6a0576 100644 --- a/product/integrations/auths-stripe/src/mandate/profile.rs +++ b/product/integrations/auths-stripe/src/mandate/profile.rs @@ -3,7 +3,9 @@ use auths_model::{ CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -33,6 +35,10 @@ pub struct StripePaymentMandateProfile; impl ActionProfile for StripePaymentMandateProfile { type Command = StripePaymentMandateCommand; + /// `canonical_action` passes `None` and `validate_canonical` rejects any + /// requested budget. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Inexpressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-stripe/src/merchant/authorize/profile.rs b/product/integrations/auths-stripe/src/merchant/authorize/profile.rs index a3f01805..d4826c1e 100644 --- a/product/integrations/auths-stripe/src/merchant/authorize/profile.rs +++ b/product/integrations/auths-stripe/src/merchant/authorize/profile.rs @@ -4,7 +4,9 @@ use auths_model::{ BudgetAlgebraId, BudgetCeiling, CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -36,6 +38,10 @@ pub struct StripePaymentAuthorizeProfile; impl ActionProfile for StripePaymentAuthorizeProfile { type Command = StripePaymentAuthorizeCommand; + /// `canonical_action` declares the exact minor-unit amount as a + /// `numeric-ceiling-v1` request. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Expressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-stripe/src/merchant/cancel/profile.rs b/product/integrations/auths-stripe/src/merchant/cancel/profile.rs index d07e4296..0623e2d7 100644 --- a/product/integrations/auths-stripe/src/merchant/cancel/profile.rs +++ b/product/integrations/auths-stripe/src/merchant/cancel/profile.rs @@ -3,7 +3,9 @@ use auths_model::{ CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -33,6 +35,10 @@ pub struct StripePaymentCancelProfile; impl ActionProfile for StripePaymentCancelProfile { type Command = StripePaymentCancelCommand; + /// `canonical_action` passes `None` and `validate_canonical_action` rejects + /// any requested budget. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Inexpressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-stripe/src/merchant/capture/profile.rs b/product/integrations/auths-stripe/src/merchant/capture/profile.rs index 7e499fc8..5c40cbc1 100644 --- a/product/integrations/auths-stripe/src/merchant/capture/profile.rs +++ b/product/integrations/auths-stripe/src/merchant/capture/profile.rs @@ -4,7 +4,9 @@ use auths_model::{ BudgetAlgebraId, BudgetCeiling, CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -36,6 +38,10 @@ pub struct StripePaymentCaptureProfile; impl ActionProfile for StripePaymentCaptureProfile { type Command = StripePaymentCaptureCommand; + /// `canonical_action` declares the exact minor-unit capture amount as a + /// `numeric-ceiling-v1` request. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Expressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-stripe/src/merchant/collect/profile.rs b/product/integrations/auths-stripe/src/merchant/collect/profile.rs index 6b982cb1..7139a686 100644 --- a/product/integrations/auths-stripe/src/merchant/collect/profile.rs +++ b/product/integrations/auths-stripe/src/merchant/collect/profile.rs @@ -4,7 +4,9 @@ use auths_model::{ BudgetAlgebraId, BudgetCeiling, CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -36,6 +38,10 @@ pub struct StripePaymentCollectProfile; impl ActionProfile for StripePaymentCollectProfile { type Command = StripePaymentCollectCommand; + /// `canonical_action` declares the exact minor-unit amount as a + /// `numeric-ceiling-v1` request. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Expressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-stripe/src/profile.rs b/product/integrations/auths-stripe/src/profile.rs index ac11d89e..d31b54a6 100644 --- a/product/integrations/auths-stripe/src/profile.rs +++ b/product/integrations/auths-stripe/src/profile.rs @@ -4,7 +4,9 @@ use auths_model::{ BudgetAlgebraId, BudgetCeiling, CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -36,6 +38,9 @@ pub struct StripeRefundProfile; impl ActionProfile for StripeRefundProfile { type Command = StripeRefundCommand; + /// `canonical_action` always declares one `numeric-ceiling-v1` unit. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Expressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-stripe/src/subscription/cancel/profile.rs b/product/integrations/auths-stripe/src/subscription/cancel/profile.rs index 1b5169dc..76c457ed 100644 --- a/product/integrations/auths-stripe/src/subscription/cancel/profile.rs +++ b/product/integrations/auths-stripe/src/subscription/cancel/profile.rs @@ -8,7 +8,9 @@ use auths_model::{ CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -36,6 +38,10 @@ pub struct StripeSubscriptionCancelProfile; impl ActionProfile for StripeSubscriptionCancelProfile { type Command = StripeSubscriptionCancelCommand; + /// `canonical_action` passes `None` and `validate_canonical` requires exact + /// equality with it. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Inexpressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-stripe/src/subscription/create/profile.rs b/product/integrations/auths-stripe/src/subscription/create/profile.rs index 46ecc398..bf6f2d2d 100644 --- a/product/integrations/auths-stripe/src/subscription/create/profile.rs +++ b/product/integrations/auths-stripe/src/subscription/create/profile.rs @@ -3,7 +3,9 @@ use auths_model::{ CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -30,6 +32,10 @@ pub struct StripeSubscriptionCreateProfile; impl ActionProfile for StripeSubscriptionCreateProfile { type Command = StripeSubscriptionCreateCommand; + /// `canonical_action` passes `None` and `validate_canonical` rejects any + /// requested budget. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Inexpressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-stripe/src/subscription/modify/profile.rs b/product/integrations/auths-stripe/src/subscription/modify/profile.rs index fc0a2e17..1e7c56c0 100644 --- a/product/integrations/auths-stripe/src/subscription/modify/profile.rs +++ b/product/integrations/auths-stripe/src/subscription/modify/profile.rs @@ -3,7 +3,9 @@ use auths_model::{ CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -30,6 +32,10 @@ pub struct StripeSubscriptionModifyProfile; impl ActionProfile for StripeSubscriptionModifyProfile { type Command = StripeSubscriptionModifyCommand; + /// `canonical_action` passes `None` and `validate_canonical` rejects any + /// requested budget. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Inexpressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/integrations/auths-stripe/src/treasury/payout/profile.rs b/product/integrations/auths-stripe/src/treasury/payout/profile.rs index ed522e98..b413b90c 100644 --- a/product/integrations/auths-stripe/src/treasury/payout/profile.rs +++ b/product/integrations/auths-stripe/src/treasury/payout/profile.rs @@ -8,7 +8,9 @@ use auths_model::{ CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_sdk::VerifiedAction; use sha2::{Digest as _, Sha256}; @@ -35,6 +37,10 @@ pub struct StripePayoutProfile; impl ActionProfile for StripePayoutProfile { type Command = StripePayoutCommand; + /// `canonical_action` passes `None` and `validate_canonical` rejects any + /// requested budget. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Inexpressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); diff --git a/product/profiles/auths-profile-api/src/lib.rs b/product/profiles/auths-profile-api/src/lib.rs index 96e71207..7d57ba7a 100644 --- a/product/profiles/auths-profile-api/src/lib.rs +++ b/product/profiles/auths-profile-api/src/lib.rs @@ -3,6 +3,7 @@ #![forbid(unsafe_code)] use auths_model::CanonicalAction; +pub use auths_model::ProfileBudgetExpression; use auths_verifier::VerifiedAction; use std::fmt; @@ -53,6 +54,24 @@ pub trait ActionProfile { /// Command type safe for a profile executor. type Command; + /// States whether this profile's canonical actions can carry a requested + /// budget. + /// + /// This is a structural fact about the profile's canonical body, not a + /// property of any one action: if the body has no budget field, no action + /// of this profile can ever declare a spend, and + /// [`ProfileBudgetExpression::Inexpressible`] says so. A profile whose + /// canonicalization ever produces a [`CanonicalAction`] with a requested + /// budget must declare [`ProfileBudgetExpression::Expressible`]. + /// + /// A verifier consumes this through the trusted registry selection to + /// decide what an *absent* requested budget means under a bounded terminal + /// ceiling: unknown spend (deny) or provably zero spend (covered). The + /// constant carries no default so that a new profile cannot ship without + /// its author answering the question; the verifier's own fallback for an + /// undeclared profile is the denying reading. + const BUDGET_EXPRESSION: ProfileBudgetExpression; + /// Canonicalizes untrusted application input and derives exact meaning. /// /// # Errors diff --git a/product/profiles/auths-profile-domains/src/lib.rs b/product/profiles/auths-profile-domains/src/lib.rs index 73f8b8cc..e7719be3 100644 --- a/product/profiles/auths-profile-domains/src/lib.rs +++ b/product/profiles/auths-profile-domains/src/lib.rs @@ -7,7 +7,9 @@ use auths_model::{ BudgetAlgebraId, BudgetCeiling, CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_receipts::{ReceiptInspectionError, ReceiptProfileInspector, ReceiptProjection}; use auths_verifier::VerifiedAction; use serde::{Serialize, de::DeserializeOwned}; @@ -28,6 +30,11 @@ trait DomainMeaning: Clone + DeserializeOwned + Serialize { const PROFILE_ID: &'static str; const PROFILE_VERSION: u16 = 1; const MEDIA_TYPE: &'static str; + /// Must agree with [`DomainMeaning::budget`]: a domain that can return + /// `Some` is `Expressible`, a domain that can only ever return `None` is + /// `Inexpressible`. `domain_budget_declaration_matches_canonical_bytes` + /// proves the two agree for every shipping domain. + const BUDGET_EXPRESSION: ProfileBudgetExpression; fn validate(&self) -> Result<(), ProfileContractError>; fn permission(&self) -> Result; @@ -37,6 +44,36 @@ trait DomainMeaning: Clone + DeserializeOwned + Serialize { } } +/// Reports whether one shipped domain profile's canonical actions can express a +/// requested budget. +/// +/// The answer is read off each profile's own +/// [`ActionProfile::BUDGET_EXPRESSION`], so it cannot drift from what +/// canonicalization actually produces. Returns `None` for any profile this +/// crate does not ship — a caller that gets `None` must keep the denying +/// reading of an absent requested budget. +#[must_use] +pub fn budget_expression(profile: &ProfileRef) -> Option { + match (profile.id().as_str(), profile.version()) { + (HttpAction::PROFILE_ID, HttpAction::PROFILE_VERSION) => { + Some(::BUDGET_EXPRESSION) + } + (GitAction::PROFILE_ID, GitAction::PROFILE_VERSION) => { + Some(::BUDGET_EXPRESSION) + } + (DeploymentAction::PROFILE_ID, DeploymentAction::PROFILE_VERSION) => { + Some(::BUDGET_EXPRESSION) + } + (SupplyChainAction::PROFILE_ID, SupplyChainAction::PROFILE_VERSION) => { + Some(::BUDGET_EXPRESSION) + } + (EdgeAction::PROFILE_ID, EdgeAction::PROFILE_VERSION) => { + Some(::BUDGET_EXPRESSION) + } + _ => None, + } +} + /// Zero-sized implementation of one exact domain profile. #[derive(Clone, Copy, Debug)] pub struct DomainProfile(PhantomData); @@ -66,6 +103,8 @@ where { type Command = DomainCommand; + const BUDGET_EXPRESSION: ProfileBudgetExpression = T::BUDGET_EXPRESSION; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_ACTION_BYTES { return Err(ProfileContractError::LimitExceeded); @@ -361,6 +400,7 @@ impl HttpAction { impl DomainMeaning for HttpAction { const PROFILE_ID: &'static str = "auths.http"; const MEDIA_TYPE: &'static str = "application/vnd.auths.http-action.v1+json"; + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Inexpressible; fn validate(&self) -> Result<(), ProfileContractError> { exact_profile(&self.profile, self.profile_version, Self::PROFILE_ID)?; @@ -477,6 +517,7 @@ impl GitAction { impl DomainMeaning for GitAction { const PROFILE_ID: &'static str = "auths.git"; const MEDIA_TYPE: &'static str = "application/vnd.auths.git-action.v1+json"; + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Inexpressible; fn validate(&self) -> Result<(), ProfileContractError> { exact_profile(&self.profile, self.profile_version, Self::PROFILE_ID)?; @@ -620,6 +661,7 @@ impl DeploymentAction { impl DomainMeaning for DeploymentAction { const PROFILE_ID: &'static str = "auths.deploy"; const MEDIA_TYPE: &'static str = "application/vnd.auths.deploy-action.v1+json"; + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Expressible; fn validate(&self) -> Result<(), ProfileContractError> { exact_profile(&self.profile, self.profile_version, Self::PROFILE_ID)?; @@ -732,6 +774,7 @@ impl SupplyChainAction { impl DomainMeaning for SupplyChainAction { const PROFILE_ID: &'static str = "auths.supply-chain"; const MEDIA_TYPE: &'static str = "application/vnd.auths.supply-chain-action.v1+json"; + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Inexpressible; fn validate(&self) -> Result<(), ProfileContractError> { exact_profile(&self.profile, self.profile_version, Self::PROFILE_ID)?; @@ -826,6 +869,7 @@ impl EdgeAction { impl DomainMeaning for EdgeAction { const PROFILE_ID: &'static str = "auths.edge"; const MEDIA_TYPE: &'static str = "application/vnd.auths.edge-action.v1+json"; + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Inexpressible; fn validate(&self) -> Result<(), ProfileContractError> { exact_profile(&self.profile, self.profile_version, Self::PROFILE_ID)?; @@ -1105,6 +1149,131 @@ mod tests { } } + /// Every domain profile's declared budget capability must match the bytes + /// its canonicalizer actually produces. + /// + /// A profile that declared `Inexpressible` while emitting a requested + /// budget would tell a verifier its actions provably spend zero when they + /// do not — the one way this mechanism could become unsound. Both sides are + /// computed here; neither is a literal. + #[test] + fn domain_budget_declaration_matches_canonical_bytes() { + let digest = "11".repeat(32); + let cases: Vec<(ProfileRef, ProfileBudgetExpression, CanonicalAction)> = vec![ + ( + profile::().unwrap(), + ::BUDGET_EXPRESSION, + HttpProfile::default() + .canonicalize( + &serde_json::to_vec(&HttpAction::new( + "POST".into(), + "https".into(), + "api.example.com".into(), + "/v1/releases".into(), + BTreeMap::new(), + BTreeMap::new(), + Some("application/json".into()), + Some(digest.clone()), + )) + .unwrap(), + ) + .unwrap(), + ), + ( + profile::().unwrap(), + ::BUDGET_EXPRESSION, + GitProfile::default() + .canonicalize( + &serde_json::to_vec(&GitAction::new( + "example/repository".into(), + "push".into(), + "heads/main".into(), + digest.clone(), + )) + .unwrap(), + ) + .unwrap(), + ), + ( + profile::().unwrap(), + ::BUDGET_EXPRESSION, + DeploymentProfile::default() + .canonicalize( + &serde_json::to_vec(&DeploymentAction::new( + "production".into(), + "eu-west-1".into(), + "deploy".into(), + digest.clone(), + digest.clone(), + digest.clone(), + "canary".into(), + 1_800_000_000, + 1_800_003_600, + 10, + )) + .unwrap(), + ) + .unwrap(), + ), + ( + profile::().unwrap(), + ::BUDGET_EXPRESSION, + SupplyChainProfile::default() + .canonicalize( + &serde_json::to_vec(&SupplyChainAction::new( + "attest".into(), + digest.clone(), + "https://slsa.dev/provenance/v1".into(), + "builder://ci.example.com/runner".into(), + )) + .unwrap(), + ) + .unwrap(), + ), + ( + profile::().unwrap(), + ::BUDGET_EXPRESSION, + EdgeProfile::default() + .canonicalize( + &serde_json::to_vec(&EdgeAction::new( + "fleet-a".into(), + "device-1".into(), + "restart".into(), + 7, + Some(digest.clone()), + )) + .unwrap(), + ) + .unwrap(), + ), + ]; + // Both readings must actually occur, otherwise the comparison below + // would pass for a table that is constant in one direction. + assert!( + cases + .iter() + .any(|(_, expression, _)| *expression == ProfileBudgetExpression::Expressible) + && cases + .iter() + .any(|(_, expression, _)| *expression + == ProfileBudgetExpression::Inexpressible) + ); + for (reference, declared, canonical) in cases { + let observed = if canonical.requested_budget().is_some() { + ProfileBudgetExpression::Expressible + } else { + ProfileBudgetExpression::Inexpressible + }; + assert_eq!( + declared, + observed, + "{reference:?} declares {declared:?} but canonicalized to {:?}", + canonical.requested_budget() + ); + assert_eq!(budget_expression(&reference), Some(declared)); + } + } + /// Regression: `DeploymentAction` used to mint the unregistered algebra /// `deploy-blast-radius-v1`. No verifier registry installs that algebra, so /// a ceiling-bearing grant could never resolve it (denied for a reason diff --git a/product/profiles/auths-profile-mcp/src/lib.rs b/product/profiles/auths-profile-mcp/src/lib.rs index cc2e9061..809a1c8a 100644 --- a/product/profiles/auths-profile-mcp/src/lib.rs +++ b/product/profiles/auths-profile-mcp/src/lib.rs @@ -10,7 +10,9 @@ use auths_model::{ Audience, CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; -use auths_profile_api::{ActionProfile, ProfileContractError, ReviewDisplay}; +use auths_profile_api::{ + ActionProfile, ProfileBudgetExpression, ProfileContractError, ReviewDisplay, +}; use auths_verifier::VerifiedAction; use rmcp::model::CallToolRequestParams; use serde::{Deserialize, Serialize}; @@ -231,9 +233,28 @@ impl McpToolCall { #[derive(Clone, Copy, Debug, Default)] pub struct McpProfile; +/// Reports whether `auths.mcp/1` canonical actions can express a requested +/// budget. +/// +/// The answer is read off [`McpProfile`]'s own +/// [`ActionProfile::BUDGET_EXPRESSION`], so it cannot drift from what +/// `canonicalize` actually produces. Returns `None` for any other profile — a +/// caller that gets `None` must keep the denying reading of an absent +/// requested budget. +#[must_use] +pub fn budget_expression(profile: &ProfileRef) -> Option { + (profile.id().as_str() == PROFILE_ID && profile.version() == PROFILE_VERSION) + .then_some(::BUDGET_EXPRESSION) +} + impl ActionProfile for McpProfile { type Command = McpCommand; + /// `McpToolCall` has no budget field, so `canonical_action` always builds a + /// `CanonicalAction` with `None` and `validate_canonical_action` rejects any + /// action that carries one. An MCP tool call provably spends zero. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Inexpressible; + fn canonicalize(&self, untrusted: &[u8]) -> Result { if untrusted.is_empty() || untrusted.len() > MAX_CANONICAL_CALL_BYTES { return Err(ProfileContractError::LimitExceeded); @@ -590,4 +611,78 @@ mod tests { reference_canonicalize(&untrusted).unwrap() ); } + + /// The declaration must match what canonicalization actually produces. + /// + /// A profile that declares `Inexpressible` while emitting a requested + /// budget would tell a verifier that its actions provably spend zero when + /// they do not. This drives the real canonicalizer, so the declaration + /// cannot drift away from the bytes. + #[test] + fn mcp_cannot_express_a_budget_and_says_so() { + assert_eq!( + ::BUDGET_EXPRESSION, + ProfileBudgetExpression::Inexpressible + ); + for call in [ + McpToolCall::new("reports", "read_report", Map::new()).unwrap(), + McpToolCall::new( + "reports", + "update", + Map::from_iter([("value".into(), Value::String("reviewed".into()))]), + ) + .unwrap(), + ] { + let canonical = McpProfile + .canonicalize(&call.canonical_bytes().unwrap()) + .unwrap(); + assert!( + canonical.requested_budget().is_none(), + "auths.mcp/1 canonicalization must never produce a requested budget" + ); + } + assert_eq!( + budget_expression(&ProfileRef::new(ProfileId::parse(PROFILE_ID).unwrap(), 1).unwrap()), + Some(ProfileBudgetExpression::Inexpressible) + ); + // A different version is a different profile and stays undeclared. + assert_eq!( + budget_expression(&ProfileRef::new(ProfileId::parse(PROFILE_ID).unwrap(), 2).unwrap()), + None + ); + assert_eq!( + budget_expression( + &ProfileRef::new(ProfileId::parse("auths.records").unwrap(), 1).unwrap() + ), + None + ); + } + + /// Rule A stays intact: an MCP action that somehow carries a requested + /// budget is a meaning mismatch, declaration or not. + #[test] + fn an_mcp_action_carrying_a_budget_is_still_a_meaning_mismatch() { + let call = McpToolCall::new("reports", "read_report", Map::new()).unwrap(); + let canonical = McpProfile + .canonicalize(&call.canonical_bytes().unwrap()) + .unwrap(); + let with_budget = CanonicalAction::new( + canonical.profile().clone(), + canonical.media_type().clone(), + canonical.body().to_vec(), + canonical.permission().clone(), + Some(auths_model::BudgetCeiling::new( + auths_model::BudgetAlgebraId::parse("numeric-ceiling-v1").unwrap(), + 1, + )), + ) + .unwrap(); + assert_eq!( + validate_canonical_action(&with_budget), + Err(ProfileContractError::MeaningMismatch) + ); + // The unmodified action is accepted, so the assertion above is about + // the budget and not about some other rejection. + assert!(validate_canonical_action(&canonical).is_ok()); + } } diff --git a/product/sdk/auths-profile-kit/src/lib.rs b/product/sdk/auths-profile-kit/src/lib.rs index 144965a6..c7bd9b21 100644 --- a/product/sdk/auths-profile-kit/src/lib.rs +++ b/product/sdk/auths-profile-kit/src/lib.rs @@ -125,6 +125,7 @@ mod tests { use auths_model::{ CanonicalAction, CapabilityId, MediaType, Permission, ProfileId, ProfileRef, ResourceId, }; + use auths_profile_api::ProfileBudgetExpression; use auths_verifier::VerifiedAction; use std::cell::Cell; @@ -136,6 +137,9 @@ mod tests { impl ActionProfile for TestProfile { type Command = (); + // `canonicalize` always passes `None`. + const BUDGET_EXPRESSION: ProfileBudgetExpression = ProfileBudgetExpression::Inexpressible; + fn canonicalize(&self, _untrusted: &[u8]) -> Result { let call = self.calls.get(); self.calls.set(call.saturating_add(1)); diff --git a/product/sdk/auths-sdk/src/lib.rs b/product/sdk/auths-sdk/src/lib.rs index e825e0bb..bfb9d236 100644 --- a/product/sdk/auths-sdk/src/lib.rs +++ b/product/sdk/auths-sdk/src/lib.rs @@ -86,6 +86,7 @@ pub struct TrustedContextBuilder { signature_suites: BTreeSet, evidence_types: BTreeSet, critical_extensions: BTreeSet, + budget_free_profiles: BTreeSet, } impl TrustedContextBuilder { @@ -131,6 +132,7 @@ impl TrustedContextBuilder { signature_suites, evidence_types, critical_extensions: BTreeSet::new(), + budget_free_profiles: BTreeSet::new(), }) } @@ -176,6 +178,21 @@ impl TrustedContextBuilder { self } + /// Declares one profile whose canonical actions cannot express a requested + /// budget, so an action of that profile provably spends zero. + /// + /// The value must come from the profile's own + /// `ActionProfile::BUDGET_EXPRESSION`; this builder cannot see profile + /// implementations. A profile that is never declared keeps the denying + /// reading of an absent request under a bounded ceiling. A declaration for + /// a profile no trust anchor accepts is dropped rather than rejected, since + /// the builder derives its accepted-profile set from the anchors. + #[must_use] + pub fn declare_budget_free_profile(mut self, profile: ProfileRef) -> Self { + self.budget_free_profiles.insert(profile); + self + } + /// Compiles one immutable trusted-context template. /// /// # Errors @@ -209,6 +226,12 @@ impl TrustedContextBuilder { .iter() .map(|requirement| requirement.claim_kind().clone()) .collect(); + let budget_free_profiles: Vec = self + .budget_free_profiles + .iter() + .filter(|profile| profiles.contains(*profile)) + .cloned() + .collect(); let accepted = AcceptedRegistries::new( auths_registries::TARGET_V1_REGISTRY_MANIFEST, principal_methods.into_iter().collect(), @@ -227,7 +250,8 @@ impl TrustedContextBuilder { self.critical_extensions.into_iter().collect(), profiles.into_iter().collect(), vec![ProfilePolicyId::parse(auths_registries::EXACT_PROFILE_V1)?], - )?; + )? + .with_budget_free_profiles(budget_free_profiles)?; Ok(TrustedContext::new( self.configuration, self.composition, From b5a95d517b722662ddab8d91e32fa3aeb6d3bace Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 04:58:57 +0100 Subject: [PATCH 24/61] bindings: one factory name per concept across both languages (UNSIGNED) The Wave 2 verify agent found the last two cross-language divergences. Every other operation and noun agreed; these did not: local factory TS createAuths (free fn) PY Auths.__init__ (constructor) service factory TS createServiceClient PY create_auths service client TS ServiceClient PY ServiceAuths The second row was the dangerous one: `create_auths` named the REMOTE client in Python while naming nothing in TypeScript, and Python's local factory was a constructor, so the same words meant different things in each language. TypeScript's spelling wins because it names the layer rather than the product, and Python conforms: local createAuths / create_auths service createServiceClient / create_service_client -> ServiceClient Superseded spellings are deleted outright, not aliased; prelaunch policy is direct cutover. TypeScript 147 tests pass across four suites; the public API shape gate stays at zero mirrored or homonymous exports. Python 133 tests pass. This lane was interrupted by ENOSPC mid-run and its Python half sat uncommitted; the work itself was complete and is verified above. Co-Authored-By: Claude Opus 5 --- bindings/python/api/public-api.txt | 5 +- bindings/python/python/auths/__init__.py | 2 + bindings/python/python/auths/__init__.pyi | 1 + bindings/python/python/auths/_product.py | 27 +- bindings/python/python/auths/_service.py | 28 +- bindings/python/python/auths/integrations.py | 4 +- bindings/python/python/auths/service.py | 8 +- .../python/tests/test_production_client.py | 8 +- .../python/tests/test_vocabulary_parity.py | 60 + docs/papers/proof/README.md | 15 + .../proof/auths-proof-formal-semantics.md | 2312 +++++++++++++++++ .../proof/auths-proof-formal-semantics.pdf | Bin 0 -> 221003 bytes docs/papers/proof/build.sh | 32 + docs/papers/proof/preamble.tex | 355 +++ docs/papers/proof/references.bib | 161 ++ docs/papers/system/README.md | 14 + docs/papers/system/auths-proof.md | 1408 ++++++++++ docs/papers/system/auths-proof.pdf | Bin 0 -> 165537 bytes docs/papers/system/build.sh | 31 + docs/papers/system/preamble.tex | 305 +++ docs/papers/system/references.bib | 470 ++++ docs/product/sdk-glossary.json | 2 +- .../LAUNCH_READINESS_INVESTIGATIONS.md | 553 ++++ docs/target-state/adoption-readiness.md | 1764 +++++++++++++ .../v1-launch-readiness-independent.md | 667 +++++ docs/testing/TESTING_INVARIANTS.md | 226 ++ output/pdf/auths-proof-formal-semantics.pdf | Bin 0 -> 221003 bytes output/pdf/auths-proof.pdf | Bin 0 -> 153087 bytes 28 files changed, 8437 insertions(+), 21 deletions(-) create mode 100644 docs/papers/proof/README.md create mode 100644 docs/papers/proof/auths-proof-formal-semantics.md create mode 100644 docs/papers/proof/auths-proof-formal-semantics.pdf create mode 100755 docs/papers/proof/build.sh create mode 100644 docs/papers/proof/preamble.tex create mode 100644 docs/papers/proof/references.bib create mode 100644 docs/papers/system/README.md create mode 100644 docs/papers/system/auths-proof.md create mode 100644 docs/papers/system/auths-proof.pdf create mode 100755 docs/papers/system/build.sh create mode 100644 docs/papers/system/preamble.tex create mode 100644 docs/papers/system/references.bib create mode 100644 docs/prompts/LAUNCH_READINESS_INVESTIGATIONS.md create mode 100644 docs/target-state/adoption-readiness.md create mode 100644 docs/target-state/v1-launch-readiness-independent.md create mode 100644 docs/testing/TESTING_INVARIANTS.md create mode 100644 output/pdf/auths-proof-formal-semantics.pdf create mode 100644 output/pdf/auths-proof.pdf diff --git a/bindings/python/api/public-api.txt b/bindings/python/api/public-api.txt index 600be3d8..279d6bce 100644 --- a/bindings/python/api/public-api.txt +++ b/bindings/python/api/public-api.txt @@ -19,6 +19,7 @@ Receipt RecommendedAction RecoveryResult RetryClass +create_auths doctor [auths.identity] @@ -88,7 +89,7 @@ RecommendedAction RetryClass ServiceAuthority ServiceAuthorityResult -ServiceAuths +ServiceClient ServiceCompleted ServiceDenied ServiceExecutionResult @@ -102,7 +103,7 @@ ServiceTransportRequest ServiceTransportResponse ServiceVerificationResult ServiceVerified -create_auths +create_service_client [auths.profiles] DevelopmentMcpProvider diff --git a/bindings/python/python/auths/__init__.py b/bindings/python/python/auths/__init__.py index 23460ea4..7b9ac340 100644 --- a/bindings/python/python/auths/__init__.py +++ b/bindings/python/python/auths/__init__.py @@ -14,6 +14,7 @@ Completed as Completed, Denied as Denied, ExecutionReference as ExecutionReference, + create_auths as create_auths, ExecutionResult as ExecutionResult, Indeterminate as Indeterminate, PlanCompleted as PlanCompleted, @@ -55,6 +56,7 @@ "RecommendedAction": "._product_errors", "RecoveryResult": "._product", "RetryClass": "._product_errors", + "create_auths": "._product", "doctor": "._doctor", } diff --git a/bindings/python/python/auths/__init__.pyi b/bindings/python/python/auths/__init__.pyi index 1dcc4373..5ce5067a 100644 --- a/bindings/python/python/auths/__init__.pyi +++ b/bindings/python/python/auths/__init__.pyi @@ -22,6 +22,7 @@ from ._product import Receipt as Receipt from ._product_errors import RecommendedAction as RecommendedAction from ._product import RecoveryResult as RecoveryResult from ._product_errors import RetryClass as RetryClass +from ._product import create_auths as create_auths from ._doctor import doctor as doctor __all__: list[str] diff --git a/bindings/python/python/auths/_product.py b/bindings/python/python/auths/_product.py index 2dc3e4a1..1a3a17b5 100644 --- a/bindings/python/python/auths/_product.py +++ b/bindings/python/python/auths/_product.py @@ -54,6 +54,7 @@ ) _CONFIGURATION_TOKEN = object() +_FACADE_TOKEN = object() _REFERENCE_TOKEN = object() @@ -226,11 +227,23 @@ def __init__( class Auths: + """The local product facade. Obtained from `create_auths`, never built here. + + The constructor is sealed for the same reason TypeScript never exports + `AuthsFacade`: `create_auths` is the one entry point for the `create` verb, + and a second reachable way to mint an `Auths` would be a second entry point + wearing a private name. `_AuthsResources` is only ever assembled by an + integration, so a caller reaching this directly has skipped composition. + """ + def __init__( self, + token: object, resources: _AuthsResources, diagnostics: tuple[str, ...], ) -> None: + if token is not _FACADE_TOKEN: + raise TypeError("sealed Auths facade; use auths.create_auths") self._resources = resources self.actor = Actor(resources.agent.identity.principal.principal.value) self.authority: Authority = resources.authority @@ -329,6 +342,7 @@ async def dispose() -> None: await agent.aclose() child = Auths( + _FACADE_TOKEN, _AuthsResources( agent, authority, @@ -401,10 +415,18 @@ def _create_auths_configuration( ) -async def _create_auths(configuration: AuthsConfiguration) -> Auths: +async def create_auths(configuration: AuthsConfiguration) -> Auths: + """Opens the local product facade over a configuration an integration built. + + The `create` verb, spelled `createAuths` in TypeScript. This is the only + public way to obtain an `Auths`: the class is the noun, this is the + operation, and the two are not the same name in either language. + """ if type(configuration) is not AuthsConfiguration: raise TypeError("Auths configuration was not created by an integration") - return Auths(await configuration._open(), configuration.diagnostics) + return Auths( + _FACADE_TOKEN, await configuration._open(), configuration.diagnostics + ) def verify_receipt(receipt: Receipt) -> None: @@ -510,6 +532,7 @@ def _project_plan_execution(value: object) -> ExecutionResult: "AuthsErrorCode", "Authority", "Completed", + "create_auths", "Denied", "decode_execution_reference", "decode_receipt", diff --git a/bindings/python/python/auths/_service.py b/bindings/python/python/auths/_service.py index 154a515c..925eee9b 100644 --- a/bindings/python/python/auths/_service.py +++ b/bindings/python/python/auths/_service.py @@ -179,7 +179,18 @@ class ServiceRejected: ] -class ServiceAuths: +class ServiceClient: + """The remote Auths runtime client -- TypeScript's `ServiceClient`. + + Deliberately not named `ServiceAuths`. The remote counterpart of the local + `Auths` facade is the `ServiceAuthority` this client returns from `create`; + this object is the transport that asks a service to mint one. Naming the + transport `Auths` would advertise a substitutability with the local facade + that does not exist: `Auths.execute` takes an action and a provider this + process holds, and `ServiceClient.execute` takes an authority and opaque + bytes over HTTPS. + """ + def __init__( self, *, @@ -342,17 +353,22 @@ def _axis(code: str) -> tuple[EffectState, RetryClass, RecommendedAction]: ) -def create_auths( +def create_service_client( *, endpoint: str, identity: bytes, profile: ServiceProfile, transport: Optional[ServiceTransport] = None, timeout_seconds: float = _DEFAULT_TIMEOUT_SECONDS, -) -> ServiceAuths: +) -> ServiceClient: + """Opens a client for a remote Auths service -- `createServiceClient` in TypeScript. + + Formerly `create_auths`, which returned something that is not an `Auths` + while `auths.integrations.development.create_auths` returned one. + """ if production_client_contract_version_v1() != 1: raise RuntimeError("Auths production client contract mismatch") - return ServiceAuths( + return ServiceClient( endpoint=endpoint, identity=identity, profile=profile, @@ -582,9 +598,9 @@ def _is_recovery_reference(value: str) -> bool: __all__ = [ "NextCall", - "ServiceAuths", "ServiceAuthority", "ServiceAuthorityResult", + "ServiceClient", "ServiceCompleted", "ServiceDenied", "ServiceExecutionResult", @@ -598,5 +614,5 @@ def _is_recovery_reference(value: str) -> bool: "ServiceTransportResponse", "ServiceVerificationResult", "ServiceVerified", - "create_auths", + "create_service_client", ] diff --git a/bindings/python/python/auths/integrations.py b/bindings/python/python/auths/integrations.py index 99558b5f..6cb346f6 100644 --- a/bindings/python/python/auths/integrations.py +++ b/bindings/python/python/auths/integrations.py @@ -29,8 +29,8 @@ Auths, AuthsConfiguration, _AuthsResources, - _create_auths, _create_auths_configuration, + create_auths, ) from ._bootstrap import prepare_raw_key_authority from .profiles._mcp import ( @@ -285,7 +285,7 @@ def __await__(self) -> Generator[Any, None, Auths]: async def _open(self) -> Auths: if self._auths is None: - self._auths = await _create_auths(self._configuration) + self._auths = await create_auths(self._configuration) return self._auths async def __aenter__(self) -> Auths: diff --git a/bindings/python/python/auths/service.py b/bindings/python/python/auths/service.py index 451da7d4..d4198020 100644 --- a/bindings/python/python/auths/service.py +++ b/bindings/python/python/auths/service.py @@ -21,7 +21,7 @@ NextCall, ServiceAuthority, ServiceAuthorityResult, - ServiceAuths, + ServiceClient, ServiceCompleted, ServiceDenied, ServiceExecutionResult, @@ -35,7 +35,7 @@ ServiceTransportResponse, ServiceVerificationResult, ServiceVerified, - create_auths, + create_service_client, ) __all__ = [ @@ -48,7 +48,7 @@ "RetryClass", "ServiceAuthority", "ServiceAuthorityResult", - "ServiceAuths", + "ServiceClient", "ServiceCompleted", "ServiceDenied", "ServiceExecutionResult", @@ -62,5 +62,5 @@ "ServiceTransportResponse", "ServiceVerificationResult", "ServiceVerified", - "create_auths", + "create_service_client", ] diff --git a/bindings/python/tests/test_production_client.py b/bindings/python/tests/test_production_client.py index c68bf459..c05e0f0e 100644 --- a/bindings/python/tests/test_production_client.py +++ b/bindings/python/tests/test_production_client.py @@ -6,7 +6,7 @@ import pytest -from auths.service import create_auths +from auths.service import create_service_client from auths._native import ( decode_production_request_v1, decode_production_response_v1, @@ -82,15 +82,15 @@ async def send( async def scenario() -> None: transport = Transport() - auths = create_auths( + client = create_service_client( endpoint="https://operator.example", identity=bytes([1]) * 32, profile=github_issue_address(), transport=transport, ) - authority = await auths.create(b"create") + authority = await client.create(b"create") assert authority.kind == "authority" - executed = await auths.execute(authority, b"execute") + executed = await client.execute(authority, b"execute") assert executed.kind == "completed" assert transport.paths == [ "/v1/authority/create", diff --git a/bindings/python/tests/test_vocabulary_parity.py b/bindings/python/tests/test_vocabulary_parity.py index e4a273df..c6a807a4 100644 --- a/bindings/python/tests/test_vocabulary_parity.py +++ b/bindings/python/tests/test_vocabulary_parity.py @@ -12,6 +12,8 @@ import typing from pathlib import Path +import pytest + import auths from auths._product_errors import EffectState, RecommendedAction, RetryClass import auths.service @@ -234,3 +236,61 @@ def test_no_name_is_exported_from_two_entry_points_as_two_different_types() -> N f"path: {homonyms}. A shared declaration re-exported from two paths is " "fine; two unrelated types under one name is not." ) + + +# --------------------------------------------------------------------------- +# The two factories: same concept, same word, one spelling per language. +# --------------------------------------------------------------------------- + + +def _typescript_source(*parts: str) -> str: + return (_TYPESCRIPT.joinpath(*parts)).read_text() + + +def test_create_is_a_factory_in_both_languages_and_not_a_constructor() -> None: + """Contract 4.2: `create` is one operation with one entry point per language. + + TypeScript never exports the class behind `Auths`, so `createAuths` is the + only way to obtain one. Python exported the class itself, which made + `Auths(...)` a second, undocumented entry point for the same verb. + """ + product = _typescript_source("product.ts") + assert "export function createAuths(" in product, ( + "TypeScript no longer spells the create verb `createAuths`; this check " + "is asserting a spelling that no longer exists" + ) + assert "export class AuthsFacade" not in product, ( + "TypeScript exported the facade class, so it too now has two entry " + "points for `create` and Python was aligned to the wrong shape" + ) + + assert callable(auths.create_auths) + assert "create_auths" in auths.__all__ + + with pytest.raises(TypeError, match="sealed Auths facade"): + auths.Auths(object(), object(), ()) + + +def test_the_service_client_is_named_the_same_thing_in_both_languages() -> None: + """Contract 4.4: the remote client is `ServiceClient`, minted by a factory. + + Python called the factory `create_auths` and the type `ServiceAuths`, so + one word named the remote client here and the local facade in + `auths.integrations`, and neither matched TypeScript. + """ + service = _typescript_source("service.ts") + assert "export function createServiceClient(" in service + assert "export interface ServiceClient {" in service + + import auths.integrations + + assert callable(auths.service.create_service_client) + assert isinstance(auths.service.ServiceClient, type) + assert not hasattr(auths.service, "create_auths"), ( + "`create_auths` is back on the remote client, where it names something " + "that is not an Auths" + ) + assert not hasattr(auths.service, "ServiceAuths") + assert callable(auths.integrations.development.create_auths), ( + "`create_auths` must keep meaning exactly one thing: make a local Auths" + ) diff --git a/docs/papers/proof/README.md b/docs/papers/proof/README.md new file mode 100644 index 00000000..456c4e5f --- /dev/null +++ b/docs/papers/proof/README.md @@ -0,0 +1,15 @@ +# Auths-Proof formal semantics paper + +This directory contains the mathematical companion paper for the Lean 4 +development under `formal/`. + +Build it from this directory: + +```sh +./build.sh +``` + +The script leaves a review copy beside the Markdown source and writes the final +artifact to `output/pdf/auths-proof-formal-semantics.pdf` at repository root. +All figures are native TikZ vectors embedded in the paper source so that the +mathematics, theorem discussion, and diagrams evolve together. diff --git a/docs/papers/proof/auths-proof-formal-semantics.md b/docs/papers/proof/auths-proof-formal-semantics.md new file mode 100644 index 00000000..cf46d82a --- /dev/null +++ b/docs/papers/proof/auths-proof-formal-semantics.md @@ -0,0 +1,2312 @@ +--- +title: "Order, Evidence, and Effects: The Formal Semantics of Auths-Proof" +author: "bordumb · bordumbb@gmail.com" +date: 16 August 2026 +abstract: | + Auths-Proof is a proof-carrying authorization system whose formal + development spans algebra, denotational semantics, transition systems, + bounded arithmetic, and refinement of shipping Rust. Its central safety + idea is order-theoretic: delegation may preserve or reduce authority, but it + may not enlarge the set of complete authorization facts admitted by a + parent. This paper reconstructs that idea from the Lean 4 artifact under + `formal/`, presents the model as mathematics rather than source-code + commentary, and explains how the abstract relations connect to executable + decisions. + + The authority scope is a heterogeneous product of profile state, finite + permission and audience sets, inclusive time intervals, an action-constraint + preorder, optional budget ceilings, freshness policies, exact assurance + identities, and pinned critical extensions. Lean proves component order + laws, semantic monotonicity, canonical antisymmetry, downward closure of + action coverage and evidence requirements, preservation of trust roots and + critical extensions, strict decrease of delegation depth, finiteness of + chains, unique accepted transitions, and sound and complete decision + procedures. A separate three-valued algebra treats denial, uncertainty, and + authorization as an ordered chain and proves threshold-classification + properties. Product and lifecycle modules formalize configuration equality, + checked arithmetic, capacity conservation, replay classification, + credential ordering, provider-call entry, and recovery from unknown + outcomes. + + To connect specification with implementation, Charon and Aeneas translate + selected pure safe Rust functions into Lean. Representation maps and + weakest-precondition proofs relate translated strings, bounded vectors, + sets, intervals, constraints, budgets, and state machines to the handwritten + model. The current assurance inventory contains 121 compiled declarations: + 66 rich-authority claims, 4 production-refinement claims, 13 product claims, + 22 lifecycle claims, and 16 composition claims. The artifact also records + its limits: canonical decoding, cryptography, external evidence, stores, and + provider behavior are not proved; representation validity is a premise; and + the checked-in translation has an explicitly proved stale optional-budget + case. The result is not whole-program verification. It is a precise, + inspectable refinement argument for the security-critical pure semantics. +--- + +\newpage +\begin{multicols}{2} +\footnotesize +\tableofcontents +\end{multicols} +\newpage + +# Introduction + +Authorization is often presented as a Boolean predicate over a subject, an +operation, and a resource. That abstraction is useful, but it is too coarse +for a system in which authority is delegated, narrowed, bound to exact action +bytes, evaluated under uncertain evidence, and ultimately converted into an +irreversible external effect. + +Auths-Proof separates four questions: + +1. **Authenticity:** who produced a signed statement? +2. **Authority:** which actions can be justified from a trust root? +3. **Eligibility:** does a domain-specific policy admit this action in this + context? +4. **Effect:** was the authorized action reserved, attempted, observed, and + settled without duplication? + +The formal directory concentrates on the pure semantics that make these +questions composable. It does not model public-key cryptography, byte decoding, +networks, clocks, databases, or providers. Instead, it gives mathematically +precise meanings to attenuation, coverage, evidence, decision composition, +bounded product policy, and effect lifecycle. It then connects selected +shipping Rust predicates to those meanings by mechanical translation. + +The organizing judgment of the paper is: + +\begin{thesisbox} +\centering +If a child authority is structurally below its parent, every complete +authorization fact admitted by the child is admitted by the parent. +\end{thesisbox} + +Writing $S_c \atten S_p$ for structural attenuation and +$\denote{S}$ for the set of complete authorization facts admitted by a scope, +the fundamental implication is + +$$ +S_c \atten S_p +\quad\Longrightarrow\quad +\denote{S_c} \subseteq \denote{S_p}. +\tag{1} +$$ + +Equation (1) is the semantic form of least authority. A delegate can keep the +same authority or move downward, but cannot create a fact that becomes +authorized only after delegation. This connects order theory to access +control: the order is not chosen for elegance alone; it is justified by set +containment of admitted behaviors. + +The work belongs to several research traditions. Trust-management systems +make delegation and authority explicit [@abadi1993calculus; @blaze1996trust]. +Capability systems and caveat-based tokens motivate monotone restriction +[@birgisson2014macaroons]. Proof-carrying systems motivate portable evidence +checked by a small verifier [@necula1997pcc; @appel1999pca]. Order theory +supplies preorders, partial orders, products, and monotone maps +[@davey2002lattices]. Program logic supplies preconditions, postconditions, +and refinement [@hoare1969axiomatic; @dijkstra1975guarded]. Lean 4 provides the +machine-checked foundation [@demoura2021lean4], while Aeneas provides a route +from safe Rust to functional Lean [@ho2022aeneas]. + +## Contributions of the formal artifact + +The development makes six concrete contributions. + +**A heterogeneous authority order.** Authority is not reduced to a scalar. +Finite sets use inclusion, intervals use containment, action constraints use a +constructor-sensitive preorder, optional budgets have an unbounded top, +freshness requirements order by method and age, assurance is invariant, and +critical extensions become exactly pinned after their first declaration. + +**A denotational safety theorem.** Structural attenuation implies semantic +attenuation: admission is downward closed for both actions and evidence. + +**A well-founded delegation semantics.** Accepted edges preserve one trust +root, preserve pinned critical extensions, link exact grant identifiers, +strictly reduce remaining depth, and determine a unique child state. + +**A three-valued composition algebra.** Denial, indeterminacy, and +authorization form a finite chain. Conjunction, disjunction, and threshold +classification preserve uncertainty rather than collapsing missing evidence +into permission. + +**A formal lifecycle for effects.** The model separates decision, reservation, +intent, credential authorization, attempt, provider entry, settlement, +unknown outcome, and reconciliation. It proves key ordering and capacity +invariants. + +**A production refinement chain.** Pure shipping Rust is translated to Lean, +then proved against the readable specification under explicit representation +premises. The proof inventory, statement hashes, axiom dependencies, source +closure, generated vectors, and mutation operators are themselves checked +artifacts. + +## Reading the claims correctly + +The paper uses three labels. + +- **Definition** means a mathematical reconstruction of a Lean definition. +- **Checked theorem** means the named proposition is compiled in the current + theorem inventory. +- **Interpretation** means a consequence or explanatory view useful to a human + reader; it should not be confused with a separately inventoried Lean theorem. + +This distinction matters. For example, the three truth values form a finite +chain and the implementations of `all` and `any` coincide with minimum and +maximum on that chain. Lean directly inventories commutativity, associativity, +and idempotence; this paper may use lattice language to explain those facts, +but it does not pretend that every familiar lattice law has its own exported +theorem. + +## The proof architecture + +The artifact is not one monolithic proof. It is a graph of specifications, +implementations, representation maps, and audit evidence. + +\begin{figure}[H] +\centering +\resizebox{0.98\linewidth}{!}{% +\begin{tikzpicture}[node distance=8mm and 11mm] + \node[axisbox=purple, minimum width=43mm, minimum height=19mm] (rich) { + \textbf{Rich mathematical model}\\ + sets, intervals, constraints, chains + }; + \node[axisbox=blue, minimum width=43mm, right=14mm of rich] (rust) { + \textbf{Pure shipping Rust}\\ + authority, policy, lifecycle kernels + }; + \node[axisbox=green, minimum width=43mm, right=14mm of rust] (aeneas) { + \textbf{Translated Lean}\\ + Charon LLBC and Aeneas output + }; + + \node[axisbox=purple, minimum width=43mm, below=12mm of rich] (algebra) { + \textbf{Algebra contract}\\ + truth order and 11 dimensions + }; + \node[kernel, minimum width=43mm, below=12mm of rust] (refine) { + REFINEMENT\\[-1pt] + \normalfont\footnotesize translated result = rich result + }; + \node[axisbox=amber, minimum width=43mm, below=12mm of aeneas] (premises) { + \textbf{Representation premises}\\ + bounded, canonical, validated values + }; + + \node[card, minimum width=43mm, below=12mm of algebra] (vectors) { + \textbf{Generated evidence}\\ + vectors and mutation witnesses + }; + \node[axisbox=green, minimum width=43mm, below=12mm of refine] (inventory) { + \textbf{121 audited declarations}\\ + exact names, statements, axioms + }; + \node[card, minimum width=43mm, below=12mm of premises] (closure) { + \textbf{Qualified source closure}\\ + tools, Rust sources, generated Lean + }; + + \node[kernel, minimum width=142mm, below=13mm of inventory] (gate) { + READ-ONLY FORMAL GATE\\[-1pt] + \normalfont\footnotesize build + audit + closure + drift + conformance + }; + + \draw[flow=blue] (rust) -- (aeneas); + \draw[flow=purple] (rich) -- (refine); + \draw[flow=green] (aeneas) -- (refine); + \draw[flow=amber] (premises) -- (refine); + \draw[flow=purple] (algebra) -- (vectors); + \draw[flow=green] (refine) -- (inventory); + \draw[flow=amber] (closure) -- (inventory); + \draw[flow=green] (vectors) -- (gate); + \draw[flow=green] (inventory) -- (gate); + \draw[flow=green] (closure) -- (gate); +\end{tikzpicture}} +\caption{\textbf{The evidence graph.} The readable mathematical model and the +mechanically translated production functions meet in refinement theorems. The +assurance layer audits both the propositions and the source closure on which +they depend.} +\end{figure} + +# Artifact map and methodological boundary + +The Lean project is organized by mathematical concern rather than by one +end-to-end verifier function. + +| Layer | Principal modules | Mathematical role | +|---|---|---| +| Finite algebra | `Generated/Algebra`, `Base`, `Composition` | Truth chain, conjunction, disjunction, thresholds, plan measures | +| Rich authority | `Rich/Types`, `Rich/Semantics`, `Rich/Theorems` | Scope order, denotation, delegation, coverage, decisions | +| Focused authority claims | `Authority`, `Attenuation` | Trust-root and critical-extension consequences | +| Product policy | `Product/*` | Commitments, checked arithmetic, eligibility, tightening | +| Effect lifecycle | `Lifecycle/*` | State transitions, capacity, replay, reconciliation | +| Rust refinement | `Refinement/Production` | Representation maps and equivalence to translated authority Rust | +| Assurance | `Theorems`, `AssuranceAudit`, manifest | Public inventory, exact statements, axiom audit, source closure | +| Qualification | `qualification/aeneas/*` | Pinned translation outputs, external models, executable cases | + +The top-level `Auths.lean` imports the model. `Auths.Theorems` enumerates the +public theorem declarations. `Auths.AssuranceAudit` queries Lean's compiled +environment, prints each declaration's actual type, and computes its +transitive axiom set. This avoids a common anti-pattern: treating the presence +of a theorem-like string in source code as evidence that the intended theorem +still exists. + +## What the formal core intentionally excludes + +The model receives already validated semantic values and explicit facts. It +does not prove: + +- canonical CBOR decoding or byte-for-byte re-encoding; +- signature schemes, key custody, or trust-registry behavior; +- evidence acquisition, wall-clock truth, or network freshness; +- database atomicity, crash durability, or concurrency control; +- external provider determinism or the truth of provider observations; +- the Rust compiler's machine-code output; or +- complete verifier control flow. + +These exclusions are not incidental. They preserve a pure kernel in which +definitions are total and explicit. The complete system claim is therefore a +composition of proof, translation, generated conformance, testing, and trusted +boundaries rather than a single universal theorem. + +\begin{boundarybox} +\textbf{Claim boundary.} Lean proves the pure relations and selected translated +Rust functions under their premises. It does not prove that arbitrary bytes +are valid Auths objects, that external facts are true, or that a remote effect +occurred. Those propositions require distinct evidence. +\end{boundarybox} + +## Snapshot of the audited theorem inventory + +The current `theoremInventory` contains 121 declarations. + +| Family | Count | Examples | +|---|---:|---| +| Rich authority | 66 | component laws, semantic containment, chains, diagnostics | +| Production refinement | 4 | author scope, terminal coverage, delegation, explicit budget gap | +| Product policy | 13 | commitments, eligibility, arithmetic, translated arithmetic | +| Lifecycle | 22 | capacity, replay, effect ordering, translated transition kernel | +| Composition | 16 | truth algebra, thresholds, plan measures | + +Auxiliary definitions and lemmas may compile without appearing in this public +inventory. The manifest is therefore a claim surface, not a count of every +fact Lean knows. + +# The three-valued decision algebra + +Authorization under incomplete evidence is not naturally Boolean. A negative +result may be permanent, or it may mean that a trustworthy fact is not yet +available. Auths-Proof uses + +$$ +\mathbb{T}_3 = \{\Denied,\Unknown,\Authorized\}, +\qquad +\Denied \sqsubset \Unknown \sqsubset \Authorized. +\tag{2} +$$ + +The Lean function `Truth.rank` embeds this chain into $\Nat$: + +$$ +\rho(\Denied)=0, +\qquad +\rho(\Unknown)=1, +\qquad +\rho(\Authorized)=2, +$$ + +and defines $x\sqsubseteq y$ by $\rho(x)\leq\rho(y)$. Injectivity of $\rho$ +is proved, so rank equality recovers truth equality. + +## Conjunctive and disjunctive composition + +Define + +$$ +x \mathbin{\wedge_3} y = \min_{\sqsubseteq}(x,y), +\qquad +x \mathbin{\vee_3} y = \max_{\sqsubseteq}(x,y). +\tag{3} +$$ + +Operationally, `all` denies if either branch denies, otherwise remains +indeterminate if either branch is indeterminate, and authorizes only when both +authorize. `any` authorizes if either branch authorizes, otherwise remains +indeterminate if either is indeterminate, and denies only when both deny. + +| $x$ | $y$ | $x\wedge_3y$ | $x\vee_3y$ | +|---|---|---|---| +| $\Denied$ | $\Denied$ | $\Denied$ | $\Denied$ | +| $\Denied$ | $\Unknown$ | $\Denied$ | $\Unknown$ | +| $\Denied$ | $\Authorized$ | $\Denied$ | $\Authorized$ | +| $\Unknown$ | $\Unknown$ | $\Unknown$ | $\Unknown$ | +| $\Unknown$ | $\Authorized$ | $\Unknown$ | $\Authorized$ | +| $\Authorized$ | $\Authorized$ | $\Authorized$ | $\Authorized$ | + +\begin{claimbox}{Checked theorem family: binary composition} +Lean proves commutativity, associativity, and idempotence for both +\texttt{all} and \texttt{any}. It also proves that the two-branch threshold at +one equals \texttt{any}, the threshold at two equals \texttt{all}, and increasing the threshold from one to two +cannot raise the result in the truth order. +\end{claimbox} + +\begin{figure}[H] +\centering +\begin{tikzpicture}[node distance=13mm] + \node[state=red, minimum width=28mm] (d) {$\Denied$\\[-1pt]\normalfont rank 0}; + \node[state=amber, minimum width=28mm, above=of d] (u) {$\Unknown$\\[-1pt]\normalfont rank 1}; + \node[state=green, minimum width=28mm, above=of u] (a) {$\Authorized$\\[-1pt]\normalfont rank 2}; + \draw[flow=muted] (d) -- node[right,note]{more informative permission} (u); + \draw[flow=muted] (u) -- (a); + \node[axisbox=purple, minimum width=49mm, right=26mm of u] (meet) { + \textbf{all} $=\min$\\ + one denial is decisive + }; + \node[axisbox=blue, minimum width=49mm, below=8mm of meet] (join) { + \textbf{any} $=\max$\\ + one authorization is decisive + }; + \draw[thinflow=purple,dashed] (meet.west) -- (u.east); + \draw[thinflow=blue,dashed] (join.west) -- (u.east); +\end{tikzpicture} +\caption{\textbf{The truth chain.} Indeterminacy is neither authorization nor +permanent denial. Conjunction takes the lower result; disjunction takes the +higher result.} +\end{figure} + +## Threshold classification + +Let $k$ be the number of authorizing branches required, $a$ the number already +authorized, and $u$ the number indeterminate. The generated classifier is + +$$ +\Theta(k,a,u)= +\begin{cases} +\Authorized, & a\geq k,\\ +\Unknown, & a,line width=0.8pt] (0,0) -- (9.4,0) node[right] {$a$ authorized}; + \draw[->,line width=0.8pt] (0,0) -- (0,6.8) node[above] {$u$ indeterminate}; + \fill[redwash] (0,0) -- (4.9,0) -- (0,4.9) -- cycle; + \fill[amberwash] (0,5) -- (5,0) -- (5,6.3) -- (0,6.3) -- cycle; + \fill[greenwash] (5,0) rectangle (9,6.3); + \draw[red,line width=1.1pt] (0,5) -- (5,0); + \draw[green,line width=1.1pt] (5,0) -- (5,6.3); + \node[text=red,font=\sffamily\bfseries,align=center] at (1.6,1.3) {$a+u0$ is a maximum age. Every policy is +below `ExpiryOnly`. Two snapshot policies compare only when their methods are +equal, and the child maximum age is no greater than the parent's. Thus a +fresher requirement is narrower. Lean proves that satisfaction is monotone +from child to parent. + +Assurance is not modeled as a lattice. It is preserved by exact identity: +$Q_c=Q_p$. This deliberately avoids inventing an ordering between assurance +schemes. + +Critical extensions follow a lock-in relation. Let $X$ be either `none` or a +canonical ordered sequence of at most 32 identifier-payload pairs. Then + +$$ +X_c\atten X_p \iff +\begin{cases} +\mathsf{true}, & X_p=\mathsf{none},\\ +\mathsf{false}, & X_c=\mathsf{none},\ X_p=\mathsf{some}(E),\\ +E_c=E_p, & X_c=\mathsf{some}(E_c),\ X_p=\mathsf{some}(E_p). +\end{cases} +\tag{13} +$$ + +The first accepted edge may declare a critical-extension sequence. Every later +edge must preserve it exactly. Unlike permissions and audiences, extensions +are represented by an ordered list because the shipping predicate compares +canonical vectors positionally. Distinct identifiers and the cardinality +bound are stored as constructor obligations. + +# Denotational authorization semantics + +The product order becomes a security statement only after scopes are given a +meaning. Auths-Proof defines that meaning over actions and trusted evidence +facts. + +## Actions and evidence + +An action carries + +$$ +a=(\mathit{actor},\mathit{terminalGrant},\mathit{profile}, +\mathit{permission},\mathit{window},\mathit{audience}, +\mathit{bodyDigest},\mathit{requestedBudget}). +$$ + +Evidence facts carry an optional status method, a status age, and an assurance +identity. Complete authorization facts are a pair $F=(a,e)$. + +Scope-level action coverage is + +$$ +\begin{aligned} +\mathsf{ActionCovers}(S,a) \iff {}& +\mathsf{profileAllows}(\Phi,a.profile)\\ +&\land a.permission\in P\\ +&\land a.window\subseteq I\\ +&\land a.audience\in A\\ +&\land \mathsf{constraintAllows}(C,a.bodyDigest)\\ +&\land \mathsf{budgetCovers}(B,a.requestedBudget). +\end{aligned} +\tag{14} +$$ + +Evidence satisfaction is + +$$ +\mathsf{EvidenceSatisfied}(S,e) +\iff \mathsf{statusSatisfied}(T,e)\land e.assurance=Q. +\tag{15} +$$ + +Finally, + +$$ +\mathsf{Admits}(S,F) +\iff \mathsf{ActionCovers}(S,F.action) +\land \mathsf{EvidenceSatisfied}(S,F.evidence). +\tag{16} +$$ + +Define the denotation + +$$ +\denote{S}=\{F\mid\mathsf{Admits}(S,F)\}. +\tag{17} +$$ + +This is the bridge between access control and order theory. + +## Structural soundness + +The semantic attenuation relation is extensional: + +$$ +S_c\atten_{\mathrm{sem}}S_p +\iff \forall F.\ \mathsf{Admits}(S_c,F)\Rightarrow +\mathsf{Admits}(S_p,F). +\tag{18} +$$ + +Equivalently, $\denote{S_c}\subseteq\denote{S_p}$. + +\begin{theorem}[Structural attenuation is semantically sound] +If $S_c\atten S_p$, then $S_c\atten_{\mathrm{sem}}S_p$. +\end{theorem} + +The Lean proof factors by concern. `action_coverage_downward_closed` uses +profile monotonicity, set membership monotonicity, interval transitivity, +constraint allowance monotonicity, and budget coverage monotonicity. +`evidence_requirements_downward_closed` uses status-satisfaction monotonicity +and assurance equality. `complete_admission_downward_closed` combines them. +`structural_scope_le_implies_semantic_attenuation` packages the result as +Equation (18). + +\begin{figure}[H] +\centering +\begin{tikzpicture} + \fill[bluewash] (0,0) ellipse (65mm and 31mm); + \draw[blue,line width=1pt] (0,0) ellipse (65mm and 31mm); + \fill[purplewash] (0,0) ellipse (39mm and 19mm); + \draw[purple,line width=1pt] (0,0) ellipse (39mm and 19mm); + \node[text=blue2,font=\sffamily\bfseries] at (0,24mm) + {$\denote{S_p}$ parent-admitted facts}; + \node[text=purple,font=\sffamily\bfseries,align=center] at (0,0) + {$\denote{S_c}$\\child-admitted facts}; + \node[axisbox=green, minimum width=49mm] at (91mm,0) (law) { + $S_c\preccurlyeq S_p$\\[2pt] + $\Longrightarrow$\\[2pt] + $\denote{S_c}\subseteq\denote{S_p}$ + }; + \draw[flow=green] (39mm,0) -- (law.west); +\end{tikzpicture} +\caption{\textbf{Denotational attenuation.} A narrower scope may remove +authorized facts, but cannot add a fact outside the parent's authorization +denotation.} +\end{figure} + +## Preorder, canonical partial order, and equivalence + +Semantic attenuation is reflexive and transitive because subset is reflexive +and transitive. Mutual semantic attenuation defines extensional equivalence. +The structural scope relation is also reflexive and transitive. + +Raw action-constraint syntax prevents global structural antisymmetry, as noted +in Section 4.3. Under canonical action constraints, however, every component is +antisymmetric. Lean then proves + +$$ +S_1\atten S_2\land S_2\atten S_1 +\quad\Longrightarrow\quad S_1=S_2. +\tag{19} +$$ + +Thus the development uses the correct algebraic level for each question: + +- a preorder for raw representations; +- semantic equivalence for denotations; and +- a partial order for canonical representatives. + +## Decidability + +Every carrier supplies decidable equality, and every component relation is +decidable. `structuralScopeLeDecide` computes a Boolean, and Lean proves + +$$ +\mathsf{structuralScopeLeDecide}(S_c,S_p)=\mathsf{true} +\iff S_c\atten S_p. +\tag{20} +$$ + +This theorem is small but architecturally important. The mathematical relation +is not merely axiomatized; it has a decision procedure whose positive result +is exactly the declared V1 relation. + +# Delegation as a well-founded transition system + +Scope order alone does not express identity linkage, trust roots, grant +lineage, or chain termination. These live in `ChainState`: + +$$ +q=(r,s,S,d,g), +\tag{21} +$$ + +where $r$ is the root principal, $s$ the current subject, $S$ the authority +scope, $d\in\Nat$ the remaining depth, and $g$ an optional identifier of the +last applied grant. + +## Rootedness and linkage + +The local predicate + +$$ +\mathsf{rooted}(q) +\iff g\neq\mathsf{none}\ \lor\ r=s +\tag{22} +$$ + +states that either an accepted edge has already been applied or a fresh state +still speaks for its root. A proposed grant $h$ preserves the root when + +$$ +\mathsf{rootPreserved}(q,h) +\iff \mathsf{rooted}(q)\land h.issuer=s. +\tag{23} +$$ + +It is linked when, additionally, $h.parent=g$. These are local semantic facts; +the formal module does not reverify signatures or reconstruct a proof graph. + +The first edge of a fresh chain must be issued by the root itself. Every +accepted edge is issued by the current subject and copies the root into its +child. An unrooted state delegates nothing and authorizes no terminal action. + +## Accepted transition + +A grant carries its issuer, new subject, selected profile, narrowed scope +coordinates, remaining depth, parent grant identifier, and critical +extensions. It passes scope and depth checks when + +$$ +d>0 +\land h.depth0$, bounded} (reserve); + \draw[flow=green] (reserve) -- node[above,note]{$x\leq a+r$} (commit); + \draw[flow=amber] (reserve) -- node[below,note]{$x\leq a+r$} (release); + \node[note,text=green, right=6mm of commit] {$m'+a'\leq c$}; + \node[note,text=green, right=6mm of release] {$m'+a'\leq c$}; +\end{tikzpicture} +\caption{\textbf{Capacity preservation.} Reserve increases active capacity, +commit moves active capacity into committed capacity, and release returns +active capacity. Every successful step preserves the ceiling invariant.} +\end{figure} + +Exclusive capacity uses a separate predicate: + +$$ +\mathsf{exclusiveAvailable}(liveOwner,exactReplay) +=\neg liveOwner\lor exactReplay. +\tag{47} +$$ + +Thus the current owner may revisit the same exact operation, but a distinct +claim cannot coexist with a live owner. + +## Replay classification + +Replay is classified from record existence and commitment equality: + +$$ +\mathsf{replay}(exists,equal)= +\begin{cases} +\mathsf{Absent},&\neg exists,\\ +\mathsf{ExactReplay},&exists\land equal,\\ +\mathsf{Conflict},&exists\land\neg equal. +\end{cases} +\tag{48} +$$ + +Lean checks that exact replay is stable, conflicting replay is never exact, +and an absent record never claims an existing effect regardless of the +irrelevant equality Boolean. + +# Refinement from shipping Rust to mathematical semantics + +A proof about a handwritten model does not by itself prove a separately +written implementation. Auths-Proof addresses this correspondence problem by +isolating pure safe Rust functions, lowering them with Charon, translating +them with Aeneas, and proving the generated Lean functions equivalent to the +rich model. This resembles translation-validation thinking +[@pnueli1998translation], but the target theorem relates translated program +semantics to a domain specification rather than validating one compiler run. + +## Three semantic levels + +The proof chain has three levels: + +$$ +\text{validated Rust representation} +\xrightarrow{\alpha} +\text{rich semantic value} +\xrightarrow{\denote{-}} +\text{authorization meaning}. +\tag{49} +$$ + +The translated Rust function runs on the left. The readable specification runs +on the middle value. Refinement proves that decisions agree. The denotational +theorems then justify the security meaning of the rich relation. + +\begin{figure}[H] +\centering +\resizebox{0.98\linewidth}{!}{% +\begin{tikzpicture}[node distance=10mm and 13mm] + \node[axisbox=blue, minimum width=42mm] (bytes) { + \textbf{Canonical bytes}\\ + outside this proof + }; + \node[axisbox=amber, minimum width=42mm, right=of bytes] (rustval) { + \textbf{Validated Rust values}\\ + constructor invariants + }; + \node[axisbox=green, minimum width=42mm, right=of rustval] (translated) { + \textbf{Aeneas evaluator}\\ + translated shipping Rust + }; + \node[axisbox=purple, minimum width=42mm, right=of translated] (rich) { + \textbf{Rich Lean value}\\ + extensional semantics + }; + \node[kernel, minimum width=102mm, below=16mm of translated] (theorem) { + REFINEMENT THEOREM\\[-1pt] + \normalfont\footnotesize translated decision equals rich decision + }; + \node[axisbox=purple, minimum width=42mm, right=13mm of theorem] (meaning) { + \textbf{Denotation}\\ + admitted facts + }; + + \draw[flow=amber,dashed] (bytes) -- node[above,note]{tested decoder} (rustval); + \draw[flow=blue] (rustval) -- node[above,note]{exact function} (translated); + \draw[flow=purple] (rustval) to[bend left=22] node[above,note]{abstraction $\alpha$} (rich); + \draw[flow=green] (translated) -- (theorem); + \draw[flow=purple] (rich) -- (theorem); + \draw[flow=purple] (rich) -- (meaning); +\end{tikzpicture}} +\caption{\textbf{Refinement layers.} Decoding is a separate boundary. +Validated Rust values feed both the translated evaluator and an abstraction +map into the rich model; the theorem equates their decisions.} +\end{figure} + +## Aeneas weakest-precondition judgments + +The production bridge uses Aeneas' functional translation and weakest- +precondition notation. Schematically, + +$$ +f(x)\ \{\!\!\{\ y\mid Q(y)\ \}\!\!\} +\tag{50} +$$ + +states that running translated function $f$ on $x$ returns a result satisfying +$Q$. Loop-bearing Rust functions are proved with decreasing natural measures +and prefix invariants. For example, a membership loop over a vector maintains +that every earlier index differs from the target; a subset loop maintains that +every earlier child element has a corresponding parent element. + +This is classic program logic applied to extracted safe Rust. The proof does +not postulate that helper predicates behave semantically. It proves leaf +specifications for byte equality, string bytes, profile equality, permission +and audience membership and subset, digest membership, interval containment, +constraints, budgets, status, and critical extensions, then composes them +through the evaluator. + +## Representation maps + +The production vocabulary instantiates opaque carriers as exact byte-oriented +keys: + +| Rich carrier | Production key | +|---|---| +| Principal | UTF-8 byte list | +| Profile | `(version, UTF-8 id bytes)` | +| Permission | `(capability bytes, resource bytes)` | +| Audience | UTF-8 byte list | +| Digest | fixed byte list | +| Budget algebra | UTF-8 byte list | +| Status method | UTF-8 byte list | +| Assurance | UTF-8 byte list | +| Grant id | digest bytes | +| Critical extension | `(id bytes, payload bytes)` | + +Strings carry a premise that their UTF-8 byte size fits Aeneas' `u32` +carrier. Windows carry a well-formedness proof. Freshness limits are positive. +Selected profiles carry membership evidence. Sets carry boundedness and, where +the Rust constructor guarantees it, canonicality. + +Permissions and audiences map lists to extensional finite sets. The refinement +therefore proves loop membership and subset against mapped keys. Critical +extensions deliberately map to an ordered rich list, not a finite set, so +positional equality remains exact. This is a good example of choosing the +abstraction by the decision being refined rather than by superficial data +shape. + +## Authority refinement theorems + +There are three principal production-evaluator theorems. + +**Author scope.** For validated parent and child `ScopeAuthorityView` values, +the translated Rust pre-signing evaluator returns exactly +`richAuthorScopeDecision`, including the first failing dimension. + +**Terminal coverage.** For validated authority and action views, an anchoring +premise, and a current optional-budget translation case, the translated Rust +coverage evaluator returns the mapped rich `evaluateCoverage` decision. + +**Delegation.** For validated parent and grant views plus anchoring, the +translated Rust grant evaluator returns the mapped rich `evaluateGrant` +decision. An accepted production transition is the exact field projection of +the rich accepted next state. + +In symbolic form, each has the shape + +$$ +\mathsf{Valid}(x)\land H(x) +\Longrightarrow +\mathsf{Aeneas}(f_{Rust})(x)=\gamma(f_{Rich}(\alpha(x))), +\tag{51} +$$ + +where $H$ records explicit bridge premises, $\alpha$ is the representation +map, and $\gamma$ maps rich decisions back to production decision codes. + +## Lifecycle and bounded-policy refinement + +The same pattern applies outside authority. + +- Every rich lifecycle state, operation, and gate maps to its translated Rust + counterpart. `translated_transition_refines_rich` proves equality for all + combinations by exhaustive constructor analysis. +- Translated terminality, exclusive capacity, additive capacity, and replay + classification agree with the rich functions. +- Translated configuration matching agrees with the pure product projection + for all four input Booleans. +- Translated `u64` checked arithmetic refines natural-number arithmetic and + correctly distinguishes successful results from overflow, underflow, or + zero division. + +These theorems turn fixed-width implementation concerns into explicit +mathematical cases. Aeneas' `U64.checked_*` specifications provide the bitvector +facts; Lean's arithmetic tactics discharge the natural-number consequences. + +## The explicit stale-translation theorem + +The checked-in Aeneas translation of `optional_budget_covers` predates a +shipping Rust correction. On one input class it returns the old, fail-open +answer: + +$$ +\mathsf{translatedBudgetCovers}(\mathsf{some}(b),\mathsf{none}) +=\mathsf{true}, +$$ + +while the current rich and shipping semantics require + +$$ +\neg\mathsf{budgetCovers}(\mathsf{some}(b),\mathsf{none}). +\tag{52} +$$ + +The development does not hand-edit generated Lean or conceal the mismatch. +It defines + +$$ +\mathsf{TranslatedBudgetCoverageCurrent}(B,R) +\iff B=\mathsf{none}\lor R\neq\mathsf{none} +\tag{53} +$$ + +and requires this premise in the coverage refinement. Separately, +`translated_budget_coverage_gap_is_the_absent_request` proves Equation (52) as +an audited claim. + +\begin{boundarybox} +\textbf{Executable disclosure.} The coverage refinement intentionally excludes +the present-ceiling/absent-request pair until the translation is regenerated. +The mismatch is a theorem, not a comment. Once regenerated Lean matches the +shipping Rust, the mismatch theorem should stop compiling and the exclusion +premise should be removed atomically. +\end{boundarybox} + +The authority bridge carries another explicit premise, +`AuthorityStateAnchored`, because the checked-in authority translation +predates a Rust root field. It states that a translated state either has a last +grant or its supplied root equals its subject. This lets the rich state satisfy +its local rootedness predicate without fabricating a translated field that is +not present. + +# Assurance engineering around the proofs + +Formal proof is only useful when the proposition, source closure, generated +artifacts, and trusted assumptions remain identifiable. The `formal/` +directory therefore contains an assurance system around Lean. + +## Public theorem inventory + +`Auths.Theorems.theoremInventory` is an explicit list of the 121 public +declarations discussed in Section 2.2. The compiled `AssuranceAudit` target +looks up each exact declaration name in Lean's environment. For each one it +records + +$$ +(name,kind,statement,transitive\ axioms). +$$ + +The manifest additionally stores a digest of the reviewed theorem statement. +Renaming, deleting, weakening, or changing a theorem changes the audit rather +than silently satisfying a source-text search. + +The global reviewed axiom allowlist is + +$$ +\{\mathsf{Classical.choice},\mathsf{Quot.sound},\mathsf{propext}\}. +\tag{54} +$$ + +Individual theorems generally depend on subsets of this list. `sorryAx` is not +allowed. The distinction between compiled and uncompiled artifacts matters: +qualification templates contain placeholder axioms, but those templates are +inventoried as uncompiled and are not imported into the audited theorem +closure. + +## Qualified mechanical translation + +The Aeneas qualification pins a complete tool and source configuration: + +| Component | Pinned value | +|---|---| +| Shipping Rust | 1.97.1 | +| Extraction Rust | nightly-2026-06-01 | +| Lean | 4.31.0 | +| Charon | 0.1.225 at commit `527ea8e...` | +| Aeneas | commit `3a8586f...` | +| Kani | 0.67.0 | + +The translated crates cover model predicates, the generated algebra kernel, +the authority evaluators, bounded-policy primitives, and lifecycle primitives. +The qualification requires zero opaque local functions and zero required +compiled external axioms. External links are enumerated by exact Rust symbol. +The primary standard-library semantic bridge is `String::as_bytes`, modeled as +exact UTF-8 bytes within its carrier bound. + +The pinned Aeneas runtime contains four general proof-support `sorry` +declarations in slice and string-iterator modules. They are explicitly +inventoried. The qualified Auths declarations do not transitively depend on +them, and the compiled assurance audit would reject their appearance in a +public claim's axiom set. + +## Source closure + +The qualification source-closure file hashes the Cargo manifests and lockfile, +shipping Rust sources, algebra contract, formal qualification configuration, +toolchain lock, and the repository code that drives the formal gate. Generated +Lean files are separately listed. Clean qualification translates twice and +requires byte-identical output before comparing with the committed artifacts. + +This is not a proof that Charon or Aeneas is correct. It is a reproducibility +and drift argument: + +$$ +\text{same pinned sources + same pinned tools} +\Longrightarrow +\text{same generated evidence}, +\tag{55} +$$ + +checked operationally rather than assumed informally. + +## Generated vectors and mutation witnesses + +The Lean vector exporter produces three portable corpora. + +**All attenuation projections.** Eleven Booleans yield +$2^{11}=2{,}048$ assignments. The expected answer is the generated conjunction. + +**Threshold states.** For each required count $1\leq k\leq16$, the exporter +enumerates every pair $0\leq a\leq16$ and +$0\leq u\leq16-a$, producing + +$$ +16\sum_{a=0}^{16}(17-a)=16\cdot153=2{,}448 +\tag{56} +$$ + +cases. + +**Rich semantic witnesses.** The current exporter contains 26 focused vectors +for interval boundaries, finite-set membership and inclusion, budget order and +coverage, status freshness, and action-constraint constructors. + +The mutation manifest contains 23 security-relevant operators, including +reversing interval and subset directions, negating membership, accepting a +different exact digest, ignoring algebra or method identity, treating an +absent budget request as covered, accepting equal delegation depth, weakening +principal or grant-id equality, and ignoring critical-extension payload +changes. + +The vectors are not substitutes for unbounded theorems. Their role is +cross-language conformance and mutation sensitivity: a binding or optimized +implementation must reproduce concrete consequences of the semantics. + +\begin{figure}[H] +\centering +\resizebox{0.97\linewidth}{!}{% +\begin{tikzpicture}[node distance=9mm and 10mm] + \node[axisbox=purple, minimum width=39mm] (decl) {theorem declaration\\exact compiled type}; + \node[axisbox=green, minimum width=39mm, right=of decl] (axiom) {axiom closure\\reviewed allowlist}; + \node[axisbox=amber, minimum width=39mm, right=of axiom] (source) {source closure\\pinned hashes}; + \node[axisbox=blue, minimum width=39mm, right=of source] (trans) {translation\\byte reproducibility}; + + \node[card, minimum width=39mm, below=13mm of decl] (vectors) {4,522 generated cases\\plus 26 rich vectors}; + \node[card, minimum width=39mm, right=of vectors] (mut) {23 mutation\\operators}; + \node[card, minimum width=39mm, right=of mut] (cases) {qualification cases\\compiled closures}; + \node[card, minimum width=39mm, right=of cases] (bounds) {bounded Rust checks\\defense in depth}; + + \node[kernel, minimum width=161mm, below=14mm of mut] (audit) { + ASSURANCE CLAIM IS A TUPLE, NOT A SLOGAN\\[-1pt] + \normalfont\footnotesize proposition + proof + source + tools + assumptions + conformance + }; + + \foreach \a/\b in {decl/axiom,axiom/source,source/trans,vectors/mut,mut/cases,cases/bounds} + \draw[flow=muted] (\a) -- (\b); + \foreach \n in {decl,axiom,source,trans,vectors,mut,cases,bounds} + \draw[thinflow=green] (\n.south) -- (audit.north); +\end{tikzpicture}} +\caption{\textbf{Assurance closure.} A theorem is paired with its exact +statement, transitive axioms, production source closure, translation inputs, +and independent conformance evidence.} +\end{figure} + +## Why proof and model checking coexist + +The Lean theorems are unbounded where their statements quantify over natural +numbers, lists, scopes, or chain values. Bounded model checking remains useful +for fixed-width implementation properties, unsafe states that should be +unconstructible, panic freedom, and mutation killing. The two techniques answer +different questions. Proof supplies universal consequences of a model; +bounded checking explores the actual implementation within declared bounds +[@kroening2023cbmc]. + +# Trusted computing base and residual assumptions + +The formal result is strongest when its boundary is stated precisely. The +trusted computing base and residual assumptions can be divided into five +classes. + +## Logical foundation + +The argument trusts the Lean kernel, the pinned Lean toolchain and libraries, +and the reviewed foundational axioms in Equation (54). This is the usual small +proof-checking base of an interactive theorem prover, not the whole IDE or +tactic implementation. + +## Translation foundation + +The argument trusts the pinned Rust, Charon, and Aeneas semantics and the +reviewed external models. Aeneas is selected because it translates ownership- +aware safe Rust into a functional representation suited to theorem proving +[@ho2022aeneas]. Auths-Proof does not claim to verify Aeneas itself. + +The Rust compiler remains trusted for correspondence between extracted Rust +semantics and shipping machine code. This is narrower than a verified compiler +chain such as CompCert [@leroy2009compcert] and different from language-level +soundness work such as RustBelt [@jung2018rustbelt]. + +## Representation foundation + +Aeneas exposes underlying carriers after erasing private Rust constructors. +Refinement theorems therefore assume the invariants established by production +constructors and validation: + +- identifiers are canonical, nonempty, bounded byte sequences; +- counters and timestamps lie within fixed-width ranges; +- validity windows are well formed; +- collections are bounded and canonical where required; +- selected profiles are members of the allowed set; +- freshness limits are nonzero; and +- critical-extension identifiers are distinct and cardinality-bounded. + +Many of these premises appear as Lean structures such as +`AuthorityStateViewValid`, `GrantAuthorityViewValid`, +`ActionAuthorityViewValid`, `ScopeAuthorityViewValid`, and +`CriticalExtensionsCanonical`. The refinement theorem does not apply to an +arbitrary malformed Lean value that safe Rust constructors could never create. + +## Environmental foundation + +The formal core receives explicit evidence facts and gate Booleans. It assumes +their interpretation is truthful. For example: + +- `notRevoked=true` must come from a trustworthy revocation mechanism; +- `reconciliationFresh=true` must correspond to a domain-defined freshness + policy; +- `definiteEffect=true` must be justified by provider-specific evidence; +- a configuration digest must be computed over the intended canonical bytes; + and +- an assurance identifier must denote the policy the verifier believes it + denotes. + +The model proves what follows if these inputs are supplied. It does not prove +the external world supplied them honestly. + +## Stateful implementation foundation + +The lifecycle relation specifies legal transitions, but a concrete store must +provide atomic compare-and-swap, durable ordering, isolation, and restart +behavior. The capacity theorems apply to successful pure ledger steps. They do +not by themselves prove that two concurrent transactions cannot both observe +the same capacity or that a provider call and durable write are atomic. + +Likewise, replay classification is pure. Exactly-once behavior requires the +runtime to claim and record one logical effect at an enforcement boundary. + +\begin{boundarybox} +\textbf{No whole-system shortcut.} A proved transition relation does not make a +database atomic. A proved coverage predicate does not make a signature valid. +A proved refinement of pure Rust does not prove a network provider. The formal +artifact is designed to make these composition boundaries explicit. +\end{boundarybox} + +## Claim matrix + +| Claim | Status in this artifact | +|---|---| +| Truth algebra and thresholds | Lean-proved; finite contract generated into Rust and Lean | +| Rich authority order and denotational safety | Lean-proved | +| Delegation root, depth, extension, and uniqueness laws | Lean-proved | +| Pure Rust authority evaluators | Aeneas-translated and refined under representation premises, with explicit current gap premises | +| Product commitment and arithmetic primitives | Lean-proved; selected Rust functions refined | +| Pure lifecycle kernel | Lean-proved and translated-Rust refined | +| Canonical decoding and signatures | Outside these theorems; separate validation boundary | +| Concrete stores and concurrency | Outside these theorems; conformance obligation | +| External evidence and provider outcomes | Explicit environmental assumption plus reconciliation semantics | + +# Cross-disciplinary interpretation + +The value of the formal development is easier to see when its mathematical, +computer-science, and security readings are placed side by side. + +## Order theory: authorization as a monotone semantics + +The map + +$$ +\denote{-}:(\mathcal{S},\atten)\to +(\powerset(\mathcal{F}),\subseteq) +\tag{57} +$$ + +is monotone. Here $\mathcal{S}$ is the set of authority scopes and +$\mathcal{F}$ the set of complete authorization facts. Structural attenuation +is valuable because it is an efficiently decidable sufficient condition for +semantic inclusion. + +The product is not a homogeneous lattice. Some coordinates are ordinary +orders, some are preorders, some use exact equality, and critical extensions +have an initially unpinned top followed by exact lock-in. The system therefore +uses a product of relations justified component by component rather than +forcing every concern into one numeric lattice. + +## Denotational semantics: meaning before implementation + +`Admits` gives scopes an extensional meaning independent of how Rust stores +them. This makes representation refinement possible. A sorted vector, a +finite set, and a canonical wire array may have different structures but can +be related by the behaviors they authorize. + +The distinction between `Exact(d)` and `Allowed({d})` illustrates the point: +they are different syntax with equal denotation. Canonicalization chooses one +representative when structural equality matters. + +## Type theory: invalid states as missing constructors + +Several invariants are stored in types: + +- inclusive windows contain a proof that start does not exceed finish; +- freshness limits contain a positivity proof; +- selected profiles contain membership evidence; +- unit quantities contain a `u64` bound; +- output commitments contain count and byte bounds; and +- critical-extension sequences contain distinctness and length proofs. + +This moves obligations from repeated runtime conditions into construction. +The production bridge then explains which Rust constructors establish the +corresponding premises. + +## Program logic: executable refinements + +The weakest-precondition lemmas have the shape familiar from Hoare logic: + +$$ +\{P\}\ f\ \{Q\}. +\tag{58} +$$ + +Loop invariants connect imperative traversal to extensional membership and +subset. The large authority refinement proofs then compose these local +contracts through the translated evaluator. This is how the artifact advances +from "the model has good laws" to "the selected shipping predicate returns the +model's decision." + +## Distributed systems: uncertainty is state + +The truth value `Indeterminate` and lifecycle state `OutcomeUnknown` address +different uncertainties. + +- `Indeterminate` means authorization evidence is insufficient but could + resolve. +- `OutcomeUnknown` means an execution attempt may or may not have produced an + effect. + +Neither is permission. The first cannot become an authorized command without +new trustworthy evidence. The second cannot become released capacity without +fresh reconciliation. Preserving these distinctions prevents fail-open logic +at both decision and effect boundaries. + +## Security engineering: negative-space theorems + +Many of the strongest results state that a dangerous shortcut is impossible: + +- an unrooted authority authorizes nothing; +- a foreign issuer falsifies the root dimension; +- a changed critical extension falsifies the extension dimension; +- a narrowed scope cannot admit a parent-rejected fact; +- a start attempt cannot succeed without credential authorization; +- a commit cannot succeed without provider entry and effect proof; +- an unknown outcome cannot be released; and +- an absent replay record cannot claim an existing effect. + +These are negative-space theorems: they rule out classes of bad executions +rather than merely exhibiting a good example. + +# Limitations and open proof obligations + +The artifact is unusually explicit about its incomplete edges. Several are +important research and engineering directions. + +## Regenerate the stale authority translation + +The optional-budget and root-field bridge premises should disappear after the +checked-in Aeneas output is regenerated from current shipping Rust. The +existing mismatch theorem makes this a fail-loud change: successful +regeneration should invalidate the stale theorem rather than leave dead +documentation behind. + +## Close constructor-to-view refinement + +Representation-validity premises are reviewed and tested, but the strongest +chain would prove that every value produced by public safe Rust constructors +satisfies the corresponding Lean validity structure and that every lossless +view preserves the fields consumed by the evaluator. + +## Strengthen plan semantics + +The composition algebra has strong truth and threshold results, but the +current `Plan` theorems about leaf visits and cost are definitional. A richer +development could define an instrumented recursive evaluator and prove +occurrence-sensitive visitation, cost bounds, duplicate policy, arbitrary +permutation invariance, and diagnostic stability for complete plans. + +## Connect state-machine relations to stores + +The lifecycle kernel proves legal pure transitions. Concrete exactly-once +claims additionally need a refinement from transactional store operations and +crash histories to the pure state relation, including concurrent final +capacity, restart, and reconciliation. + +## Separate information-flow properties + +Deterministic first-failure diagnostics do not establish noninterference, +constant-time behavior, or resistance to policy probing. Those require a +separate observation model and attacker relation. + +## Expand domain-specific evaluator proofs vertically + +`ClosedEvaluator` defines the reusable proof obligation for policy tightening, +but each domain must instantiate it with its own exact action, evidence, +reservation, obligation, and output relations. Shared product mechanisms +should follow proven semantic identity across completed domains rather than +precede it. + +# Related work + +Auths-Proof sits between authorization logic, proof-carrying systems, and +systems verification. + +Authorization logics distinguish authenticated statements from the authority +to act [@abadi1993calculus]. Trust-management systems make credentials and +policy composition explicit [@blaze1996trust]. Macaroons demonstrate +decentralized attenuation through contextual caveats +[@birgisson2014macaroons]. Auths-Proof shares the monotone-restriction goal but +formalizes a closed heterogeneous scope, exact terminal action coverage, and a +separate effect lifecycle. + +Proof-carrying code asks a producer to supply machine-checkable evidence that a +consumer validates against a safety policy [@necula1997pcc]. Proof-carrying +authentication applies related ideas to authorization [@appel1999pca]. +Auths-Proof is closer in spirit to proof-carrying authentication than to code +safety, but its shipped proof object is not a Lean proof term for every action. +Lean establishes the kernel laws; runtime objects carry signed authorization +evidence checked by that kernel. + +Large verification projects such as CompCert and seL4 demonstrate the value +of explicit refinement chains and trusted computing bases +[@leroy2009compcert; @klein2009sel4]. Auths-Proof is narrower. It verifies +security-critical pure semantics and selected production functions, not an +operating system or compiler. Aeneas makes the production linkage practical +for ownership-aware safe Rust [@ho2022aeneas], while work such as RustBelt and +Verus attacks complementary language-soundness and deductive-verification +problems [@jung2018rustbelt; @lattuada2024verus]. + +# Conclusion + +The formal work in Auths-Proof is best understood as a sequence of semantic +compressions. + +1. A complex authorization scope is compressed into a decidable product + relation without reducing every dimension to a scalar. +2. That structural relation is justified by a denotational theorem: child + admission is a subset of parent admission. +3. Delegation adds trust-root linkage, exact lineage, deterministic state + construction, critical-extension lock-in, and a well-founded depth measure. +4. Three-valued composition preserves the distinction between denial, + unresolved evidence, and authorization. +5. Product and lifecycle models carry the result toward irreversible effects + through commitments, checked arithmetic, reservations, credentials, + attempts, provider entry, unknown outcomes, and reconciliation. +6. Mechanical Rust translation and refinement connect the readable model to + selected shipping predicates under explicit representation assumptions. +7. The assurance manifest binds each public English claim to an exact compiled + proposition, axiom set, toolchain, source closure, and evidence set. + +The central theorem remains Equation (1): authority can move downward without +creating new admitted facts. Its practical power comes from the surrounding +work. Root preservation prevents re-anchoring. Strict depth prevents infinite +delegation. Pinned extensions prevent semantic stripping. Exact diagnostics +make decisions stable. Lifecycle invariants prevent authorization from being +mistaken for effect completion. Refinement prevents the mathematical model +from floating free of production Rust. + +The result is neither a decorative proof nor a claim that the entire system is +verified. It is a disciplined formal boundary: small enough to audit, rich +enough to express the dangerous cases, mechanically connected to executable +semantics, and honest about what remains outside. + +\newpage + +# Appendix A. Complete scope relation {.unnumbered} + +For reference, the complete scope order is + +$$ +\begin{aligned} +S_c\atten S_p \iff {}& +\Phi_c\atten\Phi_p\\ +&\land P_c\subseteq P_p\\ +&\land I_c\subseteq I_p\\ +&\land A_c\subseteq A_p\\ +&\land C_c\atten C_p\\ +&\land B_c\atten B_p\\ +&\land T_c\atten T_p\\ +&\land Q_c=Q_p\\ +&\land X_c\atten X_p. +\end{aligned} +\tag{A.1} +$$ + +| Dimension | Child below parent when | Coverage consequence | +|---|---|---| +| Profile | same root set; first selection or same selection | child-covered profile is parent-covered | +| Permissions | child set is a subset | child member is parent member | +| Validity | child interval is contained | child-contained action window is parent-contained | +| Audiences | child set is a subset | child member is parent member | +| Action body | allowed digest denotation is included | child-allowed digest is parent-allowed | +| Budget | parent unbounded, or same algebra with lower child ceiling | request covered by child is covered by parent | +| Status | parent expiry-only, or same method with no looser child age | evidence satisfying child satisfies parent | +| Assurance | exact identity equality | evidence assurance transfers by equality | +| Extensions | parent unpinned, or exact pinned sequence equality | chain cannot strip or alter pinned constraints | + +# Appendix B. Delegation proof skeleton {.unnumbered} + +Let $q_0$ be a parent, $h$ a grant, $i$ its identifier, and $q_1$ a proposed +child. + +\begin{enumerate} +\item Prove $\mathsf{rooted}(q_0)$ and $h.issuer=q_0.subject$. +\item Prove $h.parent=q_0.lastGrant$. +\item Prove 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

XxIk8p=fS{uH0+pKeDJl&^SimskyK^`vo-RO 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�}8*c)jC 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

E$NXY zK;+IRd6&py0F?+(MQHPf6=F-7LPcflLM1CeO4QYeLJ4yYHPrEGGtJqSkH8t(CasHC zRPf@Tf5B~kBbLjcdB5A_ZpOFKQH%mPT`Gqg?i zFK3VUVx$3AJtQh`q(jIb6cG7J-gf{BhFwEKka%QWsl4lr4AP^B@g^0hD(jc@)}xRA zq`2051rY0P$c1|aCugb!nYT4$^{I{~8xxFK)VFs)k$Y2p>q)#W* z>-r9ZdUu}p^kWrqD;%1n%o&YmrM_uWx2~zh_PkS9 zC;yg$i^QBrc#8m+$b?~2G*!r`2#VE0;F)Vth^Px|b`KJm?&36%YD*v6F>;`D{WEC0A`N_U?6^)jbnhW2)kLq?=i9pc`Sh>XDfGyZ z)H(FS^s%OBaQ1``(NS}T8M1zvJor%*DMGcdggt3yyp}~C(3(callMmJ_mEEg-gDvS zjaP?i{5eb3VlX{r0N2&E_}{Grs^Fjyf9fsjNrjH_F9p8pW)Y%hKzehKissD%={f3! zrJaQxmm}~A@_3>q;%&3edw(!%P2+?f)=h=?>69d->ycu`j|87UbBWxW8Z-4|hO~cy zBYqWi<2PnFssEtiVwm3iLrJZe~ z&@-}1k_ys=NZ?#c!GpL~Yt^_tmc?hyRq0hl?j@PPsshRmPpJr;@yp1jkp*PkV}Jtw zZg#GQtdoF*)&?56a=-$Eoz0Kb+YVRG^o3HBZ%60O zR1|88>-odDLHqk>1z1QqEO^fFVKP?$XP#{=VIX|JKfpyubLAGqke19!wZtyXWLVGJ z&?PS~j^@_lCNn3LT_GX)cj{c>QatHM)%h}FETmxVb8=ba1I8==PO)`7Z@Uw5EQ`io zZubUXj?mU+GCo;OwV5AiU{_;^0=wo5C>W|#jCD*m^r~UtEyg$eIFti%u7*rXH_Qwo z8XO5)@iXHLRcqefhc)5=Z2(MWP!NrX;pk!>FPrQN3yEDMZf(oMz1=^{4-ZRlGu{_2 zLv=meJWG}~`dI!|=RTTQUIGX2^%V>bYd*YfSRD$n&9MRSwxZ;+^)lqMO_R;$xm`q{A_KO2I>ly-+x%XNx6Om(RVROP7grYhr9C|VhJRgp+T`=cJrg%Qd% zkANnQKiG8x&s`oJZ+UIsoPz18J*6WX)}u-5DaYC=ZwLn&3)(r4am-)eOzv>Fb*ALH zBO$!AH45m;m0`SlRHSDU>w|R`!tI|8B$p=8pE0dDxoWdBv7_65KFVn7yVcZi0y0vg zrT?mfuBp;cWhHDbo^IFG($G-TQem)3boztxIg_*9YS6preh~ax>*e0?LzMme)Huu2 zU1e2V@cSgYr5X`SNzc3QV;e35g_9a4A9R4zt!3)wJ{S2A`4j$PO6rDyXWDEJW#Wb6wo^W(maL_mc(Y3|JQESQcrQf= z1$CWTykG=IgBkT=B_O2@p)C;AT2)yRYu<@CT7#G}PnD3^%(+C;dTW=cNZZ`@l1Z!i zuWRz{uQxOE#Q+bA9LF?P39*RK`LYmj1(MKQBxZ7zjx;6kGZRB}fFcC6j2#3>b(8=1 zI|;YDi_hq^yD711D0uBoc=2&n5*c`KnCyYi5_&z(ktbd&^H2dD5jqm?jB_+14YHL! zpOyaE5Gv^Z?8=%3MU2w-4<#~|fjCX#I8Czq-_QSZ1BaU-R->p}(qE4}#!pF9;~7Tu z11CFJAn;lh_?zTjNSm(&f>>-dP%y+5Llou!H{7`riaX^)95NHJ>7HBhTJ$vT%e2DP zdy2RFy;_m1xkl!3R++0(z43+EIqYMSKETVVL#ypuCV)QmfX4M;baUgXoBQ6y{Pq;C zSlT{as7+F!9g^T?t9E8-q-#eF+*Pt`TCu#ur>&-!kV7<)L&x+{q`QUWAsf5Nrwx1X zD8ka4$)FzB<705JrX5>PyGG9uB*N2Tc~urNKB}n^V|qI+alW3oG6&+sQ+>Jq_=Hc( zImlz=t8dyBh@4%`har16*%rzwRQwVG35`*%w3*-)BE}}?r9`KlPwk6$f^#++4)I>BQCnz{h5^1*HXvug1uRh1tvGe z1@Td=@+1k7-L`Jp@7GaukWwk{_vG8cqZ2c@5F^uoT7QU~>%uP>Iv=Y0C~tEpZxc^6eF+Ui*blFQb?agXtXouZGj?9-1=r^LCL#pXzS#Em)LthiTQZFG z*-HaBD^BcVj4R5)$)7wW0A)58ai--|qS{phMIHEqzBEMYsG;u)&A4W;K=~^KPU5m{ zv}Z{uNRo%PrsHUIEpBfTq^%3shi5q%0Df2kDBG;`Y;L-S&jLjx$J81}`m~tQ^)sCG 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 exit 1; } + +NAME="${SRC%.md}" +LOCAL_OUT="${NAME}.pdf" +FINAL_DIR="../../../output/pdf" +FINAL_OUT="${FINAL_DIR}/${NAME}.pdf" + +command -v pandoc >/dev/null || { echo "pandoc is required" >&2; exit 1; } +command -v xelatex >/dev/null || { echo "xelatex is required" >&2; exit 1; } + +pandoc "$SRC" \ + --from markdown+raw_tex \ + --output "$LOCAL_OUT" \ + --pdf-engine=xelatex \ + --include-in-header=preamble.tex \ + --citeproc \ + --bibliography=references.bib \ + -V geometry:margin=0.82in \ + -V fontsize=10pt \ + -V colorlinks=true \ + --highlight-style=tango + +mkdir -p "$FINAL_DIR" +cp "$LOCAL_OUT" "$FINAL_OUT" +echo "wrote $FINAL_OUT" diff --git a/docs/papers/proof/preamble.tex b/docs/papers/proof/preamble.tex new file mode 100644 index 00000000..411aef82 --- /dev/null +++ b/docs/papers/proof/preamble.tex @@ -0,0 +1,355 @@ +% Auths-Proof formal paper design system. +% XeLaTeX + STIX Two for mathematical typography; TikZ for vector figures. + +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{amsthm} +\usepackage{mathtools} +\usepackage{booktabs} +\usepackage{array} +\usepackage{longtable} +\usepackage{tabularx} +\usepackage{fancyvrb} +\usepackage{float} +\usepackage{microtype} +\usepackage{iftex} +\usepackage{xcolor} +\usepackage{titlesec} +\usepackage{caption} +\usepackage{enumitem} +\usepackage{fancyhdr} +\usepackage{etoolbox} +\usepackage{multicol} +\usepackage{tikz} +\usepackage{fontawesome5} +\usepackage[most]{tcolorbox} + +\ifXeTeX\usepackage{fontspec}\usepackage{unicode-math}\fi +\ifLuaTeX\usepackage{fontspec}\usepackage{unicode-math}\fi + +\makeatletter +\@ifpackageloaded{fontspec}{% + \IfFontExistsTF{STIXTwoText-Regular.otf}{% + \setmainfont{STIXTwoText}[ + Extension=.otf, + UprightFont=*-Regular, + BoldFont=*-Bold, + ItalicFont=*-Italic, + BoldItalicFont=*-BoldItalic]}{}% + \@ifpackageloaded{unicode-math}{% + \IfFontExistsTF{STIXTwoMath-Regular.otf}{% + \setmathfont{STIXTwoMath-Regular.otf}}{}}{}% + \IfFontExistsTF{texgyreheros-regular.otf}{% + \setsansfont{texgyreheros}[ + Extension=.otf, + UprightFont=*-regular, + BoldFont=*-bold, + ItalicFont=*-italic, + BoldItalicFont=*-bolditalic, + Scale=MatchLowercase]}{}% + \IfFontExistsTF{Menlo}{\setmonofont{Menlo}[Scale=0.76]}{% + \IfFontExistsTF{Inconsolatazi4-Regular.otf}{% + \setmonofont{Inconsolatazi4}[ + Extension=.otf, + UprightFont=*-Regular, + BoldFont=*-Bold, + Scale=MatchLowercase]}{}}% +}{} +\makeatother + +\linespread{1.035} +\frenchspacing +\setlength{\parskip}{2pt} +\setlength{\emergencystretch}{2.5em} +\setcounter{secnumdepth}{3} +\setcounter{tocdepth}{2} + +% Restrained scientific palette shared with the systems paper. +\definecolor{ink}{HTML}{202124} +\definecolor{muted}{HTML}{5F6368} +\definecolor{line}{HTML}{DADCE0} +\definecolor{blue}{HTML}{1A73E8} +\definecolor{blue2}{HTML}{174EA6} +\definecolor{bluewash}{HTML}{E8F0FE} +\definecolor{green}{HTML}{188038} +\definecolor{greenwash}{HTML}{E6F4EA} +\definecolor{amber}{HTML}{E37400} +\definecolor{amberwash}{HTML}{FEF7E0} +\definecolor{purple}{HTML}{9334E6} +\definecolor{purplewash}{HTML}{F3E8FD} +\definecolor{red}{HTML}{D93025} +\definecolor{redwash}{HTML}{FCE8E6} +\definecolor{slatewash}{HTML}{F8F9FA} + +\titleformat{\section} + {\Large\bfseries\sffamily\color{ink}} + {\textcolor{blue}{\thesection}}{0.78em}{} +\titleformat{\subsection} + {\normalsize\bfseries\sffamily\color{ink}} + {\textcolor{blue}{\thesubsection}}{0.72em}{} +\titleformat{\subsubsection} + {\normalsize\bfseries\sffamily\color{ink}} + {\textcolor{blue}{\thesubsubsection}}{0.68em}{} +\titlespacing*{\section}{0pt}{*2.8}{*1.0} +\titlespacing*{\subsection}{0pt}{*1.8}{*0.62} +\titlespacing*{\subsubsection}{0pt}{*1.4}{*0.42} + +\captionsetup{ + font=small, + labelfont={bf,sf,color=ink}, + textfont={color=muted}, + skip=6pt +} +\setlist{itemsep=1.5pt,topsep=4pt,parsep=0pt,leftmargin=1.4em} +\fvset{rulecolor=\color{line}} + +\pagestyle{fancy} +\setlength{\headheight}{19pt} +\fancyhf{} +\fancyhead[L]{\footnotesize\sffamily\color{muted} AUTHS-PROOF \enspace / \enspace FORMAL SEMANTICS} +\fancyhead[R]{\footnotesize\sffamily\color{muted}\thepage} +\renewcommand{\headrulewidth}{0.25pt} +\renewcommand{\headrule}{\hbox to\headwidth{\color{line}\leaders\hrule height \headrulewidth\hfill}} +\fancypagestyle{plain}{ + \fancyhf{} + \fancyfoot[C]{\footnotesize\sffamily\color{muted}\thepage} + \renewcommand{\headrulewidth}{0pt} +} +\AtBeginDocument{\hypersetup{ + linkcolor=blue2, + citecolor=blue2, + urlcolor=blue2 +}} + +\usetikzlibrary{ + arrows.meta, + positioning, + shapes.geometric, + fit, + backgrounds, + calc, + matrix, + decorations.pathreplacing, + decorations.pathmorphing, + shadows.blur +} + +\tikzset{ + every picture/.style={font=\sffamily}, + axisbox/.style={ + draw=#1, + fill=#1!5, + line width=0.9pt, + rounded corners=4pt, + align=center, + inner xsep=8pt, + inner ysep=6pt, + text=ink, + font=\sffamily\footnotesize + }, + card/.style={ + draw=line, + fill=white, + line width=0.65pt, + rounded corners=5pt, + align=center, + inner xsep=9pt, + inner ysep=7pt, + text=ink, + font=\sffamily\footnotesize, + blur shadow={shadow blur steps=5,shadow xshift=0.6pt,shadow yshift=-0.8pt,shadow opacity=12} + }, + kernel/.style={ + draw=blue, + fill=blue, + line width=1pt, + rounded corners=5pt, + align=center, + inner xsep=11pt, + inner ysep=9pt, + text=white, + font=\sffamily\small\bfseries, + blur shadow={shadow blur steps=6,shadow xshift=0.7pt,shadow yshift=-1pt,shadow opacity=18} + }, + boundary/.style={ + draw=muted, + densely dashed, + line width=0.8pt, + rounded corners=7pt, + inner sep=8pt + }, + flow/.style={ + ->, + >=Stealth, + draw=#1, + line width=1.05pt, + shorten >=2pt, + shorten <=2pt + }, + flow/.default=muted, + thinflow/.style={ + ->, + >=Stealth, + draw=#1, + line width=0.75pt, + shorten >=1.5pt, + shorten <=1.5pt + }, + thinflow/.default=muted, + pill/.style={ + draw=#1, + fill=white, + line width=0.7pt, + rounded corners=8pt, + text=#1, + font=\sffamily\scriptsize\bfseries, + inner xsep=7pt, + inner ysep=3pt + }, + note/.style={ + text=muted, + font=\sffamily\scriptsize, + align=center + }, + state/.style={ + draw=#1, + fill=#1!7, + line width=0.9pt, + rounded corners=4pt, + text=ink, + align=center, + font=\sffamily\scriptsize\bfseries, + inner xsep=6pt, + inner ysep=5pt + } +} + +\newtcolorbox{thesisbox}{ + enhanced, + colback=bluewash, + colframe=blue, + boxrule=0pt, + borderline west={3pt}{0pt}{blue}, + arc=4pt, + left=10pt,right=10pt,top=8pt,bottom=8pt, + fontupper=\sffamily\small, + before skip=9pt,after skip=9pt +} + +\newtcolorbox{boundarybox}{ + enhanced, + colback=amberwash, + colframe=amber, + boxrule=0pt, + borderline west={3pt}{0pt}{amber}, + arc=4pt, + left=10pt,right=10pt,top=8pt,bottom=8pt, + fontupper=\small, + before skip=8pt,after skip=8pt +} + +\newtcolorbox{claimbox}[1]{ + enhanced, + colback=greenwash, + colframe=green, + boxrule=0.6pt, + arc=4pt, + left=9pt,right=9pt,top=6pt,bottom=7pt, + title={\sffamily\bfseries\footnotesize\textcolor{green}{#1}}, + colbacktitle=greenwash, + coltitle=green, + titlerule=0pt, + before skip=7pt,after skip=7pt +} + +\newtcolorbox{definitionbox}[1]{ + enhanced, + colback=purplewash, + colframe=purple, + boxrule=0.6pt, + arc=4pt, + left=9pt,right=9pt,top=6pt,bottom=7pt, + title={\sffamily\bfseries\footnotesize\textcolor{purple}{#1}}, + colbacktitle=purplewash, + coltitle=purple, + titlerule=0pt, + before skip=7pt,after skip=7pt +} + +\newtcolorbox{modulebox}[1]{ + enhanced, + colback=slatewash, + colframe=line, + boxrule=0.6pt, + arc=4pt, + left=9pt,right=9pt,top=6pt,bottom=7pt, + title={\sffamily\bfseries\footnotesize\textcolor{blue}{#1}}, + colbacktitle=slatewash, + coltitle=blue, + titlerule=0pt, + before skip=7pt,after skip=7pt +} + +\theoremstyle{plain} +\newtheorem{theorem}{Theorem}[section] +\newtheorem{lemma}[theorem]{Lemma} +\newtheorem{proposition}[theorem]{Proposition} +\newtheorem{corollary}[theorem]{Corollary} +\theoremstyle{definition} +\newtheorem{definition}[theorem]{Definition} +\newtheorem{example}[theorem]{Example} +\theoremstyle{remark} +\newtheorem{remark}[theorem]{Remark} + +\newcommand{\Auths}{\textsc{Auths-Proof}} +\newcommand{\Denied}{\mathsf{D}} +\newcommand{\Unknown}{\mathsf{U}} +\newcommand{\Authorized}{\mathsf{A}} +\newcommand{\covers}{\mathrel{\trianglerighteq}} +\newcommand{\atten}{\mathrel{\preccurlyeq}} +\newcommand{\denote}[1]{[\![#1]\!]} +\newcommand{\Nat}{\mathbb{N}} +\newcommand{\Bool}{\mathbb{B}} +\newcommand{\powerset}{\mathcal{P}} +\newcommand{\code}[1]{\texttt{#1}} + +% Purpose-built title treatment for the mathematical companion paper. +\makeatletter +\renewcommand{\maketitle}{% + \begin{flushleft} + \vspace*{-1.2em} + {\sffamily\scriptsize\bfseries\color{blue} + MATHEMATICS \enspace / \enspace COMPUTER SCIENCE \enspace / \enspace FORMAL METHODS\par} + \vspace{0.65em} + {\sffamily\fontsize{25}{29}\selectfont\bfseries\color{ink}\@title\par} + \vspace{0.85em} + \begin{tikzpicture} + \fill[purple] (0,0) rectangle (2.7,0.08); + \fill[blue] (2.7,0) rectangle (5.4,0.08); + \fill[green] (5.4,0) rectangle (8.1,0.08); + \fill[amber] (8.1,0) rectangle (10.8,0.08); + \end{tikzpicture}\par + \vspace{0.75em} + {\sffamily\small\color{muted}\@author\hfill\@date\par} + \vspace{1.0em} + \end{flushleft} +} +\makeatother + +\renewenvironment{abstract}{% + \begin{tcolorbox}[ + enhanced, + colback=slatewash, + colframe=line, + boxrule=0.6pt, + arc=5pt, + left=11pt,right=11pt,top=8pt,bottom=9pt, + title={\sffamily\bfseries\footnotesize\textcolor{blue}{ABSTRACT}}, + colbacktitle=slatewash, + coltitle=blue, + titlerule=0pt, + before skip=2pt,after skip=12pt] + \small +}{% + \end{tcolorbox} +} diff --git a/docs/papers/proof/references.bib b/docs/papers/proof/references.bib new file mode 100644 index 00000000..751792ba --- /dev/null +++ b/docs/papers/proof/references.bib @@ -0,0 +1,161 @@ +@book{davey2002lattices, + author = {B. A. Davey and H. A. Priestley}, + title = {Introduction to Lattices and Order}, + edition = {Second}, + publisher = {Cambridge University Press}, + year = {2002}, + doi = {10.1017/CBO9780511809088} +} + +@article{hoare1969axiomatic, + author = {C. A. R. Hoare}, + title = {An Axiomatic Basis for Computer Programming}, + journal = {Communications of the ACM}, + volume = {12}, + number = {10}, + pages = {576--580}, + year = {1969}, + doi = {10.1145/363235.363259} +} + +@article{dijkstra1975guarded, + author = {Edsger W. Dijkstra}, + title = {Guarded Commands, Nondeterminacy and Formal Derivation of Programs}, + journal = {Communications of the ACM}, + volume = {18}, + number = {8}, + pages = {453--457}, + year = {1975}, + doi = {10.1145/360933.360975} +} + +@article{abadi1993calculus, + author = {Mart{\'i}n Abadi and Michael Burrows and Butler Lampson and Gordon Plotkin}, + title = {A Calculus for Access Control in Distributed Systems}, + journal = {ACM Transactions on Programming Languages and Systems}, + volume = {15}, + number = {4}, + pages = {706--734}, + year = {1993}, + doi = {10.1145/155183.155225} +} + +@inproceedings{blaze1996trust, + author = {Matt Blaze and Joan Feigenbaum and Jack Lacy}, + title = {Decentralized Trust Management}, + booktitle = {Proceedings of the 1996 IEEE Symposium on Security and Privacy}, + pages = {164--173}, + year = {1996}, + doi = {10.1109/SECPRI.1996.502679} +} + +@inproceedings{necula1997pcc, + author = {George C. Necula}, + title = {Proof-Carrying Code}, + booktitle = {Proceedings of the 24th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages}, + pages = {106--119}, + year = {1997}, + doi = {10.1145/263699.263712} +} + +@inproceedings{appel1999pca, + author = {Andrew W. Appel and Edward W. Felten}, + title = {Proof-Carrying Authentication}, + booktitle = {Proceedings of the 6th ACM Conference on Computer and Communications Security}, + pages = {52--62}, + year = {1999}, + doi = {10.1145/319709.319718} +} + +@inproceedings{birgisson2014macaroons, + author = {Arnar Birgisson and Joe Gibbs Politz and {\'U}lfar Erlingsson and Ankur Taly and Michael Vrable and Mark Lentczner}, + title = {Macaroons: Cookies with Contextual Caveats for Decentralized Authorization in the Cloud}, + booktitle = {Network and Distributed System Security Symposium}, + publisher = {Internet Society}, + year = {2014} +} + +@inproceedings{demoura2021lean4, + author = {Leonardo de Moura and Sebastian Ullrich}, + title = {The {Lean 4} Theorem Prover and Programming Language}, + booktitle = {Automated Deduction -- {CADE} 28}, + series = {Lecture Notes in Computer Science}, + volume = {12699}, + pages = {625--635}, + publisher = {Springer}, + year = {2021}, + doi = {10.1007/978-3-030-79876-5_37} +} + +@article{ho2022aeneas, + author = {Son Ho and Jonathan Protzenko}, + title = {Aeneas: Rust Verification by Functional Translation}, + journal = {Proceedings of the ACM on Programming Languages}, + volume = {6}, + number = {ICFP}, + articleno = {116}, + pages = {711--741}, + year = {2022}, + doi = {10.1145/3547647} +} + +@inproceedings{pnueli1998translation, + author = {Amir Pnueli and Michael Siegel and Eli Singerman}, + title = {Translation Validation}, + booktitle = {Tools and Algorithms for the Construction and Analysis of Systems}, + series = {Lecture Notes in Computer Science}, + volume = {1384}, + pages = {151--166}, + publisher = {Springer}, + year = {1998}, + doi = {10.1007/BFb0054170} +} + +@article{leroy2009compcert, + author = {Xavier Leroy}, + title = {Formal Verification of a Realistic Compiler}, + journal = {Communications of the ACM}, + volume = {52}, + number = {7}, + pages = {107--115}, + year = {2009}, + doi = {10.1145/1538788.1538814} +} + +@inproceedings{klein2009sel4, + author = {Gerwin Klein and Kevin Elphinstone and Gernot Heiser and June Andronick and David Cock and Philip Derrin and Dhammika Elkaduwe and Kai Engelhardt and Rafal Kolanski and Michael Norrish and Thomas Sewell and Harvey Tuch and Simon Winwood}, + title = {{seL4}: Formal Verification of an {OS} Kernel}, + booktitle = {Proceedings of the 22nd ACM Symposium on Operating Systems Principles}, + pages = {207--220}, + year = {2009}, + doi = {10.1145/1629575.1629596} +} + +@article{jung2018rustbelt, + author = {Ralf Jung and Jacques-Henri Jourdan and Robbert Krebbers and Derek Dreyer}, + title = {{RustBelt}: Securing the Foundations of the Rust Programming Language}, + journal = {Proceedings of the ACM on Programming Languages}, + volume = {2}, + number = {POPL}, + articleno = {66}, + pages = {66:1--66:34}, + year = {2018}, + doi = {10.1145/3158154} +} + +@inproceedings{lattuada2024verus, + author = {Andrea Lattuada and Travis Hance and Jay Bosamiya and Matthias Brun and Chanhee Cho and Hayley LeBlanc and Pranav Srinivasan and Reto Achermann and Tej Chajed and Chris Hawblitzel and Jon Howell and Jacob R. Lorch and Oded Padon and Bryan Parno}, + title = {Verus: A Practical Foundation for Systems Verification}, + booktitle = {Proceedings of the 30th ACM Symposium on Operating Systems Principles}, + pages = {438--454}, + year = {2024}, + doi = {10.1145/3694715.3695952} +} + +@article{kroening2023cbmc, + author = {Daniel Kroening and Peter Schrammel and Michael Tautschnig}, + title = {{CBMC}: The {C} Bounded Model Checker}, + journal = {arXiv preprint arXiv:2302.02384}, + year = {2023}, + doi = {10.48550/arXiv.2302.02384} +} diff --git a/docs/papers/system/README.md b/docs/papers/system/README.md new file mode 100644 index 00000000..443e24a6 --- /dev/null +++ b/docs/papers/system/README.md @@ -0,0 +1,14 @@ +# Auths-Proof paper + +Build the paper from this directory: + +```sh +./build.sh +``` + +Requirements: Pandoc and XeLaTeX. The script writes the review copy to +`../../output/pdf/auths-proof.pdf` and leaves a local copy beside the source. + +The figures are native TikZ vectors embedded in `auths-proof.md`. They are +intentionally part of the source so the paper, diagrams, and claims evolve +together. diff --git a/docs/papers/system/auths-proof.md b/docs/papers/system/auths-proof.md new file mode 100644 index 00000000..33d6c285 --- /dev/null +++ b/docs/papers/system/auths-proof.md @@ -0,0 +1,1408 @@ +--- +title: "Auths-Proof: Mechanically Refining Rich Authorization Semantics to Shipping Rust" +author: "bordumb · bordumbb@gmail.com" +date: 29 July 2026 +abstract: | + Authorization systems are often verified at the wrong boundary. A proof + assistant may establish elegant properties of a model while production code + independently reimplements the model, leaving semantic correspondence to + review and testing. We present the formal core of **Auths-Proof**, a + deterministic proof-carrying authorization kernel, and a refinement chain + that connects its rich authority semantics to the exact safe Rust functions + used in production. + + Auths-Proof models permissions and audiences as finite sets, validity as + inclusive intervals, action authority as a closed constraint algebra, + budgets as optional ceilings, status as an ordered freshness policy, profile + selection as a state transition, and delegation as a linked, strictly + depth-decreasing chain. Lean 4 proves the component laws, semantic + containment, downward-closed action coverage and evidence requirements, + unique accepted transitions, ordered diagnostics, and three-valued + composition properties. Production authoring, delegation, and terminal + coverage were reshaped into total, pure, `unsafe`-free Rust evaluators over + validated borrowed views. Charon and Aeneas translate those exact functions + into Lean; three refinement theorems prove agreement with the handwritten + rich specification. + + The evaluated artifact inventories 72 compiled Lean statements: 56 rich + authority and production-refinement claims and 16 composition claims. It + supplements the proofs with 23 rich semantic vectors, a required 22-mutation + matrix, all 2,048 Boolean attenuation projections, 2,448 threshold states + through the default 16-leaf deployment bound, and four Kani harnesses. + Statement hashes, transitive axioms, translation source closure, pinned + tools, external models, and generated artifacts are checked in a read-only + repository gate. + + This is not a proof of the complete verifier. Canonical decoding, + cryptography, evidence acquisition, registries, clocks, stores, adapters, + credentials, external effects, the Rust compiler, and the reviewed + representation-validity premises remain outside or inside an explicit + trusted computing base. The result is stronger than two matching + implementations and narrower than whole-program verification: production + authority decisions are mechanically represented and proved against a + readable rich model, while the remaining assumptions stay inspectable. +--- + +# 1. Introduction + +An authorization verifier answers a deceptively small question: + +\begin{thesisbox} +\centering +\textbf{Does authority trusted by this verifier authorize this exact action +under this exact context?} +\end{thesisbox} + +The answer is security-critical because identity is not authority. A valid +signature establishes control of a key. A WebAuthn ceremony establishes +control of a credential under ceremony conditions. A certificate establishes +a path to a trust root. None of those facts alone grants permission to deploy +software, spend a budget, invoke a tool, or mutate a repository. Authentication +logics and authorization logics have long treated these as different +judgments [@burrows1990logic; @abadi1993calculus]. Capability and +trust-management systems likewise make authority and delegation explicit +[@dennis1966capabilities; @blaze1996trust; @keynote1999]. + +Auths-Proof implements this separation as an offline kernel: + +$$ +\operatorname{verify}(P,A,C) +\rightarrow +\operatorname{Authorized}(S) +\mid \operatorname{Denied}(d) +\mid \operatorname{Indeterminate}(q). +$$ + +$P$ is a portable proof graph, $A$ is a profile-canonical action, and $C$ is +verifier-trusted context. The kernel performs no network, clock, filesystem, +database, or key-custody I/O. Only `Authorized` contains a sealed action value +eligible for execution. `Denied` records a stable permanent failure. +`Indeterminate` records a stable missing trustworthy fact. The latter two +outcomes never permit execution. + +This paper is about a narrower problem than the full protocol: how do we know +that the algebra proved in Lean is the algebra executed by shipping Rust? + +Writing the same function twice does not answer that question. Tests over a few +examples do not answer it. A shared trait name does not answer it. Even a +machine-checked proof can create false confidence when its definitions are +detached from production. Experience from verified compilers and kernels shows +that useful assurance depends on an explicit refinement chain and an explicit +trusted computing base [@leroy2009compcert; @klein2009sel4]. + +Auths-Proof therefore treats linkage as a first-class artifact at two +boundaries. A small versioned contract generates the finite Boolean algebra +surface in Rust and Lean. Separately, Charon and Aeneas translate the exact +pure Rust authority evaluators and their leaf predicates into Lean. Handwritten +theorems refine the translated functions to a readable rich specification. +Compiled-statement auditing, semantic vectors, mutation witnesses, and Kani +provide independent change detectors around that proof chain. Figure 1 +summarizes the resulting evidence graph. + +\begin{figure}[H] +\centering +\resizebox{0.97\linewidth}{!}{% +\begin{tikzpicture}[node distance=7mm and 9mm] + \node[axisbox=purple, minimum width=43mm, minimum height=18mm] (rich) { + \textbf{Handwritten rich Lean}\\[-1pt] + sets · intervals · constraints · transitions + }; + \node[axisbox=blue, minimum width=43mm, right=12mm of rich] (rust) { + \textbf{Shipping pure Rust}\\[-1pt] + author · delegate · terminal coverage + }; + \node[card, minimum width=43mm, right=12mm of rust] (translated) { + \textcolor{green}{\faCogs}\quad\textbf{Aeneas Lean}\\ + exact translated evaluators + }; + + \node[axisbox=purple, minimum width=43mm, below=10mm of rich] (contract) { + \textbf{Algebra contract}\\ + truth order · 10 fields · threshold + }; + \node[kernel, minimum width=43mm, below=10mm of rust] (proofs) { + REFINEMENT THEOREMS\\[-1pt] + \normalfont\footnotesize exact decisions + transitions + }; + \node[axisbox=green, minimum width=43mm, below=10mm of translated] (qualification) { + \textbf{Qualified translation}\\ + pinned closure · no opaque locals + }; + + \node[card, minimum width=43mm, below=10mm of contract] (generated) { + \textbf{Generated Rust + Lean}\\ + conjunction + threshold classifier + }; + \node[axisbox=green, minimum width=43mm, below=10mm of proofs] (evidence) { + \textbf{Conformance evidence}\\ + vectors · 22 mutations · 4 Kani + }; + \node[axisbox=amber, minimum width=43mm, below=10mm of qualification] (manifest) { + \textbf{Assurance manifest}\\ + 72 statements · axioms · hashes + }; + + \node[kernel, minimum width=139mm, below=11mm of evidence] (gate) { + \faLock\quad \texttt{cargo xtask formal}\\[2pt] + \normalfont\footnotesize compiled audit · source closure · generated drift · + translated refinement · vectors · mutations · Kani + }; + + \draw[flow=blue] (rust) -- (translated); + \draw[flow=purple] (rich) -- (proofs); + \draw[flow=green] (translated) -- (proofs); + \draw[flow=purple] (contract) -- (generated); + \draw[flow=blue] (generated) -- (evidence); + \draw[flow=green] (proofs) -- (evidence); + \draw[flow=green] (qualification) -- (manifest); + \draw[flow=amber] (manifest) -- (gate); + \draw[flow=green] (evidence) -- (gate); + \draw[thinflow=blue,dashed] (rust.south) -- (proofs.north); +\end{tikzpicture}} +\caption{\textbf{The production refinement chain.} The finite algebra remains +generated from one contract. The rich authority path translates shipping Rust, +then proves its decisions against a handwritten semantic model. Both paths +converge in one reproducible gate.} +\end{figure} + +## 1.1 Contributions + +This paper makes five contributions. + +**A closed authorization algebra.** Delegation combines a product order over +profile, permissions, validity, audiences, action constraint, budget, status, +and assurance with exact root linkage and strictly decreasing depth. +Composition is a three-valued algebra that preserves trustworthy uncertainty. + +**A rich, unbounded Lean model.** The Lean development represents finite +permission and audience sets, inclusive windows, action constraints, optional +budgets, status freshness, profile selection, chain linkage, and terminal +action membership directly. It proves order, coverage, evidence, +delegation-depth, accepted-transition, diagnostic, and composition properties. + +**A generated language boundary.** A declarative TOML contract generates the +Rust trait and functions and the corresponding Lean structure and functions. +Changing a dimension is a cross-language schema change rather than two +independent edits. + +**A mechanical rich-authority refinement.** Shipping code calls pure +production evaluators that Charon and Aeneas translate into Lean. Refinement +theorems cover pre-signing scope decisions, delegation linkage and its unique +accepted transition, terminal action coverage, and each caller's stable +first-failure result under validated-representation premises. + +**An executable assurance ledger.** A 72-claim manifest binds English claims +to exact compiled theorem statements, hashes, source closures, Rust symbols, +evidence, transitive axioms, toolchain locks, and residual assumptions. Rich +vectors, a required mutation matrix, finite exhaustive vectors, and Kani +harnesses supplement rather than substitute for the proofs. + +## 1.2 Scope and terminology + +The paper uses *proof* in three different senses: + +- an **authorization proof** is untrusted protocol input; +- a **Lean proof** is a kernel-checked theorem; +- a **Kani proof harness** is a bounded symbolic program check. + +These are not interchangeable. The formal model excludes parsing, canonical +CBOR correctness, cryptographic soundness, principal adapters, graph +resolution, clocks, evidence acquisition, durable stores, external effects, +and complete verifier control flow. It does include rich authority semantics +and the isolated production decisions that author, delegate, and cover +terminal actions. Those conclusions hold over constructor-validated Rust +views satisfying explicitly stated representation invariants. + +# 2. Authorization semantics + +## 2.1 Trusted context and portable evidence + +The portable proof carries signed grants, signed actions, evidence objects, +bindings, and an authorization plan. It does not carry verifier trust. Trust +anchors, accepted registries, evaluation time, expected challenge and audience, +status and assurance policy, composition floors, and resource limits enter +through $C$. + +\begin{figure}[H] +\centering +\resizebox{0.93\linewidth}{!}{% +\begin{tikzpicture}[node distance=7mm and 14mm] + \node[card, minimum width=43mm, minimum height=24mm] (portable) { + \textcolor{blue}{\faFileSignature}\quad\textbf{Portable proof}\\[2pt] + signed grants + actions\\ + evidence + bindings + plan + }; + \node[card, minimum width=43mm, minimum height=24mm, below=of portable] (action) { + \textcolor{amber}{\faFingerprint}\quad\textbf{Canonical action}\\[2pt] + profile meaning + exact bytes\\ + permission + digest + budget + }; + \node[card, minimum width=43mm, minimum height=24mm, below=of action] (context) { + \textcolor{purple}{\faLock}\quad\textbf{Trusted context}\\[2pt] + roots + time + status + policy\\ + registries + limits + challenge + }; + + \node[kernel, minimum width=52mm, minimum height=39mm, + right=20mm of action] (kernel) { + AUTHS-PROOF CORE\\[5pt] + \normalfont\footnotesize + resolve · verify control\\ + attenuate · compose\\ + seal + }; + + \node[verdict=green, minimum width=39mm, right=17mm of kernel, yshift=14mm] (yes) { + AUTHORIZED\\[-1pt]\normalfont\scriptsize sealed action + }; + \node[verdict=red, minimum width=39mm, right=17mm of kernel] (no) { + DENIED\\[-1pt]\normalfont\scriptsize stable reason + }; + \node[verdict=amber, minimum width=39mm, right=17mm of kernel, yshift=-14mm] (maybe) { + INDETERMINATE\\[-1pt]\normalfont\scriptsize stable requirement + }; + + \draw[flow=blue] (portable.east) -- (kernel.west); + \draw[flow=amber] (action.east) -- (kernel.west); + \draw[flow=purple] (context.east) -- (kernel.west); + \draw[flow=green] (kernel.east) -- (yes.west); + \draw[flow=red] (kernel.east) -- (no.west); + \draw[flow=amber] (kernel.east) -- (maybe.west); +\end{tikzpicture}} +\caption{\textbf{The verifier's narrow waist.} Identity evidence contributes +facts, but only the local authority computation can produce a sealed action.} +\end{figure} + +This partition instantiates verifier sovereignty. A proof can demonstrate a +chain from a root, but it cannot choose the local root. It can carry a status +statement, but it cannot choose the accepted status method or freshness limit. +It can request a composition plan, but the verifier can impose additional +branch, actor, and root diversity floors. + +## 2.2 Three-valued truth + +Each proof branch produces one value in + +$$ +\mathbb{T} = \{\bot,\ ?,\ \top\}, +\qquad +\bot \preceq ? \preceq \top, +$$ + +where $\top$ is authorized, $\bot$ is denied, and $?$ is indeterminate. +Indeterminate is not an error code disguised as authority. It means that a +recognized trustworthy fact is absent or unavailable and that authorization +would still be reachable if that fact were supplied. + +For conjunction and disjunction: + +$$ +x \wedge y = \min_{\preceq}(x,y), +\qquad +x \vee y = \max_{\preceq}(x,y). +$$ + +The implementation evaluates all members in canonical order so diagnostic +selection remains deterministic even when the truth algebra is permutation +invariant. + +For a threshold requiring $k$ successes, let $a$ be the number of authorized +branches and $u$ the number of indeterminate branches: + +$$ +\operatorname{threshold}(k,a,u)= +\begin{cases} +\top & a \ge k,\\ +? & a < k \land a+u \ge k,\\ +\bot & a+u < k. +\end{cases} +$$ + +\begin{figure}[H] +\centering +\begin{tikzpicture}[x=0.72cm,y=0.72cm] + \draw[->,draw=muted] (0,0) -- (10.8,0) + node[note,below=3pt] {authorized count \(a\)}; + \draw[->,draw=muted] (0,0) -- (0,7.8) + node[note,rotate=90,above=4pt] {indeterminate count \(u\)}; + + \fill[redwash] (0,0) -- (6,0) -- (0,6) -- cycle; + \fill[amberwash] (0,6) -- (6,0) -- (6,7) -- (0,7) -- cycle; + \fill[greenwash] (6,0) rectangle (10,7); + + \draw[red,line width=1.1pt] (0,6) -- (6,0); + \draw[green,line width=1.1pt] (6,0) -- (6,7); + + \node[font=\sffamily\small\bfseries,text=red] at (2.0,1.6) {DENIED}; + \node[font=\sffamily\small\bfseries,text=amber] at (2.7,5.3) {INDETERMINATE}; + \node[font=\sffamily\small\bfseries,text=green] at (8.0,3.5) {AUTHORIZED}; + + \node[note,anchor=west] at (10.25,6.7) {\(k=6\)}; + \foreach \x in {0,2,4,6,8,10} + \draw[muted] (\x,0.08) -- (\x,-0.08) node[note,below=2pt] {\x}; + \foreach \y in {0,2,4,6} + \draw[muted] (0.08,\y) -- (-0.08,\y) node[note,left=2pt] {\y}; +\end{tikzpicture} +\caption{\textbf{Threshold partition for \(k=6\).} The three regions are total +and mutually exclusive. Increasing \(k\) moves the authorization boundary +rightward and cannot create authority.} +\end{figure} + +Preserving $?$ matters operationally. Collapsing it into $\bot$ loses the +difference between "the statement is invalid" and "a required status snapshot +is unavailable." Collapsing it into $\top$ fails open. + +## 2.3 Delegation as a product order + +The rich model separates semantic scope from chain position. An authority scope +is: + +$$ +S = (p,\pi,v,a,c,b,t,h), +$$ + +where $p$ is profile-selection state, $\pi$ a finite permission set, $v$ an +inclusive validity interval, $a$ a finite audience set, $c$ an action +constraint, $b$ an optional budget ceiling, $t$ a status policy, and $h$ an +assurance identifier. A chain state is: + +$$ +E = (r,s,S,d,g), +$$ + +with local root $r$, current subject $s$, scope $S$, remaining delegation depth +$d$, and optional last-grant identifier $g$. Subject, depth, and last grant are +transition state; treating them as ordinary partially ordered authority would +make false antisymmetry claims. + +For child scope $S'$ and parent scope $S$: + +$$ +\begin{aligned} +S' \sqsubseteq S \iff {}& +p'\le p +\land \pi'\subseteq\pi +\land v'\le v +\land a'\subseteq a\\ +&\land c'\le c +\land b'\le b +\land t'\le t +\land h'=h. +\end{aligned} +$$ + +A valid grant transition is stricter than scope containment: + +$$ +\operatorname{delegates}(E,G,E') +\Rightarrow +S'\sqsubseteq S \land r'=r \land d'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%0Ai>s$c<)qT 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իM*0Ul1LIn< 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

$KOK}yA)y#p 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 literal 0 HcmV?d00001 diff --git a/docs/papers/system/build.sh b/docs/papers/system/build.sh new file mode 100755 index 00000000..f955dc9a --- /dev/null +++ b/docs/papers/system/build.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Build the Auths-Proof systems paper and place the final artifact in output/pdf. +set -euo pipefail +cd "$(dirname "$0")" + +SRC="${1:-auths-proof.md}" +[ -f "$SRC" ] || { echo "no such file: $SRC" >&2; exit 1; } + +NAME="${SRC%.md}" +LOCAL_OUT="${NAME}.pdf" +FINAL_DIR="../../output/pdf" +FINAL_OUT="${FINAL_DIR}/${NAME}.pdf" + +command -v pandoc >/dev/null || { echo "pandoc is required" >&2; exit 1; } +command -v xelatex >/dev/null || { echo "xelatex is required" >&2; exit 1; } + +pandoc "$SRC" \ + --from markdown+raw_tex \ + --output "$LOCAL_OUT" \ + --pdf-engine=xelatex \ + --include-in-header=preamble.tex \ + --citeproc \ + --bibliography=references.bib \ + -V geometry:margin=0.88in \ + -V fontsize=10pt \ + -V colorlinks=true \ + --highlight-style=tango + +mkdir -p "$FINAL_DIR" +cp "$LOCAL_OUT" "$FINAL_OUT" +echo "wrote $FINAL_OUT" diff --git a/docs/papers/system/preamble.tex b/docs/papers/system/preamble.tex new file mode 100644 index 00000000..c1b30559 --- /dev/null +++ b/docs/papers/system/preamble.tex @@ -0,0 +1,305 @@ +% auths-proof paper design system. +% XeLaTeX + STIX Two for scientific typography; TikZ for native vector figures. + +\usepackage{amsmath} +\usepackage{booktabs} +\usepackage{array} +\usepackage{fancyvrb} +\usepackage{float} +\usepackage{microtype} +\usepackage{iftex} +\usepackage{xcolor} +\usepackage{titlesec} +\usepackage{caption} +\usepackage{enumitem} +\usepackage{fancyhdr} +\usepackage{etoolbox} +\usepackage{multicol} +\usepackage{tikz} +\usepackage{fontawesome5} +\usepackage[most]{tcolorbox} + +\ifXeTeX\usepackage{fontspec}\usepackage{unicode-math}\fi +\ifLuaTeX\usepackage{fontspec}\usepackage{unicode-math}\fi + +\makeatletter +\@ifpackageloaded{fontspec}{% + \IfFontExistsTF{STIXTwoText-Regular.otf}{% + \setmainfont{STIXTwoText}[ + Extension=.otf, + UprightFont=*-Regular, + BoldFont=*-Bold, + ItalicFont=*-Italic, + BoldItalicFont=*-BoldItalic]}{}% + \@ifpackageloaded{unicode-math}{% + \IfFontExistsTF{STIXTwoMath-Regular.otf}{% + \setmathfont{STIXTwoMath-Regular.otf}}{}}{}% + \IfFontExistsTF{texgyreheros-regular.otf}{% + \setsansfont{texgyreheros}[ + Extension=.otf, + UprightFont=*-regular, + BoldFont=*-bold, + ItalicFont=*-italic, + BoldItalicFont=*-bolditalic, + Scale=MatchLowercase]}{}% + \IfFontExistsTF{Menlo}{\setmonofont{Menlo}[Scale=0.78]}{% + \IfFontExistsTF{Inconsolatazi4-Regular.otf}{% + \setmonofont{Inconsolatazi4}[ + Extension=.otf, + UprightFont=*-Regular, + BoldFont=*-Bold, + Scale=MatchLowercase]}{}}% +}{} +\makeatother + +\linespread{1.035} +\frenchspacing +\setlength{\parskip}{2pt} +\setlength{\emergencystretch}{2em} + +% Corporate editorial palette: dark neutral ink plus four semantic axes. +\definecolor{ink}{HTML}{202124} +\definecolor{muted}{HTML}{5F6368} +\definecolor{line}{HTML}{DADCE0} +\definecolor{blue}{HTML}{1A73E8} +\definecolor{blue2}{HTML}{174EA6} +\definecolor{bluewash}{HTML}{E8F0FE} +\definecolor{green}{HTML}{188038} +\definecolor{greenwash}{HTML}{E6F4EA} +\definecolor{amber}{HTML}{E37400} +\definecolor{amberwash}{HTML}{FEF7E0} +\definecolor{purple}{HTML}{9334E6} +\definecolor{purplewash}{HTML}{F3E8FD} +\definecolor{red}{HTML}{D93025} +\definecolor{redwash}{HTML}{FCE8E6} +\definecolor{slatewash}{HTML}{F8F9FA} + +\titleformat{\section} + {\Large\bfseries\sffamily\color{ink}} + {\textcolor{blue}{\thesection}}{0.78em}{} +\titleformat{\subsection} + {\normalsize\bfseries\sffamily\color{ink}} + {\textcolor{blue}{\thesubsection}}{0.72em}{} +\titleformat{\subsubsection} + {\normalsize\bfseries\sffamily\color{ink}} + {}{0pt}{} +\titlespacing*{\section}{0pt}{*2.8}{*1.05} +\titlespacing*{\subsection}{0pt}{*1.8}{*0.65} +\titlespacing*{\subsubsection}{0pt}{*1.4}{*0.45} + +\captionsetup{ + font=small, + labelfont={bf,sf,color=ink}, + textfont={color=muted}, + skip=6pt +} +\setlist{itemsep=1.5pt,topsep=4pt,parsep=0pt,leftmargin=1.4em} +\fvset{rulecolor=\color{line}} + +\pagestyle{fancy} +\setlength{\headheight}{19pt} +\fancyhf{} +\fancyhead[L]{\footnotesize\sffamily\color{muted} AUTHS-PROOF \enspace / \enspace FORMAL REFINEMENT} +\fancyhead[R]{\footnotesize\sffamily\color{muted}\thepage} +\renewcommand{\headrulewidth}{0.25pt} +\renewcommand{\headrule}{\hbox to\headwidth{\color{line}\leaders\hrule height \headrulewidth\hfill}} +\fancypagestyle{plain}{ + \fancyhf{} + \fancyfoot[C]{\footnotesize\sffamily\color{muted}\thepage} + \renewcommand{\headrulewidth}{0pt} +} +\AtBeginDocument{\hypersetup{ + linkcolor=blue2, + citecolor=blue2, + urlcolor=blue2 +}} + +\usetikzlibrary{ + arrows.meta, + positioning, + shapes.geometric, + fit, + backgrounds, + calc, + matrix, + decorations.pathreplacing, + shadows.blur +} + +\tikzset{ + every picture/.style={font=\sffamily}, + axisbox/.style={ + draw=#1, + fill=#1!5, + line width=0.9pt, + rounded corners=4pt, + align=center, + inner xsep=8pt, + inner ysep=6pt, + text=ink, + font=\sffamily\footnotesize + }, + card/.style={ + draw=line, + fill=white, + line width=0.65pt, + rounded corners=5pt, + align=center, + inner xsep=9pt, + inner ysep=7pt, + text=ink, + font=\sffamily\footnotesize, + blur shadow={shadow blur steps=5,shadow xshift=0.6pt,shadow yshift=-0.8pt,shadow opacity=12} + }, + kernel/.style={ + draw=blue, + fill=blue, + line width=1pt, + rounded corners=5pt, + align=center, + inner xsep=11pt, + inner ysep=9pt, + text=white, + font=\sffamily\small\bfseries, + blur shadow={shadow blur steps=6,shadow xshift=0.7pt,shadow yshift=-1pt,shadow opacity=18} + }, + boundary/.style={ + draw=muted, + densely dashed, + line width=0.8pt, + rounded corners=7pt, + inner sep=8pt + }, + flow/.style={ + ->, + >=Stealth, + draw=#1, + line width=1.05pt, + shorten >=2pt, + shorten <=2pt + }, + flow/.default=muted, + thinflow/.style={ + ->, + >=Stealth, + draw=#1, + line width=0.75pt, + shorten >=1.5pt, + shorten <=1.5pt + }, + thinflow/.default=muted, + pill/.style={ + draw=#1, + fill=white, + line width=0.7pt, + rounded corners=8pt, + text=#1, + font=\sffamily\scriptsize\bfseries, + inner xsep=7pt, + inner ysep=3pt + }, + note/.style={ + text=muted, + font=\sffamily\scriptsize, + align=center + }, + verdict/.style={ + draw=#1, + fill=#1!7, + line width=1pt, + rounded corners=4pt, + text=#1, + align=center, + font=\sffamily\footnotesize\bfseries, + inner xsep=8pt, + inner ysep=6pt + } +} + +\newtcolorbox{thesisbox}{ + enhanced, + colback=bluewash, + colframe=blue, + boxrule=0pt, + borderline west={3pt}{0pt}{blue}, + arc=4pt, + left=10pt,right=10pt,top=8pt,bottom=8pt, + fontupper=\sffamily\small, + before skip=9pt,after skip=9pt +} + +\newtcolorbox{limitbox}{ + enhanced, + colback=amberwash, + colframe=amber, + boxrule=0pt, + borderline west={3pt}{0pt}{amber}, + arc=4pt, + left=10pt,right=10pt,top=8pt,bottom=8pt, + fontupper=\small, + before skip=8pt,after skip=8pt +} + +\newtcolorbox{invariantbox}[1]{ + enhanced, + colback=slatewash, + colframe=line, + boxrule=0.6pt, + arc=4pt, + left=9pt,right=9pt,top=6pt,bottom=7pt, + title={\sffamily\bfseries\footnotesize\textcolor{blue}{#1}}, + colbacktitle=slatewash, + coltitle=blue, + titlerule=0pt, + before skip=7pt,after skip=7pt +} + +\newcommand{\axislabel}[2]{\textcolor{#1}{\sffamily\bfseries #2}} +\newcommand{\authsword}{\textsc{Auths-Proof}} + +% Purpose-built title treatment: editorial, restrained, and visually tied to +% the four adapter axes used throughout the figures. +\makeatletter +\renewcommand{\maketitle}{% + \begin{flushleft} + \vspace*{-1.2em} + {\sffamily\scriptsize\bfseries\color{blue} + FORMAL METHODS \enspace / \enspace AUTHORIZATION KERNEL\par} + \vspace{0.65em} + {\sffamily\fontsize{25}{29}\selectfont\bfseries\color{ink}\@title\par} + \vspace{0.85em} + \begin{tikzpicture} + \fill[green] (0,0) rectangle (2.4,0.08); + \fill[purple] (2.4,0) rectangle (4.8,0.08); + \fill[amber] (4.8,0) rectangle (7.2,0.08); + \fill[blue] (7.2,0) rectangle (10.5,0.08); + \end{tikzpicture}\par + \vspace{0.75em} + {\sffamily\small\color{muted}\@author\hfill\@date\par} + \vspace{1.0em} + \end{flushleft} +} +\makeatother + +\renewenvironment{abstract}{% + \begin{tcolorbox}[ + enhanced, + colback=slatewash, + colframe=line, + boxrule=0.6pt, + arc=5pt, + left=11pt,right=11pt,top=8pt,bottom=9pt, + title={\sffamily\bfseries\footnotesize\textcolor{blue}{ABSTRACT}}, + colbacktitle=slatewash, + coltitle=blue, + titlerule=0pt, + before skip=2pt,after skip=12pt] + \small +}{% + \end{tcolorbox} +} + +% A compact two-column reference block avoids a nearly empty trailing page and +% gives the bibliography the visual density expected of a systems paper. +\BeforeBeginEnvironment{CSLReferences}{\footnotesize\begin{multicols}{2}\raggedcolumns\raggedright} +\AfterEndEnvironment{CSLReferences}{\end{multicols}} diff --git a/docs/papers/system/references.bib b/docs/papers/system/references.bib new file mode 100644 index 00000000..867469e5 --- /dev/null +++ b/docs/papers/system/references.bib @@ -0,0 +1,470 @@ +@inproceedings{necula1997pcc, + author = {George C. Necula}, + title = {Proof-Carrying Code}, + booktitle = {Proceedings of the 24th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages}, + year = {1997}, + pages = {106--119}, + doi = {10.1145/263699.263712}, + url = {https://doi.org/10.1145/263699.263712} +} + +@inproceedings{appel1999pca, + author = {Andrew W. Appel and Edward W. Felten}, + title = {Proof-Carrying Authentication}, + booktitle = {Proceedings of the 6th ACM Conference on Computer and Communications Security}, + year = {1999}, + pages = {52--62}, + doi = {10.1145/319709.319718}, + url = {https://doi.org/10.1145/319709.319718} +} + +@inproceedings{bauer2002pcaweb, + author = {Lujo Bauer and Michael A. Schneider and Edward W. Felten}, + title = {A General and Flexible Access-Control System for the Web}, + booktitle = {Proceedings of the 11th USENIX Security Symposium}, + year = {2002}, + pages = {93--108}, + publisher = {USENIX Association}, + url = {https://www.usenix.org/legacy/events/sec02/full_papers/bauer/bauer_html/} +} + +@article{abadi1993calculus, + author = {Mart{\'i}n Abadi and Michael Burrows and Butler Lampson and Gordon Plotkin}, + title = {A Calculus for Access Control in Distributed Systems}, + journal = {ACM Transactions on Programming Languages and Systems}, + year = {1993}, + volume = {15}, + number = {4}, + pages = {706--734}, + doi = {10.1145/155183.155225}, + url = {https://doi.org/10.1145/155183.155225} +} + +@inproceedings{blaze1996trust, + author = {Matt Blaze and Joan Feigenbaum and Jack Lacy}, + title = {Decentralized Trust Management}, + booktitle = {Proceedings of the 1996 IEEE Symposium on Security and Privacy}, + year = {1996}, + pages = {164--173}, + doi = {10.1109/SECPRI.1996.502679}, + url = {https://doi.org/10.1109/SECPRI.1996.502679} +} + +@techreport{ellison1999spki, + author = {Carl M. Ellison and Bill Frantz and Butler Lampson and Ron Rivest and Brian M. Thomas and Tatu Ylonen}, + title = {{SPKI} Certificate Theory}, + institution = {RFC Editor}, + type = {RFC}, + number = {2693}, + year = {1999}, + doi = {10.17487/RFC2693}, + url = {https://www.rfc-editor.org/info/rfc2693} +} + +@inproceedings{birgisson2014macaroons, + author = {Arnar Birgisson and Joe Gibbs Politz and {\'U}lfar Erlingsson and Ankur Taly and Michael Vrable and Mark Lentczner}, + title = {Macaroons: Cookies with Contextual Caveats for Decentralized Authorization in the Cloud}, + booktitle = {Network and Distributed System Security Symposium}, + year = {2014}, + publisher = {Internet Society}, + url = {https://research.google/pubs/macaroons-cookies-with-contextual-caveats-for-decentralized-authorization-in-the-cloud/} +} + +@inproceedings{pang2019zanzibar, + author = {Ruoming Pang and Ramon Caceres and Mike Burrows and Zhifeng Chen and Pratik Dave and Nathan Germer and Alexander Golynski and Kevin Graney and Nina Kang and Lea Kissner and Jeffrey L. Korn and Abhishek Parmar and Christina D. Richards and Mengzhi Wang}, + title = {Zanzibar: Google's Consistent, Global Authorization System}, + booktitle = {2019 USENIX Annual Technical Conference}, + year = {2019}, + pages = {33--46}, + publisher = {USENIX Association}, + url = {https://www.usenix.org/conference/atc19/presentation/pang} +} + +@article{smith2019keri, + author = {Samuel M. Smith}, + title = {Key Event Receipt Infrastructure ({KERI})}, + journal = {arXiv preprint arXiv:1907.02143}, + year = {2019}, + doi = {10.48550/arXiv.1907.02143}, + url = {https://arxiv.org/abs/1907.02143} +} + +@techreport{w3c2022did, + author = {Manu Sporny and Amy Guy and Markus Sabadello and Drummond Reed}, + title = {Decentralized Identifiers ({DIDs}) v1.0}, + institution = {World Wide Web Consortium}, + type = {W3C Recommendation}, + year = {2022}, + month = jul, + url = {https://www.w3.org/TR/did-core/} +} + +@inproceedings{melara2015coniks, + author = {Marcela S. Melara and Aaron Blankstein and Joseph Bonneau and Edward W. Felten and Michael J. Freedman}, + title = {{CONIKS}: Bringing Key Transparency to End Users}, + booktitle = {24th USENIX Security Symposium}, + year = {2015}, + pages = {383--398}, + publisher = {USENIX Association}, + url = {https://www.usenix.org/conference/usenixsecurity15/technical-sessions/presentation/melara} +} + +@techreport{laurie2013ct, + author = {Ben Laurie and Adam Langley and Emilia Kasper}, + title = {Certificate Transparency}, + institution = {RFC Editor}, + type = {RFC}, + number = {6962}, + year = {2013}, + doi = {10.17487/RFC6962}, + url = {https://www.rfc-editor.org/info/rfc6962} +} + +@inproceedings{torresarias2019intoto, + author = {Santiago Torres-Arias and Hammad Afzali and Trishank Karthik Kuppusamy and Reza Curtmola and Justin Cappos}, + title = {in-toto: Providing Farm-to-Table Guarantees for Bits and Bytes}, + booktitle = {28th USENIX Security Symposium}, + year = {2019}, + pages = {1393--1410}, + publisher = {USENIX Association}, + url = {https://www.usenix.org/conference/usenixsecurity19/presentation/torres-arias} +} + +@inproceedings{newman2022sigstore, + author = {Zachary Newman and John Speed Meyers and Santiago Torres-Arias}, + title = {Sigstore: Software Signing for Everybody}, + booktitle = {Proceedings of the 2022 ACM SIGSAC Conference on Computer and Communications Security}, + year = {2022}, + pages = {2353--2367}, + doi = {10.1145/3548606.3560596}, + url = {https://doi.org/10.1145/3548606.3560596} +} + +@techreport{bormann2020cbor, + author = {Carsten Bormann and Paul Hoffman}, + title = {Concise Binary Object Representation ({CBOR})}, + institution = {RFC Editor}, + type = {RFC}, + number = {8949}, + year = {2020}, + doi = {10.17487/RFC8949}, + url = {https://www.rfc-editor.org/info/rfc8949} +} + +@techreport{rundgren2020jcs, + author = {Anders Rundgren and Bret Jordan and Samuel Erdtman}, + title = {{JSON} Canonicalization Scheme ({JCS})}, + institution = {RFC Editor}, + type = {RFC}, + number = {8785}, + year = {2020}, + doi = {10.17487/RFC8785}, + url = {https://www.rfc-editor.org/info/rfc8785} +} + +@inproceedings{haas2017wasm, + author = {Andreas Haas and Andreas Rossberg and Derek L. Schuff and Ben L. Titzer and Michael Holman and Dan Gohman and Luke Wagner and Alon Zakai and JF Bastien}, + title = {Bringing the Web up to Speed with {WebAssembly}}, + booktitle = {Proceedings of the 38th ACM SIGPLAN Conference on Programming Language Design and Implementation}, + year = {2017}, + pages = {185--200}, + doi = {10.1145/3062341.3062363}, + url = {https://doi.org/10.1145/3062341.3062363} +} + +@techreport{backman2024httpsig, + author = {Annabelle Backman and Justin Richer and Manu Sporny}, + title = {{HTTP} Message Signatures}, + institution = {RFC Editor}, + type = {RFC}, + number = {9421}, + year = {2024}, + doi = {10.17487/RFC9421}, + url = {https://www.rfc-editor.org/info/rfc9421} +} + +@misc{openid2023core, + author = {{OpenID Foundation}}, + title = {{OpenID Connect Core 1.0} Incorporating Errata Set 2}, + year = {2023}, + url = {https://openid.net/specs/openid-connect-core-1_0.html} +} + +@misc{spiffe2026, + author = {{Cloud Native Computing Foundation}}, + title = {{SPIFFE} Overview}, + year = {2026}, + url = {https://spiffe.io/docs/latest/spiffe-about/overview/}, + note = {Accessed 2026-07-24} +} + +@software{iroh2026, + author = {{n0 computer}}, + title = {Iroh: Dial by Public Key}, + year = {2026}, + url = {https://github.com/n0-computer/iroh}, + version = {1.x} +} + +@misc{mcp2025tools, + author = {{Model Context Protocol}}, + title = {Tools}, + year = {2025}, + url = {https://modelcontextprotocol.io/specification/2025-11-25/server/tools} +} + +@article{dennis1966capabilities, + author = {Jack B. Dennis and Earl C. Van Horn}, + title = {Programming Semantics for Multiprogrammed Computations}, + journal = {Communications of the ACM}, + year = {1966}, + volume = {9}, + number = {3}, + pages = {143--155}, + doi = {10.1145/365230.365252}, + url = {https://doi.org/10.1145/365230.365252} +} + +@article{saltzer1975protection, + author = {Jerome H. Saltzer and Michael D. Schroeder}, + title = {The Protection of Information in Computer Systems}, + journal = {Proceedings of the IEEE}, + year = {1975}, + volume = {63}, + number = {9}, + pages = {1278--1308}, + doi = {10.1109/PROC.1975.9939}, + url = {https://doi.org/10.1109/PROC.1975.9939} +} + +@article{burrows1990logic, + author = {Michael Burrows and Mart{\'i}n Abadi and Roger Needham}, + title = {A Logic of Authentication}, + journal = {ACM Transactions on Computer Systems}, + year = {1990}, + volume = {8}, + number = {1}, + pages = {18--36}, + doi = {10.1145/77648.77649}, + url = {https://doi.org/10.1145/77648.77649} +} + +@techreport{keynote1999, + author = {Matt Blaze and Joan Feigenbaum and John Ioannidis and Angelos D. Keromytis}, + title = {The {KeyNote} Trust-Management System Version 2}, + institution = {RFC Editor}, + type = {RFC}, + number = {2704}, + year = {1999}, + doi = {10.17487/RFC2704}, + url = {https://www.rfc-editor.org/info/rfc2704} +} + +@inproceedings{li2002rt, + author = {Ninghui Li and John C. Mitchell and William H. Winsborough}, + title = {Design of a Role-Based Trust-Management Framework}, + booktitle = {Proceedings of the 2002 IEEE Symposium on Security and Privacy}, + year = {2002}, + pages = {114--130}, + doi = {10.1109/SECPRI.2002.1004376}, + url = {https://doi.org/10.1109/SECPRI.2002.1004376} +} + +@techreport{rivest1996sdsi, + author = {Ronald L. Rivest and Butler Lampson}, + title = {{SDSI}: A Simple Distributed Security Infrastructure}, + institution = {Massachusetts Institute of Technology}, + year = {1996}, + type = {Technical memo}, + url = {https://www.microsoft.com/en-us/research/publication/sdsi-a-simple-distributed-security-infrastructure/} +} + +@article{coker2011attestation, + author = {George Coker and Joshua Guttman and Peter Loscocco and Amy Herzog and Jonathan Millen and Brian O'Hanlon and John Ramsdell and Ariel Segall and Justin Sheehy and Brian Sniffen}, + title = {Principles of Remote Attestation}, + journal = {International Journal of Information Security}, + year = {2011}, + volume = {10}, + number = {2}, + pages = {63--81}, + doi = {10.1007/s10207-011-0124-7}, + url = {https://doi.org/10.1007/s10207-011-0124-7} +} + +@inproceedings{sailer2004ima, + author = {Reiner Sailer and Xiaolan Zhang and Trent Jaeger and Leendert van Doorn}, + title = {Design and Implementation of a {TCG}-Based Integrity Measurement Architecture}, + booktitle = {Proceedings of the 13th USENIX Security Symposium}, + year = {2004}, + pages = {223--238}, + publisher = {USENIX Association}, + url = {https://www.usenix.org/conference/13th-usenix-security-symposium/design-and-implementation-tcg-based-integrity-measurement} +} + +@article{avizienis1985nversion, + author = {Algirdas Avizienis}, + title = {The {N}-Version Approach to Fault-Tolerant Software}, + journal = {IEEE Transactions on Software Engineering}, + year = {1985}, + volume = {SE-11}, + number = {12}, + pages = {1491--1501}, + doi = {10.1109/TSE.1985.231893}, + url = {https://doi.org/10.1109/TSE.1985.231893} +} + +@inproceedings{claessen2000quickcheck, + author = {Koen Claessen and John Hughes}, + title = {{QuickCheck}: A Lightweight Tool for Random Testing of Haskell Programs}, + booktitle = {Proceedings of the Fifth ACM SIGPLAN International Conference on Functional Programming}, + year = {2000}, + pages = {268--279}, + doi = {10.1145/351240.351266}, + url = {https://doi.org/10.1145/351240.351266} +} + +@article{miller1990fuzz, + author = {Barton P. Miller and Lars Fredriksen and Bryan So}, + title = {An Empirical Study of the Reliability of {UNIX} Utilities}, + journal = {Communications of the ACM}, + year = {1990}, + volume = {33}, + number = {12}, + pages = {32--44}, + doi = {10.1145/96267.96279}, + url = {https://doi.org/10.1145/96267.96279} +} + +@techreport{rfc5280, + author = {David Cooper and Stefan Santesson and Stephen Farrell and Sharon Boeyen and Russell Housley and W. Timothy Polk}, + title = {Internet {X.509} Public Key Infrastructure Certificate and Certificate Revocation List ({CRL}) Profile}, + institution = {RFC Editor}, + type = {RFC}, + number = {5280}, + year = {2008}, + doi = {10.17487/RFC5280}, + url = {https://www.rfc-editor.org/info/rfc5280} +} + +@techreport{rfc8032, + author = {Simon Josefsson and Ilari Liusvaara}, + title = {Edwards-Curve Digital Signature Algorithm ({EdDSA})}, + institution = {RFC Editor}, + type = {RFC}, + number = {8032}, + year = {2017}, + doi = {10.17487/RFC8032}, + url = {https://www.rfc-editor.org/info/rfc8032} +} + +@techreport{w3c2026webauthn, + author = {Tim Cappalli and Michael B. Jones and Akshay Kumar and Emil Lundberg and Matthew Miller}, + title = {Web Authentication: An {API} for Accessing Public Key Credentials, Level 3}, + institution = {World Wide Web Consortium}, + type = {W3C Candidate Recommendation Snapshot}, + year = {2026}, + month = may, + url = {https://www.w3.org/TR/webauthn-3/} +} + +@inproceedings{demoura2021lean4, + author = {Leonardo de Moura and Sebastian Ullrich}, + title = {The {Lean 4} Theorem Prover and Programming Language}, + booktitle = {Automated Deduction -- {CADE} 28}, + series = {Lecture Notes in Computer Science}, + volume = {12699}, + pages = {625--635}, + publisher = {Springer}, + year = {2021}, + doi = {10.1007/978-3-030-79876-5_37}, + url = {https://doi.org/10.1007/978-3-030-79876-5_37} +} + +@article{ho2022aeneas, + author = {Son Ho and Jonathan Protzenko}, + title = {Aeneas: Rust Verification by Functional Translation}, + journal = {Proceedings of the ACM on Programming Languages}, + volume = {6}, + number = {ICFP}, + articleno = {116}, + pages = {711--741}, + year = {2022}, + doi = {10.1145/3547647}, + url = {https://doi.org/10.1145/3547647} +} + +@inproceedings{pnueli1998translation, + author = {Amir Pnueli and Michael Siegel and Eli Singerman}, + title = {Translation Validation}, + booktitle = {Tools and Algorithms for the Construction and Analysis of Systems}, + series = {Lecture Notes in Computer Science}, + volume = {1384}, + pages = {151--166}, + publisher = {Springer}, + year = {1998}, + doi = {10.1007/BFb0054170}, + url = {https://doi.org/10.1007/BFb0054170} +} + +@article{leroy2009compcert, + author = {Xavier Leroy}, + title = {Formal Verification of a Realistic Compiler}, + journal = {Communications of the ACM}, + volume = {52}, + number = {7}, + pages = {107--115}, + year = {2009}, + doi = {10.1145/1538788.1538814}, + url = {https://doi.org/10.1145/1538788.1538814} +} + +@inproceedings{klein2009sel4, + author = {Gerwin Klein and Kevin Elphinstone and Gernot Heiser and June Andronick and David Cock and Philip Derrin and Dhammika Elkaduwe and Kai Engelhardt and Rafal Kolanski and Michael Norrish and Thomas Sewell and Harvey Tuch and Simon Winwood}, + title = {{seL4}: Formal Verification of an {OS} Kernel}, + booktitle = {Proceedings of the 22nd ACM Symposium on Operating Systems Principles}, + pages = {207--220}, + year = {2009}, + doi = {10.1145/1629575.1629596}, + url = {https://doi.org/10.1145/1629575.1629596} +} + +@article{jung2018rustbelt, + author = {Ralf Jung and Jacques-Henri Jourdan and Robbert Krebbers and Derek Dreyer}, + title = {{RustBelt}: Securing the Foundations of the Rust Programming Language}, + journal = {Proceedings of the ACM on Programming Languages}, + volume = {2}, + number = {POPL}, + articleno = {66}, + pages = {66:1--66:34}, + year = {2018}, + doi = {10.1145/3158154}, + url = {https://doi.org/10.1145/3158154} +} + +@inproceedings{lattuada2024verus, + author = {Andrea Lattuada and Travis Hance and Jay Bosamiya and Matthias Brun and Chanhee Cho and Hayley LeBlanc and Pranav Srinivasan and Reto Achermann and Tej Chajed and Chris Hawblitzel and Jon Howell and Jacob R. Lorch and Oded Padon and Bryan Parno}, + title = {Verus: A Practical Foundation for Systems Verification}, + booktitle = {Proceedings of the 30th ACM Symposium on Operating Systems Principles}, + pages = {438--454}, + year = {2024}, + doi = {10.1145/3694715.3695952}, + url = {https://doi.org/10.1145/3694715.3695952} +} + +@article{kroening2023cbmc, + author = {Daniel Kroening and Peter Schrammel and Michael Tautschnig}, + title = {{CBMC}: The {C} Bounded Model Checker}, + journal = {arXiv preprint arXiv:2302.02384}, + year = {2023}, + doi = {10.48550/arXiv.2302.02384}, + url = {https://arxiv.org/abs/2302.02384} +} + +@article{delmas2026kani, + author = {R{\'e}mi Delmas and Zyad Hassan and Qinheping Hu and Rahul Kumar and Felipe R. Monteiro and Thanh Nguyen and Adri{\'a}n Palacios and Celina Val and Michael Tautschnig and Justus Adam and Daniel Schwartz-Narbonne and Carolyn Zech}, + title = {Kani: A Model Checker for Rust}, + journal = {arXiv preprint arXiv:2607.01504}, + year = {2026}, + doi = {10.48550/arXiv.2607.01504}, + url = {https://arxiv.org/abs/2607.01504} +} diff --git a/docs/product/sdk-glossary.json b/docs/product/sdk-glossary.json index 8946f93b..08c6e97c 100644 --- a/docs/product/sdk-glossary.json +++ b/docs/product/sdk-glossary.json @@ -31,7 +31,7 @@ { "concept": "create", "typescript": "createAuths", - "python": "Auths" + "python": "create_auths" }, { "concept": "delegate", diff --git a/docs/prompts/LAUNCH_READINESS_INVESTIGATIONS.md b/docs/prompts/LAUNCH_READINESS_INVESTIGATIONS.md new file mode 100644 index 00000000..14f51eab --- /dev/null +++ b/docs/prompts/LAUNCH_READINESS_INVESTIGATIONS.md @@ -0,0 +1,553 @@ +# Launch-readiness investigations + +Two independent investigation prompts. Run them as **separate agents** — ideally in parallel, and +without letting either read the other's output. They are deliberately scoped to different questions, +and their value comes from being independent. + +- **Prompt A — Code readiness.** What must change in the code before v1.0 ships. +- **Prompt B — Adoption readiness.** What must change for anyone to actually use it. + +Both are investigations, not implementation tasks. Neither should change shipping code. + +--- + +# Prompt A — Code readiness for v1.0 + +You have zero prior context. Read this whole brief before running anything. + +## What you are looking at + +`/Users/bordumb/workspace/repositories/auths-proof-base/auths-proof` + +Auths is a proof-carrying authorization system. An actor — a person or an AI agent — receives +precise, portable, cryptographically bounded permission to do **one exact thing**, and the system +leaves verifiable evidence of what happened. Delegation can only narrow, never widen. Everything +fails closed. + +Roughly 110 Rust crates, plus TypeScript, Python, and WASM bindings, an independent Go +implementation, Lean 4 proofs under `formal/`, Kani harnesses, and ~50 `cargo xtask` gates. +Rust owns all semantics; every other language projects them. + +## Your question + +**What must change in this codebase before it can ship a credible 1.0?** + +Credible means: a competent external security reviewer reads it and does not find something that +undermines the product's claims. Auths sells verifiable authority. Any gap between what it asserts +and what it actually checks is not an ordinary bug — it contradicts the thing being sold. + +## Read these first + +1. `AGENTS.md` — the repository contract. Prelaunch, zero external users, direct cutover, no + compatibility shims. Violent refactors permitted; sloppy ones are not. +2. `docs/target-state/v1-api-contract.md` — the frozen API contract, including ratified decisions + (§10A) and the empirical-resolution policy (§10Z). Treat ratified decisions as settled. +3. `git log --oneline bbeb654..HEAD` — work already completed. **Do not re-investigate what these + commits fixed.** Read them to understand what is already addressed. +4. `docs/target-state/v1-api-review-findings.md` and `-bindings.md` — a prior audit produced 139 + findings with file:line citations. + +## On the prior audit — read this carefully + +The findings in those documents are **already known**, and much of the highest-severity material is +already fixed or in progress. Reading them prevents you from spending your budget rediscovering +known problems. + +**A separate launch-readiness assessment also exists. It has been deliberately withheld from you.** + +That is on purpose. We want an independent view, and an agent handed someone else's ranked list +tends to produce that list back with more words. Form your own judgment from the code. + +Your value is concentrated in two places: + +- **What is not in the prior audit at all.** It was scoped to API surface, semantic ownership, and + the error model. Large areas of this repository were never examined through a launch lens. +- **Where you disagree with the prior audit's severity.** If something it called minor is actually + launch-blocking, say so and prove it. + +If your conclusions substantially overlap the prior work, that is a useful signal too — but only if +you reached them independently. + +## Method + +**Determine things empirically. Do not reason from names, and do not guess.** + +- "Does this code do X?" → write the test, run it, the result is the answer +- "Do two implementations agree?" → differential test on identical inputs +- "Does this gate check what it claims?" → break the thing deliberately, confirm the gate goes red. + A gate that stays green under a deliberate break is not a gate. +- Bounded exhaustive question → Kani (`cargo kani -p `) +- Must-hold-for-all-inputs → Lean (`cd formal && lake build`) + +Every finding carries `file:line` and, where you ran something, the verbatim output. A finding +without evidence is an opinion. + +## Verification surface + +`cargo xtask `. Run `cargo xtask help` for the full list. Among them: `arch`, +`compliance`, `conformance`, `cross-language`, `binding-semantics`, `core-boundary`, `public-naming`, +`semantic-freeze`, `sdk-experience`, `sdk-vocabulary`, `product-waist-conformance`, +`mechanism-conformance`, `production-contract`, `evolution-policy`, `error-registry`, +`adversarial-conformance`, `fuzz-smoke`, `release-check`, `formal`. + +Capture a baseline before you change anything. + +**Two gates are currently red on purpose. Do not try to fix either:** + +- `semantic-freeze` — six frozen-meaning identities have drifted deliberately and receive version + assignments at the end of the current effort. +- `cargo xtask formal` — clearing the translation source closure requires re-running pinned charon + and aeneas binaries, and neither is installed on this host. Use `lake` directly inside `formal/`. + +## Scope + +Everything in this repository is in scope. Deliberately including areas the prior audit did not +reach. Consider — and go beyond — operational readiness, failure and recovery behavior under real +conditions, resource exhaustion and denial-of-service surface, dependency and supply-chain posture, +concurrency and data integrity, upgrade and version-skew behavior, observability sufficient to +diagnose a production incident, cryptographic agility, test quality as opposed to test count, build +reproducibility, and anything else you would want answered before putting this on a payment path. + +That list is a starting point, not a checklist. **If the most important thing you find is not on +it, that is the best possible outcome.** + +## What "launch-blocking" means + +Be disciplined about severity. Reserve **blocker** for things that would make launching irresponsible +— a security hole, a correctness bug on a money path, a claim the code does not support, or a defect +that breaks users on day one. Everything else is **major** or **minor**. + +An honest short list of real blockers is worth more than a long list padded with preferences. + +## Deliverable + +Write `docs/target-state/v1-launch-readiness-independent.md`. + +### The bar your document must clear + +**A different agent, with zero context — no memory of this conversation, no knowledge of this +repository — must be able to open your document and implement any finding in it without asking a +single question.** + +That is the acceptance test for your work. Before you submit, reread each finding and ask: *could +someone who has never seen this codebase execute this?* If they would have to go hunting for the +file, guess at the intended end state, or invent a way to check whether they succeeded, the finding +is not done. + +This means findings are **work orders**, not observations. "The error model is inconsistent" is an +observation and is worthless. A work order names the file, the line, the current bytes, the required +bytes, and the command that proves it worked. + +### Required structure + +```markdown +# v1.0 Launch Readiness — Independent Assessment + +## Verdict +Can this ship? The single most important thing standing in the way. Ten sentences maximum. + +## Summary table +| ID | Title | Severity | Area | Est. effort | Depends on | +|----|-------|----------|------|-------------|------------| +| LR-001 | ... | blocker | ... | 2h / 1d / 3d | — | + +## Findings +(every finding in the template below, blockers first, then major, then minor) + +## Recommended execution order +A numbered sequence with the reasoning. Which findings unblock others, which can run in +parallel, and which must not be attempted at the same time because they touch the same files. + +## Disagreements with the prior audit +Each with evidence. + +## Areas examined that the prior audit did not +Named explicitly. + +## Unresolved +What you could not determine, and the exact test, harness, or tool access that would settle it. + +## Coverage statement +What you read. Honestly, what you did not. +``` + +### Required per-finding template + +Every finding uses exactly this shape. No exceptions, including for minor ones. + +```markdown +### LR-001 — + +- **Severity:** blocker | major | minor +- **Area:** e.g. rust-core / bindings / packaging / operations / supply-chain +- **Estimated effort:** e.g. 2 hours / 1 day / 3 days +- **Depends on:** LR-00N, or "—" +- **Files:** every path that must change, with line numbers + +**What is true today** +Quote the actual code or output. Not a description of it — the bytes. + +```rust +// core/crates/example/src/lib.rs:42-45 +pub fn thing() -> bool { true } +``` + +**Why this blocks launch** +The concrete failure. Name the input, the code path, and the wrong result. If it is a security +finding, state the attack: what an adversary supplies and what they gain. If you could not +demonstrate the failure, say PLAUSIBLE rather than asserting it. + +**Evidence** +The command you ran and its verbatim output. If you wrote a test to prove it, include the test +source and its failing output. + +**Required end state** +What the code must do afterwards, precisely enough to implement. Where a rename or a specific +signature is required, give the exact identifier. Where a design decision remains open, say so +explicitly and give the options with a recommendation — do not leave the implementer guessing. + +**How to implement** +Ordered steps. Concrete enough that someone unfamiliar with this repository can follow them. +Name the functions, the call sites, and the tests that will break. + +**Blast radius** +What else breaks when this changes. Which crates, bindings, generated artifacts, fixtures, or +gates. If it drifts a frozen-meaning identity, name the identity and its current version. + +**How to verify it worked** +The exact commands, and what passing looks like. + +```bash +cargo test -p some-crate specific_test_name +cargo xtask cross-language +``` + +**Rollback** +How to undo it if it turns out to be wrong. +``` + +### Worked example of the required bar + +This is what "implementable by a zero-context agent" looks like. Match this level of specificity. + +> **What is true today** +> +> ```rust +> // core/crates/auths-model/src/lib.rs:929-936 +> pub fn optional_budget_covers(ceiling: Option<&Budget>, requested: Option<&Budget>) -> bool { +> match (ceiling, requested) { +> (_, None) | (None, Some(_)) => true, +> (Some(ceiling), Some(requested)) => ceiling.covers(requested), +> } +> } +> ``` +> +> **Why this blocks launch** +> +> `(Some(bounded_ceiling), None)` returns `true` — a bounded ceiling is treated as covering an +> action that declares no budget at all. The system denies that case today only because a guard at +> `auths-verifier/src/lib.rs:2543` runs at `:2227`, before `authorizes` at `:2251`. Correct behavior +> depends on statement order, not on the algebra. Reorder those two lines, or reach `authorizes` by +> any other path, and an action with no bound on what it may spend is authorized. +> +> **Evidence** +> +> ``` +> $ cargo test -p auths-model --lib optional_budget +> test tests::optional_budget_no_request ... ok +> ``` +> The passing test at `core/crates/auths-model/src/lib.rs:4856` asserts the wrong answer: +> `assert!(optional_budget_covers(Some(&zero), None));` +> +> **Required end state** +> +> An absent ceiling covers everything. An absent request under a *present* ceiling does not. +> This matches the three other implementations: `auths-verifier/src/lib.rs:2543`, +> `bindings/independent/go/auths/semantic.go:1341`, +> `bindings/independent/typescript/semantic-verifier.ts:1499`. +> +> **Blast radius** +> +> Inverts the assertion at `auths-model/src/lib.rs:4856` and the mutation-kill oracle at +> `auths-formal-refinement/src/lib.rs:412-416`, which currently requires `canonical == true` for +> this input. Verify the mutation matrix still kills its mutant. Drifts frozen-meaning identity +> `auths.core.protocol` (currently v15). +> +> **How to verify it worked** +> +> ```bash +> cargo test -p auths-model --lib +> cargo test -p auths-formal-refinement +> cargo xtask cross-language # all four implementations must agree +> ``` + +### Rules for the document + +- Do not modify shipping code. Throwaway tests to answer a question are expected — delete them or + mark them clearly, and say which you did. +- Every claim carries `file:line`. A finding without evidence is an opinion, and opinions do not + belong in this document. +- If you could not prove something, label it **PLAUSIBLE** and say what would settle it. Do not + round uncertainty up to certainty. +- Where a fix requires a judgment call you are not positioned to make, state the options, give a + recommendation, and mark it as needing a decision. Never leave it silent. + +--- + +# Prompt B — Adoption readiness + +You have zero prior context. Read this whole brief before running anything. + +## What you are looking at + +`/Users/bordumb/workspace/repositories/auths-proof-base/auths-proof` — the product. +`/Users/bordumb/workspace/repositories/auths-proof-base/auths-docs` — its documentation site. + +Auths is a proof-carrying authorization system. An actor — a person or an AI agent — receives +precise, portable, cryptographically bounded permission to do **one exact thing**, and the system +leaves verifiable evidence. Delegation can only narrow, never widen. Everything fails closed. + +The technical core is strong and getting stronger: Lean-verified attenuation, four independent +implementations held in agreement by a canonical corpus, machine-checked security claims. + +## Your question + +**Why would anyone adopt this, and what currently stops them?** + +You are not evaluating whether the technology is good. Assume it is. You are evaluating whether a +competent engineer who has never heard of Auths can get from "curious" to "this is running in my +service" — and whether they would want to. + +The relevant history: Stripe did not win on payments technology, it won on seven lines of code. +Docker did not invent containers. Kubernetes was a worse Borg that anyone could run. Technical +excellence is necessary and never sufficient. The technically superior option loses routinely. + +## The method that matters most: actually use it + +**Do not theorize about ergonomics. Install the thing and time yourself.** + +1. Start from the README as a newcomer would. Follow it literally. **Record every point at which you + are confused, blocked, or have to read source code to continue.** Those moments are the finding. +2. Time each attempt. How many minutes from clone to a first successful authorization? To a first + *denial* you triggered on purpose? For an authorization system the denial is the more important + demo — it is the moment the value becomes visible. +3. Try each language: Rust, TypeScript, Python. Note where they diverge in difficulty. Note where a + documented step does not work, and record the exact error. +4. Try the quickstarts and examples that are advertised. Verify each one actually runs. + `bindings/recipes/` and `examples/` are starting points; `demos/` contains roughly 25 more. +5. Try the reference deployment in `demos/open-production-reference/`. + +Your transcript of failures is more valuable than any analysis you write on top of it. Include the +verbatim errors. + +## Areas to consider + +Explore whichever of these prove productive, and add your own: + +**First contact.** What does the README promise, and does the product deliver it in the time it +implies? What is the shortest honest path to value? Is it obvious within thirty seconds what problem +this solves and who has that problem? + +**Documentation.** `auths-docs` is a Next.js site with content authored as TypeScript and JSON data +structures, not Markdown — read `content/page-builders.ts` first or you will waste an hour looking +for MDX. Assess it as a developer would: can you find what you need, is it accurate, do the examples +run? Note that shipping code was recently renamed in several places, so some documentation may +describe an API that no longer exists — verify rather than assume. + +**Packaging and distribution.** What actually ships to npm, PyPI, and crates.io? Install each in a +clean environment and see what you get. Do the type declarations match the runtime surface? Does the +wheel work on the platforms it claims? + +**The agent wedge.** The urgent unsolved problem in this space is AI agents acting with authority — +today the state of the art is handing an agent a broad API key and hoping. Auths is aimed directly at +that. Assess how well positioned it is: how hard is it to put Auths in front of an MCP server or an +agent framework? What would an integration look like for someone already using one? Is there a path +where an agent framework adopts this as its default authority mechanism, and what stands in the way? + +**Demonstrations.** `demos/` and `docs/prompts/ambitious-demos/` exist. Which demos would actually +convince a skeptic, and do those exist? What is the single most persuasive thing this product could +show someone in two minutes, and can it show that today? + +**Positioning.** Auths competes with "just use OAuth scopes" and "just use short-lived tokens." +Being correct does not win that argument; being easier than the wrong thing does. Where is Auths +genuinely easier, where is it genuinely harder, and is the harder part justified by something the +user will care about? + +**Integration surface.** `product/integrations/` has real adapters — Stripe, PostgreSQL, Kubernetes, +GitHub, OpenTofu, Radicle, did:web. How discoverable and usable are they? Does adopting Auths require +replacing things people already run, or can it sit alongside them? + +**Evidence a buyer needs.** Security reviewers, procurement, and platform teams ask for specific +artifacts. Determine what exists, what is claimed but missing, and what a serious evaluator would ask +for on day one. + +Explore what proves interesting. **If the most important thing you find is not on this list, that is +the best possible outcome.** + +## Rules + +- **Verify, never assume.** If documentation says something works, run it. If an example is + advertised, execute it. Report the verbatim error when it fails. +- **Do not fabricate.** Do not invent benchmarks, adoption numbers, competitor claims, or user + research. A gap you name is fine; a gap you make up is not. +- **Separate "hard because the problem is hard" from "hard because we made it hard."** Precise + authority is intrinsically more work than a bearer token. Say which friction is essential and which + is self-inflicted — only the second is worth fixing. +- **Be specific about who.** "Developers want X" is not a finding. "An engineer adding Stripe refund + authorization to an existing Node service hits X at step 3" is. + +## Deliverable + +Write `docs/target-state/adoption-readiness.md`. + +### The bar your document must clear + +**A different agent, with zero context — no memory of this conversation, no knowledge of this +repository or its documentation site — must be able to open your document and implement any +recommendation in it without asking a single question.** + +That is the acceptance test. Adoption findings fail this more often than code findings do, because +it is easy to write "the quickstart is confusing" and think you have said something. You have not. +The implementable version names the file, quotes the confusing text, supplies the replacement, and +states how to tell whether it worked. + +**"Improve the docs" is not a recommendation. "Replace lines 40-58 of `content/get-started-pages.ts` +with this text, because a first-time reader cannot tell what `provider` is, and verify by running +`npm run build` and checking `/get-started/quickstarts/agent-delegation` renders a runnable example" +is a recommendation.** + +### Required structure + +```markdown +# Adoption Readiness + +## First-run report +Lead with this. The raw transcript of trying to use the product, with verbatim errors and +timings. It is the most valuable section and must come first. + +## Time to first success +| Language | Clone → first authorization | Clone → first deliberate denial | Blocked at | +|----------|------------------------------|----------------------------------|------------| + +## Verdict +Would a competent engineer adopt this today? What single change would most increase the odds? + +## Summary table +| ID | Title | Impact | Area | Est. effort | Depends on | +|----|-------|--------|------|-------------|------------| +| AD-001 | ... | critical | onboarding | 1d | — | + +## Recommendations +(every one in the template below, ranked by impact on adoption) + +## Recommended execution order +Numbered, with reasoning about what unblocks what. + +## The agent wedge +Assessment plus the shortest concrete path to an agent-framework integration. + +## The two-minute demo +What would most convince a skeptic, whether it exists, and if not, exactly what to build. + +## Essential vs self-inflicted friction +The explicit split. + +## What a buyer will ask for +Artifact by artifact: exists / claimed but missing / absent. + +## Coverage statement +What you ran. Honestly, what you did not. +``` + +### Required per-recommendation template + +```markdown +### AD-001 — + +- **Impact:** critical | high | medium | low — and *on whom*: e.g. "every first-time TypeScript user" +- **Area:** onboarding / docs / packaging / demos / integrations / positioning / evidence +- **Estimated effort:** e.g. 4 hours / 2 days +- **Depends on:** AD-00N, or "—" +- **Files:** every path that must change, with line numbers. Include the repo, since two are in + play: `auths-proof` and `auths-docs`. + +**What a user hits today** +The concrete experience, in the second person, with the verbatim error or the quoted text. Not +"the example is broken" — the actual command, the actual output. + +``` +$ npx tsx examples/rest-effect/typescript/main.ts +ReferenceError: provider is not defined +``` + +**Why this costs adoption** +Who abandons, at which step, and why. Be specific about the persona: "an engineer adding refund +authorization to an existing Node service" beats "developers". + +**Required end state** +What the user should experience instead, concretely enough to build. If it is text, supply the +replacement text. If it is code, supply the code or state exactly what it must demonstrate. + +**How to implement** +Ordered steps a stranger to this repository can follow. Name files, commands, and any generator +that must be re-run — note that the docs site authors content as TypeScript and JSON data +structures, not Markdown, so edits go through `content/` and not through page files. + +**How to verify it worked** +The exact commands, and the observable result. Where possible make it a timing or a pass/fail, +not a judgment: "a new user reaches a denial in under 10 minutes" beats "the docs read better". + +```bash +npm run build && npm test -- examples +``` + +**Blast radius** +What else must change to stay consistent. Other languages, the docs site, generated artifacts, +release manifests. +``` + +### Worked example of the required bar + +> **What a user hits today** +> +> The site advertises seven quickstarts. `examples/scenarios.json` declares three, under different +> IDs (`rest-effect` vs `local-rest-effect`). Running the one that exists: +> +> ``` +> $ npx tsx examples/rest-effect/typescript/main.ts +> ReferenceError: provider is not defined +> ``` +> +> `provider` is referenced at `examples/rest-effect/typescript/main.ts:6` but never imported or +> constructed. The same defect exists in the Python and Rust variants. +> +> **Why this costs adoption** +> +> This is the first code a reader executes. An engineer evaluating Auths for a Stripe refund path +> copies it, gets a `ReferenceError` in under a minute, and concludes the project is unfinished. +> Nothing later in the funnel gets a chance to matter. +> +> **Required end state** +> +> Each example is a complete runnable program: imports, provider construction, error handling, and +> the fail-closed path. A reader can copy the file, run one command, and see both a success and a +> denial. Expected stdout is documented alongside it. +> +> **How to verify it worked** +> +> ```bash +> npx tsx examples/rest-effect/typescript/main.ts # prints "completed" +> npx tsx examples/rest-effect/typescript/denied.ts # prints "denied: action_mismatch" +> ``` +> and a CI job executes every example on every push. + +### Rules for the document + +- **Verify, never assume.** If the documentation says something works, run it. Report the verbatim + error when it does not. +- **Do not fabricate.** No invented benchmarks, adoption numbers, competitor claims, or user + research. A gap you name is fine; a gap you make up is not. +- **Timings are data.** Measure them. "Slow onboarding" is an opinion; "23 minutes and three source + files read before the first successful call" is a finding. +- Do not modify shipping code. Fixing something trivial to get yourself unblocked is fine — note it + clearly, and do not commit it. diff --git a/docs/target-state/adoption-readiness.md b/docs/target-state/adoption-readiness.md new file mode 100644 index 00000000..e6776e7e --- /dev/null +++ b/docs/target-state/adoption-readiness.md @@ -0,0 +1,1764 @@ +# Adoption Readiness + +Assessment date: 15 August 2026. Product checkout: `auths-proof` on branch +`dev-cleanup`; documentation checkout: `auths-docs` on branch +`codex/docs-gold-pages`. Both working trees already contained unrelated local +changes. I preserved them and evaluated the checked-out bytes rather than +constructing a clean revision. + +## First-run report + +This section is intentionally chronological and minimally interpreted. Times are +wall-clock times from entering the product repository at 20:44:31 BST, except +where a command's own `real` time is shown. The first attempts ran in a restricted +network sandbox; every network-dependent failure was retried with registry access. +Those environment-only failures are labelled as such. Product failures reproduced +after access was available. + +### 00:00 — the repository README is a kernel inventory, not a start + +The first thirty seconds do explain the differentiator: + +> Auths is an open protocol and SDK for proof-carrying, bounded machine +> authority. + +The next 76 lines enumerate the sealed verifier and 35 target packages. There is +no install command, language choice, documentation link, agent example, or +deliberate denial. The first runnable command at `auths-proof/README.md:91-99` is +an **identity without authorization** browser workbench. The first authorization +command is one item in “Focused validation” at `README.md:101-109`: + +```text +cargo run -p auths-proof-offline-example +``` + +At this point a newcomer knows that the implementation is sophisticated, but not +which package belongs in an application or how Auths sits in front of an effect. +The README does honestly say “prelaunch and pre-audit” at lines 122-123. + +### 01:58 — first Rust authorization succeeds from the checkout + +Following the only authorization-shaped README command produced: + +```console +$ cargo run -p auths-proof-offline-example + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.13s + Running `target/debug/auths-proof-offline-example` +verdict=Authorized code=authorized stage=Complete retryable=false work_units=312 +proof=[…] action=[…] context=[…] required_configuration=Some([…]) local_configuration=[…] +``` + +The first cold build reached that verdict 1 minute 58 seconds after opening the +README. A warm measurement was: + +```text +real 6.31 +user 0.10 +sys 0.06 +``` + +This proves the offline verifier. It does not create authority, protect a handler, +execute an effect, emit a receipt, or offer a failure switch. Reading +`demos/offline-verification/src/main.rs:4-61` was necessary to learn that it always +loads the positive fixture. + +### 03:57 — the advertised TypeScript package installs, but it is another product + +The language README at `bindings/typescript/README.md:6-12` says: + +```text +npm install @auths-dev/sdk +``` + +The first restricted-network attempt failed after 70.36 seconds. This error is an +evaluation-environment limitation, not an Auths defect: + +```text +npm error code ENOTFOUND +npm error syscall getaddrinfo +npm error errno ENOTFOUND +npm error network request to https://registry.npmjs.org/@auths-dev%2fsdk failed, reason: getaddrinfo ENOTFOUND registry.npmjs.org +``` + +With registry access, install succeeded in 5.39 seconds. It installed +`@auths-dev/sdk@0.1.16`. That artifact exposes only `.` and conformance JSON; its +README describes decentralized identity and Git-native storage. Running the exact +“Protect one MCP action” imports from `bindings/typescript/README.md:14-43` failed +in 0.13 seconds: + +```console +$ node quickstart.mjs +node:internal/modules/esm/resolve:313 + return new ERR_PACKAGE_PATH_NOT_EXPORTED( + ^ + +Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './integrations' is not defined by "exports" in /node_modules/@auths-dev/sdk/package.json imported from /quickstart.mjs + at exportsNotFound (node:internal/modules/esm/resolve:313:10) + at packageExportsResolve (node:internal/modules/esm/resolve:660:9) + at packageResolve (node:internal/modules/esm/resolve:773:12) + at moduleResolve (node:internal/modules/esm/resolve:853:18) + at defaultResolve (node:internal/modules/esm/resolve:983:11) + at #cachedDefaultResolve (node:internal/modules/esm/loader:731:20) + at ModuleLoader.resolve (node:internal/modules/esm/loader:708:38) + at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:310:38) + at ModuleJob._link (node:internal/modules/esm/module_job:182:49) { + code: 'ERR_PACKAGE_PATH_NOT_EXPORTED' +} + +Node.js v22.23.1 +real 0.13 +``` + +No authorization or denial is possible through the documented registry path. + +### 06:00 — the repository-local TypeScript RC works immediately once found + +The checkout contains `target/npm-package/auths-dev-sdk-1.0.0-rc.1.tgz`, but no +newcomer-facing page points to it. The first install hit a machine-local npm cache +ownership problem: + +```text +npm error code EPERM +npm error syscall mkdtemp +npm error path /Users/bordumb/.npm/_cacache/tmp/… +npm error Your cache folder contains root-owned files… +real 0.53 +``` + +Using a disposable cache installed it in 0.81 seconds. A complete program based +on the README, with an allowed `publish_report` call followed by a disallowed tool +call, then produced: + +```console +$ node quickstart.mjs +{"attempt":"allowed","kind":"completed","calls":1} +{"attempt":"denied","kind":"denied","code":"permission-not-granted","calls":1} +real 0.20 +``` + +The `calls` counter remaining at one makes the value visible: denied bytes never +entered the provider. The intended `1.0.0-rc.1` tarball has all seven declared +entry points, and its export map pairs each runtime entry with a declaration file. +The exercised implementation works; distribution and discovery do not. + +### 08:00 — the advertised Python package has the same predecessor mismatch + +With registry access, `python -m pip install auths` completed in 10.79 seconds and +installed `auths==0.1.16`. The wheel is the former identity product. A runnable +wrapper around `bindings/python/README.md:15-38` failed in 0.73 seconds: + +```console +$ python quickstart.py +Traceback (most recent call last): + File "/quickstart.py", line 3, in + from auths.integrations import development +ModuleNotFoundError: No module named 'auths.integrations' +real 0.73 +``` + +Installing the local +`target/python-wheels/auths-1.0.0rc1-cp39-abi3-macosx_11_0_arm64.whl` took 0.96 +seconds. The documented one-argument handler did not complete the effect; it +returned: + +```text +RecoveryResult(kind='recoverable', execution_id='…', reference=<…>) +real 0.55 +``` + +Source inspection at +`bindings/python/python/auths/profiles/_mcp.py:441-528` was necessary to discover +that the handler contract is `(arguments, context)`, while the README supplies +only `(arguments)`. Correcting the temporary program—not shipping code—made both +paths work: + +```console +$ python quickstart.py +{"allowed": "completed", "denied": "denied", "code": "permission-not-granted"} +real 0.16 +``` + +### 10:00 — crates.io also resolves to the predecessor + +The documentation site's guide says `cargo add auths-sdk` at +`auths-docs/app/guides/protect-rest-effect/protect-rest-effect.mdx:21`. With +registry access the command completed in 7.40 seconds and selected +`auths-sdk v0.1.16`, locking 433 packages. The site then tells the reader to use +`RestAction::post` at line 37. No `RestAction` exists in the checked-out product, +and it does not exist in the public crate. A clean compile spent 197.25 seconds +building the dependency graph, then failed: + +```console +$ cargo check +error[E0432]: unresolved import `auths_sdk::RestAction` + --> src/main.rs:1:5 + | +1 | use auths_sdk::RestAction; + | ^^^^^^^^^^^^^^^^^^^^^ no `RestAction` in the root + +For more information about this error, try `rustc --explain E0432`. +error: could not compile `auths_adoption` (bin "auths_adoption") due to 1 previous error +real 197.25 +user 181.96 +sys 50.91 +``` + +This is especially costly: the engineer learns only after a full cold Rust build +that the guide describes an API which neither the registry artifact nor current +source implements. + +### 14:46 — first deliberate Rust denial requires source and fixture work + +The offline example has no denial argument. I located +`core/fixtures/v1/denied/action-permission-not-granted.*.cbor`, read the verifier +API, and wrote a temporary external Rust program. Its first dependency attempt +was blocked by the restricted network after 22.40 seconds: + +```text +Updating crates.io index +warning: spurious network error… +error: failed to get `minicbor` as a dependency of package `auths-rust-denial` + +Caused by: + download of config.json failed + +Caused by: + failed to download from `https://index.crates.io/config.json` + +Caused by: + [6] Couldn't resolve host name (Could not resolve host: index.crates.io) +``` + +Re-running from the local cache with `--offline` produced the intended result: + +```console +$ cargo run --offline +verdict=Denied code=permission-not-granted +real 74.38 +user 0.08 +sys 0.24 +``` + +The denial arrived about 14 minutes 46 seconds after opening the README and about +13 minutes after the first positive verdict. Most of that gap was discovery and +writing a consumer the repository should already provide. + +### Recipes — real implementations, an undisclosed runner, and one broken Python import + +The brief's suggested top-level `examples/` starting point does not exist in this +checkout. The runnable material is split between `bindings/recipes/` and +`demos/`. + +`bindings/recipes/manifest.json` declares five TypeScript/Python recipe pairs. +Recipes 3 and 4 contain meaningful denied paths. There is no README or top-level +run command under `bindings/recipes/`; generated product docs say only “install +the single Auths package.” The TypeScript recipe package has no scripts. + +After compiling the TypeScript sources, recipe 3 ran against the local RC: + +```console +$ node bindings/recipes/typescript/build/03-execute-exact-action.js +{"recipe":"03-execute-exact-action","outcome":"completed","denied":true,"calls":1} +real 0.18 +``` + +The all-recipe runner initially received a relative interpreter path. Because it +changes to a temporary working directory, it reported only: + +```text +Error: python/01-authenticate-identity: undefined + at run (bindings/recipes/tools/run.mjs:51:34) +real 0.26 +``` + +Using an absolute interpreter path reached Python recipe 3 and reproduced a +shipping-source error: + +```text +Error: python/03-execute-exact-action: Traceback (most recent call last): + File "…/bindings/recipes/python/03_execute_exact_action.py", line 6, in + from auths import verify_receipt +ImportError: cannot import name 'verify_receipt' from 'auths' (…/bindings/python/python/auths/__init__.py) + at run (bindings/recipes/tools/run.mjs:51:34) +real 1.00 +``` + +`verify_receipt` is intentionally public from `auths.verify`, as recorded in +`bindings/python/api/public-api.txt:59-95`; recipe 5 already imports it there. +The current recipe evidence file is also candid: +`bindings/recipes/experience-evidence.json:22-28` says the unfamiliar-developer +cohort is awaiting, with zero participants. + +### Documentation — it builds, while its code examples remain unchecked + +The brief's statement that the site has no MDX is stale. The primary guide is +`auths-docs/app/guides/protect-rest-effect/protect-rest-effect.mdx`. It promises a +“10 MINUTE GUIDE” and a refund integration at lines 1-12, but every language +sample uses a REST profile/API absent from shipping source. The site test asserts +that those strings render; it does not compile them. + +The complete documented validation passed: + +```console +$ npm test +… +Build complete. Run `vinext start` to start the production server. +… +# tests 3 +# suites 0 +# pass 3 +# fail 0 +# duration_ms 742.298792 +``` + +Passing means the inaccurate install commands and examples rendered successfully. +It does not mean any SDK snippet ran. + +### MCP demo — authentic semantics, slow cold path, success only + +The Rust MCP profile genuinely maps the official `rmcp` call type at +`product/profiles/auths-profile-mcp/src/lib.rs:72-90`. The demo compiled and ran: + +```console +$ cargo run -p auths-mcp-demo -- demo --transport memory + Finished `dev` profile [unoptimized + debuginfo] target(s) in 2m 02s + Running `target/debug/auths-mcp-demo demo --transport memory` +transport in-memory +outcome Completed { result: [123, 34, 110, 97, 109, 101, 34, 58, 34, 113, 51, 34, 44, 34, 115, 116, 97, 116, 117, 115, 34, 58, 34, 97, 112, 112, 114, 111, 118, 101, 100, 34, 125] } +proof_bytes 883 +total_micros 3801 +verification_micros 1911 +execution_micros 450 +request_id c9a39cc890ceda5393c33001677c9c5addad697c82050b291782166018383a81 +real 123.61 +user 0.23 +sys 0.38 +``` + +Even with `--transport memory`, the cold build compiles the Iroh stack. The output +shows the result as a byte array, and the CLI has no denial or replay subcommand. +It is protocol evidence, not a persuasive first demo. + +### Reference deployment — setup ambiguity, very large cold start, compile failure + +The “Fifteen-minute path” at +`demos/open-production-reference/README.md:15-36` says to copy an already checked-in +configuration, “provide” a specially encoded seed without a generation command, +and create certificates through another runbook. Running the shown Compose command +literally first failed before contacting Docker: + +```text +error while interpolating x-auths-node.environment.AUTHS_LOCAL_SEED: required variable AUTHS_LOCAL_SEED is missing a value: provide a 32-byte unpadded base64url seed +``` + +Docker Desktop was initially stopped; the independent environment error was: + +```text +Cannot connect to Docker daemon at unix:///Users/bordumb/.docker/run/docker.sock. Is docker daemon running? +``` + +After starting Docker, generating a disposable seed, and using the checked-in +local certificates, the cold build pulled PostgreSQL, Prometheus, Grafana, +OpenTelemetry, nginx, a Rust base image, a second Rust 1.97.1 toolchain inside that +image, and the complete crate dependency graph. Before any Auths service started, +the `auths-node` release build failed. The Docker log recorded 305.3 seconds for +the Rust base-image stage and failed 127.1 seconds into its compile step, after the +earlier infrastructure-image pulls. I did not capture a reliable single wrapper +time for the whole attempt, so I do not present an invented total. + +```text +error[E0277]: `?` couldn't convert the error: `KeriError: std::error::Error` is not satisfied + --> product/runtime/auths-node/src/main.rs:46:54 + | +38 | fn kernel(config: &NodeConfig) -> Result> { + | ---------------------------------------------- required `KeriError: std::error::Error` because of this +... +46 | Box::new(auths_did_keri::DidKeriMethod::new()?), + | ------------------------------------^ the trait `std::error::Error` is not implemented for `KeriError` + | | + | this has type `Result<_, KeriError>` + | + = note: required for `Box` to implement `From` + +error: could not compile `auths-node` (bin "auths-node") due to 1 previous error +``` + +The cause is concrete: the workspace dependency disables default features at +`auths-proof/Cargo.toml:150`; `KeriError` implements `std::error::Error` only under +the `std` feature at `core/adapters/auths-did-keri/src/lib.rs:1235-1236`; and +`product/runtime/auths-node/Cargo.toml:13` does not re-enable it. The failed build +created no Compose containers. The full doctor and installed-SDK tests therefore +could not run. + +### Public demos — the flagship is live; the smaller lab is half-live + +Reachability was checked from an unrestricted network on 15 August 2026: + +- `https://auths-incident-demo-control-room.vercel.app/` returned 200. +- Northstar, EdgeShield, and agent `/healthz` endpoints each returned 200. +- `https://auths-live-demo.vercel.app/` returned 200, but its configured backend + `auths-live-demo.fly.dev` did not resolve in DNS, including `/healthz`. The UI + therefore advertises a native execution it cannot currently reach. + +The cross-company incident-response demo is the strongest implemented story: it +has separate trust domains, humans and agents, exact ordered effects, replay and +widening attacks, outcome-unknown recovery, and disclosure-controlled receipts. +Its README also says cloud URLs must be redeployed at the exact revision before +being treated as implementation evidence. I verified service health, not the +complete browser ceremony. + +## Time to first success + +| Language | Clone → first authorization | Clone → first deliberate denial | Blocked at | +|----------|------------------------------|----------------------------------|------------| +| Rust | 1m 58s, checkout-only offline fixture | approximately 14m 46s; required locating a negative fixture and writing a temporary consumer | Public `auths-sdk@0.1.16` lacks the docs site's API; the checkout example has no denial switch | +| TypeScript | Documented registry path: **blocked**. Repository-local RC: 0.81s install + 0.20s allowed/denied run after finding the tarball | Same 0.20s local run | npm resolves to predecessor `0.1.16`; `./integrations` and `./profiles` are not exported | +| Python | Documented registry path: **blocked**. Repository-local RC: 0.96s install + 0.16s corrected allowed/denied run | Same 0.16s corrected local run | PyPI resolves to predecessor `0.1.16`; then the current README's handler arity is stale | + +“Blocked” is not converted into a synthetic time. A newcomer following the +documented package path never reaches either outcome. + +## Verdict + +**A competent engineer should not adopt Auths into a service from the public +developer path today.** They can evaluate the checkout and the intended local RC, +and those bytes demonstrate a strong closed execution model. They cannot install +the product described by the docs from npm, PyPI, or crates.io. The primary site +then presents an invented REST API, while the real recipes are undiscoverable and +one Python recipe is broken. That combination makes a working implementation look +like vaporware. + +The single change most likely to increase adoption is: **make one denial-first MCP +quickstart truthful from registry to provider call.** After the existing release +gates authorize an RC, a newcomer must be able to run the advertised install +command, copy one complete program, observe one allowed tool call, mutate it, and +observe a denial with an unchanged provider-call count in under five minutes. All +three home surfaces—repository README, documentation home, and package +README—must point to that same tested program. Until publication is authorized, +those surfaces must say “not publicly installable” instead of resolving silently +to the predecessor. + +## Summary table + +| ID | Title | Impact | Area | Est. effort | Depends on | +|----|-------|--------|------|-------------|------------| +| AD-001 | Make the advertised package coordinates resolve to the documented SDK | critical | packaging | 3 days after release authorization | AD-003, AD-010, release authorization | +| AD-002 | Replace the fictional REST guide with one executable denial-first MCP guide | critical | docs | 2 days | AD-001 | +| AD-003 | Repair and expose the installed-artifact recipe runner | high | onboarding | 1 day | — | +| AD-004 | Turn the repository README into a five-minute application front door | high | onboarding | 1 day | AD-001, AD-002 | +| AD-005 | Ship drop-in MCP server enforcement middleware | high | integrations | 4 days for TypeScript, 3 days for Python | AD-001 | +| AD-006 | Make the reference deployment start with one command and add a lite evaluator | high | demos | 3 days | — | +| AD-007 | Publish a healthy two-minute allowed/mutated/replay demonstration | high | demos | 4 days | AD-002, AD-005 | +| AD-008 | Give security evaluators one candidate-bound evidence index | high | evidence | 2 days | AD-001 | +| AD-009 | Turn private integration crates into a discoverable adoption catalogue | medium | integrations | 2 days | AD-004 | +| AD-010 | Complete the unfamiliar-engineer recipe cohort before claiming time-to-value | high | evidence | 1 engineering day plus cohort time | AD-003 | + +## Recommendations + +### AD-001 — Make the advertised package coordinates resolve to the documented SDK + +- **Impact:** critical — every first-time Rust, TypeScript, and Python user +- **Area:** packaging +- **Estimated effort:** 3 days after release authorization +- **Depends on:** AD-003, AD-010, and the existing release authorization gates +- **Files:** `auths-proof/release/public-naming.toml:74-98`, + `auths-proof/bindings/typescript/package.json:1-79`, + `auths-proof/bindings/typescript/sdk-capability.json:10-16`, + `auths-proof/bindings/python/pyproject.toml:5-31`, + `auths-proof/bindings/python/sdk-capability.json:23-57`, + `auths-proof/product/sdk/auths-sdk/Cargo.toml:1-35`, + `auths-proof/.github/workflows/release.yml:286-348`, + `auths-proof/.github/workflows/release-builder.yml:137-216`, + `auths-docs/app/lib/sdk-languages.ts:9-13`, and + `auths-docs/app/guides/protect-rest-effect/protect-rest-effect.mdx:15-25` + +**What a user hits today** + +You run the exact install command and get the predecessor at all three +coordinates: + +```text +npm: @auths-dev/sdk@0.1.16 +PyPI: auths==0.1.16 +Cargo: auths-sdk v0.1.16 +``` + +The current repository declares the intended artifacts as `1.0.0-rc.1` / +`1.0.0rc1` and explicitly marks publication as blocked. npm then fails on +`@auths-dev/sdk/integrations`; Python fails on `auths.integrations`; Rust fails on +the site's nonexistent `RestAction` import. + +**Why this costs adoption** + +An engineer adding authority to an MCP server or refund path reasonably assumes a +successful registry install is the product. The package name, organization, and +README agree. Discovering that it is an unrelated earlier product looks like an +abandoned rename or supply-chain mistake. Nothing in the SDK can recover trust +after the first import fails. + +**Required end state** + +There are two honest states, never a mixture: + +1. Before release authorization, public docs say: + + > Auths 1.0 RC is not yet published. The names below currently resolve to the + > predecessor identity SDK. Evaluate the candidate from a release artifact or + > the source checkout; do not use the bare registry commands yet. + +2. After the existing promotion gate authorizes publication, docs use exact RC + pins: + + ```text + cargo add auths-sdk@1.0.0-rc.1 + npm install @auths-dev/sdk@1.0.0-rc.1 + python -m pip install auths==1.0.0rc1 + ``` + +Each coordinate returns the new exact-authority product, and a post-publication +smoke test exercises the installed artifact rather than a workspace path or local +tarball. This recommendation does not weaken or bypass the release, assurance, or +independent-review gates; it makes public claims switch atomically when those +gates permit them. + +**How to implement** + +1. Add `publicationStatus` as an input to the docs build, sourced from the two + `sdk-capability.json` files and the Rust release manifest. Render the pre-release + warning whenever any surface is blocked. +2. In `.github/workflows/release.yml`, keep publication downstream of the current + candidate verification and explicit environment authorization. After each + registry publish, create a new empty consumer and install the exact version + from the public registry with caches disabled. +3. In that consumer, import every npm export from `package.json:9-37`, every Python + public module, and the Rust facade. Run the same allowed/denied MCP fixture and + assert provider calls equal one. +4. Query the registry metadata again and assert package version, repository URL, + description, and README all identify the same candidate. Fail before the docs + deployment if any coordinate still returns `0.1.16`. +5. Only then change `sdk-languages.ts` and the package READMEs from the warning to + the exact pinned commands. Do not advertise an unpinned bare coordinate during + the RC period. + +**How to verify it worked** + +From three empty directories with no workspace links: + +```bash +npm view @auths-dev/sdk@1.0.0-rc.1 version +npm install --ignore-scripts @auths-dev/sdk@1.0.0-rc.1 +node -e 'Promise.all([import("@auths-dev/sdk"),import("@auths-dev/sdk/profiles"),import("@auths-dev/sdk/integrations")]).then(() => console.log("ok"))' + +python -m pip install --no-cache-dir auths==1.0.0rc1 +python -c 'import importlib.metadata, auths, auths.profiles, auths.integrations; print(importlib.metadata.version("auths"))' + +cargo add auths-sdk@1.0.0-rc.1 +cargo check +``` + +The denial-first smoke described in AD-002 must print `completed`, then +`denied: permission-not-granted`, with `provider_calls=1` in each supported +language. Before publication, the docs build must instead contain the warning and +must not contain a bare install command. + +**Blast radius** + +Registry metadata, package lockfiles, release subjects, SPDX/CycloneDX SBOMs, +SLSA provenance subjects, docs.rs links, generated SDK docs, the predecessor +supersession notice, and every quickstart must switch together. A partial publish +is a failed release, not a docs-only incident. + +### AD-002 — Replace the fictional REST guide with one executable denial-first MCP guide + +- **Impact:** critical — application engineers and agent builders entering through the documentation home +- **Area:** docs +- **Estimated effort:** 2 days +- **Depends on:** AD-001 +- **Files:** `auths-docs/app/page.tsx:5-31,64-100`, + `auths-docs/app/guides/protect-rest-effect/protect-rest-effect.mdx:1-77`, + `auths-docs/public/guides/protect-rest-effect.md`, + `auths-docs/app/components/SdkReference.tsx:32-185`, + `auths-docs/public/sdk/sections/*.md`, + `auths-docs/tests/rendered-html.test.mjs:32-70`, + `auths-proof/bindings/recipes/typescript/03-execute-exact-action.ts`, and + `auths-proof/bindings/recipes/python/03_execute_exact_action.py` + +**What a user hits today** + +The home page says “Protect your first effect” and sends you to a “10 MINUTE +GUIDE.” It then offers: + +```rust +let action = RestAction::post("/v1/refunds/rf_82k")? +``` + +The TypeScript and Python tabs similarly use `rest.post`, `actor`, `gateway`, and +object-shaped `auths.create` calls that are not the qualified API in the current +packages. The docs test passes because it checks rendered phrases, not compiled +programs. The page also claims replay returns `replay-detected` and changed bytes +return `commitment-mismatch`; the executable RC path observed +`permission-not-granted` for the undeclared action. + +**Why this costs adoption** + +An engineer evaluating Auths for an existing Node MCP server reaches the primary +CTA, copies code, and fails before seeing a denial. A security product with +nonexistent security examples loses more trust than one with no examples. The +fictional REST abstraction also hides the best current wedge: the repository has +a real, qualified MCP profile using official request types. + +**Required end state** + +Rename the route and page to **Protect one MCP tool call**. The first screen must +state: + +> Allow `publish_report` once. Then ask for an undeclared tool and watch Auths +> deny it before your handler runs. You will finish with one provider call and a +> verifiable receipt. + +The page's final expected output must be: + +```json +{"attempt":"allowed","kind":"completed","providerCalls":1} +{"attempt":"undeclared-tool","kind":"denied","code":"permission-not-granted","providerCalls":1} +``` + +Use recipe 3 as the only source of executable example bytes. Initially show only +language tabs whose **installed public artifact** passes the snippet. Do not show +a Rust tab containing pseudocode; add it when an external-crate recipe with the +same behavior exists. + +For TypeScript, the complete displayed program must use the current API shape: + +```ts +import { development } from "@auths-dev/sdk/integrations"; +import { mcp } from "@auths-dev/sdk/profiles"; + +let providerCalls = 0; +const provider = mcp.developmentProvider({ + tools: { + async publish_report(arguments_) { + providerCalls += 1; + return { published: true, arguments: arguments_ }; + }, + }, +}); +const auths = await development.createAuths({ + authority: mcp.allowTools(["publish_report"]), +}); +try { + const allowed = await auths.execute({ + action: mcp.callTool({ name: "publish_report", arguments: { period: "weekly" } }), + provider, + }); + console.log(JSON.stringify({ attempt: "allowed", kind: allowed.kind, providerCalls })); + + const denied = await auths.execute({ + action: mcp.callTool({ name: "delete_report", arguments: { period: "weekly" } }), + provider, + }); + console.log(JSON.stringify({ + attempt: "undeclared-tool", + kind: denied.kind, + code: denied.kind === "denied" ? denied.code : undefined, + providerCalls, + })); +} finally { + await auths.close(); +} +``` + +The Python version must be a complete `asyncio.run(main())` program and its +handler must accept `(arguments, context)`. + +**How to implement** + +1. Rename the route to `/guides/protect-one-mcp-tool`; leave a permanent redirect + from `/guides/protect-rest-effect` so existing links do not break. +2. Import or generate the code blocks from recipe 3 instead of duplicating them in + MDX and `SdkReference.tsx`. Extend `bindings/recipes/tools/generate-docs.mjs` + to emit a small JSON/Markdown artifact consumed by the docs repository. +3. Replace the REST hero, refund mock receipt, and invented stable codes with the + exact tool name, arguments, provider counter, result kinds, and receipt + projection emitted by the installed recipe. +4. Add a docs fixture job that downloads the promoted tarball/wheel, runs each + displayed file in an empty directory, captures stdout, and compares it with the + rendered expected output. +5. Make the rendered-HTML test assert the MCP commands and add a separate + executable-snippet test. Rendering alone must not satisfy the guide gate. + +**How to verify it worked** + +```bash +# auths-proof: build packages and execute the maintained sources +npm --prefix bindings/typescript run test:examples +AUTHS_RECIPE_PYTHON="$(pwd)/bindings/python/.venv/bin/python" \ + node bindings/recipes/tools/run.mjs + +# auths-docs: render and execute imported snippets +npm test +``` + +In a clean, network-enabled consumer, start the timer before install. Both +supported language paths must reach the two exact JSON lines in under five +minutes, and the second line must still say `providerCalls:1`. A repository-wide +search of the public docs must find no `RestAction::post` or unimplemented +`rest.post` claim. + +**Blast radius** + +Home-page copy and links, route redirects, social cards, SDK navigation, package +READMEs, generated Markdown for “Copy for LLM,” search indexes, recipe docs, and +release notes all need the same MCP terminology and output. + +### AD-003 — Repair and expose the installed-artifact recipe runner + +- **Impact:** high — evaluators who find the strongest runnable examples, and maintainers relying on the recipe gate +- **Area:** onboarding +- **Estimated effort:** 1 day +- **Depends on:** — +- **Files:** `auths-proof/bindings/recipes/python/03_execute_exact_action.py:6`, + `auths-proof/bindings/recipes/tools/run.mjs:10-19,38-57`, + `auths-proof/bindings/recipes/typescript/package.json:1-8`, + `auths-proof/bindings/recipes/tools/generate-docs.mjs:21-29`, + `auths-proof/docs/product/recipes/README.md`, and + `auths-proof/.github/workflows/sdk-recipes.yml` + +**What a user hits today** + +There is no `bindings/recipes/README.md`, no package script, and no command in the +generated recipe page. Guessing the runner with a relative interpreter yields: + +```text +Error: python/01-authenticate-identity: undefined +``` + +Using an absolute interpreter reaches recipe 3 and fails: + +```text +ImportError: cannot import name 'verify_receipt' from 'auths' +``` + +The runner hides `spawnSync().error`, so a missing executable is rendered as the +word `undefined`. + +**Why this costs adoption** + +An evaluator who goes beyond the glossy guide has found the best product proof: +ten installed-artifact programs, adversarial assertions, and cross-language +receipt verification. The lack of a command and the first broken pair tell that +motivated engineer the examples are internal test debris. The same defect also +undermines the `timeToValue: verified` metadata claim. + +**Required end state** + +`bindings/recipes/README.md` must begin with one command for a source checkout: + +```text +npm run recipes +``` + +It must state prerequisites, build/install the local tarball and wheel into clean +temporary consumers, run all 12 checks, and print a table with recipe, language, +outcome, and elapsed milliseconds. A missing interpreter must say, for example: + +```text +python/01-authenticate-identity: could not start /path/to/python (ENOENT) +``` + +**How to implement** + +1. Change recipe 3 line 6 to: + + ```python + from auths.verify import verify_receipt + ``` + +2. Resolve `AUTHS_RECIPE_PYTHON` before any per-recipe `cwd` change. If it is not + absolute, resolve it against the repository root or use `command -v` once. +3. At `run.mjs:50-51`, check `result.error` first and include its `code`, `path`, + and message. Then handle signal and numeric status separately; never interpolate + absent stdout/stderr as `undefined`. +4. Add `build` and `recipes` scripts to the recipe package. The `recipes` script + must compile TypeScript, build/install the candidate artifacts into temporary + environments, and invoke the runner with the interpreter it created. +5. Generate `bindings/recipes/README.md` and `docs/product/recipes/*.md` from the + same manifest. Replace “install the single Auths package” at + `generate-docs.mjs:23` with the literal command and the expected last JSON line. +6. Make `sdk-recipes.yml` run the public `npm run recipes` entry point, so CI and a + newcomer use the same path. + +**How to verify it worked** + +```bash +git clean -ndx bindings/recipes # inspection only; do not delete source +npm --prefix bindings/recipes/typescript ci +npm --prefix bindings/recipes/typescript run recipes +``` + +The command must exit zero, report five TypeScript recipes, five Python recipes, +and two cross-language receipt checks, and include the denied branch for recipes +3 and 4. Re-run with `AUTHS_RECIPE_PYTHON=/does/not/exist`; the error must contain +`ENOENT` and the exact path. Add a CI assertion that +`experience-evidence.json.automated.timings` has the same schema and row count as +the runner output. + +**Blast radius** + +The generated recipe Markdown, timing evidence, customer-journey matrix, SDK +capability scorecards, CI workflow, and both package example tests must be +regenerated after the import and runner contract change. + +### AD-004 — Turn the repository README into a five-minute application front door + +- **Impact:** high — every engineer landing on the source repository +- **Area:** onboarding +- **Estimated effort:** 1 day +- **Depends on:** AD-001, AD-002 +- **Files:** `auths-proof/README.md:3-123`, + `auths-proof/docs/product/recipes/03_EXECUTE_ONE_ACTION.md:1-118`, and + `auths-proof/docs/product/PRODUCTION_SDK_QUICKSTART.md:1-81` + +**What a user hits today** + +After a good two-sentence definition, you receive a package inventory from lines +43-79. The first runnable path is explicitly “Identity without authorization,” +and the authorization example is the last line of a five-command validation +block. There is no package install, expected output, denial, agent use case, or +link to the public docs. A reader has to infer whether they are an SDK user, +protocol implementer, or repository contributor. + +**Why this costs adoption** + +An engineer considering protection for an existing agent tool has perhaps one +minute to decide whether this is an embeddable product or a research kernel. The +README leads with mechanism breadth and presents the actual effect workflow as an +internal repository detail. The engineer who would value the formal work most +never reaches it because the application boundary is missing. + +**Required end state** + +Replace the content before the current sealed-pipeline section with this front +door, substituting the exact promoted version from AD-001: + +> # Auths +> +> Auths lets a person or agent prove it may perform one exact action—then denies +> changed, broader, expired, or replayed actions before provider credentials are +> used. Successful execution leaves a signed, independently verifiable receipt. +> +> **Best first use:** put Auths in front of one MCP `tools/call` handler. Keep +> your existing identity provider, MCP server, and provider credentials. +> +> ## See the boundary in five minutes +> +> Install the SDK for your language, run the complete program below, and observe +> one completed call followed by one denied call. The provider counter remains +> one. [Open the denial-first quickstart](). +> +> Auths 1.0 RC is prelaunch and has not completed independent security review. +> See [release status]() before production evaluation. + +Immediately below it, embed the TypeScript program and expected two-line output +from AD-002. Follow with three links labelled **Use the SDK**, **Understand the +protocol**, and **Contribute to the repository**. Move the package inventory under +an “Architecture and protocol implementation” heading after the quickstart. + +**How to implement** + +1. Preserve the current definition, sealed pipeline, and package list, but move + them below the application quickstart. Do not delete the accurate kernel + boundary. +2. Make the quickstart source a generated include or checked digest of recipe 3; + do not hand-copy a third version. +3. Render package-publication state from `sdk-capability.json`. Before AD-001 is + promoted, replace install commands with the honest pre-release warning and a + source-checkout evaluator command. +4. Add links to the docs guide, exact versioned API reference, security policy, + compatibility/support page, and candidate evidence summary. +5. Add a README check that extracts and runs fenced blocks marked + `auths-executable`, using only packed artifacts. + +**How to verify it worked** + +Give a fresh clone to a runner that has Node but no Rust toolchain. Starting at +the README, the runner must install the promoted tarball from npm and reach: + +```text +completed providerCalls=1 +denied permission-not-granted providerCalls=1 +``` + +in under five minutes without opening another source file. CI must byte-compare +the README code with recipe 3 and fail if either diverges. Before publication, CI +must instead prove the README does not contain a runnable bare registry command. + +**Blast radius** + +Repository description, package READMEs, docs home, social preview, generated +recipe pages, and any “getting started” links must use the same persona, promise, +status warning, and canonical quickstart URL. + +### AD-005 — Ship drop-in MCP server enforcement middleware + +- **Impact:** high — Node and Python teams that already operate an MCP server +- **Area:** integrations +- **Estimated effort:** 4 days for TypeScript, then 3 days for Python +- **Depends on:** AD-001 +- **Files:** `auths-proof/product/profiles/auths-profile-mcp/src/lib.rs:15-90`, + `auths-proof/bindings/typescript/src/profiles/mcp/index.ts:445-560`, + `auths-proof/bindings/typescript/src/integrations.ts:1-239`, + `auths-proof/bindings/typescript/src/integrations/mcp-server.ts` (new), + `auths-proof/bindings/python/python/auths/profiles/_mcp.py:441-528`, + `auths-proof/bindings/python/python/auths/integrations.py:1-672`, + `auths-proof/bindings/python/python/auths/_mcp_server.py` (new), and + `auths-proof/bindings/recipes/typescript/06-protect-existing-mcp-server.ts` (new), and + `auths-proof/bindings/recipes/python/06_protect_existing_mcp_server.py` (new) + +**What a user hits today** + +The Rust profile correctly converts an official `rmcp::CallToolRequestParams`. +The TypeScript and Python SDKs provide a development provider into which a user +registers Auths-owned handlers. There is no maintained adapter for an existing MCP +server, no middleware entry point, and no LangChain, CrewAI, or OpenAI Agents SDK +adapter in the checkout. To adopt Auths, an MCP operator must understand provider +sessions, profile canonicalization, handler contracts, lifecycle reservation, and +receipts before wrapping one request. + +**Why this costs adoption** + +The highest-urgency persona is a platform engineer whose agent already calls MCP +tools with a broad service credential. That engineer is not choosing an +authorization framework from scratch; they need one interception point that keeps +their existing server and handler. Requiring an Auths-specific provider rewrite +makes the safer path harder than leaving the API key in place. + +**Required end state** + +The TypeScript integration must support a shape this small: + +```ts +import { secureMcpToolHandler } from "@auths-dev/sdk/integrations"; + +server.setRequestHandler(CallToolRequestSchema, secureMcpToolHandler({ + auths, + service: "reports", + async handle(request, extra) { + return existingToolRouter(request, extra); + }, + onReceipt(receipt) { + receiptStore.append(receipt); + }, +})); +``` + +Python must offer the same concepts in idiomatic async form. The wrapper must: + +1. accept the official immediate `tools/call` request type; +2. reject MCP task and `_meta` extensions exactly as the V1 profile does; +3. canonicalize service, name, and arguments once; +4. authorize and durably reserve before calling the existing handler; +5. pass the **verified decoded arguments**, not the original mutable object; +6. invoke the handler at most once; +7. persist a receipt or recoverable reference; and +8. map denied/recoverable/indeterminate states to explicit MCP errors without + pretending transport success is authorization. + +It must not acquire or own the downstream provider credential; the existing +handler keeps that application responsibility. + +**How to implement** + +1. Define the public wrapper contract in TypeScript using the official MCP SDK's + request/response types. Put profile meaning behind existing `mcp.callTool` and + Auths execution; the wrapper must not reproduce canonicalization. +2. Add a conformance harness with a fake existing handler and an invocation + counter. Cover allowed, changed arguments, changed name, undeclared tool, + exact replay, concurrent replay, cancellation, handler failure, and + outcome-unknown. +3. Export only the high-level wrapper from `/integrations`; keep reservation and + command handles non-constructible. +4. Add recipe 6 as a complete minimal MCP server with one `publish_report` tool. + The recipe must be executable against the packed artifact, not a source alias. +5. Port the contract to Python after TypeScript behavior is frozen. Keep the + two-argument `(arguments, context)` handler shape and run the same fixture + corpus in both languages. +6. Document how the wrapper composes alongside an existing OAuth session or API + key: those authenticate/access the provider; Auths gates the exact tool call. + +**How to verify it worked** + +```bash +npm --prefix bindings/typescript test -- --test-name-pattern='MCP server middleware' +python -m pytest -q bindings/python/tests -k mcp_server_middleware +node bindings/recipes/tools/run.mjs +``` + +The packed-artifact recipe must start an MCP server, complete exactly one allowed +call, deny a mutated call and a replay, leave the existing handler count at one, +and verify the resulting receipt. A consumer example must contain no imports from +`framework`, `testkit`, internal paths, or Rust crates. + +**Blast radius** + +Public API snapshots, npm/Python type declarations, topology manifests, MCP +profile conformance, error docs, recipe evidence, package READMEs, and the agent +guide all change. Adding a third-party MCP SDK dependency also affects lockfiles, +SBOMs, licenses, and the release subject digest. + +### AD-006 — Make the reference deployment start with one command and add a lite evaluator + +- **Impact:** high — platform engineers evaluating deployability and security reviewers reproducing evidence +- **Area:** demos +- **Estimated effort:** 3 days +- **Depends on:** — +- **Files:** `auths-proof/Cargo.toml:150`, + `auths-proof/product/runtime/auths-node/Cargo.toml:10-23`, + `auths-proof/demos/open-production-reference/Dockerfile:1-8`, + `auths-proof/demos/open-production-reference/README.md:15-36`, + `auths-proof/demos/open-production-reference/config/local.toml:16-28`, + `auths-proof/demos/open-production-reference/compose/compose.yaml:3-113`, + `auths-proof/demos/open-production-reference/compose/compose.lite.yaml` (new), + `auths-proof/demos/open-production-reference/scripts/evaluate-local.sh` (new), and + `auths-proof/.github/workflows/open-production-reference.yml:1-136` + +**What a user hits today** + +The literal Compose command first fails because the required seed is not set. On +a cold machine, the full operator topology is mandatory: three Auths nodes, +PostgreSQL, SoftHSM, OpenTelemetry, Prometheus, Grafana, and nginx. The Dockerfile +starts from Rust 1.91, while the checkout pins 1.97.1, so it downloads a second +toolchain. It then fails to compile `auths-node` because `auths-did-keri` has +`default-features = false` in the workspace and the node does not request `std`. + +Even after that compile fix, source inspection shows a second startup blocker: +`local.toml:28` points at `/run/config/trusted-context.cbor`, but no such file is +checked into the reference directory or mounted by `compose.yaml:11-13`. + +**Why this costs adoption** + +A platform engineer uses the reference deployment to answer “what will I have to +operate?” The current fifteen-minute path spends most of its time downloading +observability infrastructure and then fails before `doctor`. It makes the +intrinsic production concerns—durable lifecycle, custody, TLS, exact gateways— +look inseparable from accidental demo setup work. + +**Required end state** + +The README starts with: + +```text +./demos/open-production-reference/scripts/evaluate-local.sh +``` + +That script generates disposable seed and certificates, generates or copies a +canonical trusted-context fixture, starts **one node + PostgreSQL + ingress**, +runs `doctor`, runs the installed TypeScript and Python allowed/denied/replay +flow, prints elapsed time, and tears down on request. The lite stack is explicitly +an evaluator, not the production topology. + +The current three-node, HSM, and observability stack remains as `--full` for an +operator review. Both modes use the repository-pinned Rust toolchain directly in +the builder image and share the same `auths-node` binary. + +**How to implement** + +1. Change `product/runtime/auths-node/Cargo.toml:13` to: + + ```toml + auths-did-keri = { workspace = true, features = ["std"] } + ``` + + Add a binary compile test that constructs `kernel()` so feature unification in + unrelated workspace packages cannot hide this failure. +2. Update the pinned Docker builder to the exact `rust-toolchain.toml` version and + a reviewed digest. Add a check that rejects drift between those values. +3. Generate the local trusted context through the existing canonical Rust codec; + write it under a gitignored evaluator state directory and mount it read-only at + `/run/config/trusted-context.cbor`. +4. Have `evaluate-local.sh` call the existing certificate generator, generate 32 + random bytes as unpadded base64url, store evaluator environment in a mode-0600 + gitignored file, and validate `docker compose config` before building. +5. Add `compose.lite.yaml` with one node, PostgreSQL, and ingress. Provide a local + telemetry sink or an explicit disabled telemetry setting; do not leave a + hostname for a service absent from the lite topology. +6. Build candidate npm/wheel artifacts or download the candidate-bound versions + before running `tests/installed-sdk-e2e.mjs` and + `tests/test_installed_sdk.py`. Explicitly install/provide + `auths-sandbox-request`, which those tests invoke at lines 24 and 32. +7. Keep the full Compose file and runbooks, but move them after the successful + evaluator path and state their cold-pull size/time variability honestly. + +**How to verify it worked** + +On a CI runner after pruning Auths reference images and caches: + +```bash +/usr/bin/time -p demos/open-production-reference/scripts/evaluate-local.sh --lite --wait +AUTHS_LOCAL_SEED=… docker compose \ + -f demos/open-production-reference/compose/compose.lite.yaml \ + exec auths-1 auths-node /etc/auths/local.toml doctor +node demos/open-production-reference/tests/installed-sdk-e2e.mjs +python -m pytest -q demos/open-production-reference/tests/test_installed_sdk.py +``` + +The first command must reach a healthy node and deliberate replay/widening denial +in under fifteen minutes on the documented minimum machine. The tests must use +installed artifacts only. Then run the same semantic flow with `--full`; all three +nodes and the observability health checks must pass. CI must fail if the trusted +context is missing or the node binary is built without the KERI `std` feature. + +**Blast radius** + +The node dependency graph, Docker digest, SBOM, Compose policy tests, Kubernetes +image reference, local runbook, release evidence, trusted-context fixture, and +reference CI workflow all change. The lite topology must never be promoted as the +production availability or custody recommendation. + +### AD-007 — Publish a healthy two-minute allowed/mutated/replay demonstration + +- **Impact:** high — skeptical developers, security engineers, and technical buyers +- **Area:** demos +- **Estimated effort:** 4 days +- **Depends on:** AD-002, AD-005 +- **Files:** `auths-proof/demos/cross-company-incident-response/control-room/src/app.ts:17-347`, + `auths-proof/demos/cross-company-incident-response/README.md:1-173`, + `auths-proof/demos/cross-company-incident-response/tests/browser-smoke.mjs:1-64`, + `auths-proof/demos/live-lab/web/index.html:1-20`, + `auths-proof/demos/live-lab/web/app.js:52-86,418-494`, + `auths-proof/demos/live-lab/tests/web-smoke.mjs:1-179`, + `auths-proof/docs/target-state/LIVE_DEMO_PLAN.md:13-74`, + `auths-docs/app/page.tsx:64-83`, and + `auths-proof/.github/workflows/ci.yml:1-861` + +**What a user hits today** + +The incident-response demo is the strongest real implementation, but its first +happy path has seven conceptual stages and four services. The smaller Live Lab +claims browser/native execution and returns a healthy UI, while its configured +Fly backend does not resolve. The repository's simple MCP CLI completes only the +positive path and prints provider output as a byte array. None gives a skeptic a +healthy, guided, allowed-then-denied proof in two minutes. + +**Why this costs adoption** + +A staff engineer or buyer needs the product's irreducible “aha” before investing +in the multi-organization architecture. A long ceremony invites debate about +simulated identities and infrastructure. A dead backend makes all live claims +suspect. The shortest convincing fact is much smaller: exact allowed bytes enter +the handler once; changed and replayed bytes do not; the receipt verifies. + +**Required end state** + +Add a **Two-minute tour** mode to the incident control room, backed by real Auths +semantics and the existing live services: + +1. Show one agent request: + `publish_report {"period":"weekly","destination":"board"}`. +2. Click **Authorize & run**. Show handler counter `0 → 1` and a completed receipt. +3. Automatically change `destination` to `public`. Show the stable denial, highlight + the changed field, and keep handler counter at `1`. +4. Replay the original exact request. Show lifecycle replay denial and keep the + counter at `1`. +5. Verify the first receipt independently and finish with three large facts: + **one approved effect, zero unauthorized entries, one verifiable receipt**. + +The default tour hides CBOR, graph internals, deployment topology, and advanced +attacks behind **Inspect how**. The full incident scenario remains available as +**Open the attack lab**. + +If the standalone Live Lab backend is not restored and health-gated, remove it +from public navigation rather than serving a half-live experience. + +**How to implement** + +1. Reuse the MCP middleware and fixture from AD-005. Do not add a demo-only verdict + switch or hand-authored denial JSON. +2. Add a bounded guided-state machine to the control room. Each transition waits + for the real API response and reads provider-entry count from server evidence. +3. Add release ID and source commit to the UI. The backend must reject a frontend + built for different semantics. +4. Add browser tests that inspect network responses, provider count, stable codes, + and receipt verification—not only visible text. +5. Add a scheduled health check for UI, all API `/healthz` endpoints, one complete + tour, and release-ID agreement. On failure, mark the demo unavailable in docs + or page the owner; do not leave the CTA green. +6. Make the docs home primary CTA **Watch one changed action fail** and deep-link + directly to the guided mode. + +**How to verify it worked** + +```bash +node demos/cross-company-incident-response/tests/browser-smoke.mjs --mode two-minute +python demos/cross-company-incident-response/tests/integration.py +``` + +On the deployed candidate, start a stopwatch at first paint. An unauthenticated +visitor must complete the tour within 120 seconds. The browser test must observe +one provider entry, a denial for changed bytes, a denial for replay, and a verified +receipt. All four service health checks and the exact revision check must pass +before the docs CTA is published. + +**Blast radius** + +Demo API schema, frontend state, receipt presentation, deployment health checks, +docs and site CTAs, telemetry, test fixtures, release metadata, and the Live Demo +plan need updating. Keep the full attack lab's stronger claims and caveats intact. + +### AD-008 — Give security evaluators one candidate-bound evidence index + +- **Impact:** high — security reviewers, procurement teams, and platform owners deciding whether to run a pilot +- **Area:** evidence +- **Estimated effort:** 2 days +- **Depends on:** AD-001 +- **Files:** `auths-proof/SECURITY.md:1-51`, + `auths-proof/docs/threat-model.md:1-84`, + `auths-proof/release/SLSA_BUILD_LEVEL_3_ASSESSMENT.md:1-87`, + `auths-proof/release/assurance/open-production-candidate-1/summary.md:1-39`, + `auths-proof/release/assurance/open-production-candidate-1/manifest.json:1-43`, + `auths-proof/docs/product/COMPATIBILITY_AND_SUPPORT.md:1-31`, + `auths-docs/content/assurance-status.json` (new generated file), + `auths-docs/app/security/page.tsx` (new), and + `auths-docs/tests/rendered-html.test.mjs:1-71` + +**What a user hits today** + +The repository contains unusually strong evidence machinery: dual licenses, a +security policy, a detailed threat model, conformance and formal artifacts, +release manifests, SPDX/CycloneDX generation, signed hosted-build provenance, +runbooks, and an executable assurance gate. It is scattered across source, +`release/`, `target/`, and an in-progress candidate directory. + +The most decision-relevant status exists only deep in the tree: + +```text +Immutable candidate binding Pending +Sustained qualification 0 of 2,592,000 seconds +Required evidence families 0 of 7 +Independent security review Pending +Signed statement Absent +Production release eligible No +``` + +Meanwhile, the SLSA assessment says it passes Build Level 3 for one exact reusable +builder and observed execution, but explicitly is not an independent security +review or production-readiness claim. + +**Why this costs adoption** + +A security reviewer cannot reward evidence they cannot locate or bind to the +package under evaluation. If a marketing or docs page says only “machine-checked” +while the candidate record says “0 of 7,” the reviewer has to reconcile the claim +manually. The likely result is a long questionnaire or a rejected pilot despite +the repository having better raw material than many projects. + +**Required end state** + +Publish `/security` as an automatically generated evidence index for one exact +candidate. The first screen contains: + +- candidate version, source commit, package/image digests, and update time; +- **Evaluation only / Production eligible** derived from the signed assurance + manifest, never authored by the docs site; +- independent review status and link to scope/report when present; +- qualification duration and evidence-family count; +- links and digests for threat model, SBOM, SLSA provenance, conformance/formal + manifest, release notes, compatibility policy, known limitations, and security + reporting; and +- a plain statement of what is not covered. + +When the manifest is incomplete, the page must lead with: + +> This candidate is for evaluation only. It has no completed independent security +> review, sustained qualification, or signed production statement. Do not use it +> as the sole authorization control for high-value production actions. + +**How to implement** + +1. Add a generator in the product release process that reads only the verified + assurance manifest and release manifest, resolves each digest-bound artifact, + and emits a redacted `auths.assurance-public-index/1` JSON file. +2. Copy that generated file into `auths-docs/content/assurance-status.json` during + the candidate docs build. Record the source digest; fail on hand-edited status + fields or an unbound package set. +3. Render the page from the JSON. Do not infer “pass” from file existence and do + not collapse pending, absent, failed, and out-of-scope into one badge. +4. Add downloadable links to retained evidence. If an artifact exists only in a + short-lived CI run, mark it unavailable rather than linking to a plan or fixture. +5. Add a security contact/response-expectation section. If there is no response + SLA, say so; do not invent one. +6. Link `/security` from the docs header, package READMEs, release page, and the + reference-deployment README. + +**How to verify it worked** + +```bash +cargo xtask assurance summarize release/assurance/open-production-candidate-1/manifest.json +npm --prefix ../auths-docs test +``` + +For the current manifest, the rendered page must contain “Evaluation only,” +`0 of 2,592,000 seconds`, `0 of 7`, “Independent security review: Pending,” and +“Production release eligible: No.” A fixture that changes the candidate digest +without regenerating evidence must fail the docs build. A future eligible fixture +must render “Production eligible” only after the existing assurance verifier also +passes. + +**Blast radius** + +Release retention, artifact hosting, docs deployment, security policy, package +metadata, candidate notes, and any marketing assurance claims are affected. The +public index is a projection of signed evidence, never a new source of truth. + +### AD-009 — Turn private integration crates into a discoverable adoption catalogue + +- **Impact:** medium — Rust platform teams evaluating GitHub, Kubernetes, OpenTofu, PostgreSQL, Stripe, Radicle, did:web, KMS, or PKCS#11 fit +- **Area:** integrations +- **Estimated effort:** 2 days +- **Depends on:** AD-004 +- **Files:** `auths-proof/product/integrations/auths-custody-aws-kms/Cargo.toml:1-22`, + `auths-proof/product/integrations/auths-custody-pkcs11/Cargo.toml:1-22`, + `auths-proof/product/integrations/auths-custody/Cargo.toml:1-19`, + `auths-proof/product/integrations/auths-enforcement/Cargo.toml:1-16`, + `auths-proof/product/integrations/auths-evidence-assemblers/Cargo.toml:1-21`, + `auths-proof/product/integrations/auths-github/Cargo.toml:1-35`, + `auths-proof/product/integrations/auths-kubernetes/Cargo.toml:1-31`, + `auths-proof/product/integrations/auths-opentofu/Cargo.toml:1-31`, + `auths-proof/product/integrations/auths-postgresql/Cargo.toml:1-34`, + `auths-proof/product/integrations/auths-radicle/Cargo.toml:1-40`, + `auths-proof/product/integrations/auths-records-api/Cargo.toml:1-34`, + `auths-proof/product/integrations/auths-resolver-did-web/Cargo.toml:1-19`, + `auths-proof/product/integrations/auths-stripe/Cargo.toml:1-40`, + `auths-proof/product/integrations/auths-custody-aws-kms/README.md` (new), + `auths-proof/product/integrations/auths-custody-pkcs11/README.md` (new), + `auths-proof/product/integrations/auths-custody/README.md` (new), + `auths-proof/product/integrations/auths-enforcement/README.md` (new), + `auths-proof/product/integrations/auths-evidence-assemblers/README.md` (new), + `auths-proof/product/integrations/auths-github/README.md` (new), + `auths-proof/product/integrations/auths-kubernetes/README.md` (new), + `auths-proof/product/integrations/auths-opentofu/README.md` (new), + `auths-proof/product/integrations/auths-postgresql/README.md` (new), + `auths-proof/product/integrations/auths-radicle/README.md` (new), + `auths-proof/product/integrations/auths-records-api/README.md` (new), + `auths-proof/product/integrations/auths-resolver-did-web/README.md` (new), + `auths-proof/product/integrations/auths-stripe/README.md` (new), + `auths-proof/product/integrations/README.md` (new), + `auths-proof/docs/product/integrations/catalog.json` (new generated file), + `auths-docs/app/integrations/page.tsx` (new), and + `auths-docs/tests/rendered-html.test.mjs:1-71` + +**What a user hits today** + +`product/integrations/` contains 13 substantive crates, but none has a README. +Their only discovery metadata is a one-line Cargo description. Most are +`publish = false`, and none appears as a copyable integration on the docs site. +The names also mix different layers: effect gateways (Stripe, PostgreSQL), +resource adapters (GitHub, Kubernetes, OpenTofu, Radicle), custody (AWS KMS, +PKCS#11), evidence assembly, enforcement, and did:web resolution. + +**Why this costs adoption** + +A platform engineer evaluating a PostgreSQL update or Stripe operation cannot +tell whether the crate is a supported public dependency, a reference adapter, a +demo-only gateway, or internal runtime code. They therefore assume adopting Auths +requires replacing their provider client or operating a separate control plane. +The repository misses its strongest positioning fact: these gateways can sit next +to existing OAuth, TLS, API-key, database, and cloud controls and narrow one +effect. + +**Required end state** + +Publish a generated catalogue with one row per integration: + +| Field | Required meaning | +| --- | --- | +| Status | `qualified`, `reference`, `experimental`, or `internal`; machine-sourced | +| Layer | profile/gateway, custody, evidence, resolver, enforcement, or demo | +| Existing system retained | e.g. Stripe credential and client remain application-owned | +| Exact effect | the operation/body/resource committed by Auths | +| Failure boundary | when provider credentials may be acquired and handler entered | +| Runtime needs | state store, resolver, KMS/HSM, network, TLS | +| Public package | exact package/import, or “not publicly packaged” | +| Runnable proof | one command and expected allowed/denied output | +| Evidence | conformance suite, fixture, limitations, supported versions | + +Every directory README must answer “Can I use this outside this monorepo today?” +in its first paragraph. A `publish = false` crate must not be labelled installable. + +**How to implement** + +1. Add explicit integration metadata beside each `Cargo.toml`; do not derive + qualification merely from the directory name or compilation success. +2. Generate `catalog.json` and the repository README. Validate every listed demo, + profile ID, package coordinate, and evidence path. +3. Author one short README per crate with architecture boundary, minimal composition + code, credential ownership, failure semantics, production requirements, and a + link to a runnable demo. Generate repeated status fields from the catalogue. +4. Render `/integrations` with filters by existing system and maturity. Lead with + MCP, then the currently qualified/reference effect verticals; put custody and + evidence helpers in separate sections. +5. Add an “adopt alongside” diagram/text for each gateway. For example: OAuth or + workload identity authenticates the process to GitHub; Auths determines whether + this exact issue action may be released to that process-owned client. +6. Add a catalogue validation command to `cargo xtask product`. + +**How to verify it worked** + +```bash +cargo xtask product +test "$(find product/integrations -mindepth 2 -maxdepth 2 -name README.md | wc -l | tr -d ' ')" = "13" +npm --prefix ../auths-docs test +``` + +The generated catalogue must have exactly one entry per integration crate, reject +an install command for every `publish = false` entry, and resolve every runnable +proof. A first-time reader must be able to identify the MCP path, one existing +provider path, and whether each is public without opening Rust source. + +**Blast radius** + +Crate metadata, docs navigation/search, release qualification labels, demo links, +support policy, SBOM package descriptions, and future publication decisions must +stay consistent with the catalogue. + +### AD-010 — Complete the unfamiliar-engineer recipe cohort before claiming time-to-value + +- **Impact:** high — release decision-makers and every newcomer represented by current time-to-value claims +- **Area:** evidence +- **Estimated effort:** 1 engineering day plus time to recruit and run five sessions +- **Depends on:** AD-003 +- **Files:** `auths-proof/bindings/recipes/experience-evidence.json:1-28`, + `auths-proof/bindings/customer-journey-matrix-v1.json:1-513`, + `auths-proof/bindings/typescript/sdk-capability.json:41-50`, + `auths-proof/docs/product/COMPATIBILITY_AND_SUPPORT.md:3-8`, and + `auths-proof/.github/workflows/sdk-recipes.yml:1-91` + +**What a user hits today** + +The SDK capability metadata calls time-to-value “verified,” and the automated +evidence records sub-second recipe execution. The human gate says: + +```json +{ + "status": "awaiting-independent-cohort", + "requiredCompletions": 4, + "requiredCohort": 5, + "maximumMinutes": 15, + "participants": [] +} +``` + +The compatibility page correctly lists `moderated-recipe-three-cohort` as a +stable-publication blocker. Automated runtime is not newcomer setup time; this +evaluation reproduced the difference. + +**Why this costs adoption** + +Release owners otherwise optimize for a green internal runner while unfamiliar +engineers spend time finding artifacts, interpreting `undefined`, or reading +source. The missing cohort is not cosmetic research: it is the only current gate +that measures the adoption question directly. + +**Required end state** + +Run recipe 3 with five people who have not worked on Auths and do not have an +Auths-prepared machine. Give each only the same public README/URL. Record, without +fabricating or smoothing: + +- runtime, OS, language choice, and prior relevant experience category; +- wall time from first page to allowed outcome and deliberate denial; +- every command and verbatim failure; +- every source file or secondary page opened; +- whether the provider-call count stayed one; and +- completion/abandonment plus the first blocking step. + +The gate passes only if at least four of five independently reach both outcomes in +15 minutes with no maintainer intervention. Publish anonymized aggregate timings +and failure categories; retain raw session evidence privately if it contains +identifying machine paths. + +**How to implement** + +1. Fix AD-003 and freeze one candidate tarball/wheel plus digest. Do not change the + guide during the five measured sessions. +2. Write a facilitator script containing only start/stop criteria and allowed + safety interventions. It must not teach Auths concepts or provide hidden setup. +3. Add an append-only cohort record schema with participant IDs, candidate digest, + timestamps, outcomes, intervention count, and redacted transcript artifact + digests. +4. Update the generator so `timeToValue` distinguishes + `automated-execution-verified` from `unfamiliar-developer-verified`. +5. If the gate fails, rank the observed blockers, fix the highest-frequency first, + cut a new candidate digest, and run a new cohort. Do not merge cohorts across + changed instructions or artifacts. + +**How to verify it worked** + +```bash +cargo xtask sdk-experience +cargo xtask evolution-policy +``` + +The commands must reject empty participants, duplicate participants, a mismatched +candidate digest, missing denial, intervention-dependent completion, or fewer than +four sub-15-minute completions. The public evidence must report median/range only +from the completed, digest-matched cohort and retain the failure count. No adoption +number beyond this five-person test may be inferred. + +**Blast radius** + +Stable-publication policy, SDK capability scorecards, documentation claims, +release notes, customer-journey metadata, and any “five/ten/fifteen minute” copy +must derive from the same cohort status. + +## Recommended execution order + +1. **AD-003 — repair the recipe gate first.** It is a one-day fix to the canonical + executable sources and makes all later quickstart, middleware, and cohort work + measurable. Do not build a new guide on a failing runner. +2. **AD-006 — restore the reference binary and lite stack.** The one-line feature + fix, missing trusted-context mount, and one-command setup are independent of + public package authorization. They re-establish a deployable integration + target for the SDK smoke tests. +3. **AD-005 — add the existing-server MCP wrapper.** This converts the real MCP + semantics into the shortest useful integration and fixes the product boundary + before documentation freezes it. +4. **AD-002 — generate the denial-first guide from the wrapper recipe.** Build it + against candidate artifacts and keep it unpublished or clearly labelled until + registry coordinates are truthful. +5. **AD-004 and AD-009 — change discovery after the canonical path is real.** Point + the README and integration catalogue to one tested guide; do not create more + competing entry points. +6. **AD-010 — run the unfamiliar-engineer cohort on the frozen candidate.** Feed + only observed blockers back into steps 3-5. The cohort is part of release + evidence, not a post-launch survey. +7. **AD-001 — publish atomically after all existing authorization gates pass.** + Immediately run public-registry smokes, then switch docs from the pre-release + warning to exact pinned install commands. +8. **AD-007 — promote the two-minute live story.** It depends on the same public + middleware and guide, and it must be health-gated before receiving the primary + CTA. +9. **AD-008 — publish the candidate-bound evidence index with the release.** The + page can be built earlier, but its public status and artifact links must bind + the actually published package set. + +## The agent wedge + +Auths is well aimed at the agent-authority problem semantically and poorly exposed +to agent builders operationally. + +The strong part already exists. `auths.mcp/1` commits the exact service, tool name, +arguments, audience, and optional channel binding. It maps the official Rust MCP +request, rejects unsupported task/metadata extensions, releases a non-constructible +verified command, couples execution to one-use lifecycle state, and produces +receipts. The local TypeScript and Python RCs can run one allowed handler and deny +an undeclared tool before another handler call. This is materially more specific +than giving an agent a broad service token. + +The missing part is the insertion point. Existing MCP operators have a server and +handlers; Auths currently asks them to construct an Auths provider. There is no +drop-in server middleware and no maintained integration with a named agent +framework. The public packages do not contain the RC at all. Consequently, the +path from “my agent uses MCP” to “this tool is protected” requires source reading +and an application rewrite. + +The shortest concrete path is **TypeScript MCP server middleware**, not a bespoke +LangChain/CrewAI/OpenAI abstraction: + +```text +existing agent + → official MCP tools/call request + → Auths secureMcpToolHandler + → canonical auths.mcp/1 action + → verify + atomic reserve + → existing handler, using verified arguments + → receipt / recovery reference + → existing provider client and credential +``` + +MCP is the common low-level boundary that multiple agent frameworks can call. One +official-SDK wrapper lets those frameworks adopt Auths without Auths owning their +planning model, memory, authentication, or provider client. After the middleware +is stable, framework packages should be tiny recipes that configure it, not new +semantic implementations. + +The credible path to framework-default authority is: + +1. a public, versioned SDK with the middleware and a five-minute denial demo; +2. conformance proving one handler entry under replay/concurrency/mutation; +3. a no-Auths-server development composition and an explicit production port; +4. one framework-maintainer-quality example showing OAuth/API keys remain behind + the handler; and +5. a stable error/receipt projection suitable for agent traces. + +What stands in the way today is distribution, missing middleware, no unfamiliar +developer evidence, and incomplete production qualification—not MCP semantics. + +## The two-minute demo + +The most persuasive existing demonstration is the **cross-company incident +response** control room. It proves a problem OAuth scopes handle awkwardly: +separate organizations authorize an exact ordered plan without sharing an identity +provider or broad infrastructure credential; widened, replayed, compromised, and +ambiguous paths remain visible. On the assessment date, its UI and all three +service health endpoints responded successfully. + +It is not the best first two minutes. Its seven-stage happy path, approval +ceremony, two transports, disclosure tiers, and attack lab are evidence after the +viewer understands the primitive. The smaller Live Lab would be closer, but its +backend DNS was absent while the UI remained public. + +The single most persuasive two-minute sequence is therefore: + +```text +Agent asks: publish_report weekly → board +Auths: authorized → handler count becomes 1 → signed receipt + +Agent changes one field: destination = public +Auths: denied before handler → changed field highlighted → count remains 1 + +Agent replays the approved request +Auths: replay denied → count remains 1 + +Independent verifier: first receipt valid +``` + +Auths can show all underlying behaviors today in separate recipes and demos, but +cannot show this cohesive sequence through a healthy public two-minute path. AD-007 +specifies the build. The success criterion is not visual polish: it is one real +provider entry, two real denials, and a real receipt, all bound to the displayed +release. + +## Essential vs self-inflicted friction + +| Friction | Essential or self-inflicted | Why | +| --- | --- | --- | +| Defining a canonical action profile | Essential | Exact authority needs a deterministic statement of the bytes/fields being authorized. A generic bearer token avoids this work by providing a weaker guarantee. | +| Selecting trusted identity evidence, anchors, time, audience, and verifier configuration | Essential | A proof cannot decide whom or what the application trusts. These are deployment decisions, though safe profiles can package defaults. | +| Durable replay/use reservation | Essential for one-use or budgeted effects | Stateless verification cannot by itself prove that an effect has not already consumed authority. | +| Closed gateway and outcome-unknown recovery | Essential for side effects | Authorization must cover the bytes actually executed, and an ambiguous provider response must not invite blind retry. | +| Production key custody and independent review | Essential for high-value deployment | Software fixture keys and author-controlled review cannot establish production assurance. | +| npm, PyPI, and crates.io serving predecessor `0.1.16` under advertised names | Self-inflicted | The intended RC works locally; the public coordinate and docs are out of sync. | +| A primary guide built around nonexistent REST APIs | Self-inflicted | The repository already has a qualified MCP profile and runnable recipe. | +| Positive-only Rust and MCP demos | Self-inflicted | Negative fixtures and denial semantics already exist; there is no user-facing switch. | +| Python handler arity and recipe import drift | Self-inflicted | Both failures are one-line documentation/source mismatches, not protocol complexity. | +| No recipe command and `undefined` runner error | Self-inflicted | The runner exists; discovery and process-error handling are missing. | +| Full observability/HSM topology on the first reference path | Self-inflicted | Those are legitimate operator concerns, but not prerequisites for proving one local exact effect. A lite evaluator can preserve semantics. | +| Missing KERI feature and trusted-context mount in the reference stack | Self-inflicted | These are build/composition defects. | +| No existing-MCP-server wrapper | Self-inflicted | Official MCP request mapping already exists; the high-level interception adapter does not. | +| Public Live Lab UI pointing at an unresolvable backend | Self-inflicted | Health gating can hide or repair a partially unavailable demo. | + +### Where Auths is easier—and where it should not pretend to be + +Auths is genuinely easier than composing OAuth scopes, short-lived tokens, +application idempotency, delegation attenuation, cross-organization approvals, +and audit receipts separately when the effect is destructive, agent-initiated, +cross-trust-domain, or expensive. The proof travels with the action, can be checked +offline, and expresses “this exact thing” rather than “this client has a broad +scope.” It can sit alongside an existing IdP and provider credential. + +Auths is genuinely harder for an ordinary read endpoint inside one trust domain. +The application must define canonical action meaning, arrange issuance/trust, +reserve use, and operate a closed gateway. That cost is justified only when +widening, replay, confused-deputy behavior, or independent evidence matters. The +positioning should say this explicitly. “Use Auths for every authorization” is a +weaker claim than “use Auths at the few effect boundaries where an agent must not +inherit a broad credential.” + +## What a buyer will ask for + +Status here is about the checked-out repository and candidate record, not an +external certification claim. + +| Artifact or answer | Status | Verified location / gap | What to do before a serious production evaluation | +| --- | --- | --- | --- | +| License and redistribution terms | **Exists** | `LICENSE-MIT`, `LICENSE-APACHE`, package manifests use `MIT OR Apache-2.0` | Include both licenses and notices in every released package/image and evidence index. | +| Vulnerability reporting | **Exists, limited** | `SECURITY.md` directs private GitHub Security Advisories and lists scope/limitations; it gives no response SLA | Publish supported-version policy and response expectations when support exists. | +| Threat model and trust boundary | **Exists** | `docs/threat-model.md`, product/reference threat models, explicit host responsibilities in `SECURITY.md` | Bind the reviewed threat-model digest to the candidate and link it from packages/docs. | +| Known limitations | **Exists** | `release/assurance/open-production-candidate-1/limitations.md` and reference runbook | Surface them on the public candidate page; keep evaluator sandbox limits distinct from production claims. | +| Deterministic conformance/formal evidence | **Exists in repository** | Canonical fixtures, Lean/Kani/Aeneas-related manifests and release gates are present | Provide retained, candidate-bound reports and independent-review interpretation, not only source generators. | +| SBOM | **Generator and schema exist; current production candidate not bound** | `xtask/src/release.rs` generates SPDX 2.3 and CycloneDX; release manifest requires SPDX. Candidate summary says no package set/evidence family is bound | Publish downloadable candidate SBOMs and verify subject coverage against every crate, wheel, tarball, WASM, and image. | +| Build provenance | **Exists for an exact preparation workflow; scope is limited** | `release/SLSA_BUILD_LEVEL_3_ASSESSMENT.md` records two GitHub attestations and a self-assessed SLSA 1.2 Build L3 result | Retain and expose attestations for the actually published candidate; keep “not a security audit” adjacent. | +| Reproducible/independent build | **Partly evidenced** | Two hosted preparations matched according to the SLSA assessment; both are within the project-controlled workflow/platform boundary | Give evaluators the offline verification command and retained bundles; distinguish independent reproduction from independent security review. | +| Immutable candidate binding | **Claimed process, currently missing result** | Candidate manifest status is `pending`; no image/package set bound | Bind exact package/image/config/schema/source/semantic digests before pilot approval. | +| Independent security review | **Explicitly missing** | Candidate status `pending`; `SECURITY.md` says none completed | Commission review over the scope already enumerated at manifest lines 25-32, publish scope, report or attestation, findings status, and candidate digest. | +| Sustained qualification | **Explicitly missing** | `0 of 2,592,000 seconds` | Run and retain the candidate-bound 30-day program across the supported runtime/profile matrix. | +| Required evidence families | **Explicitly missing** | `0 of 7`, empty `testEvidence` | Populate only through the executable assurance recorder and retained artifacts. | +| Signed production statement | **Absent** | Candidate `statement` is null | Sign only after the existing verifier passes all required gates. | +| Package/runtime support matrix | **Declared and CI-configured; not independently verified here** | Python workflow spans Linux/macOS/Windows and CPython 3.9-3.14; local checkout held only a macOS arm64 wheel | Retain per-platform installed-wheel results for the candidate and link them from `/security`. | +| Compatibility and retirement | **Policy exists; stable publication blocked** | `docs/product/COMPATIBILITY_AND_SUPPORT.md` records 12-month/90-day targets and blockers | Attach the policy to the released major and name who provides maintenance. | +| Deployment/runbooks | **Substantial but reference path currently broken** | TLS, rotation, restore, privacy, rolling restart, and provider-unknown runbooks exist; first Compose build failed | Complete AD-006 and give an immutable known-good deployment digest. | +| Operational telemetry/privacy | **Design and runbooks exist** | Reference includes OTLP/Prometheus/Grafana and a privacy-audit runbook | Provide example redacted events, retention assumptions, and tested no-secret/no-payload assertions for the candidate. | +| Support owner, SLA, incident communications | **Absent for the open candidate** | Business plans discuss future support; no enforceable public SLA or named support channel was found | State community-only support honestly, or publish contractual terms/contact/escalation for paid pilots. | +| Compliance certifications, pen-test letter, SOC 2/ISO evidence | **Absent; not claimed** | No such completed artifact was found | Do not imply certification. Supply only if a target buyer requires it and after independent completion. | +| Data processing/subprocessor answer | **Not packaged as a buyer artifact** | The core is offline and the reference is self-operated; public demos use hosted services | Publish a deployment/data-flow statement separating offline SDK, operator runtime, docs telemetry, and hosted-demo data. | + +The evidence posture is better than the adoption surface suggests: the project is +careful not to overclaim. The immediate buyer task is not to manufacture badges; +it is to bind, retain, index, and expose what exists while keeping the missing +independent review and qualification impossible to miss. + +## Coverage statement + +### Executed + +- Read the repository instructions, root README, language READMEs, package + manifests/capability files, public naming policy, recipe sources/runner/evidence, + primary docs-site pages and tests, MCP profile/demo source, integration manifests, + reference deployment/config/tests/runbooks, live-demo plans, security policy, + threat/evidence summaries, release workflows, and compatibility policy. +- Timed the checkout Rust offline positive example and a temporary external Rust + negative-fixture consumer. +- In clean temporary environments, installed the public npm, PyPI, and crates.io + coordinates after registry access was allowed; recorded the actual `0.1.16` + artifacts and reproduced their import/compile failures against current docs. +- Installed the local `1.0.0-rc.1` npm tarball and macOS arm64 Python wheel; ran + allowed and deliberate denied MCP calls with provider counters. +- Ran TypeScript recipe 3 and the cross-language recipe runner far enough to + reproduce both its relative-interpreter diagnostic and Python recipe 3 import + failure. +- Built and tested the docs site: all three rendered-page tests passed. +- Built and ran the Rust MCP memory demo successfully. +- Attempted the README's identity workbench command; stopped after repeated local + Cargo build-lock contention before the server started. This was local concurrent + build interference, not classified as a product failure. +- Started Docker Desktop, followed the open-production reference path with a + disposable seed, pulled/built the stack, and captured the `auths-node` compile + failure. Inspected the unmounted trusted-context path that would block the next + stage. Confirmed the failed Compose project left no containers. +- Checked public-demo reachability on 15 August 2026: incident UI and three service + health endpoints returned 200; Live Lab UI returned 200 and its configured + backend failed DNS resolution. + +### Not executed or not established + +- I did not run all roughly 25 demo directories or `cargo xtask demos`. The MCP + demo ran; the reference deployment failed; incident-demo service health was + checked, but its complete OIDC/PKCE browser ceremony and attack lab were not + replayed locally. +- I did not complete the identity/Iroh browser workbench, the full Rust validation + suite, fuzzing, formal proofs, release check, long benchmarks, or 30-day + qualification. Existing evidence was inspected, not re-derived. +- I tested one platform: macOS arm64 with Node 22.23.1 and CPython 3.10.7. I did + not execute Linux, Windows, browser-engine, Node 20, or CPython 3.9/3.11-3.14 + package matrices. I inspected the npm runtime/declaration export pairs but did + not run every public type through an external-project compiler. CI configuration + is not reported as a local pass. +- I did not query GitHub check history or independently verify the hosted SLSA + attestations. The assessment's exact scope and caveats are reported from the + checked-in evidence. +- I did not commission or simulate user research. The repository's five-person + cohort remains empty; this document's timings are one evaluator, not adoption + statistics. +- The product working tree was already dirty, including fixture, Stripe, lifecycle, + and xtask changes, and the docs tree had an untracked TypeScript build-info file. + I did not clean, revert, or commit them. Runtime findings apply to the checked-out + bytes, not a claimed clean release candidate. +- Restricted-network `ENOTFOUND`/DNS errors from initial registry attempts were + retried with network access and are labelled environment limitations. The public + Live Lab backend DNS failure reproduced with unrestricted access and is reported + as deployment state. + +No shipping code was changed to obtain these results. Temporary consumer programs +and generated recipe JavaScript were used only for evaluation and were not +committed. diff --git a/docs/target-state/v1-launch-readiness-independent.md b/docs/target-state/v1-launch-readiness-independent.md new file mode 100644 index 00000000..19fda72d --- /dev/null +++ b/docs/target-state/v1-launch-readiness-independent.md @@ -0,0 +1,667 @@ +# v1.0 Launch Readiness — Independent Assessment + +## Verdict + +The codebase is not ready to be represented as a credible v1.0 release. The principal blocker is not polish: the stock `auths-node` executable used by the production Kubernetes manifests rejects every production configuration, so the shipped reference deployment cannot start. Effectful HTTP requests can also return a timeout while their blocking operation continues detached, with no client-known recovery handle and no admission bound. The intended absent-budget semantic change is correct in the kernel but is not an atomic cutover: clean `HEAD` retains the old result in supported TypeScript/Python MCP and formal paths, and the compliance run fails 16 tests; concurrent uncommitted Lean work does not yet close that cross-language failure. The authoritative and release gates also stop on stale error-lifecycle metadata, before reaching the two intentionally red gates. Adversarial conformance exists but is not invoked by the aggregate compliance or release path, so a deliberately corrupted expected result passes `cargo xtask conformance`. Finally, the advertised OTLP configuration is parsed but does not cause the node to export OTLP or report telemetry readiness. Four blockers and two major findings must close before v1.0; no minor findings are being used to dilute that conclusion. + +## Summary table + +| ID | Title | Severity | Area | Est. effort | Depends on | +|----|-------|----------|------|-------------|------------| +| LR-001 | Ship a production assembly in the stock `auths-node` image | blocker | production runtime | 5 days | LR-002 | +| LR-002 | Make deadlines effect-safe and bound blocking admission | blocker | runtime/effect recovery | 6–8 days | — | +| LR-003 | Complete the absent-budget cutover atomically across SDKs and Lean | blocker | semantics/bindings/formal | 3 days | — | +| LR-004 | Synchronize active error lifecycle metadata with the registry | blocker | release metadata | 0.5 day | — | +| LR-005 | Put adversarial conformance on the aggregate release path | major | compliance | 1 day | — | +| LR-006 | Make configured OTLP export real and observable | major | operations | 4–6 days | LR-001 | + +## Baseline + +The command baseline was taken from `dev-cleanup` at `ac5b968` (`core: make terminal budget coverage the kernel's own answer (UNSIGNED)`) after reading `AGENTS.md`, the ratified API contract, commits `bbeb654..HEAD`, and both prior API audits. While this document was being written, `HEAD` advanced to `f82cb08` (`formal: run the 31 ungated Kani harnesses and fix what they proved (UNSIGNED)`). Its complete commit message and diff were reviewed: it closes Kani gate/harness weaknesses in lifecycle, bounded policy, and Stripe but does not close any of LR-001 through LR-006; the intentionally red formal command should nevertheless be rebuilt before its digest is used. Concurrent uncommitted formal-vector/Lean edits are visible in the shared working tree but are not treated as completed or verified work in this assessment. The separately supplied launch-readiness investigation list was deliberately not read. No shipping source was changed by this assessment; two temporary negative controls were restored/deleted after execution. + +The aggregate gates currently stop on an unexpected lifecycle-registry mismatch: + +```text +$ ./target/debug/xtask ci authoritative +architecture policy and dependency snapshots passed +binding semantics passed: 103 files, 14 patterns, 13 declared allowances (0 temporary) +xtask: active error lifecycle metadata does not exactly cover the Rust registry +exit=1 + +$ ./target/debug/xtask release-check +architecture policy and dependency snapshots passed +binding semantics passed: 103 files, 14 patterns, 13 declared allowances (0 temporary) +xtask: active error lifecycle metadata does not exactly cover the Rust registry +exit=1 +``` + +The two red gates explicitly anticipated by the launch brief are still red and were not treated as newly discovered defects: + +```text +$ ./target/debug/xtask semantic-freeze +xtask: semantic freeze drifted; assign new semantic identities or versions, then run `cargo xtask semantic-freeze --update` +exit=1 + +$ ./target/debug/xtask formal +xtask: production translation source closure drifted; run `cargo xtask formal qualify aeneas --update` (computed digest e15edc03ddaf02c2318cc02de20925ea3ad1b71a876c06447ece0fd3e193722c) +exit=1 +``` + +The permitted direct Lean build succeeds; its three `sorry` warnings are in the vendored Aeneas standard library and are not raised as launch findings: + +```text +$ cd formal && lake build +⚠ [3077/3200] Replayed Aeneas.Std.Slice +warning: Aeneas/Std/Slice.lean:363:4: declaration uses `sorry` +warning: Aeneas/Std/Slice.lean:586:8: declaration uses `sorry` +⚠ [3156/3200] Replayed Aeneas.Std.StringIter +warning: Aeneas/Std/StringIter.lean:12:4: declaration uses `sorry` +warning: Aeneas/Std/StringIter.lean:15:4: declaration uses `sorry` +Build completed successfully (3284 jobs). +``` + +The full compliance run gets through the Rust/product corpus and then fails in the TypeScript SDK with the cross-language absent-budget drift described in LR-003. These are the verbatim milestones and terminal summary retained from that run: + +```text +compliance inventory covers 68 declared product surfaces +specification, registry, and result-code registries are synchronized +516 golden vector files are byte-stable +target V1 canonical corpus conformance passed +exchange transport conformance passed +product MCP and Iroh conformance passed +product fixtures are stable +Stripe profile boundary passed (6 families, 11 profiles) +bounded-domain boundary passed (7 domains, 11 required scenarios) +Auths Lab matrix: 504 nominal points, 396 baseline-compatible points +1..87 +# tests 94 +# suites 0 +# pass 78 +# fail 16 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 3591.035083 +xtask: npm test failed with exit status: 1 +``` + +## Findings + +### LR-001 — Ship a production assembly in the stock `auths-node` image + +- **Severity:** blocker +- **Area:** Production runtime and reference deployment +- **Estimated effort:** 5 engineering days +- **Depends on:** LR-002 +- **Files:** `product/runtime/auths-node/Cargo.toml:10-39`; `product/runtime/auths-node/src/lib.rs:3-14`; `product/runtime/auths-node/src/main.rs:55-120`; `product/runtime/auths-node/src/config.rs:16-88`; `product/runtime/auths-node/src/profiles.rs:158-338`; `product/runtime/auths-node/src/production.rs` (new); `product/runtime/auths-node/tests/production_assembly.rs` (new); `product/integrations/auths-opentofu/src/service.rs:82-125`; `product/integrations/auths-postgresql/src/service.rs:75-115`; `product/integrations/auths-github/src/service.rs:57-135`; `product/stores/auths-stores/src/lib.rs:7-12`; `product/integrations/auths-custody-aws-kms/src/lib.rs:164-328`; `product/integrations/auths-custody-pkcs11/src/lib.rs:165-308`; `demos/open-production-reference/Dockerfile:1-10`; `demos/open-production-reference/config/production.example.toml:1-36`; `demos/open-production-reference/deploy/kubernetes/base/config-map.yaml:6-35`; `demos/open-production-reference/deploy/kubernetes/base/deployment.yaml:36-63`; `demos/open-production-reference/tests/production-smoke.sh` (new); `demos/open-production-reference/README.md:7-13` + +**What is true today** + +The executable loads the configuration and immediately rejects it when `sandbox_providers = false`; only the sandbox branch constructs a runtime, using a fixture seed and `PostgresSandboxStore` (`product/runtime/auths-node/src/main.rs:64-98`). The production port traits and `ClosedProfileRegistry` exist (`product/runtime/auths-node/src/profiles.rs:158-338`), but repository-wide searches found no implementations of the five production port traits and no construction of `ClosedProfileRegistry`. Meanwhile, the production Kubernetes configuration sets production mode and disables sandbox providers, and its Deployment runs the stock image (`demos/open-production-reference/deploy/kubernetes/base/config-map.yaml:6-35`; `demos/open-production-reference/deploy/kubernetes/base/deployment.yaml:36-63`). + +```rust +// product/runtime/auths-node/src/main.rs:64-70 +let config = NodeConfig::from_path(Path::new(&config_path))?; +if !config.sandbox_providers() { + if command.as_deref() == Some("doctor") { + println!("{}", serde_json::to_string_pretty(&config.doctor(false))?); + } + return Err("production ports must be assembled with the auths-node library".into()); +} +``` + +**Why this blocks launch** + +The primary deployment artifact cannot start in the configuration it ships. A README instruction that operators must assemble the library themselves (`demos/open-production-reference/README.md:7-13`) does not make the supplied image or manifest a runnable production reference. This blocks deployment validation, production end-to-end evidence, operational qualification, and any honest claim that the three ratified integrations are available through the node. + +**Evidence** + +The exact production doctor invocation reports the configured surface and then refuses assembly: + +```text +$ ./target/debug/auths-node demos/open-production-reference/config/production.example.toml doctor +{ + "ready": false, + "sections": [ + {"name":"Configuration","status":"PASS","detail":"contract 1 / auths.open-production/1"}, + {"name":"Lifecycle DB","status":"FAIL","detail":"TLS / auths.lifecycle.postgresql/3"}, + {"name":"Custody","status":"PASS","detail":"aws-kms-p256-v1"}, + {"name":"Profiles","status":"PASS","detail":"auths.opentofu.saved-plan-apply/1 / auths.postgresql.bounded-update/1 / auths.github.issue-address/1"} + ] +} +auths-node: production ports must be assembled with the auths-node library +exit=1 +``` + +The failure is unconditional for production configurations at `product/runtime/auths-node/src/main.rs:64-70`. The Dockerfile compiles that executable into the runtime image (`demos/open-production-reference/Dockerfile:1-10`), rather than a separate operator-provided binary. + +The production port/registry construction search returned no implementations or call sites: + +```text +$ rg -n 'impl (AuthorityPort|ExactProfilePort|WorkflowPort|ReceiptPort|ReadinessPort)|ClosedProfileRegistry::(new|with_profile)' --glob '*.rs' . +(no output) + +$ rg -n 'impl .*AwsKmsApi|impl .*Pkcs11Api' product/integrations/auths-custody-aws-kms/src/lib.rs product/integrations/auths-custody-pkcs11/src/lib.rs +product/integrations/auths-custody-aws-kms/src/lib.rs:406: impl AwsKmsApi for FakeKms { +product/integrations/auths-custody-pkcs11/src/lib.rs:415: impl Pkcs11Api for FakeToken { +``` + +**Required end state** + +One supported, stock production image constructs the kernel authority, PostgreSQL lifecycle store, configured custody implementation, durable workflow/receipt stores, and the three closed qualified profile services. `doctor` probes the exact assembled dependencies, the server becomes ready only when required dependencies are usable, and the checked-in Kubernetes base plus AWS overlay can run that same image without source customization. + +**How to implement** + +1. Add an `auths-node` production assembly module implementing `AuthorityPort`, `ExactProfilePort`, `WorkflowPort`, `ReceiptPort`, and `ReadinessPort`, then construct `ClosedProfileRegistry` from it. +2. Adapt the already-qualified `SavedPlanService`, `BoundedUpdateService`, and `GitHubIssueWorkflowService` rather than duplicating profile logic. +3. Use `auths_stores::PostgresLifecycleStore` for production lifecycle state. Supply concrete, non-test `AwsKmsApi` and `Pkcs11Api` clients to the existing custody adapters; expose the currently private custody configuration through bounded typed getters rather than reparsing TOML. +4. Wire the production branch in `main.rs` and make missing dependencies fail closed with section-specific doctor output. +5. Add a production assembly integration test using test doubles plus one containerized PostgreSQL path. Add `tests/production-smoke.sh` to build the stock image, apply the checked-in Kubernetes base/selected custody overlay to a disposable cluster with bounded test providers, wait for `/ready`, and exercise all three profiles. +6. Complete LR-002’s idempotency/recovery interface before freezing this assembly’s public request contract. + +**Blast radius** + +This touches the highest-risk composition seam: custody, lifecycle durability, profile providers, recovery, receipts, readiness, packaging, and deployment configuration. Keep the existing sandbox branch for local fixtures, make production adapters narrow wrappers around qualified services, and do not allow production configuration to fall back to sandbox providers. If the production request schema changes under LR-002, assign the next `auths.product.open-production-contract` version before regenerating the semantic freeze (`release/semantic-freeze.json:851-874`). + +**How to verify it worked** + +```text +cargo test -p auths-node +cargo test -p auths-node --test production_assembly +cargo xtask production-contract +demos/open-production-reference/tests/production-smoke.sh +cargo xtask ci compliance +``` + +Acceptance requires a healthy production `doctor`, `/ready`, and one create/delegate/verify plus execute/status/receipt flow for each enabled profile through the stock image. It must also include a negative test proving production configuration cannot instantiate sandbox custody, stores, or providers. + +**Rollback** + +Keep production assembly behind the existing explicit production mode while sandbox mode remains unchanged. If an individual qualified profile fails qualification, disable only that profile in the closed registry; do not ship a production mode that silently substitutes fixtures. A release containing no runnable production assembly is not an acceptable fallback for v1.0. + +### LR-002 — Make deadlines effect-safe and bound blocking admission + +- **Severity:** blocker +- **Area:** HTTP runtime, effect recovery, and resource admission +- **Estimated effort:** 6–8 engineering days +- **Depends on:** — +- **Files:** `product/runtime/auths-node/src/api.rs:102-145`; `product/runtime/auths-node/src/api.rs:250-385`; `product/runtime/auths-node/src/config.rs:16-30`; `product/runtime/auths-node/src/config.rs:174-217`; `product/runtime/auths-node/src/config.rs:262-405`; `product/runtime/auths-node/src/kernel.rs:428-545`; `product/runtime/auths-node/src/profiles.rs:158-338`; `product/runtime/auths-node/tests/effect_deadlines.rs` (new); `product/runtime/auths-production-client/src/lib.rs:250-387`; `product/runtime/auths-production-client/src/lib.rs:474-595`; `product/fixtures/v1/production-client/contract-v1.json:1`; `product/fixtures/v1/production-client/manifest.json:1`; `bindings/typescript/src/production-client.ts:153-162`; `bindings/typescript/src/production-client.ts:252-335`; `bindings/python/python/auths/_production_client.py:248-303`; `bindings/python/python/auths/_production_client.py:338-372`; `demos/open-production-reference/config/local.toml:1-34`; `demos/open-production-reference/config/production.example.toml:1-36`; `demos/open-production-reference/deploy/kubernetes/base/config-map.yaml:6-35` + +**What is true today** + +The router applies a blanket `TimeoutLayer` to all routes but no concurrency or queue limit (`product/runtime/auths-node/src/api.rs:102-145`). Every runtime call, including effectful execution and recovery, is put into `tokio::task::spawn_blocking` (`product/runtime/auths-node/src/api.rs:250-385`). Dropping the awaiting future at the HTTP deadline does not cancel that blocking closure. The Rust production client recognizes that a post-transmission transport failure can mean an unknown effect (`product/runtime/auths-production-client/src/lib.rs:480-585`), but TypeScript and Python still synthesize narrower transport results (`bindings/typescript/src/production-client.ts:252-301`; `bindings/python/python/auths/_production_client.py:248-303`). The server currently mints the recovery reference after processing, so a lost response cannot communicate it to the client (`product/runtime/auths-node/src/kernel.rs:428-545`). + +```rust +// product/runtime/auths-node/src/api.rs:138-143,376-385 +.layer(TimeoutLayer::with_status_code( + StatusCode::REQUEST_TIMEOUT, + config.request_timeout(), +)) + +async fn call_runtime( + call: impl FnOnce() -> Result + Send + 'static, +) -> Result +where + T: Send + 'static, +{ + tokio::task::spawn_blocking(call) + .await + .unwrap_or(Err(RuntimeFailure::Unavailable)) +} +``` + +**Why this blocks launch** + +A caller can receive HTTP 408, retry, and cause or collide with an effect that is still executing. With no client-known operation key, the caller cannot reliably reconcile the first attempt if its response is lost. Unbounded `spawn_blocking` admission also lets a request burst occupy an unbounded blocking queue, defeating the advertised timeout as a resource bound. For money, infrastructure, database, and GitHub mutations, this is an effect-safety and recovery-contract failure rather than a generic availability issue. + +**Evidence** + +A temporary integration test used a `NodeRuntime` whose `handle` slept for 250 ms and then set an atomic flag. The router deadline was 100 ms. These are the verbatim retained failure lines: + +```text +$ cargo test -p auths-node --test launch_readiness_tmp -- --nocapture +running 1 test +effectful work continued after the server returned its timeout response +timeout_status=408 Request Timeout completed_after_timeout=true +error: test failed, to rerun pass `-p auths-node --test launch_readiness_tmp` +``` + +The material test source was: + +```rust +struct SlowRuntime(Arc); + +impl NodeRuntime for SlowRuntime { + fn handle(&self, _: ProductionRequest) -> Result { + std::thread::sleep(Duration::from_millis(250)); + self.0.store(true, Ordering::Release); + completed_response() + } + // The remaining trait methods returned bounded typed test failures. +} + +#[tokio::test] +async fn timed_out_effectful_work_must_not_continue_detached() { + let completed = Arc::new(AtomicBool::new(false)); + let mut config = test_config(); + config.request_timeout_ms = 100; + let router = app(&config, Arc::new(SlowRuntime(Arc::clone(&completed))), accepting()); + let response = router.oneshot(valid_execute_request()).await.unwrap(); + let timeout_status = response.status(); + tokio::time::sleep(Duration::from_millis(300)).await; + let completed_after_timeout = completed.load(Ordering::Acquire); + println!("timeout_status={timeout_status} completed_after_timeout={completed_after_timeout}"); + assert!( + !completed_after_timeout, + "effectful work continued after the server returned its timeout response" + ); +} +``` + +The throwaway test file was deleted after execution. The result follows directly from the timeout wrapping `spawn_blocking` at `product/runtime/auths-node/src/api.rs:138-143` and `product/runtime/auths-node/src/api.rs:376-385`. + +**Required end state** + +Before any provider entry, each effectful request has a client-generated opaque operation key mapped durably to a recovery record. Replays with the same key are idempotent. If a deadline or connection loss happens after transmission, every supported SDK returns outcome-unknown/reconcile with the already-known recovery handle. The server never emits a bare timeout that suggests an effectful request stopped. Blocking admission has explicit maximum in-flight and queue bounds, overload behavior is typed and fail-closed, and non-effectful read deadlines remain conventional and bounded. + +**How to implement** + +1. Add a client-generated operation key/recovery handle to `ProductionRequest` before transmission; this is the clean pre-1.0 point for that protocol change. +2. Persist an accepted/recoverable record before entering the provider, pass cancellation/deadline context only to adapters that can honor it, and otherwise let the durable workflow own completion. +3. Split blanket router timeout behavior: retain short timeouts for health/read-only routes, but have effectful routes return a protocol response tied to the durable handle. +4. Add `maximum_in_flight_requests` and a bounded wait/queue policy to `NodeConfig`, acquire a permit before `spawn_blocking`, and reject overload before effects begin. +5. Bring Rust, TypeScript, and Python transport classifiers to one decision table. +6. Add tests for lost response, deadline before/after durable admission, same-key replay, overload, process restart, and status reconciliation. + +**Blast radius** + +This changes the pre-1.0 production wire request, SDK behavior, runtime traits, configuration, persistence, and provider adapters. It should not change proof semantics. Assign a new `auths.product.open-production-contract` version and, if installed SDK surface changes, `auths.product.public-sdk-contract` (currently recorded at `release/semantic-freeze.json:851-918`). The largest migration risk is accidental duplicate execution, so do not implement the handle only as an in-memory cache. + +**How to verify it worked** + +```text +cargo test -p auths-node --test effect_deadlines +cargo test -p auths-node --test production_assembly +cargo test -p auths-production-client +npm test --prefix bindings/typescript +pytest -q bindings/python/tests +cargo xtask ci compliance +``` + +The negative control above must pass with `completed_after_timeout=false` only when the provider was never durably admitted; the admitted case must instead return a client-known handle and converge through status lookup. A load test must prove the configured in-flight/queue maximum is never exceeded. + +**Rollback** + +Because this is a correctness contract, do not feature-flag the unsafe behavior in production. A short-lived compatibility decoder may accept the old request only in sandbox mode. Production rollout can gate provider execution on the durable-admission capability and remain not-ready until its backing store is available. + +### LR-003 — Complete the absent-budget cutover atomically across SDKs and Lean + +- **Severity:** blocker +- **Area:** Core semantics, MCP bindings, generated vectors, and formal model +- **Estimated effort:** 3 engineering days +- **Depends on:** — +- **Files:** `core/crates/auths-model/src/lib.rs:927-946`; `core/crates/auths-authority/src/lib.rs:330-345`; `product/profiles/auths-profile-mcp/src/lib.rs:234-251`; `product/profiles/auths-profile-mcp/src/lib.rs:354-370`; `bindings/wasm/auths-proof-wasm/examples/generate-node-vectors.rs:194-250`; `bindings/typescript/test/integration/profiles/mcp.test.js:219-248`; `bindings/python/tests/test_mcp_workflow.py:319-337`; `formal/Auths/VectorExport.lean:142`; `formal/Auths/Rich/Semantics.lean:28`; `formal/Auths/Rich/Theorems.lean:138` + +**What is true today** + +The Rust kernel now answers the intended rule: when a parent has a ceiling and the request supplies no computed budget, coverage is false (`core/crates/auths-model/src/lib.rs:927-946`), and authority verification delegates that decision to the kernel (`core/crates/auths-authority/src/lib.rs:330-345`). MCP canonicalization supplies no requested budget (`product/profiles/auths-profile-mcp/src/lib.rs:234-251`; `product/profiles/auths-profile-mcp/src/lib.rs:354-370`), but the Node-vector generator still creates budgeted MCP trust anchors and grants (`bindings/wasm/auths-proof-wasm/examples/generate-node-vectors.rs:194-250`). TypeScript and Python MCP tests also construct budgeted delegations and expect authorization (`bindings/typescript/test/integration/profiles/mcp.test.js:219-248`; `bindings/python/tests/test_mcp_workflow.py:319-337`). At clean `f82cb08`, Lean's exported vector, semantics, and proof retain the old true outcome (`f82cb08:formal/Auths/VectorExport.lean:142-146`; `f82cb08:formal/Auths/Rich/Semantics.lean:28-35`; `f82cb08:formal/Auths/Rich/Theorems.lean:138-146`). Concurrent uncommitted changes now alter those formal files, but they have not removed the still-failing SDK/generator half of this blocker and were not part of the retained green `lake build`. + +```rust +// core/crates/auths-model/src/lib.rs:938-945 +match (ceiling, requested) { + (None, _) => true, + (Some(_), None) => false, + (Some(ceiling), Some(requested)) => ceiling.covers(requested), +} + +// bindings/wasm/auths-proof-wasm/examples/generate-node-vectors.rs:205-208 +Some(auths_model::BudgetCeiling::new( + auths_model::BudgetAlgebraId::parse("numeric-ceiling-v1")?, + 20, +)), +``` + +**Why this blocks launch** + +The kernel change is the correct safety direction, but releasing midway through the cutover makes supported language paths disagree about authorization. The compliance suite already fails 16 tests, including outcome changes from `authorized`/`indeterminate` to `denied` and error changes to `budget-ceiling-exceeded`. Formal artifacts then document a rule the executable kernel no longer implements. This is not one of the two red gates intentionally allowed by the launch brief. + +**Evidence** + +Representative verbatim TypeScript assertion excerpts from the compliance run are: + +```text +# Subtest: inspection exposes copied bounded evidence and no capability +Expected values to be strictly equal: ++ actual - expected ++ 'denied' +- 'authorized' +# Subtest: MCP authorization preserves indeterminate as a value ++ 'denied' +- 'indeterminate' +# Subtest: MCP authorization preserves denial as a value ++ 'budget-ceiling-exceeded' +- 'invalid-signature' +# pass 78 +# fail 16 +xtask: npm test failed with exit status: 1 +``` + +Commit `ac5b968` itself records that `formal/Auths/VectorExport.lean:142` still expects the old result. The source mismatch explains the failures without weakening the new kernel rule. + +**Required end state** + +MCP is explicitly budgetless from trust-anchor and grant construction through all supported SDK fixtures: it supplies no parent ceiling and no delegated budget. Budget-bearing profiles derive an explicit requested budget before asking the kernel; they do not rely on absent-budget inheritance. Lean returns false for `(some ceiling, no requested budget)` and proves the executable rule. Generated vectors, TypeScript, Python, Rust, and formal refinement tests all agree. + +**How to implement** + +1. Remove budgets from MCP trust anchors/grants and delegation helpers in the vector generator and language tests. +2. Regenerate binding vectors and checked-in diffs. +3. Review and complete the in-progress uncommitted Lean/vector edits, ensure the three assessed locations implement the terminal kernel rule, and prove the changed branch. +4. Audit every qualified money/bounded profile for explicit requested-budget derivation, but preserve generic core testkit cases that deliberately exercise budget coverage. +5. Do not restore the former fallback or special-case MCP in the kernel. +6. Once all semantic edits are complete, assign the next `auths.core.protocol` version and update the freeze once, not piecemeal (`release/semantic-freeze.json:72-90`). + +**Blast radius** + +The deliberate behavior change affects any caller that issued a parent budget ceiling but omitted the requested computed budget. That is a protocol-level semantic change, while the MCP fixture cleanup removes an incoherent feature from a profile that cannot compute the budget. Explicitly version it, regenerate every binding corpus, and call it out in migration notes. The principal risk is hiding a real money-profile omission by deleting its ceiling; those profiles must instead calculate the request budget. + +**How to verify it worked** + +```text +cargo test -p auths-model +cargo test -p auths-authority +cargo test -p auths-formal-refinement +npm test --prefix bindings/typescript +pytest -q bindings/python/tests/test_mcp_workflow.py +cd formal && lake build +cargo xtask ci compliance +``` + +Add one shared vector for each of `(ceiling, no request) => denied`, `(ceiling, covered request) => allowed`, `(ceiling, excessive request) => denied`, and `(no ceiling, no request) => allowed`, and require all supported bindings plus Lean refinement to consume it. + +**Rollback** + +Do not roll back by weakening the kernel. If the cross-language cutover cannot land atomically, hold the release or temporarily remove the affected profile from the supported surface. A semantic compatibility mode that authorizes an absent request budget would reintroduce the v1 safety ambiguity. + +### LR-004 — Synchronize active error lifecycle metadata with the registry + +- **Severity:** blocker +- **Area:** Release evolution policy +- **Estimated effort:** 0.5 engineering day +- **Depends on:** — +- **Files:** `release/evolution-lifecycle-v1.json:1-65`; `docs/product/COMPATIBILITY_AND_SUPPORT.md:1-87`; `release/semantic-freeze.json:1015-1037` + +**What is true today** + +The Rust registry contains 48 active codes while the lifecycle metadata contains 45. The evolution-policy implementation requires exact set equality (`xtask/src/evolution_policy.rs:285-315`), so both authoritative CI and release-check stop immediately. The missing codes are the three new core authorization/principal results. + +```rust +// xtask/src/evolution_policy.rs:312-315 +if registered != active || lifecycle.len() != registry.errors.len() { + return Err( + "active error lifecycle metadata does not exactly cover the Rust registry".to_owned(), + ); +} +``` + +**Why this blocks launch** + +This blocks the authoritative and release gates and leaves public compatibility metadata incomplete for errors that supported callers can receive. It is mechanically small, but it is a blocker because no v1.0 candidate should bypass the repository's own evolution contract. + +**Evidence** + +```text +$ comm -13 /tmp/auths-lifecycle-codes.txt /tmp/auths-registry-codes.txt +core.authorization-denied +core.authorization-indeterminate +core.unauthenticated-principal + +$ wc -l /tmp/auths-lifecycle-codes.txt /tmp/auths-registry-codes.txt +45 /tmp/auths-lifecycle-codes.txt +48 /tmp/auths-registry-codes.txt +93 total +``` + +The gate output is reproduced in the Baseline section. Updating the generated support document alone cannot fix this: the update path validates lifecycle data before writing it (`xtask/src/evolution_policy.rs:194-229`). + +**Required end state** + +All 48 active registry codes have active lifecycle entries, with `final_version` and replacement fields unset where appropriate, and the generated compatibility/support table reflects them. The authoritative and release gates advance past evolution policy without an exception or allowance. + +**How to implement** + +1. Add sorted active entries for `core.authorization-denied`, `core.authorization-indeterminate`, and `core.unauthenticated-principal` to `release/evolution-lifecycle-v1.json`. +2. Run the supported evolution-policy update to regenerate `docs/product/COMPATIBILITY_AND_SUPPORT.md`. +3. Review the public descriptions and migration meaning. +4. Assign the next `auths.release.evolution-contract` version, then update semantic freeze in the final versioning batch (`release/semantic-freeze.json:1015-1037`). + +**Blast radius** + +Low implementation risk, but the metadata is a public compatibility promise. The entries must describe active v1 codes rather than marking them deprecated or aliasing semantically distinct denied, indeterminate, and unauthenticated outcomes. + +**How to verify it worked** + +```text +cargo xtask evolution-policy +cargo xtask ci authoritative +cargo xtask release-check +``` + +The latter two may then stop only at a separately known gate; they must no longer mention lifecycle coverage. + +**Rollback** + +There is no runtime feature flag. If the three codes are not actually part of v1, remove them atomically from the public Rust registry and every consumer instead of leaving an incomplete lifecycle set. The current mixed state must not ship. + +### LR-005 — Put adversarial conformance on the aggregate release path + +- **Severity:** major +- **Area:** Compliance and release gating +- **Estimated effort:** 1 engineering day +- **Depends on:** — +- **Files:** `xtask/src/checks.rs:73-87`; `xtask/src/compliance.rs:5-20`; `xtask/src/compliance.rs:502-539`; `xtask/src/conformance.rs:5-190` + +**What is true today** + +There is an `adversarial-conformance` command and it correctly detects mismatched expected outcomes, but neither the `ci_compliance` aggregate in `xtask/src/checks.rs:73-87` nor the standalone compliance composition in `xtask/src/compliance.rs:5-20` invokes it. `release-check` delegates to aggregate CI (`xtask/src/release.rs:138-159`). Consequently, negative corpus expectations can drift while the positive `conformance` command remains green. + +```rust +// xtask/src/checks.rs:73-87 +pub(crate) fn ci_compliance() -> Result<(), String> { + let compliance_inventory = compliance_inventory()?; + abi()?; + exchange_conformance()?; + product_conformance()?; + product_fixtures(false)?; + stripe_profiles()?; + bounded_domains()?; + matrix()?; + bindings_conformance()?; + package_check()?; + wasm()?; + live_demo()?; + write_compliance_report(&compliance_inventory) +} +``` + +**Why this blocks launch** + +The adversarial corpus is evidence that failures are classified correctly at hostile boundaries, not just that canonical successes work. An opt-in command provides no release assurance if a release engineer can run every documented aggregate gate without executing it. This is major rather than blocker because the checker itself works and the fix is narrowly in gate composition. + +**Evidence** + +As a controlled negative test, the first manifest case's expected code at `core/conformance/v1/manifest.json:9` was temporarily changed from `verifier-configuration-mismatch` to `invalid-signature`: + +The following are the verbatim result fields from that run (the long manifest digest is omitted, not abbreviated): + +```text +$ ./target/debug/xtask conformance +target V1 canonical corpus conformance passed +exit=0 + +$ ./target/debug/xtask adversarial-conformance --case context/raw-key-chain/configuration-bitflip/full-verifier +{ + "schema":"auths-proof-conformance-result/v1", + "cases":1,"passed":0,"failed":1, + "coverage":{"context_fields":"1/14","principal_methods":"0/7","common_contract":"0/7"}, + "executions":[{"case":"context/raw-key-chain/configuration-bitflip/full-verifier","boundary":"full-verifier","expected_code":"invalid-signature","actual_code":"verifier-configuration-mismatch","passed":false}] +} +xtask: 1 of 1 adversarial conformance cases failed +exit=1 +``` + +After restoring the manifest, the same targeted command reported one case passed, zero failed, with expected and actual both `verifier-configuration-mismatch`. The mutation left no source diff. + +**Required end state** + +`cargo xtask ci compliance`, the standalone compliance command, authoritative CI, and `release-check` all execute the full adversarial corpus and fail on any case mismatch. Their report clearly identifies case, boundary, expected code, and actual code. One checked regression test proves aggregate composition cannot silently drop this gate. + +**How to implement** + +1. Invoke `adversarial_conformance(Vec::new())` from both aggregate composition points, adjacent to target/corpus conformance. +2. Preserve the targeted subcommand for diagnosis. +3. Add a gate-composition test or CI negative control that mutates a temporary copy/fixture manifest rather than the canonical corpus, then asserts aggregate compliance fails. +4. If aggregate reports enumerate constituent checks, record adversarial conformance explicitly there as well. + +**Blast radius** + +Runtime behavior is unchanged. CI duration increases by the adversarial corpus runtime, and previously hidden expectation drift may become newly visible. Because `xtask/src/checks.rs` is covered by release metadata, include any resulting `auths.release.public-surface` freeze change in the final semantic-version batch rather than accepting an unexplained drift (`release/semantic-freeze.json:1040-1135`). + +**How to verify it worked** + +```text +cargo xtask adversarial-conformance +cargo xtask ci compliance +cargo xtask release-check +``` + +Repeat the controlled one-case mismatch against a temporary corpus and require all aggregate paths to fail. Restore it and require all paths to pass. + +**Rollback** + +Do not feature-flag a release gate off. If corpus runtime is temporarily excessive, shard it deterministically while requiring all shards for release. Keep the targeted command for local iteration, not as a substitute for aggregate execution. + +### LR-006 — Make configured OTLP export real and observable + +- **Severity:** major +- **Area:** Operations telemetry and readiness +- **Estimated effort:** 4–6 engineering days +- **Depends on:** LR-001 +- **Files:** `product/runtime/auths-node/Cargo.toml:10-39`; `product/runtime/auths-node/src/config.rs:16-30`; `product/runtime/auths-node/src/config.rs:73-78`; `product/runtime/auths-node/src/config.rs:174-217`; `product/runtime/auths-node/src/config.rs:262-405`; `product/runtime/auths-node/src/api.rs:71-78`; `product/runtime/auths-node/src/api.rs:102-113`; `product/runtime/auths-node/src/api.rs:289-325`; `product/runtime/auths-node/src/main.rs:55-120`; `product/runtime/auths-node/src/shutdown.rs:10-18`; `product/runtime/auths-node/tests/otlp_export.rs` (new); `product/operations/auths-operations-otel/Cargo.toml:10-14`; `product/operations/auths-operations-otel/src/lib.rs:52-168`; `demos/open-production-reference/config/local.toml:20-22`; `demos/open-production-reference/config/production.example.toml:22-24`; `demos/open-production-reference/deploy/kubernetes/base/config-map.yaml:6-35`; `demos/open-production-reference/compose/otel/collector.yaml:1-25`; `demos/open-production-reference/README.md:40-50` + +**What is true today** + +The node parses and validates an OTLP endpoint and service name (`product/runtime/auths-node/src/config.rs:73-78`) but has no public getters or runtime consumer for them. `AppState` contains only a `PrometheusProjection`, and every recorded operation goes only to it (`product/runtime/auths-node/src/api.rs:102-113`; `product/runtime/auths-node/src/api.rs:289-325`). The operations crate contains a bounded in-memory `BoundedOtlpExporter` and `CombinedSink` abstraction (`product/operations/auths-operations-otel/src/lib.rs:52-168`), but no node path instantiates a network exporter. Doctor's summary hard-codes the telemetry family and has no telemetry health section (`product/runtime/auths-node/src/config.rs:346-405`). The demo collector accepts OTLP, but also scrapes Prometheus; node events reach only the latter (`demos/open-production-reference/compose/otel/collector.yaml:1-25`). + +```rust +// product/runtime/auths-node/src/api.rs:71-78,318-325 +struct AppState { + runtime: Arc, + release: Arc, + semantic_id: Arc, + accepting: Arc, + metrics: Arc, +} + +state.metrics.record(&OperationalEventV2::runtime( + None, + operation_stage, + outcome, + reason, + elapsed, +)); +``` + +**Why this blocks launch** + +The reference documentation claims privacy-safe OTLP operations export (`demos/open-production-reference/README.md:40-50`), while the configuration has no effect. Operators can believe forensic events are leaving the process when they are not. Prometheus aggregate metrics reduce this from blocker to major, but they do not provide the bounded event evidence or exporter health implied by the operations contract. + +**Evidence** + +Repository-wide searches for the OTLP endpoint and service-name fields found only parsing, validation, and configuration tests; no runtime getter or exporter construction. `record_operation` ends with `state.metrics.record(...)` at `product/runtime/auths-node/src/api.rs:289-325`. The production doctor evidence in LR-001 contains only Configuration, Lifecycle DB, Custody, and Profiles—no Telemetry section—despite OTLP being mandatory configuration. + +```text +$ rg -n 'otlp_endpoint|service_name' product/runtime/auths-node/src product/operations/auths-operations-otel/src demos/open-production-reference/compose/otel/collector.yaml +product/runtime/auths-node/src/api.rs:537:otlp_endpoint = "http://otel:4317" +product/runtime/auths-node/src/api.rs:538:service_name = "auths-node" +product/runtime/auths-node/src/config.rs:76: otlp_endpoint: String, +product/runtime/auths-node/src/config.rs:77: service_name: String, +product/runtime/auths-node/src/config.rs:198: if !self.telemetry.otlp_endpoint.starts_with("http://") +product/runtime/auths-node/src/config.rs:199: && !self.telemetry.otlp_endpoint.starts_with("https://") +product/runtime/auths-node/src/config.rs:203: if !valid_label(&self.telemetry.service_name, 96) { +product/runtime/auths-node/src/config.rs:471:otlp_endpoint = "http://otel:4317" +product/runtime/auths-node/src/config.rs:472:service_name = "auths-node" + +$ rg -n 'BoundedOtlpExporter|CombinedSink' product/runtime/auths-node/src +(no output) +``` + +**Required end state** + +When OTLP is configured, the node sends the bounded `OperationalEventV2` stream to that endpoint while retaining the Prometheus projection. Queue capacity, batching/flush timing, and backpressure policy are explicit configuration with bounded memory. Export success, failures, and dropped-event counts are observable. Readiness follows a ratified policy: recommended `DropNewest` degradation does not stop authorization but is prominently unhealthy/degraded, while a configured blocking/fail-closed assurance mode affects readiness. Shutdown performs a bounded final flush. Doctor probes exporter configuration/connectivity and reports Telemetry separately. + +**How to implement** + +1. Expose validated telemetry getters in `NodeConfig`. +2. Add a real OTLP transport around the existing bounded exporter, instantiate `CombinedSink` in production assembly, and store an `Arc` in `AppState` while preserving `/metrics`. +3. Add a bounded export worker with explicit capacity, retry/backoff, flush interval, counters, and shutdown deadline; never place secrets or unbounded labels in events. +4. Extend doctor/readiness and the Kubernetes/local configuration with the selected failure policy. +5. Add a collector-backed integration test proving one known event arrives, plus unavailable-collector, full-queue, restart, and shutdown tests. + +**Blast radius** + +This adds a network task, dependency, shutdown work, and operational behavior. A blocking exporter must never stall the authorization path accidentally. Default to bounded non-blocking behavior unless the operator explicitly selects fail-closed assurance. If the operations event/export contract changes, assign the next `auths.product.operations` version before the final freeze update (`release/semantic-freeze.json:877-896`). + +**How to verify it worked** + +```text +cargo test -p auths-operations-otel +cargo test -p auths-node --test otlp_export +cargo xtask production-contract +docker compose -f demos/open-production-reference/compose/compose.yaml up --build --wait +demos/open-production-reference/tests/compose-smoke.sh +docker compose -f demos/open-production-reference/compose/compose.yaml exec auths-1 auths-node /etc/auths/local.toml doctor +``` + +The test must observe a known bounded event at the collector, force a full queue and verify the selected policy/counter, take the collector down and verify readiness semantics, then prove shutdown completes within the configured drain bound. + +**Rollback** + +The exporter transport may be disabled only by an explicit telemetry mode, with doctor and readiness accurately reporting that state; configuration must never claim OTLP while silently emitting only Prometheus. If transport causes instability, switch to bounded `DropNewest`, preserve failure/drop counters, and keep authorization isolated while repairing export. + +## Recommended execution order + +1. **LR-004 first:** unblock authoritative/release execution so subsequent work receives full gate feedback. +2. **LR-003 second:** finish the already-started semantic cutover atomically and restore cross-language/formal agreement without weakening the kernel. +3. **LR-002 third:** ratify and implement the pre-transmission operation key, durable recovery, effect-safe deadline behavior, and bounded admission; this defines production runtime interfaces. +4. **LR-001 fourth:** assemble production ports and qualified services against LR-002's final interfaces, then qualify the stock image and Kubernetes reference. +5. **LR-006 fifth:** attach real OTLP export to the production assembly and exercise it with the reference collector. +6. **LR-005 in parallel with LR-001/LR-006:** wire the already-working adversarial checker into aggregates and add the negative control. +7. **Version/freeze only after semantic work settles:** assign every affected identity once, update semantic freeze, qualify the formal source closure, then run compliance, authoritative CI, and release-check from a clean tree. + +## Disagreements with the prior audit + +1. The prior main audit correctly identified client/server transport ambiguity and a bare server timeout, then proposed surfacing a recovery reference (`docs/target-state/v1-api-review-findings.md:319-350`). That is necessary but not sufficient: a server-random reference carried only in the lost response cannot help the caller. The temporary runtime test proves the server can return 408 while an effect continues. LR-002 therefore requires a client-known operation key/recovery handle established before transmission and durable admission before effects. +2. The prior audit correctly classified absent-budget inheritance as a blocker and prescribed one kernel answer (`docs/target-state/v1-api-review-findings.md:394-405`). Commit `ac5b968` implements that kernel answer, so this assessment does not reopen the decision. It finds new cutover collateral: supported MCP SDK fixtures and Lean still encode the old result, and current compliance fails 16 tests. The blocker remains open until the atomic cross-language/formal cutover is complete. +3. The earlier `auths-node` kernel-rebuild concern was materially addressed by `d82d57f`; it is not repeated here. LR-001 is narrower and newly evidenced: the stock production executable and the checked-in Kubernetes production configuration are mutually incompatible even though the library now has a closed runtime abstraction. + +## Areas examined that the prior audit did not cover + +- The exact executable built into the open-production Docker image and its compatibility with the Kubernetes base/AWS production configuration. +- Runtime cancellation semantics of Tower deadlines around `spawn_blocking`, demonstrated through a throwaway effect-continuation test. +- Blocking admission bounds and the absence of a client-known pre-transmission recovery key. +- Aggregate-gate composition under a controlled adversarial-corpus mutation. +- Exact error-registry versus evolution-lifecycle set equality. +- End-to-end consumption of OTLP configuration, event sinks, collector wiring, doctor output, and shutdown/readiness policy. + +## Unresolved + +- A real production end-to-end run cannot be performed until LR-001 supplies a production assembly; that missing evidence is itself part of the blocker rather than a reason to infer readiness. +- The full compliance command did not reach Python and later stages because TypeScript failed first. After LR-003, rerun it from the beginning and retain the complete report. +- `cargo xtask formal` remains intentionally red until the Aeneas source-closure digest is qualified. The retained `lake build` was green before the concurrent uncommitted formal edits; settle those edits, then run `cd formal && lake build` and `cargo test -p auths-formal-refinement` before updating the closure. +- Sustained production qualification, independent review, and the 30-day assurance window remain non-code release evidence outside this code-readiness assessment (`release/assurance/open-production-candidate-1/summary.md:3-15`). +- Provider sandbox credentials and a production-grade custody/lifecycle environment were not available. No claim of real AWS KMS, PKCS#11, GitHub, PostgreSQL mutation, or OpenTofu apply qualification is made here. + +## Coverage statement + +This assessment read the repository instructions and required abstraction plan, the ratified v1 API contract, every commit from `bbeb654` through current `HEAD`, and both prior main/bindings API audits before testing. It examined the core budget decision, authority use, MCP canonicalization, generated binding vectors, supported TypeScript/Python MCP paths, Lean semantics, evolution policy, compliance/release composition, adversarial corpus behavior, production client transport classification, node routing/runtime/configuration/profiles/kernel, qualified integration service seams, stores/custody exports, open-production Docker/Kubernetes/collector assets, telemetry sinks, and semantic-freeze ownership. It ran the authoritative, release, semantic-freeze, formal, Lean, compliance, production-doctor, detached-effect, and adversarial-mutation checks described above. It deliberately did not read `docs/prompts/LAUNCH_READINESS_INVESTIGATIONS.md`, did not treat the anticipated semantic-freeze/formal closure failures as findings, did not exhaustively re-audit unrelated crates or duplicate previously accepted API findings, and did not claim supply-chain, performance, fuzz-duration, or live-provider evidence that was not run. The temporary corpus mutation was restored and the temporary Rust integration test was deleted; this document is the only assessment artifact added by this work. diff --git a/docs/testing/TESTING_INVARIANTS.md b/docs/testing/TESTING_INVARIANTS.md new file mode 100644 index 00000000..4f53d875 --- /dev/null +++ b/docs/testing/TESTING_INVARIANTS.md @@ -0,0 +1,226 @@ +# Testing invariants + +Rules for writing tests, harnesses, and proofs in this repository. Every rule here was learned from +a real defect found in this codebase — the examples are not hypothetical. + +Applies to humans and agents equally. + +--- + +## The one principle + +**A check that cannot fail is worse than no check.** + +No check is an honest gap. A check that cannot fail looks like coverage, counts as coverage, and +tells you nothing. It also survives review indefinitely, because reviewers read the *name* of a +check, not its falsifiability. + +Every rule below is a way this failure mode hides. + +## The one universal technique + +**Mutation: break the thing under test, confirm the check goes red, restore it.** + +If a check stays green while the code it guards is deliberately broken, that check is decoration. +This is the only way to know a check bites, and it applies to every test type in this document. + +Do it when you write the check, not later. + +```bash +# 1. write / already have the check +# 2. break the production code it guards (invert a condition, drop a branch) +# 3. run the check — it MUST go red +# 4. restore the production code +# 5. run again — it MUST go green +``` + +Real finding: four Kani harnesses passed for months. Mutating the code they "proved" left all four +**successful**. They were rewritten only because someone mutated first. + +--- + +## Rules that apply to every test type + +| # | Rule | The defect it prevents | +|---|---|---| +| 1 | The check must be able to fail. Prove it by mutation. | `assert_eq!(x, x)` — a real harness in this repo | +| 2 | Call production code. Never transcribe its logic into the test. | A harness re-implementing the arithmetic it proves passes forever while production drifts | +| 3 | A literal in a decision position is a bug. | `root_preserved: true`, `extensionsAttenuate := true` | +| 4 | Write the failing test **first**. The red is the finding, proven. | Fixes that were never needed; findings that were never real | +| 5 | Green on the first run is a result, not a failure. Record it. | A corrected false positive is as valuable as a confirmed bug | +| 6 | Never weaken a check to make it pass. | Every entry in this table started as someone doing exactly that | +| 7 | If behavior legitimately changed, update the check **and say so**, naming the change. | Silent assertion edits that erase the record | +| 8 | Test the **denial** path as hard as the success path. | This is an authorization system. Denials are the product. | +| 9 | A check must scan or run everything it claims to. | `binding_semantics` scanned 2 of 4 surfaces; the Kani gate ran 2 of 5 packages | +| 10 | Cited evidence must resolve to a real symbol. | Nine fixtures cited a Kani harness that does not exist | + +--- + +## Unit tests + +**Invariant: the test reaches the code under test directly, not through a caller that might be doing +the work for it.** + +- Call the function you are testing. If an upstream guard would deny first, **bypass it** — otherwise + you are testing the guard. +- Assert the specific reason, not just the outcome. `assert!(result.is_err())` passes for the wrong + error. +- One behavior per test. A test named for one property that asserts five will be deleted by whoever + breaks four of them. +- Name the test after the property, not the function. `denies_absent_budget_under_bounded_ceiling` + beats `test_budget_covers`. + +**Real defect:** the system denied `(bounded ceiling, absent request)` only because a verifier guard +ran at line 2227, before the algebra at line 2251. The algebra itself returned `Authorized`. Correct +behavior rested on statement order. No unit test reached the algebra directly, so nothing caught it. + +**Rule:** when a guard protects a component, test the component **with the guard bypassed.** Otherwise +a regression in the component hides behind the guard forever. + +## Integration tests + +**Invariant: the seam is exercised, not mocked away.** + +- If you mock the thing the test is named after, you are testing your mock. +- Assert on the boundary contract: the exact error code, the exact state transition, the exact bytes. +- Test the failure modes of the seam, not only its happy path: timeout, partial write, restart + mid-operation, concurrent access. +- Any operation that can be interrupted needs a test that interrupts it. + +## End-to-end and cross-boundary tests + +**Invariant: meaning survives every boundary it crosses.** + +Every serialization, FFI hop, or language projection is a place where a distinction can be silently +flattened. Test that it is not. + +- Drive the **full** value space across the boundary, not one representative. If an enum has three + variants, all three cross. +- Assert on the far side in that language's own terms — read the value a real caller would receive. +- Timing and ordering count as meaning. So does the *reason* for a decision, not just the decision. + +**Real defect:** every error crossing the WASM boundary became a bare JS string. Code identity, +effect state, and recommended action were all destroyed. A round-trip test existed; it asserted only +that an error occurred. + +**The specific rule for this codebase:** the effect axis — whether a real-world effect +`not-applied` / `possible` / `applied` — must arrive intact at every public API in every language. +Losing that distinction is a safety bug, not a formatting bug. + +## Differential tests + +**Invariant: identical inputs, both implementations, identical outputs — including the reason.** + +Use this whenever two things claim to implement the same semantics: a runtime versus the kernel, a +binding versus its owner, an independent reimplementation versus the reference. + +- Same bytes in. No translation layer between the two sides, or you are testing the translator. +- Assert the **full** result: verdict, denial reason, and requirement. Matching verdicts with + different reasons is a disagreement. +- Drive it from the canonical corpus so the inputs are real. + +**Real defect:** `auths-node` disagreed with the kernel on **103 of 103** canonical inputs. Nothing +compared them, so nobody knew. The differential test is now the acceptance criterion for that crate. + +## Corpus and fixture tests + +**Invariant: the corpus covers the input *space*, not just the code paths.** + +A branch that no fixture reaches is untested no matter how green the suite is. + +- When you add a conditional, add a fixture that takes each side. +- Optional fields need a fixture where the field is **absent**. Absence is the case everyone forgets. +- Adding a fixture is additive and needs no justification. **Changing an existing fixture's bytes is + a protocol change** and needs written justification naming the decision that authorized it. +- Fixtures are oracles. Never edit one to make a refactor pass. + +**Real defect:** all 102 canonical fixtures declared a `requested_budget`. The absent-budget branch +was never exercised, so three independent implementations silently disagreed about it for months. +The disagreement was invisible until one fixture was added. + +## Kani harnesses + +**Invariant: symbolic input over the real domain, and it must call production code.** + +```rust +// WRONG — a unit test wearing a universally-quantified name +#[kani::proof] +fn exact_replay_never_becomes_absent_or_conflict() { + assert_eq!(replay_code(true, true), ReplayCode::ExactReplay); +} + +// RIGHT — quantifies, and calls the production function +#[kani::proof] +fn exact_replay_never_becomes_absent_or_conflict() { + let seen: bool = kani::any(); + let same: bool = kani::any(); + let code = replay_code(seen, same); // production code, not a copy + kani::assert(!(seen && same) || code == ReplayCode::ExactReplay, "..."); +} +``` + +- **Zero `kani::any()` means it is not a proof.** It is a unit test with a misleading name. +- Never transcribe the arithmetic into the harness. If production logic is not reachable, **extract + it into a function** and call that from both. +- The harness name is a claim. If it says "never", it must quantify over everything that could make + it happen. +- Mutation-test every harness. Four in this repo survived deliberate breakage. +- **Every `#[kani::proof]` must sit under a gated root.** `cargo xtask` enforces this via + `kani_harness_inventory`; a harness outside a gated package fails the build rather than silently + never running. + +## Lean, Charon, Aeneas + +**Invariant: the model must be able to express the property, and the theorem must be able to be +false.** + +- **If the model lacks the field, the theorem is vacuous.** Check that the structure can represent + what you are proving before you prove it. +- A theorem that holds for *any* definition of the thing it constrains proves nothing. If the proof + never uses the hypothesis, it is not about the hypothesis. +- **Adding a hypothesis makes a theorem weaker.** That is sometimes unavoidable — disclose it + explicitly and say why. +- Mutation applies here too: revert the definition to a literal, run `lake build`, confirm the + theorems go **red**. Any theorem still green does not constrain the property. +- Refinement theorems must cover every dimension the Rust checks. A model narrower than the code + means the refinement claim is narrower than it sounds. +- **Renaming a theorem means updating both citation sites** — `formal/Auths/Theorems.lean` and + `formal/assurance-manifest-v1.toml`. A stale citation breaks the assurance audit. +- The translation source-closure pin asserts *"this Lean was produced from this source."* Updating it + without re-running charon and aeneas fabricates that claim. Never write the digest by hand. + +**Real defect:** `delegate_preserves_root` existed, was proved, and was trivially true — the root +field was copied forward, so the theorem held for any definition of delegation. Separately, +`extensionsAttenuate := true` was a literal because `Rich.Grant` had no extensions field: the proof +covered 10 of 11 dimensions and reported 11. + +## Gates and conformance checks + +**Invariant: a gate scans everything it claims to, and enforces at the level the leak travels.** + +- Enumerate what the gate covers and compare it to what it claims. They drift. +- Enforce at the **symbol** level, not the crate level, when leaks travel through re-exports. A + `Cargo.toml` can look clean while the source is coupled. +- Every declared contract needs a gate. An unenforced contract file drifts silently and nobody + notices until it is wrong in production. +- If a gate cannot run locally (missing toolchain), say so explicitly. Do not let it pass by absence. +- Prefer inventory gates: rather than listing what to check, fail when something appears **outside** + the checked set. That way the class cannot return. + +--- + +## Before you open a PR + +1. Did every new check fail before the fix? Paste the red output. +2. Did you mutation-test it? Which mutation, and did it go red? +3. Does any check assert a value against itself, or against a constant it also computes? +4. Does any harness transcribe production logic instead of calling it? +5. Is there a literal sitting where a decision belongs? +6. Did you add a conditional without a fixture for each side? An optional field without an + absent case? +7. Does the denial path have the same coverage as the success path? +8. If you weakened anything — an assertion, a theorem, a gate's scope — did you say so explicitly + and name the behavior change that justifies it? +9. If you renamed a symbol something cites, did you update the citation? + +Any "no" is a change request, including on your own work. diff --git a/output/pdf/auths-proof-formal-semantics.pdf b/output/pdf/auths-proof-formal-semantics.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ada0f53ac77622135d5206e3f27b455610c2473a GIT binary patch literal 221003 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

XxIk8p=fS{uH0+pKeDJl&^SimskyK^`vo-RO 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�}8*c)jC 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

E$NXY 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%0Df2kDBG;`Y;L-S&jLjx$J81}`m~tQ^)sCG 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%0AN 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

UCpR9h;$!)yF9bmEL%XE@dL_ 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 literal 0 HcmV?d00001 From 334e34c1b2121d1a25c2f55f2baa82b68134aa48 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 05:41:35 +0100 Subject: [PATCH 25/61] release: assign semantic versions for every identity this branch drifted (UNSIGNED) semantic-freeze has been red since before this branch began: PR #109 changed auths.product.lifecycle under frozen identity v8 without assigning v9, and every wave since added more drift. It now passes. Computing the drift from owner paths rather than iterating the validator one error at a time found 26 of 63 identities drifted -- considerably more than the 6 the implementing agents self-reported, because each only knew its own lane. 20 frozen-meaning core.protocol 15->16, portable-abi-bindings 49->50, product.receipts 4->5, product.lifecycle 8->9, product.facade 9->10, public-sdk-contract 40->41, mcp-closed-execution 12->13, and 13 more 6 frozen-bytes dependency-graph 26->27, core fixtures manifest 3->4, identity-abi 3->4, assurance-manifest 3->4, errors manifest 3->4, bounded-policy manifest 1->2 FREEZE_VERSION 110 -> 111 assurance-manifest-v1.toml and qualification.toml shared one match arm; only the first drifted, so the arm is split rather than asserting a change to a file that did not change. Derived artifacts regenerated after review, not blindly: - architecture snapshot: 4 added edges, 0 removed. WASM and pyo3 now depend on auths-errors to carry the structured envelope, and WASM on js-sys to construct a real JS Error. bindings -> product is the correct direction. - sdk-experience baseline: TypeScript 193 -> 185 symbols across 7 -> 8 entry points (the dual-root collapse plus /service); Python 159 -> 168 across 7 -> 8 modules (the effect-axis vocabulary it previously could not name). - evolution lifecycle: the three codes auths-node's kernel rebuild registered (core.authorization-denied, core.authorization-indeterminate, core.unauthenticated-principal) gained active entries; registry is 48. - public-naming: two justified stale-name allowances for the audit document, which quotes public-naming.toml's own replace mappings. A third was REJECTED by the gate because auths-proof-algebra-contract/v1 is not a forbidden name -- an allowance without a violation is meaningless, and the gate said so. One genuine regression fixed, caught by the npm install smoke test rather than by any gate I was tracking: Wave 2 renamed ProductionProfile to ServiceProfile correctly but moved the factories with the type, so ./profiles lost githubIssueAddress, opentofuSavedPlanApply and postgresqlBoundedUpdate -- three of the four qualifiedProfiles -- while mcp, the fourth, stayed. A profile is a vertical, not a transport concept, and public-topology-v1.json maps the vertical layer to ./profiles. Restored by re-exporting the same declarations, so the shape gate still reports zero homonyms: one declaration, two paths, one meaning. Gates 17/17. Rust 776 tests, TypeScript 147, Python 133, all passing. Co-Authored-By: Claude Opus 5 --- architecture/dependency-graph.dot | 3 + architecture/dependency-graph.json | 52 ++++ bindings/customer-journey-matrix-v1.json | 8 +- bindings/typescript/api/public-api.txt | 7 +- bindings/typescript/src/profiles.ts | 14 + docs/product/COMPATIBILITY_AND_SUPPORT.md | 3 + release/evolution-lifecycle-v1.json | 345 +++++++++++++++++++--- release/public-naming.toml | 10 + release/semantic-freeze.json | 112 +++---- xtask/src/semantic_freeze.rs | 55 ++-- 10 files changed, 474 insertions(+), 135 deletions(-) diff --git a/architecture/dependency-graph.dot b/architecture/dependency-graph.dot index 57155116..48dcf4fc 100644 --- a/architecture/dependency-graph.dot +++ b/architecture/dependency-graph.dot @@ -353,6 +353,7 @@ digraph auths_architecture { "auths-profile-kit" -> "auths-profile-api" [label="normal"]; "auths-profile-kit" -> "auths-verifier" [label="dev"]; "auths-profile-mcp" -> "auths-codec" [label="normal"]; + "auths-profile-mcp" -> "auths-errors" [label="dev"]; "auths-profile-mcp" -> "auths-model" [label="normal"]; "auths-profile-mcp" -> "auths-profile-api" [label="normal"]; "auths-profile-mcp" -> "auths-verifier" [label="normal"]; @@ -428,6 +429,7 @@ digraph auths_architecture { "auths-proof-python" -> "auths-custody" [label="normal"]; "auths-proof-python" -> "auths-did-keri" [label="normal"]; "auths-proof-python" -> "auths-did-key" [label="normal"]; + "auths-proof-python" -> "auths-errors" [label="normal"]; "auths-proof-python" -> "auths-identity" [label="normal"]; "auths-proof-python" -> "auths-identity-raw-key" [label="normal"]; "auths-proof-python" -> "auths-lifecycle" [label="normal"]; @@ -448,6 +450,7 @@ digraph auths_architecture { "auths-proof-wasm" -> "auths-codec" [label="normal"]; "auths-proof-wasm" -> "auths-did-keri" [label="normal"]; "auths-proof-wasm" -> "auths-did-key" [label="normal"]; + "auths-proof-wasm" -> "auths-errors" [label="normal"]; "auths-proof-wasm" -> "auths-identity" [label="normal"]; "auths-proof-wasm" -> "auths-identity-raw-key" [label="normal"]; "auths-proof-wasm" -> "auths-model" [label="normal"]; diff --git a/architecture/dependency-graph.json b/architecture/dependency-graph.json index f4864322..48a74368 100644 --- a/architecture/dependency-graph.json +++ b/architecture/dependency-graph.json @@ -6983,6 +6983,18 @@ "default_features": false, "features": [] }, + { + "source": "auths-profile-mcp", + "source_layer": "product", + "target": "auths-errors", + "target_layer": "product", + "scope": "internal", + "kind": "dev", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [] + }, { "source": "auths-profile-mcp", "source_layer": "product", @@ -8300,6 +8312,20 @@ "std" ] }, + { + "source": "auths-proof-python", + "source_layer": "bindings", + "target": "auths-errors", + "target_layer": "product", + "scope": "internal", + "kind": "normal", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [ + "std" + ] + }, { "source": "auths-proof-python", "source_layer": "bindings", @@ -8659,6 +8685,20 @@ "default_features": false, "features": [] }, + { + "source": "auths-proof-wasm", + "source_layer": "bindings", + "target": "auths-errors", + "target_layer": "product", + "scope": "internal", + "kind": "normal", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [ + "std" + ] + }, { "source": "auths-proof-wasm", "source_layer": "bindings", @@ -8865,6 +8905,18 @@ "default_features": true, "features": [] }, + { + "source": "auths-proof-wasm", + "source_layer": "bindings", + "target": "js-sys", + "target_layer": null, + "scope": "external", + "kind": "normal", + "target_condition": null, + "optional": false, + "default_features": true, + "features": [] + }, { "source": "auths-proof-wasm", "source_layer": "bindings", diff --git a/bindings/customer-journey-matrix-v1.json b/bindings/customer-journey-matrix-v1.json index 8feed6e7..63cf5398 100644 --- a/bindings/customer-journey-matrix-v1.json +++ b/bindings/customer-journey-matrix-v1.json @@ -6,10 +6,10 @@ "schema": "auths.sdk-experience-metadata/1", "enforcement": "baseline", "baseline": { - "typescriptEntryPoints": 7, - "typescriptPublicSymbols": 193, - "pythonModules": 7, - "pythonPublicSymbols": 159, + "typescriptEntryPoints": 8, + "typescriptPublicSymbols": 190, + "pythonModules": 8, + "pythonPublicSymbols": 168, "maintainedTypescriptRecipes": 5, "maintainedPythonRecipes": 5 }, diff --git a/bindings/typescript/api/public-api.txt b/bindings/typescript/api/public-api.txt index a6e37304..7eb65f30 100644 --- a/bindings/typescript/api/public-api.txt +++ b/bindings/typescript/api/public-api.txt @@ -1,5 +1,5 @@ # Installed @auths-dev/sdk public API v1 -# declaration-sha256 d1658d004ab4a6f8449e119f54bd2536ec003aa4b26a537366970ebbe1b7d768 +# declaration-sha256 2ae2287043286afa59c3a5931ffa9801c38f53fb2b8fc36aa1fe3209512c94f1 . Actor type . approval value . ApprovalPolicy type @@ -113,6 +113,7 @@ ./service TransportFailure type ./profiles executeMcpClosed value ./profiles executeMcpPlanClosed value +./profiles githubIssueAddress value ./profiles mcp value ./profiles McpAction value+type ./profiles McpAuthority type @@ -139,8 +140,12 @@ ./profiles McpToolAuthority value+type ./profiles McpToolContext type ./profiles McpToolHandler type +./profiles opentofuSavedPlanApply value +./profiles postgresqlBoundedUpdate value ./profiles resourcesForMcpAuthority value ./profiles resumeMcpClosed value +./profiles ServiceProfile type +./profiles ServiceProfileId type ./integrations development value ./integrations DevelopmentAuthsOptions type ./integrations production value diff --git a/bindings/typescript/src/profiles.ts b/bindings/typescript/src/profiles.ts index 5b22480b..87a05f0a 100644 --- a/bindings/typescript/src/profiles.ts +++ b/bindings/typescript/src/profiles.ts @@ -1,2 +1,16 @@ /** Qualified, profile-owned effect verticals. */ export * from "./profiles/mcp/index.js"; + +// The remaining three qualified profiles are declared alongside the service +// client because it routes on them, but a profile is a VERTICAL, not a +// transport concept: bindings/public-topology-v1.json maps the vertical layer +// to this entry point, and all four qualifiedProfiles belong here. These are +// re-exports of the same declarations, not copies, so `mcp` and its three peers +// are reachable from one place without giving either name two meanings. +export { + githubIssueAddress, + opentofuSavedPlanApply, + postgresqlBoundedUpdate, + type ServiceProfile, + type ServiceProfileId, +} from "./service.js"; diff --git a/docs/product/COMPATIBILITY_AND_SUPPORT.md b/docs/product/COMPATIBILITY_AND_SUPPORT.md index e781feb0..28507628 100644 --- a/docs/product/COMPATIBILITY_AND_SUPPORT.md +++ b/docs/product/COMPATIBILITY_AND_SUPPORT.md @@ -33,6 +33,8 @@ Current blockers: independent-security-review, moderated-recipe-three-cohort, se | Code | Status | Replacement | Final producing version | | --- | --- | --- | --- | +| `core.authorization-denied` | active | — | — | +| `core.authorization-indeterminate` | active | — | — | | `core.forged-execution-reference` | active | — | — | | `core.internal-invariant` | active | — | — | | `core.invalid-configuration` | active | — | — | @@ -44,6 +46,7 @@ Current blockers: independent-security-review, moderated-recipe-three-cohort, se | `core.runtime-cancelled` | active | — | — | | `core.runtime-conflict` | active | — | — | | `core.runtime-unavailable` | active | — | — | +| `core.unauthenticated-principal` | active | — | — | | `core.unsupported-abi` | active | — | — | | `core.unsupported-semantic-subject` | active | — | — | | `core.workflow-terminal` | active | — | — | diff --git a/release/evolution-lifecycle-v1.json b/release/evolution-lifecycle-v1.json index 3412655d..f7200dc9 100644 --- a/release/evolution-lifecycle-v1.json +++ b/release/evolution-lifecycle-v1.json @@ -12,54 +12,305 @@ } ], "errors": [ - { "code": "core.forged-execution-reference", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "core.internal-invariant", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "core.invalid-configuration", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "core.malformed-input", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "core.native-runtime-unavailable", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "core.observation-inconclusive", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "core.observation-pending", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "core.outcome-unknown", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "core.runtime-cancelled", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "core.runtime-conflict", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "core.runtime-unavailable", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "core.unsupported-abi", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "core.unsupported-semantic-subject", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "core.workflow-terminal", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.cancelled", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.denied", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.descriptor-mismatch", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.disabled-key", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.evidence-mismatch", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.invalid-provider-response", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.key-version-mismatch", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.lifecycle-not-permitted", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.malformed-signature", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.non-canonical-signature", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.principal-mismatch", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.provider-unknown", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.request-mismatch", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.revoked-key", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.signature-verification-failed", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.throttled", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.transaction-mismatch", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "custody.unavailable", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "mcp.cancelled-before-entry", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "mcp.handler-failed", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "mcp.handler-timeout", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "mcp.invalid-handler-output", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "mcp.receipt-persist-failed", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "mcp.reconciliation-pending", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "mcp.replay", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "mcp.reservation-conflict", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "plan.action-substituted", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "plan.member-failed-before-entry", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "plan.member-interrupted", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "plan.reconciliation-pending", "status": "active", "replacement": null, "finalProducingVersion": null }, - { "code": "plan.resume-reference-invalid", "status": "active", "replacement": null, "finalProducingVersion": null } + { + "code": "core.authorization-denied", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.authorization-indeterminate", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.forged-execution-reference", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.internal-invariant", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.invalid-configuration", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.malformed-input", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.native-runtime-unavailable", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.observation-inconclusive", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.observation-pending", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.outcome-unknown", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.runtime-cancelled", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.runtime-conflict", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.runtime-unavailable", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.unauthenticated-principal", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.unsupported-abi", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.unsupported-semantic-subject", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "core.workflow-terminal", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.cancelled", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.denied", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.descriptor-mismatch", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.disabled-key", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.evidence-mismatch", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.invalid-provider-response", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.key-version-mismatch", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.lifecycle-not-permitted", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.malformed-signature", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.non-canonical-signature", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.principal-mismatch", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.provider-unknown", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.request-mismatch", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.revoked-key", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.signature-verification-failed", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.throttled", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.transaction-mismatch", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "custody.unavailable", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "mcp.cancelled-before-entry", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "mcp.handler-failed", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "mcp.handler-timeout", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "mcp.invalid-handler-output", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "mcp.receipt-persist-failed", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "mcp.reconciliation-pending", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "mcp.replay", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "mcp.reservation-conflict", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "plan.action-substituted", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "plan.member-failed-before-entry", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "plan.member-interrupted", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "plan.reconciliation-pending", + "status": "active", + "replacement": null, + "finalProducingVersion": null + }, + { + "code": "plan.resume-reference-invalid", + "status": "active", + "replacement": null, + "finalProducingVersion": null + } ], "conformanceSuites": [ - { "id": "auths.mechanism-profile-conformance", "version": 1, "status": "prelaunch" }, - { "id": "auths.simplified-product-waist-conformance", "version": 1, "status": "prelaunch" } + { + "id": "auths.mechanism-profile-conformance", + "version": 1, + "status": "prelaunch" + }, + { + "id": "auths.simplified-product-waist-conformance", + "version": 1, + "status": "prelaunch" + } ] } diff --git a/release/public-naming.toml b/release/public-naming.toml index d3957940..ed7a563b 100644 --- a/release/public-naming.toml +++ b/release/public-naming.toml @@ -775,3 +775,13 @@ reason = "The authoritative migration record must name its source platform schem path = "docs/target-state/BUSINESS_MODEL.md" token = "heading:Auths Proof product" reason = "The target-state business model uses Auths Proof as the historical name of the open protocol layer, not the current umbrella product." + +[[stale_name_allowances]] +path = "docs/target-state/v1-api-review-findings.md" +token = "auths-proof-release-evidence/v1" +reason = "The v1.0 API audit quotes release/public-naming.toml's own replace mapping verbatim to explain why the algebra contract schema is unlisted; the name appears only as the rejected predecessor." + +[[stale_name_allowances]] +path = "docs/target-state/v1-api-review-findings.md" +token = "auths-proof-platform/v1" +reason = "Quoted from release/public-naming.toml's platform-artifact-schema replace mapping in the same audit finding; named only as the superseded coordinate." diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index 42243ac4..734d04fe 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 110, + "freezeVersion": 111, "publicSurface": { "rustRoots": [ "auths", @@ -70,7 +70,7 @@ "entries": [ { "id": "auths.core.protocol", - "version": 15, + "version": 16, "classification": "frozen-meaning", "categories": [ "protocol-versions", @@ -87,11 +87,11 @@ "core/crates/auths-verifier/src", "core/spec/v1" ], - "sha256": "68a9f58cbf304c933e3ee1f883166c946248c071a836f4b9b0e819a892d60527" + "sha256": "e5320ad6ebb314a3644c92d53a8c7bed76309eff012d83c18a4f3743407be1cf" }, { "id": "auths.frozen-bytes/architecture/dependency-graph.json", - "version": 26, + "version": 27, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -99,11 +99,11 @@ "owners": [ "architecture/dependency-graph.json" ], - "sha256": "6393632f23fdd4648d89194a79753f138d13dd104d5e2561c9fceb6e43520f70" + "sha256": "7dc4d1116402841ffabe7f0d2da7e473da5fc11caf3d0542bad95d266053f15c" }, { "id": "auths.frozen-bytes/bindings/wasm/auths-proof-wasm/identity-abi-v1.json", - "version": 3, + "version": 4, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -111,7 +111,7 @@ "owners": [ "bindings/wasm/auths-proof-wasm/identity-abi-v1.json" ], - "sha256": "dbce582b98ce7a7c6e0e08f0ea6eb69b37b3d3756b963b05f4ff5c0bb7b3cc04" + "sha256": "da90ed777b4f7b1d43ce09c1d94581d589098e623bdfa02eab9192c5ef9df22d" }, { "id": "auths.frozen-bytes/bounded-domains.toml", @@ -163,7 +163,7 @@ }, { "id": "auths.frozen-bytes/core/fixtures/v1/manifest.json", - "version": 3, + "version": 4, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -171,7 +171,7 @@ "owners": [ "core/fixtures/v1/manifest.json" ], - "sha256": "7a05e02f6e3227ac401eaef099f6204168515077642614b45c19e85ae1edd20c" + "sha256": "5e0dc2659eb754e162b8f92fc19f71a5b04a520bd41297e82d8779734bfb5326" }, { "id": "auths.frozen-bytes/core/formal-vectors/v1/manifest.json", @@ -199,7 +199,7 @@ }, { "id": "auths.frozen-bytes/formal/assurance-manifest-v1.toml", - "version": 3, + "version": 4, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -207,7 +207,7 @@ "owners": [ "formal/assurance-manifest-v1.toml" ], - "sha256": "52f24e81b06f1a9689cf26785b634fd0311611f22a37515db53870353529356a" + "sha256": "85d45607bb6a166a40e45ea94f25b284069f74650132fea442101208052506e2" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/generated", @@ -271,7 +271,7 @@ }, { "id": "auths.frozen-bytes/product/fixtures/v1/bounded-policy/manifest.json", - "version": 1, + "version": 2, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -279,7 +279,7 @@ "owners": [ "product/fixtures/v1/bounded-policy/manifest.json" ], - "sha256": "7ea9054107afccae87c2e0e917f25eb41769f95166b068445d78792824377c6f" + "sha256": "9161e8b01c06c7dc1d7bdd9ed6f89b7504944604a78c5d7c32ee601b6497462d" }, { "id": "auths.frozen-bytes/product/fixtures/v1/custody/manifest.json", @@ -295,7 +295,7 @@ }, { "id": "auths.frozen-bytes/product/fixtures/v1/errors/manifest.json", - "version": 3, + "version": 4, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -303,7 +303,7 @@ "owners": [ "product/fixtures/v1/errors/manifest.json" ], - "sha256": "9425bc2eb09b4a6c4ccd8c92dc2e70eae0f37def11182d8bfa48e966660a2482" + "sha256": "feb0e436a6d516f8eeedc4ed468d38ed60a55c492adfd65e3386160cbb6dda18" }, { "id": "auths.frozen-bytes/product/fixtures/v1/github/manifest.json", @@ -559,7 +559,7 @@ }, { "id": "auths.identity.protocol", - "version": 28, + "version": 29, "classification": "frozen-meaning", "categories": [ "identity-protocol-versions", @@ -582,11 +582,11 @@ "core/fixtures/identity/v1/vectors.json", "core/spec/identity/v1" ], - "sha256": "8568caa99f0d1ed03747894893357e53fb024f31cc42f07b5f16e689f94979b7" + "sha256": "8e067c614c52645fef6695a246b708c2e88b9c2c9c0c5a5fdb5f6d5b25a308a0" }, { "id": "auths.modular-components", - "version": 7, + "version": 8, "classification": "frozen-meaning", "categories": [ "published-neutral-ports", @@ -616,11 +616,11 @@ "exchange/crates/auths-byte-channel/README.md", "xtask/src/fixtures.rs" ], - "sha256": "89d0a3a2ddc44afc49174d5ec0ab268db7e65c81b1ca4c53d5fd056bb4ad997d" + "sha256": "87fa324581fcc2357a57c091c962d58a2c77060841d71235522f66a3fe6d982d" }, { "id": "auths.portable-abi-bindings", - "version": 49, + "version": 50, "classification": "frozen-meaning", "categories": [ "portable-abi", @@ -637,11 +637,11 @@ "core/crates/auths-model/src/lib.rs", "core/spec/v1/auths-proof.cddl" ], - "sha256": "24f3e71ec5b838b9ae20cc7306ab6eead60f465a6f2e854175ccfc4a57e9b8d1" + "sha256": "8f071b7bbef86d105509e30d8d92e4ef40c92d9f9a9f9d2b954537832eb0cb19" }, { "id": "auths.product.bounded-domains", - "version": 6, + "version": 7, "classification": "frozen-meaning", "categories": [ "bounded-domain-inventory", @@ -662,11 +662,11 @@ "product/integrations/auths-records-api/src", "product/integrations/auths-stripe/src" ], - "sha256": "fad328d01a4d04e42b01274525c8a1f70f9a52381b54741cf4a490da0d24e7a2" + "sha256": "39b1b167d9db0995b57a0447afe93951a045c89a6367ece04d69b99a7290c3e2" }, { "id": "auths.product.bounded-policy", - "version": 1, + "version": 2, "classification": "frozen-meaning", "categories": [ "policy-semantic-ids", @@ -677,7 +677,7 @@ "product/fixtures/v1/bounded-policy/registry.toml", "product/policy/auths-bounded-policy/src" ], - "sha256": "016fda0634a31edeed423a1b81220d66fe9034ef76234ef4c71db688bfbdb1d4" + "sha256": "65bfe681e2cac581aeac3c5c8d0106fec8fc3b0e4ba157789a9af6e8e08526c0" }, { "id": "auths.product.configuration-commitments", @@ -695,7 +695,7 @@ }, { "id": "auths.product.development-composition", - "version": 7, + "version": 8, "classification": "frozen-meaning", "categories": [ "explicit-development-mode", @@ -710,11 +710,11 @@ "bindings/typescript/src/internal/development-store-node.ts", "bindings/typescript/src/internal/development.ts" ], - "sha256": "51a37c092bc0418e67cb027d3cb1fd72bc16a5a9f6be0cb247202a97dc4b846b" + "sha256": "1f7128df9ee76f5985b4c569a60f3728928ed461e710481279f2e1e2486114e8" }, { "id": "auths.product.error-recovery-contract", - "version": 10, + "version": 11, "classification": "frozen-meaning", "categories": [ "error-envelope", @@ -734,7 +734,7 @@ "product/fixtures/v1/errors", "xtask/src/error_registry.rs" ], - "sha256": "acee21757634da1977e02b63f64883132f473a4e9ad734c3c0565376592901d2" + "sha256": "9bc484e086224bb032842b86ebba86946cddd5bc9ac67566c456d33bac54e2bf" }, { "id": "auths.product.external-custody", @@ -762,7 +762,7 @@ }, { "id": "auths.product.facade", - "version": 9, + "version": 10, "classification": "frozen-meaning", "categories": [ "create", @@ -773,19 +773,19 @@ ], "owners": [ "bindings/python/python/auths/_product.py", - "bindings/python/python/auths/_production_client.py", + "bindings/python/python/auths/_service.py", "bindings/python/python/auths/profiles/__init__.py", "bindings/python/python/auths/profiles/_mcp.py", "bindings/typescript/src/product.ts", - "bindings/typescript/src/production-client.ts", "bindings/typescript/src/profiles.ts", - "bindings/typescript/src/profiles/mcp/index.ts" + "bindings/typescript/src/profiles/mcp/index.ts", + "bindings/typescript/src/service.ts" ], - "sha256": "ad0fe95af1ea607d042af0d088f6a7dede8b592baf9579981ea89bb0f9d3c50c" + "sha256": "c69f25e26bab731a22c4f8be8916d6eb32ff4e5426275f89dcf06abec24cefb3" }, { "id": "auths.product.lifecycle", - "version": 8, + "version": 9, "classification": "frozen-meaning", "categories": [ "reservation-state", @@ -802,11 +802,11 @@ "product/stores/auths-stores/tests/postgres_lifecycle.rs", "product/stores/auths-stores/tests/postgres_tls" ], - "sha256": "77882aa1b6160930d64ac094a14e9c2c0021cf488de3dfa5beaa1e91f4a5a1b5" + "sha256": "3f1e0738e7007f2264dcd0eb76ba92621cc3f958cc48850305709c75c6541267" }, { "id": "auths.product.mcp-closed-execution", - "version": 12, + "version": 13, "classification": "frozen-meaning", "categories": [ "profile-session", @@ -826,11 +826,11 @@ "product/profiles/auths-profile-mcp/src/session.rs", "xtask/src/mcp_session_contract.rs" ], - "sha256": "ebedd7266053a45b0422c04fb5e4389abb3efe43dfc7ae29c08d3374ddd24802" + "sha256": "f82c780cac1b1575c26a8fa56888d8db480e983686fc474859bdcf5a5ed9f3b0" }, { "id": "auths.product.mechanism-profile-conformance", - "version": 4, + "version": 5, "classification": "frozen-meaning", "categories": [ "contract-inventory", @@ -845,11 +845,11 @@ "product/conformance/v1/mechanism-profile-conformance.json", "xtask/src/mechanism_conformance.rs" ], - "sha256": "c439415a2e2acc030644a626d1eb8c0ccb1353164d426166baeacd6dfc445159" + "sha256": "d5d6cee5d536166c1be34e7a92802b013068eb766fe55fb664a2e576b22fe1be" }, { "id": "auths.product.open-production-contract", - "version": 12, + "version": 13, "classification": "frozen-meaning", "categories": [ "production-topology", @@ -871,11 +871,11 @@ "release/open-production-candidate.toml", "xtask/src/production_contract.rs" ], - "sha256": "733d72a0efc18b9aa8e4595e164a6bed85f7d5dda7883def469b5d8491473e04" + "sha256": "7f02334c1774a4920316309332cf26c9f42cd312836593f9db193d8e98d05772" }, { "id": "auths.product.operations", - "version": 4, + "version": 5, "classification": "frozen-meaning", "categories": [ "privacy-safe-events", @@ -893,11 +893,11 @@ "product/operations/v2", "product/runtime/auths-runtime/src/production.rs" ], - "sha256": "298cbee39ac801e1bd110541c409c11d31fe41f3b5f3e60265af26e15a94bd00" + "sha256": "45c5d70fd3d9946b7be6e45b0cf39d1250749d2caa2d4bdfb3ed97d8078705a9" }, { "id": "auths.product.public-sdk-contract", - "version": 40, + "version": 41, "classification": "frozen-meaning", "categories": [ "rust-sdk-contract", @@ -915,11 +915,11 @@ "product/runtime/auths-runtime/src", "product/sdk/auths-sdk/src" ], - "sha256": "35b68bb4c8a545214990c916bb553bb45dae96fd61c69707e3e8ef37c9990f8f" + "sha256": "c0fcf6548d0c0239f19d83d8d957acfc5a1c3476d8b4aabdd3a561c1c77e555b" }, { "id": "auths.product.receipts", - "version": 4, + "version": 5, "classification": "frozen-meaning", "categories": [ "receipt-schemas", @@ -929,7 +929,7 @@ "product/receipts/auths-receipts/src", "product/spec/v1/receipts.md" ], - "sha256": "36a99f363d252b1d21da3a0a62f5bf368e27ad9a968e29e3bb308ba9a8db1968" + "sha256": "d85e50be95645e25c1fc89832355af1a288f80514ffbff4ab7b5a9c3ac4efe09" }, { "id": "auths.product.release-assurance", @@ -954,7 +954,7 @@ }, { "id": "auths.product.simplified-waist", - "version": 7, + "version": 8, "classification": "frozen-meaning", "categories": [ "product-waist-invariants", @@ -970,11 +970,11 @@ "product/conformance/v1/simplified-product-waist.json", "xtask/src/product_waist.rs" ], - "sha256": "0337538cb4800c66b47e3ab8ad06d6e6c60d5d778bd591624c5082975de56c4e" + "sha256": "7d0d19f47fb654a69fda442e4170c92799a0b90ed3ee989ef908eee1cb1732a4" }, { "id": "auths.product.vocabulary", - "version": 7, + "version": 8, "classification": "frozen-meaning", "categories": [ "customer-vocabulary", @@ -992,7 +992,7 @@ "product/sdk/auths-sdk/Cargo.toml", "xtask/src/sdk_vocabulary.rs" ], - "sha256": "a36219f8c6d6024d84295b396aed7f7c981e6ebfb61c17963693181b3acb9694" + "sha256": "2e0f6c8de28e3c5027a4edd12073a9af29e81b58583b9b92813fe42b111f032f" }, { "id": "auths.release.benchmark-contract", @@ -1013,7 +1013,7 @@ }, { "id": "auths.release.evolution-contract", - "version": 12, + "version": 13, "classification": "frozen-meaning", "categories": [ "version-axes", @@ -1034,11 +1034,11 @@ "release/fixtures/evolution", "xtask/src/evolution_policy.rs" ], - "sha256": "414e64267198eedf2712e18ed5c5f7d6e25e7b9aba567bb1a9088d36bc7e58a4" + "sha256": "498236476d6e2d3767572bcaa9e384716f6aca680d9a39510d1f0eaaa61b27c1" }, { "id": "auths.release.public-surface", - "version": 109, + "version": 110, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "f65a0fbaa400471242de5edc23289e70b2bdb8dd342252688184a9ca48d011b5" + "sha256": "450239d2bbddef3737de3a8450e5fc597102dd23a0f968709081bc2b1ecd7a30" } ] } diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index 7f3d551e..d7ead407 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 110; +const FREEZE_VERSION: u64 = 111; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -167,7 +167,7 @@ fn generate_inventory() -> Result { let mut entries = vec![ freeze_entry( "auths.core.protocol", - 15, + 16, FreezeClassification::FrozenMeaning, &[ "protocol-versions", @@ -187,7 +187,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.identity.protocol", - 28, + 29, FreezeClassification::FrozenMeaning, &[ "identity-protocol-versions", @@ -213,7 +213,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.modular-components", - 7, + 8, FreezeClassification::FrozenMeaning, &[ "published-neutral-ports", @@ -246,7 +246,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.portable-abi-bindings", - 49, + 50, FreezeClassification::FrozenMeaning, &["portable-abi", "authoring-abi", "binding-contracts"], vec![ @@ -262,7 +262,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.public-sdk-contract", - 40, + 41, FreezeClassification::FrozenMeaning, &[ "rust-sdk-contract", @@ -283,7 +283,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.mcp-closed-execution", - 12, + 13, FreezeClassification::FrozenMeaning, &[ "profile-session", @@ -306,7 +306,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.simplified-waist", - 7, + 8, FreezeClassification::FrozenMeaning, &[ "product-waist-invariants", @@ -325,7 +325,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.facade", - 9, + 10, FreezeClassification::FrozenMeaning, &[ "create", @@ -347,7 +347,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.development-composition", - 7, + 8, FreezeClassification::FrozenMeaning, &[ "explicit-development-mode", @@ -365,7 +365,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.mechanism-profile-conformance", - 4, + 5, FreezeClassification::FrozenMeaning, &[ "contract-inventory", @@ -383,7 +383,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.vocabulary", - 7, + 8, FreezeClassification::FrozenMeaning, &[ "customer-vocabulary", @@ -404,7 +404,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.error-recovery-contract", - 10, + 11, FreezeClassification::FrozenMeaning, &[ "error-envelope", @@ -427,7 +427,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.bounded-policy", - 1, + 2, FreezeClassification::FrozenMeaning, &[ "policy-semantic-ids", @@ -441,7 +441,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.bounded-domains", - 6, + 7, FreezeClassification::FrozenMeaning, &[ "bounded-domain-inventory", @@ -456,7 +456,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.lifecycle", - 8, + 9, FreezeClassification::FrozenMeaning, &[ "reservation-state", @@ -476,7 +476,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.receipts", - 4, + 5, FreezeClassification::FrozenMeaning, &["receipt-schemas", "receipt-commitment-meanings"], vec![ @@ -499,7 +499,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.open-production-contract", - 12, + 13, FreezeClassification::FrozenMeaning, &[ "production-topology", @@ -568,7 +568,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.operations", - 4, + 5, FreezeClassification::FrozenMeaning, &[ "privacy-safe-events", @@ -589,7 +589,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.release.evolution-contract", - 12, + 13, FreezeClassification::FrozenMeaning, &[ "version-axes", @@ -629,15 +629,16 @@ fn generate_inventory() -> Result { for (id, path) in frozen_byte_inventories()? { let version = match path.as_str() { - "architecture/dependency-graph.json" => 26, - "bindings/wasm/auths-proof-wasm/identity-abi-v1.json" => 3, - "core/fixtures/v1/manifest.json" => 3, + "architecture/dependency-graph.json" => 27, + "bindings/wasm/auths-proof-wasm/identity-abi-v1.json" => 4, + "core/fixtures/v1/manifest.json" => 4, "product/conformance/v1/simplified-product-waist.json" => 2, - "formal/assurance-manifest-v1.toml" - | "formal/qualification/aeneas/qualification.toml" => 3, + "formal/assurance-manifest-v1.toml" => 4, + "formal/qualification/aeneas/qualification.toml" => 3, "formal/qualification/aeneas/generated" => 4, "formal/qualification/aeneas/source-closure.json" => 12, - "product/fixtures/v1/errors/manifest.json" => 3, + "product/fixtures/v1/errors/manifest.json" => 4, + "product/fixtures/v1/bounded-policy/manifest.json" => 2, "product/fixtures/v1/github/manifest.json" | "product/fixtures/v1/opentofu/manifest.json" => 2, "product/fixtures/v1/lifecycle/manifest.json" => 2, @@ -695,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 109, + 110, FreezeClassification::ReleaseMetadata, &[ "package-names", From c5784277aae67b252183a73eaca741e00141fa20 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 16 Aug 2026 04:47:17 +0000 Subject: [PATCH 26/61] chore(formal): regenerate qualification artifacts Source-SHA: 334e34c1b2121d1a25c2f55f2baa82b68134aa48 Workflow-Run: 31927345632 --- formal/qualification/aeneas/source-closure.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/formal/qualification/aeneas/source-closure.json b/formal/qualification/aeneas/source-closure.json index 1c5867ac..ddbb6509 100644 --- a/formal/qualification/aeneas/source-closure.json +++ b/formal/qualification/aeneas/source-closure.json @@ -1,6 +1,6 @@ { "schema": "auths-proof-translation-source-closure/v2", - "digest": "9b3255fbec6e6181fabe435370b05ae2a8b266179bfc3d25935226ba091023bd", + "digest": "c9ab4c8e66d4c2f00947a9c71acfc32c6dc6ad6bc57c2cec69cdf0404cabdfd0", "files": [ { "path": "Cargo.lock", @@ -22,7 +22,7 @@ }, { "path": "core/crates/auths-algebra-kernel/src/lib.rs", - "sha256": "cc4f56f8568eab59e2a08eed701eb5e6be3815fb23619629d2764dda80290a3a" + "sha256": "6654667eeb9b353d6648954c9fd2e8838462a82ce6b2803eb49e1366892c68c6" }, { "path": "core/crates/auths-authority/Cargo.toml", @@ -30,7 +30,7 @@ }, { "path": "core/crates/auths-authority/src/lib.rs", - "sha256": "ec9be4188cb40016f70c4372787655f5cc90e7b4390a8624c609a1fcc89f01b0" + "sha256": "a99955461a8df1978a7aace9a3891ed0c3b606044ab3de40b2b05048217e7919" }, { "path": "core/crates/auths-model/Cargo.toml", @@ -38,7 +38,7 @@ }, { "path": "core/crates/auths-model/src/lib.rs", - "sha256": "8f31ea29b3b574d23440711fc83fa9167174679921406f92cd5e9a807a95a699" + "sha256": "3aef299e43b58c12a0b11356668cf6674fc4d72f37bfb0ace1f384c56c1362ad" }, { "path": "formal/algebra-contract-v1.toml", @@ -70,7 +70,7 @@ }, { "path": "product/runtime/auths-lifecycle/src/kernel.rs", - "sha256": "335d103efef3248e2277b5e97a9dd8a13389e086d2abeaa023a39890bbb7a943" + "sha256": "2abb8c094b36d35b364e74d78091d14c5d6f3d1eb6552cca3b5538dbd017ce23" }, { "path": "product/runtime/auths-lifecycle/src/lib.rs", @@ -90,7 +90,7 @@ }, { "path": "xtask/src/formal.rs", - "sha256": "b51f7a985f7856883cde246696cc09b72e9ffdfd6910cead9cea98a83ccf2cd2" + "sha256": "cda1f9895df58b7a63e5b32ce71b1e181c66424a991f423f88543ca03aa93a76" }, { "path": "xtask/src/formal_qualification.rs", From 7b8571de26bff44e6d13991e9a6448915b633e77 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 05:50:03 +0100 Subject: [PATCH 27/61] style: cargo fmt (UNSIGNED) The aggregate 'cargo xtask ci authoritative' gate runs cargo fmt --all --check before anything else, so two unformatted files were masking the rest of the authoritative run. Co-Authored-By: Claude Opus 5 --- product/profiles/auths-profile-mcp/src/session.rs | 9 +++++++-- xtask/src/sdk_experience.rs | 6 +++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/product/profiles/auths-profile-mcp/src/session.rs b/product/profiles/auths-profile-mcp/src/session.rs index eea69c21..0482a67d 100644 --- a/product/profiles/auths-profile-mcp/src/session.rs +++ b/product/profiles/auths-profile-mcp/src/session.rs @@ -212,7 +212,10 @@ impl McpTerminal { Self::ExactReplay { .. } => Some("mcp.replay"), Self::Conflict { .. } => Some("mcp.reservation-conflict"), Self::Recoverable { - recovery, cause, resumed, .. + recovery, + cause, + resumed, + .. } => Some(match recovery { // Reserved, never entered: non-effect is still provable. RecoveryKind::Reserved => "mcp.cancelled-before-entry", @@ -1016,7 +1019,9 @@ mod tests { session.accept_provider_entry().unwrap(); session.next_step().unwrap(); session - .accept_handler(McpHandlerResult::parse(McpHandlerEffect::Possible, None, cause).unwrap()) + .accept_handler( + McpHandlerResult::parse(McpHandlerEffect::Possible, None, cause).unwrap(), + ) .unwrap(); session.terminal().unwrap().clone() } diff --git a/xtask/src/sdk_experience.rs b/xtask/src/sdk_experience.rs index 0849fe0a..c955c464 100644 --- a/xtask/src/sdk_experience.rs +++ b/xtask/src/sdk_experience.rs @@ -414,7 +414,11 @@ mod tests { // `exports` does: "." for the root, "./identity" for the rest. let suffix = entry.strip_prefix("@auths-dev/sdk").unwrap_or(entry); let owned = format!(".{suffix}"); - let subpath = if suffix.is_empty() { "." } else { owned.as_str() }; + let subpath = if suffix.is_empty() { + "." + } else { + owned.as_str() + }; assert_ne!( classify_typescript_entry(subpath), "internal-leak", From 17038d9020e86f66c1e57018ec5743f7bd3349f2 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 06:23:29 +0100 Subject: [PATCH 28/61] ci: fix the three failures from the first CI run (UNSIGNED) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. cargo fmt --all --check — resolved upstream in c578427. 2. compliance / node-smoke: "exported but undeclared: productionTransportFailureV1" Wave 2 added the transport-failure classifier at bindings/wasm/auths-proof-wasm/src/lib.rs:156 — the function whose whole job is to stop a possibly-applied effect being reported as safe to retry — and wired it into bindings/typescript/src/service.ts:202,424 and verifier/wasm.ts:43, but never declared it in the ABI manifests. Declared in authoring-abi-v1.json; identity is the wrong home for a transport concern. 3. postgresql-live: "TLS PostgreSQL environment slots are required" Not an infrastructure problem, and not fixable with a fixture. The test was run from TWO workflows, correctly from one: - postgres-lifecycle.yml stands up the TLS fixture in tests/postgres_tls/compose.yaml and supplies all three slots (AUTHS_POSTGRES_URL, AUTHS_POSTGRES_CA_PEM, AUTHS_POSTGRES_SERVER_NAME). - ci.yml set AUTHS_LIFECYCLE_POSTGRES_URL, which configured() does not read, against the plain demo database on 54329 which has no TLS and no CA. It could never have satisfied the requirement, so it panicked on every run. Removed the ci.yml invocation. The duplication was the defect. On replacing it with a fixture: the test asserts multi-process capacity, restart replay, and transaction abort atomicity — properties only a real transactional engine exhibits. A fake would be a check that cannot fail in the way that matters. It already costs developers nothing: it is #[ignore]d, so it never runs in a normal `cargo test`, and its "real infra" is already a throwaway docker-compose fixture, not shared infrastructure. Freeze resynced for the ABI manifest and the formatting: portable-abi-bindings 50->51, evolution-contract 13->14, mcp-closed-execution 13->14, public-sdk-contract 41->42, release.public-surface 110->111, FREEZE_VERSION 111->112. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 20 +++++++++++------ .../auths-proof-wasm/authoring-abi-v1.json | 3 ++- release/semantic-freeze.json | 22 +++++++++---------- xtask/src/semantic_freeze.rs | 12 +++++----- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d29e7f87..44f9e569 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -606,13 +606,19 @@ jobs: - name: Real PostgreSQL rollback, restart, and reconciliation contract working-directory: demos/postgresql-data-change run: npm run test:live-recovery - - name: Transactional multi-process lifecycle-store contract - env: - AUTHS_LIFECYCLE_POSTGRES_URL: postgresql://migration_admin:${{ env.POSTGRES_ADMIN_PASSWORD }}@127.0.0.1:54329/auths_demo - run: >- - cargo test --locked -p auths-stores - --test postgres_lifecycle - -- --ignored --test-threads=1 +# The transactional multi-process lifecycle-store contract is NOT run here. + # + # postgres_lifecycle's `configured()` requires AUTHS_POSTGRES_URL, + # AUTHS_POSTGRES_CA_PEM and AUTHS_POSTGRES_SERVER_NAME -- a TLS PostgreSQL + # with a CA certificate. This job runs the plain demo database on 54329 and + # was setting AUTHS_LIFECYCLE_POSTGRES_URL, which `configured()` does not + # read, so the test panicked with "TLS PostgreSQL environment slots are + # required" on every run. + # + # It is owned by .github/workflows/postgres-lifecycle.yml, which stands up + # the TLS fixture in tests/postgres_tls/compose.yaml, supplies all three + # slots, and triggers on changes to auths-lifecycle or auths-stores. + # Running it in two places, correctly in one, was the defect. - name: Privilege, RLS, injection, concurrency, timeout, and drift contract working-directory: demos/postgresql-data-change run: npm run test:live-database diff --git a/bindings/wasm/auths-proof-wasm/authoring-abi-v1.json b/bindings/wasm/auths-proof-wasm/authoring-abi-v1.json index c8f985ce..7f1ca946 100644 --- a/bindings/wasm/auths-proof-wasm/authoring-abi-v1.json +++ b/bindings/wasm/auths-proof-wasm/authoring-abi-v1.json @@ -39,7 +39,8 @@ "commitPlanApprovalV1", "beginMcpExecutionV1", "resumeMcpExecutionV1", - "McpExecutionSessionV1" + "McpExecutionSessionV1", + "productionTransportFailureV1" ], "types": [ "AuthoringSigningRequestV1", diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index 734d04fe..0cd3fa87 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 111, + "freezeVersion": 112, "publicSurface": { "rustRoots": [ "auths", @@ -620,7 +620,7 @@ }, { "id": "auths.portable-abi-bindings", - "version": 50, + "version": 51, "classification": "frozen-meaning", "categories": [ "portable-abi", @@ -637,7 +637,7 @@ "core/crates/auths-model/src/lib.rs", "core/spec/v1/auths-proof.cddl" ], - "sha256": "8f071b7bbef86d105509e30d8d92e4ef40c92d9f9a9f9d2b954537832eb0cb19" + "sha256": "c6dbb975797a1778ca9992b473266912026f11b938d115ae633a6441dd5b1af3" }, { "id": "auths.product.bounded-domains", @@ -806,7 +806,7 @@ }, { "id": "auths.product.mcp-closed-execution", - "version": 13, + "version": 14, "classification": "frozen-meaning", "categories": [ "profile-session", @@ -826,7 +826,7 @@ "product/profiles/auths-profile-mcp/src/session.rs", "xtask/src/mcp_session_contract.rs" ], - "sha256": "f82c780cac1b1575c26a8fa56888d8db480e983686fc474859bdcf5a5ed9f3b0" + "sha256": "00ffc07c04fa73722ac697902cff1bb0fe6082adcfa080c67f5d70d7d9825cc8" }, { "id": "auths.product.mechanism-profile-conformance", @@ -897,7 +897,7 @@ }, { "id": "auths.product.public-sdk-contract", - "version": 41, + "version": 42, "classification": "frozen-meaning", "categories": [ "rust-sdk-contract", @@ -915,7 +915,7 @@ "product/runtime/auths-runtime/src", "product/sdk/auths-sdk/src" ], - "sha256": "c0fcf6548d0c0239f19d83d8d957acfc5a1c3476d8b4aabdd3a561c1c77e555b" + "sha256": "37c371e7c2bb7869c03e27fd7ec70f69afeb363416e7897f70e91775729dd137" }, { "id": "auths.product.receipts", @@ -1013,7 +1013,7 @@ }, { "id": "auths.release.evolution-contract", - "version": 13, + "version": 14, "classification": "frozen-meaning", "categories": [ "version-axes", @@ -1034,11 +1034,11 @@ "release/fixtures/evolution", "xtask/src/evolution_policy.rs" ], - "sha256": "498236476d6e2d3767572bcaa9e384716f6aca680d9a39510d1f0eaaa61b27c1" + "sha256": "499ea44085fed0e1b66daaac9b7fae768128c30a9338e6d3e5c5a99ae51512eb" }, { "id": "auths.release.public-surface", - "version": 110, + "version": 111, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "450239d2bbddef3737de3a8450e5fc597102dd23a0f968709081bc2b1ecd7a30" + "sha256": "fc87d1d7ada0f74f6591c3ac3e6520121814d98632e0aa1486a43c0aff8d9440" } ] } diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index d7ead407..93f4efc3 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 111; +const FREEZE_VERSION: u64 = 112; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -246,7 +246,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.portable-abi-bindings", - 50, + 51, FreezeClassification::FrozenMeaning, &["portable-abi", "authoring-abi", "binding-contracts"], vec![ @@ -262,7 +262,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.public-sdk-contract", - 41, + 42, FreezeClassification::FrozenMeaning, &[ "rust-sdk-contract", @@ -283,7 +283,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.mcp-closed-execution", - 13, + 14, FreezeClassification::FrozenMeaning, &[ "profile-session", @@ -589,7 +589,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.release.evolution-contract", - 13, + 14, FreezeClassification::FrozenMeaning, &[ "version-axes", @@ -696,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 110, + 111, FreezeClassification::ReleaseMetadata, &[ "package-names", From 697bd99fdb004d6b6e4add263ecf58b1695cf1bf Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 06:42:29 +0100 Subject: [PATCH 29/61] ci: make auths-node build standalone, and regenerate the stale recipe doc (UNSIGNED) 1. auths-node failed the Docker release build with "`?` couldn't convert the error: `KeriError: std::error::Error` is not satisfied" at main.rs:46, while `cargo check --workspace` passed. FEATURE UNIFICATION MASKED IT. auths-did-key, auths-did-keri and auths-registries all gate their Error impl: #[cfg(feature = "std")] impl std::error::Error for KeriError {} and the workspace declares them `default-features = false` (Cargo.toml:150). A whole-workspace build has some other crate turning on `.../std`, so the impls exist and every local check passes. The Dockerfile runs `cargo build --locked --release -p auths-node`, which resolves features for ONE package with no unification, so std stays off and the impls vanish. auths-node now requests `features = ["std"]` from all three rather than relying on incidental unification. Only auths-did-keri had failed; the other two were the same latent trap one build-order change away from firing. Verified with the exact Docker command, not a workspace check: cargo build --locked --release -p auths-node -> exit 0 2. docs/product/recipes/03_EXECUTE_ONE_ACTION.md was stale. The recipe source bindings/recipes/python/03_execute_exact_action.py:6 was corrected to `from auths.verify import verify_receipt` when the Python surface was aligned, but bindings/recipes/tools/generate-docs.mjs was never re-run, so the published recipe still told readers to import a symbol the root package does not export. This is the broken recipe the adoption review reproduced. Regenerated. 3. Freeze resynced: aeneas source-closure 12->13 (upstream c578427 regenerated the qualification artifacts), product.vocabulary 8->9 (the recipe doc), release.public-surface 111->112, FREEZE_VERSION 112->113. Co-Authored-By: Claude Opus 5 --- docs/product/recipes/03_EXECUTE_ONE_ACTION.md | 2 +- product/runtime/auths-node/Cargo.toml | 6 +++--- release/semantic-freeze.json | 14 +++++++------- xtask/src/semantic_freeze.rs | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/product/recipes/03_EXECUTE_ONE_ACTION.md b/docs/product/recipes/03_EXECUTE_ONE_ACTION.md index 97a5fec1..cc992a4c 100644 --- a/docs/product/recipes/03_EXECUTE_ONE_ACTION.md +++ b/docs/product/recipes/03_EXECUTE_ONE_ACTION.md @@ -57,7 +57,7 @@ from __future__ import annotations import asyncio import json -from auths import verify_receipt +from auths.verify import verify_receipt from auths.integrations import development from auths.profiles import mcp diff --git a/product/runtime/auths-node/Cargo.toml b/product/runtime/auths-node/Cargo.toml index 75e10ddb..1cefef08 100644 --- a/product/runtime/auths-node/Cargo.toml +++ b/product/runtime/auths-node/Cargo.toml @@ -9,8 +9,8 @@ publish = false [dependencies] auths-codec.workspace = true -auths-did-key.workspace = true -auths-did-keri.workspace = true +auths-did-key = { workspace = true, features = ["std"] } +auths-did-keri = { workspace = true, features = ["std"] } auths-errors.workspace = true auths-model.workspace = true auths-operations.workspace = true @@ -18,7 +18,7 @@ auths-operations-otel.workspace = true auths-ports.workspace = true auths-production-client.workspace = true auths-raw-key.workspace = true -auths-registries.workspace = true +auths-registries = { workspace = true, features = ["std"] } auths-signature.workspace = true auths-verifier.workspace = true axum.workspace = true diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index 0cd3fa87..f064897b 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 112, + "freezeVersion": 113, "publicSurface": { "rustRoots": [ "auths", @@ -235,7 +235,7 @@ }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/source-closure.json", - "version": 12, + "version": 13, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -243,7 +243,7 @@ "owners": [ "formal/qualification/aeneas/source-closure.json" ], - "sha256": "2e3be36cbc63cd9f1b80e45ac26f514599a7aaca5851e7b4218bf5a4ad2cbf0b" + "sha256": "3fda7eab0ae605b30b15c8f969833e7dbb1373db7d48db9e81a16818a22dd155" }, { "id": "auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json", @@ -974,7 +974,7 @@ }, { "id": "auths.product.vocabulary", - "version": 8, + "version": 9, "classification": "frozen-meaning", "categories": [ "customer-vocabulary", @@ -992,7 +992,7 @@ "product/sdk/auths-sdk/Cargo.toml", "xtask/src/sdk_vocabulary.rs" ], - "sha256": "2e0f6c8de28e3c5027a4edd12073a9af29e81b58583b9b92813fe42b111f032f" + "sha256": "70aa07a3354df6ff3a0875ea871467c02ae87b02982013a8935d7ea6845cd735" }, { "id": "auths.release.benchmark-contract", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 111, + "version": 112, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "fc87d1d7ada0f74f6591c3ac3e6520121814d98632e0aa1486a43c0aff8d9440" + "sha256": "196dd6d106ece4f2f365bf07e26a32ef50dcc0771e9e1b6e56a334205227efda" } ] } diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index 93f4efc3..56b4393b 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 112; +const FREEZE_VERSION: u64 = 113; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -383,7 +383,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.vocabulary", - 8, + 9, FreezeClassification::FrozenMeaning, &[ "customer-vocabulary", @@ -636,7 +636,7 @@ fn generate_inventory() -> Result { "formal/assurance-manifest-v1.toml" => 4, "formal/qualification/aeneas/qualification.toml" => 3, "formal/qualification/aeneas/generated" => 4, - "formal/qualification/aeneas/source-closure.json" => 12, + "formal/qualification/aeneas/source-closure.json" => 13, "product/fixtures/v1/errors/manifest.json" => 4, "product/fixtures/v1/bounded-policy/manifest.json" => 2, "product/fixtures/v1/github/manifest.json" @@ -696,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 111, + 112, FreezeClassification::ReleaseMetadata, &[ "package-names", From a91a4e22662ff62b16f9c56093e09d16a19c13b5 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 07:03:44 +0100 Subject: [PATCH 30/61] demos: make the reference-stack smoke test say what failed (UNSIGNED) The three-node reference job failed with: Error: Process completed with exit code 35. and nothing else. 35 is curl's CURLE_SSL_CONNECT_ERROR, so the TLS handshake against the ingress on 8443 failed -- but the script reported no step name, no container state, no ingress log, and no handshake detail. A reader cannot act on that, and neither could I: with the stack unavailable locally there was no way to tell a dead upstream from a bad certificate from an nginx that never loaded its key. This is the same shape as the rest of this branch: a check that runs, correctly fails, and then declines to say why. The script now names the step it was on and, on any failure, reports: - the CA and ingress certificate subjects and validity windows - whether the CA actually signs the ingress certificate - `docker compose ps` - the last 40 lines of the ingress log - a verbose curl handshake Behaviour on success is unchanged. Verified by running it with no stack up: it correctly identifies `docker compose config` as the failing step and prints the certificate chain state. This does not fix the handshake. It makes the next run diagnosable rather than a number, which is the honest next step -- the previous push was the first in which auths-node compiled at all, so this job had never reached the smoke test before and there is no prior evidence about why it fails. Separately confirmed NOT causes: the certificate material is gitignored and regenerated by the workflow before the stack starts, the generator writes to the directories compose mounts, and nginx's SANs cover localhost and 127.0.0.1. Co-Authored-By: Claude Opus 5 --- .../tests/compose-smoke.sh | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/demos/open-production-reference/tests/compose-smoke.sh b/demos/open-production-reference/tests/compose-smoke.sh index 802e45b8..acaad0af 100755 --- a/demos/open-production-reference/tests/compose-smoke.sh +++ b/demos/open-production-reference/tests/compose-smoke.sh @@ -1,10 +1,48 @@ #!/bin/sh set -eu compose_file=$(CDPATH= cd -- "$(dirname -- "$0")/../compose" && pwd)/compose.yaml +compose_dir=$(dirname -- "$compose_file") +ca="$compose_dir/postgres/certs/ca.crt" + +# Every check below used to fail as a bare curl exit code. `exit 35` on a +# TLS handshake told a reader nothing about which container was wrong, whether +# it was even running, or what it logged. Report the state on the way out. +diagnose() { + status=$? + [ "$status" -eq 0 ] && exit 0 + echo "--- compose-smoke failed (exit $status): $1" >&2 + if [ -f "$ca" ]; then + echo "--- CA validity" >&2 + openssl x509 -in "$ca" -noout -subject -dates >&2 2>&1 || true + echo "--- ingress certificate validity" >&2 + openssl x509 -in "$compose_dir/ingress/certs/server.crt" -noout -subject -dates >&2 2>&1 || true + echo "--- does the CA sign the ingress certificate?" >&2 + openssl verify -CAfile "$ca" "$compose_dir/ingress/certs/server.crt" >&2 2>&1 || true + else + echo "--- no CA at $ca; run tests/generate-local-certificates.sh first" >&2 + fi + echo "--- container state" >&2 + docker compose -f "$compose_file" ps >&2 2>&1 || true + echo "--- ingress log" >&2 + docker compose -f "$compose_file" logs --tail 40 ingress >&2 2>&1 || true + echo "--- verbose handshake" >&2 + curl --verbose --cacert "$ca" https://localhost:8443/live >&2 2>&1 || true + exit "$status" +} +trap 'diagnose "$step"' EXIT + +step="docker compose config" docker compose -f "$compose_file" config >/dev/null -curl --fail --silent --cacert "$(dirname -- "$compose_file")/postgres/certs/ca.crt" https://localhost:8443/live >/dev/null -curl --fail --silent --cacert "$(dirname -- "$compose_file")/postgres/certs/ca.crt" https://localhost:8443/ready >/dev/null + +step="https://localhost:8443/live through the TLS ingress" +curl --fail --silent --cacert "$ca" https://localhost:8443/live >/dev/null + +step="https://localhost:8443/ready through the TLS ingress" +curl --fail --silent --cacert "$ca" https://localhost:8443/ready >/dev/null + for port in 18081 18082 18083; do + step="http://localhost:$port/ready" curl --fail --silent "http://localhost:$port/ready" >/dev/null + step="http://localhost:$port/metrics exposes auths_operations_total" curl --fail --silent "http://localhost:$port/metrics" | grep -q 'auths_operations_total' done From 26306e1c57c9025cd4a8e742cd402bacde574eb5 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 07:34:12 +0100 Subject: [PATCH 31/61] demos: give the reference stack the trusted context it requires (UNSIGNED) Step 1 of the reference-stack repair. All three replicas were crash-looping: auths-1 Restarting (1) auths-2 Restarting (1) auths-3 Restarting (1) with nginx cycling behind the dead upstreams, which is why the smoke test saw a TLS handshake reset (curl 35) rather than a connection refused. CAUSE. d82d57f made `[verification] trusted_context_path` mandatory -- correctly: "a node that cannot state its trust anchors cannot decide anything" -- and pointed local.toml at /run/config/trusted-context.cbor. Nothing ever produced that file for compose. main.rs:39 read it, got ENOENT, and exited 1. The demo has been unstartable since that commit; it was invisible because the Docker build failed earlier for the unrelated KeriError reason fixed in 697bd99. FIX. A new `auths-node::local_fixture` module derives a trust anchor from the stack's own AUTHS_LOCAL_SEED and assembles a real TrustedContext: raw-key principal method, ed25519 suite, the three reference profiles, a namespace and capability per profile, and status snapshots whose freshness window matches the anchor validity. `auths-local-context` writes the canonical bytes; tests/generate-local-context.sh runs it; compose mounts the result; CI generates it before `docker compose up`, alongside the TLS material it already generates. The anchor key is DOMAIN-SEPARATED from the node's custody key. The seed signs receipts, so an anchor deriving to the same key would let one key both grant authority and attest to its own exercise. A test asserts they differ. Generated per run, never committed, and gitignored: the context carries an evaluation time and a snapshot freshness window, so a checked-in copy would go stale exactly the way a checked-in certificate does. This is a LOCAL FIXTURE and says so in three places. Anyone holding the seed can derive the anchor and author grants against it -- the point of a self-contained demo, disqualifying anywhere else. Production mounts operator-held bytes, as config/production.example.toml already shows. Verified: 4 unit tests (derivation is deterministic, seed-dependent, distinct from custody, and the context round-trips canonically); the binary emits 1248 canonical bytes from the CI seed; `docker compose config` accepts the stack. STILL BROKEN, next steps. The installed-client e2e calls the deleted `auths-sandbox-request` and asserts `issuer.create(...)` returns an authority -- but kernel.rs:233 now always returns UnauthenticatedPrincipal, by design. That test asserts the fail-open this branch removed. Authority has to be authored offline against this anchor and arrive inside the proof, which is steps 2-5. Co-Authored-By: Claude Opus 5 --- .../workflows/open-production-reference.yml | 2 + demos/open-production-reference/.gitignore | 2 + .../compose/compose.yaml | 5 + .../compose/context/README.md | 14 + .../tests/generate-local-context.sh | 18 + .../auths-node/src/bin/auths-local-context.rs | 73 ++++ product/runtime/auths-node/src/lib.rs | 1 + .../runtime/auths-node/src/local_fixture.rs | 321 ++++++++++++++++++ 8 files changed, 436 insertions(+) create mode 100644 demos/open-production-reference/compose/context/README.md create mode 100755 demos/open-production-reference/tests/generate-local-context.sh create mode 100644 product/runtime/auths-node/src/bin/auths-local-context.rs create mode 100644 product/runtime/auths-node/src/local_fixture.rs diff --git a/.github/workflows/open-production-reference.yml b/.github/workflows/open-production-reference.yml index 3c073992..8fa80e62 100644 --- a/.github/workflows/open-production-reference.yml +++ b/.github/workflows/open-production-reference.yml @@ -95,6 +95,8 @@ jobs: python-version: "3.12" - name: Generate isolated TLS material run: sh demos/open-production-reference/tests/generate-local-certificates.sh + - name: Generate the local-fixture trusted context + run: sh demos/open-production-reference/tests/generate-local-context.sh - name: Start the reference stack run: docker compose -f demos/open-production-reference/compose/compose.yaml up -d --build --wait - name: Verify health and readiness diff --git a/demos/open-production-reference/.gitignore b/demos/open-production-reference/.gitignore index ea80bd7c..be545c06 100644 --- a/demos/open-production-reference/.gitignore +++ b/demos/open-production-reference/.gitignore @@ -3,3 +3,5 @@ compose/postgres/certs/* compose/ingress/certs/* !compose/ingress/certs/README.md tests/__pycache__/ +compose/context/* +!compose/context/README.md diff --git a/demos/open-production-reference/compose/compose.yaml b/demos/open-production-reference/compose/compose.yaml index 46449dac..7b6c1da7 100644 --- a/demos/open-production-reference/compose/compose.yaml +++ b/demos/open-production-reference/compose/compose.yaml @@ -11,6 +11,11 @@ x-auths-node: &auths-node volumes: - ../config/local.toml:/etc/auths/local.toml:ro - ./postgres/certs/ca.crt:/run/secrets/postgres-ca.pem:ro + # Generated by tests/generate-local-context.sh before the stack starts. + # `[verification] trusted_context_path` is mandatory -- a node that cannot + # state its trust anchors cannot decide anything -- so without this mount + # every replica exits 1 on startup. + - ./context/trusted-context.cbor:/run/config/trusted-context.cbor:ro depends_on: postgres: condition: service_healthy diff --git a/demos/open-production-reference/compose/context/README.md b/demos/open-production-reference/compose/context/README.md new file mode 100644 index 00000000..484bdb2b --- /dev/null +++ b/demos/open-production-reference/compose/context/README.md @@ -0,0 +1,14 @@ +# Generated trust material + +`trusted-context.cbor` is written here by `tests/generate-local-context.sh` +before the compose stack starts, and is deliberately not committed. + +`[verification] trusted_context_path` is mandatory: a node that cannot state its +trust anchors cannot decide anything. The context carries an evaluation time and +status-snapshot freshness window, so a checked-in copy would expire the same way +a checked-in certificate does. + +The anchor key is derived from `AUTHS_LOCAL_SEED`, domain-separated from the +node's own custody key. **This is a local fixture**: anyone holding the seed can +derive the anchor and author grants against it. A production deployment mounts +operator-held context bytes as a secret -- see `config/production.example.toml`. diff --git a/demos/open-production-reference/tests/generate-local-context.sh b/demos/open-production-reference/tests/generate-local-context.sh new file mode 100755 index 00000000..7f4044d5 --- /dev/null +++ b/demos/open-production-reference/tests/generate-local-context.sh @@ -0,0 +1,18 @@ +#!/bin/sh +# Writes the local-fixture TrustedContext the compose stack mounts at +# /run/config/trusted-context.cbor. +# +# `[verification] trusted_context_path` is mandatory: a node that cannot state +# its trust anchors cannot decide anything. Nothing produced a context for the +# compose demo, so all three replicas crash-looped on startup with +# "the trusted context is unavailable". +# +# Generated per run rather than committed. A checked-in context carries an +# evaluation time and snapshot freshness window, so it would go stale exactly +# the way a checked-in certificate does. +set -eu +root=$(CDPATH= cd -- "$(dirname -- "$0")/../compose" && pwd) +mkdir -p "$root/context" +: "${AUTHS_LOCAL_SEED:?provide the same 32-byte unpadded base64url seed the stack uses}" +cargo run --locked -q -p auths-node --bin auths-local-context -- \ + "$root/context/trusted-context.cbor" "${AUTHS_CONTEXT_LIFETIME_SECONDS:-21600}" diff --git a/product/runtime/auths-node/src/bin/auths-local-context.rs b/product/runtime/auths-node/src/bin/auths-local-context.rs new file mode 100644 index 00000000..069e000c --- /dev/null +++ b/product/runtime/auths-node/src/bin/auths-local-context.rs @@ -0,0 +1,73 @@ +//! Emits the local-fixture `TrustedContext` the reference compose stack needs. +//! +//! `[verification] trusted_context_path` is mandatory and nothing produced a +//! context for the compose demo, so every replica exited 1 on startup. This +//! writes one derived from the stack's own `AUTHS_LOCAL_SEED`. +//! +//! Local fixture only. The anchor key is derivable from the seed by anyone who +//! holds it; a production deployment mounts operator-held context bytes. + +use auths_node::local_fixture::{SEED_ENV, build_context}; +use base64ct::{Base64UrlUnpadded, Encoding as _}; +use std::{ + env, fs, + process::ExitCode, + time::{SystemTime, UNIX_EPOCH}, +}; + +const DEFAULT_LIFETIME_SECONDS: u64 = 6 * 60 * 60; + +fn main() -> ExitCode { + let mut arguments = env::args().skip(1); + let Some(output) = arguments.next() else { + eprintln!("usage: auths-local-context [lifetime-seconds]"); + return ExitCode::from(1); + }; + let lifetime = arguments + .next() + .as_deref() + .unwrap_or(&DEFAULT_LIFETIME_SECONDS.to_string()) + .parse::() + .ok() + .filter(|value| (60..=30 * 24 * 60 * 60).contains(value)); + if arguments.next().is_some() { + eprintln!("auths-local-context: unexpected extra argument"); + return ExitCode::from(1); + } + let Some(lifetime) = lifetime else { + eprintln!("auths-local-context: lifetime must be between 60 and 2592000 seconds"); + return ExitCode::from(1); + }; + let Ok(encoded) = env::var(SEED_ENV) else { + eprintln!("auths-local-context: {SEED_ENV} is not set"); + return ExitCode::from(1); + }; + let mut seed = [0_u8; 32]; + if Base64UrlUnpadded::decode(encoded.trim(), &mut seed).is_err() { + eprintln!("auths-local-context: {SEED_ENV} is not 32 unpadded base64url bytes"); + return ExitCode::from(1); + } + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_or(0, |duration| duration.as_secs()); + let context = match build_context(&seed, now, lifetime) { + Ok(context) => context, + Err(error) => { + eprintln!("auths-local-context: {error}"); + return ExitCode::from(1); + } + }; + let Ok(bytes) = auths_codec::encode_verifier_context(&context) else { + eprintln!("auths-local-context: the context could not be encoded canonically"); + return ExitCode::from(1); + }; + if fs::write(&output, &bytes).is_err() { + eprintln!("auths-local-context: {output} could not be written"); + return ExitCode::from(1); + } + eprintln!( + "auths-local-context: wrote {} canonical bytes to {output}, valid for {lifetime}s (LOCAL FIXTURE)", + bytes.len() + ); + ExitCode::SUCCESS +} diff --git a/product/runtime/auths-node/src/lib.rs b/product/runtime/auths-node/src/lib.rs index a591907c..6545b9fe 100644 --- a/product/runtime/auths-node/src/lib.rs +++ b/product/runtime/auths-node/src/lib.rs @@ -3,6 +3,7 @@ pub mod api; pub mod config; pub mod kernel; +pub mod local_fixture; pub mod profiles; mod sandbox_store; pub mod shutdown; diff --git a/product/runtime/auths-node/src/local_fixture.rs b/product/runtime/auths-node/src/local_fixture.rs new file mode 100644 index 00000000..221df8e0 --- /dev/null +++ b/product/runtime/auths-node/src/local_fixture.rs @@ -0,0 +1,321 @@ +//! Local-fixture trust material for the open production reference stack. +//! +//! `[verification] trusted_context_path` is mandatory: a node that cannot state +//! its trust anchors cannot decide anything. That is deliberate, and it means +//! the reference compose stack needs a real `TrustedContext` on disk before it +//! can start. Nothing produced one, so all three replicas crash-looped on +//! `the trusted context is unavailable`. +//! +//! This module builds one, deterministically, from the same `AUTHS_LOCAL_SEED` +//! the stack already supplies. It is a LOCAL FIXTURE. The anchor's private key +//! is derivable by anyone holding the seed, which is the point for a +//! self-contained demo and disqualifying for anything else. A production +//! deployment supplies operator-held context bytes as a secret, exactly as +//! `config/production.example.toml` shows. + +use auths_model::{ + AcceptedRegistries, AssuranceClaimId, AssurancePolicy, AssurancePolicyId, AssuranceQuantifier, + AssuranceRequirement, Audience, AudienceSet, BudgetAlgebraId, BudgetCeiling, CapabilityId, + Challenge, ChannelBindingId, CompositionRequirement, EvidenceTypeId, GrantStatusSnapshot, + ParticipantRole, Permission, PermissionSet, PrincipalId, PrincipalMethodId, PrincipalStatusSnapshot, + ProfileId, + ProfilePolicyId, ProfileRef, ResourceId, ResourceMatcherId, SignatureSuiteId, StatusPolicy, + StatusSnapshotId, Timestamp, TrustAnchor, TrustAnchorId, TrustedContext, ValidityWindow, + VerifierConfigurationId, VerifierLimits, +}; +use auths_ports::{PrincipalMethod, SignatureSuite}; +use auths_raw_key::{RAW_KEY_V1, RawKeyDescriptor, RawKeyMethod, RawKeyType}; +use auths_signature::{ED25519_V1, Ed25519Suite}; +use ed25519_dalek::SigningKey; +use sha2::{Digest as _, Sha256}; + +/// Environment slot carrying the stack's 32-byte unpadded base64url seed. +pub const SEED_ENV: &str = "AUTHS_LOCAL_SEED"; + +/// Domain separator: the anchor key must not be the node's custody key. +/// +/// The node signs receipts with the seed directly. An authority root that could +/// also sign the receipts attesting to its own use would collapse two roles the +/// protocol keeps apart, so the anchor key is a separate derivation. +const ANCHOR_DOMAIN: &[u8] = b"auths.local-fixture.trust-anchor/1"; + +const ASSURANCE_POLICY: &str = "raw-key-baseline"; +const RESOURCE_MATCHER: &str = "uri-namespace-v1"; +const PROFILE_POLICY: &str = "exact-v1"; +const CHANNEL_POLICY: &str = "none-v1"; +const BUDGET_ALGEBRA: &str = "numeric-ceiling-v1"; + +/// Audience the reference stack answers for. +pub const REFERENCE_AUDIENCE: &str = "auths.open-production/1"; + +/// The three profiles the reference stack enables. +pub const REFERENCE_PROFILES: [&str; 3] = [ + "auths.opentofu.saved-plan-apply/1", + "auths.postgresql.bounded-update/1", + "auths.github.issue-address/1", +]; + +/// Namespace the fixture anchor may delegate within, one per profile. +const REFERENCE_NAMESPACES: [&str; 3] = [ + "opentofu://reference", + "postgresql://reference", + "github://reference", +]; + +const REFERENCE_CAPABILITIES: [&str; 3] = ["apply", "update", "address"]; + +/// Anything that can go wrong assembling the fixture. +#[derive(Debug)] +pub struct FixtureError(pub String); + +impl std::fmt::Display for FixtureError { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str(&self.0) + } +} + +impl std::error::Error for FixtureError {} + +fn fail(what: &str) -> FixtureError { + FixtureError(what.to_owned()) +} + +/// Derives the fixture trust anchor's signing key from the stack seed. +/// +/// Domain-separated so the anchor key and the node's custody key are +/// independent even though one seed produces both. +#[must_use] +pub fn anchor_signing_key(seed: &[u8; 32]) -> SigningKey { + let mut hasher = Sha256::new(); + hasher.update(ANCHOR_DOMAIN); + hasher.update(seed); + let derived: [u8; 32] = hasher.finalize().into(); + SigningKey::from_bytes(&derived) +} + +/// Derives the fixture trust anchor's raw-key descriptor and principal. +/// +/// # Errors +/// +/// Returns [`FixtureError`] when the derived key cannot form a raw-key +/// principal. +pub fn anchor_principal(seed: &[u8; 32]) -> Result<(SigningKey, PrincipalId), FixtureError> { + let signing = anchor_signing_key(seed); + let descriptor = RawKeyDescriptor::new( + RawKeyType::Ed25519, + signing.verifying_key().to_bytes().to_vec(), + ) + .map_err(|_| fail("the derived anchor key is not a valid raw-key descriptor"))?; + let principal = descriptor + .principal() + .map_err(|_| fail("the derived anchor key has no principal"))?; + Ok((signing, principal)) +} + +fn verifier_configuration() -> Result { + let method = RawKeyMethod::new().map_err(|_| fail("raw-key method unavailable"))?; + let suite = Ed25519Suite::new().map_err(|_| fail("ed25519 suite unavailable"))?; + auths_registries::ImmutableRegistries::new( + &[&method as &dyn PrincipalMethod], + &[&suite as &dyn SignatureSuite], + ) + .map(|registries| registries.configuration_id()) + .map_err(|_| fail("the verifier configuration could not be computed")) +} + +fn profiles() -> Result, FixtureError> { + REFERENCE_PROFILES + .iter() + .map(|qualified| { + // A qualified profile is `/`; ProfileRef keeps them apart. + let (id, version) = qualified + .rsplit_once('/') + .ok_or_else(|| fail("a reference profile id carries no version"))?; + let version = version + .parse::() + .map_err(|_| fail("a reference profile version is not a number"))?; + ProfileRef::new( + ProfileId::parse(id).map_err(|_| fail("a reference profile id is malformed"))?, + version, + ) + .map_err(|_| fail("a reference profile reference is invalid")) + }) + .collect() +} + +fn permissions() -> Result { + let mut entries = Vec::new(); + for (capability, namespace) in REFERENCE_CAPABILITIES.iter().zip(REFERENCE_NAMESPACES) { + entries.push(Permission::new( + CapabilityId::parse(capability).map_err(|_| fail("a reference capability is malformed"))?, + ResourceId::parse(namespace).map_err(|_| fail("a reference namespace is malformed"))?, + )); + } + PermissionSet::new(entries).map_err(|_| fail("the reference permission set is invalid")) +} + +fn registries(profiles: &[ProfileRef]) -> Result { + AcceptedRegistries::new( + auths_registries::TARGET_V1_REGISTRY_MANIFEST, + vec![PrincipalMethodId::parse(RAW_KEY_V1).map_err(|_| fail("raw-key id"))?], + vec![SignatureSuiteId::parse(ED25519_V1).map_err(|_| fail("ed25519 id"))?], + vec![EvidenceTypeId::parse(RAW_KEY_V1).map_err(|_| fail("raw-key evidence id"))?], + Vec::new(), + Vec::new(), + vec![ + AssuranceClaimId::parse("offline-verifiable").map_err(|_| fail("assurance claim"))?, + AssuranceClaimId::parse("self-certifying-identifier") + .map_err(|_| fail("assurance claim"))?, + ], + Vec::new(), + vec![ResourceMatcherId::parse(RESOURCE_MATCHER).map_err(|_| fail("resource matcher"))?], + vec![BudgetAlgebraId::parse(BUDGET_ALGEBRA).map_err(|_| fail("budget algebra"))?], + Vec::new(), + profiles.to_vec(), + vec![ProfilePolicyId::parse(PROFILE_POLICY).map_err(|_| fail("profile policy"))?], + ) + .map_err(|_| fail("the accepted registries are inconsistent")) +} + +fn assurance() -> Result<(AssurancePolicyId, AssurancePolicy), FixtureError> { + let id = AssurancePolicyId::parse(ASSURANCE_POLICY).map_err(|_| fail("assurance policy id"))?; + let claim = AssuranceClaimId::parse("self-certifying-identifier") + .map_err(|_| fail("assurance claim id"))?; + let policy = AssurancePolicy::new( + id.clone(), + vec![ + AssuranceRequirement::new( + ParticipantRole::Root, + AssuranceQuantifier::Every, + claim.clone(), + None, + ), + AssuranceRequirement::new(ParticipantRole::Actor, AssuranceQuantifier::Every, claim, None), + ], + ) + .map_err(|_| fail("the assurance policy is invalid"))?; + Ok((id, policy)) +} + +/// Builds the reference stack's trusted context. +/// +/// `lifetime_seconds` sets both the anchor validity and the status-snapshot +/// freshness window. The stack is long-lived relative to a CI job, so this is +/// generated per run rather than committed -- a checked-in context would go +/// stale exactly the way a checked-in certificate does. +/// +/// # Errors +/// +/// Returns [`FixtureError`] when any component is rejected by the model. +pub fn build_context( + seed: &[u8; 32], + now: u64, + lifetime_seconds: u64, +) -> Result { + let (_signing, principal) = anchor_principal(seed)?; + let profile_refs = profiles()?; + let expires = now.saturating_add(lifetime_seconds); + let audience = + Audience::parse(REFERENCE_AUDIENCE).map_err(|_| fail("the reference audience is malformed"))?; + let (assurance_id, assurance_policy) = assurance()?; + let anchor = TrustAnchor::new( + TrustAnchorId::parse(principal.as_str()).map_err(|_| fail("the anchor id is malformed"))?, + principal, + vec![PrincipalMethodId::parse(RAW_KEY_V1).map_err(|_| fail("raw-key id"))?], + profile_refs.clone(), + permissions()?, + REFERENCE_NAMESPACES + .iter() + .map(|namespace| { + ResourceId::parse(namespace).map_err(|_| fail("a reference namespace is malformed")) + }) + .collect::, _>>()?, + AudienceSet::new(vec![audience.clone()]).map_err(|_| fail("the audience set is invalid"))?, + ValidityWindow::new( + Timestamp::new(now.saturating_sub(60)), + Timestamp::new(expires), + ) + .map_err(|_| fail("the anchor validity window is invalid"))?, + Some(BudgetCeiling::new( + BudgetAlgebraId::parse(BUDGET_ALGEBRA).map_err(|_| fail("budget algebra"))?, + 2, + )), + 2, + assurance_id, + StatusPolicy::ExpiryOnly, + ) + .map_err(|_| fail("the fixture trust anchor is invalid"))?; + TrustedContext::new( + verifier_configuration()?, + CompositionRequirement::new(None, 1, 1, 1) + .map_err(|_| fail("the composition requirement is invalid"))?, + vec![anchor], + registries(&profile_refs)?, + audience, + Challenge::new([0x22; 32]), + Timestamp::new(now), + assurance_policy, + PrincipalStatusSnapshot::new( + StatusSnapshotId::new([0x63; 32]), + Timestamp::new(now.saturating_sub(60)), + Timestamp::new(expires), + Vec::new(), + Vec::new(), + ) + .map_err(|_| fail("the principal status snapshot is invalid"))?, + GrantStatusSnapshot::new( + StatusSnapshotId::new([0x64; 32]), + Timestamp::new(now.saturating_sub(60)), + Timestamp::new(expires), + Vec::new(), + Vec::new(), + ) + .map_err(|_| fail("the grant status snapshot is invalid"))?, + ResourceMatcherId::parse(RESOURCE_MATCHER).map_err(|_| fail("resource matcher"))?, + ProfilePolicyId::parse(PROFILE_POLICY).map_err(|_| fail("profile policy"))?, + ChannelBindingId::parse(CHANNEL_POLICY).map_err(|_| fail("channel policy"))?, + VerifierLimits::default(), + ) + .map_err(|_| fail("the fixture trusted context is invalid")) +} + +#[cfg(test)] +mod tests { + use super::{anchor_signing_key, build_context}; + + const SEED: [u8; 32] = [7; 32]; + + #[test] + fn the_anchor_key_is_not_the_custody_key() { + // The seed signs receipts. If the anchor derived to the same key, one + // key would both grant authority and attest to its own exercise. + let anchor = anchor_signing_key(&SEED); + assert_ne!(anchor.to_bytes(), SEED); + } + + #[test] + fn the_same_seed_always_derives_the_same_anchor() { + assert_eq!( + anchor_signing_key(&SEED).to_bytes(), + anchor_signing_key(&SEED).to_bytes() + ); + } + + #[test] + fn a_different_seed_derives_a_different_anchor() { + let other = anchor_signing_key(&[9; 32]); + assert_ne!(anchor_signing_key(&SEED).to_bytes(), other.to_bytes()); + } + + #[test] + fn the_context_encodes_canonically() { + let context = build_context(&SEED, 1_700_000_000, 3_600).expect("fixture context"); + let bytes = auths_codec::encode_verifier_context(&context).expect("canonical bytes"); + assert!(!bytes.is_empty()); + let decoded = auths_codec::decode_verifier_context(&bytes).expect("round trip"); + assert_eq!( + auths_codec::encode_verifier_context(&decoded).expect("re-encode"), + bytes + ); + } +} From 4b91cd6961de27400d160ab06f32e3fd89931d10 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 13:21:39 +0100 Subject: [PATCH 32/61] formal: bind the root-linkage temporaries so aeneas can translate them (UNSIGNED) Two fixes from the first CI run that reached these jobs. 1. cargo fmt on the new local_fixture.rs. Four wrapping differences. 2. formal-translation crashed: Uncaught exception: Aeneas.Errors.CFailure(_) [Error] Could not translate the body of function 'auths_authority::evaluate_grant_view' Source: core/crates/auths-authority/src/lib.rs, lines 236:25-237:61 The reported span is the `depth_decreases` boolean, which is trivial. The cause is the line above it: `root_preserved(&root_linkage(&parent, ...))` takes a reference to a temporary in argument position, which aeneas's borrow model does not accept. Both call sites are from the root-preservation fix, so this is drift I introduced, not a pre-existing aeneas limitation. Both now bind the linkage to a local first. Identical value, identical evaluation order, one name. The second site at what was line 321 gets the same treatment even though the crash only named the first, because it is the same construct and would fail the moment the first one stopped shadowing it. NOT VERIFIED LOCALLY. charon and aeneas are not installed on this host, so this is a hypothesis CI has to confirm. If it is wrong, the next run reports the same CFailure on the same span and the cause is elsewhere in the body. auths-authority's 11 unit tests still pass, which is all that can be checked here. Not addressed, expected: the three-node job now gets PAST the trusted-context crash loop -- the smoke test passes, all three replicas start -- and fails later on `spawnSync auths-sandbox-request ENOENT` in the installed-client e2e. That is the intermediate state step 1 was expected to produce, and steps 2-4 replace that binary and the two tests that call it. Co-Authored-By: Claude Opus 5 --- core/crates/auths-authority/src/lib.rs | 10 +++++-- .../runtime/auths-node/src/local_fixture.rs | 26 ++++++++++++------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/core/crates/auths-authority/src/lib.rs b/core/crates/auths-authority/src/lib.rs index 6c8e0140..f1fe1b25 100644 --- a/core/crates/auths-authority/src/lib.rs +++ b/core/crates/auths-authority/src/lib.rs @@ -231,8 +231,13 @@ pub fn evaluate_grant_view<'grant>( grant_id: GrantId, grant: GrantAuthorityView<'grant>, ) -> DelegationEvaluation<'grant> { + // Bound to a local rather than borrowed inline. `root_preserved` takes a + // reference, and a reference to a temporary in argument position is what + // aeneas failed to translate here (Aeneas.Errors.CFailure on this span). + // Same value, same order of evaluation, one name. + let linkage = root_linkage(&parent, grant.issuer); let checks = AttenuationChecks { - root_preserved: root_preserved(&root_linkage(&parent, grant.issuer)), + root_preserved: root_preserved(&linkage), depth_decreases: parent.remaining_depth > 0 && grant.remaining_depth < parent.remaining_depth, profile_attenuates: selected_profile_attenuates( @@ -318,7 +323,8 @@ pub fn evaluate_action_coverage_view( // Terminal coverage is the same chain claim as a delegation edge with the // actor in the issuer position: an authority that never descended from the // root it claims authorizes nothing. - if !root_preserved(&root_linkage(&authority, action.actor)) + let linkage = root_linkage(&authority, action.actor); + if !root_preserved(&linkage) || !optional_grant_id_equal(action.terminal_grant, authority.last_grant) { return CoverageDecision::Denied(DenialReason::BrokenGrantChain); diff --git a/product/runtime/auths-node/src/local_fixture.rs b/product/runtime/auths-node/src/local_fixture.rs index 221df8e0..086549f6 100644 --- a/product/runtime/auths-node/src/local_fixture.rs +++ b/product/runtime/auths-node/src/local_fixture.rs @@ -17,11 +17,10 @@ use auths_model::{ AcceptedRegistries, AssuranceClaimId, AssurancePolicy, AssurancePolicyId, AssuranceQuantifier, AssuranceRequirement, Audience, AudienceSet, BudgetAlgebraId, BudgetCeiling, CapabilityId, Challenge, ChannelBindingId, CompositionRequirement, EvidenceTypeId, GrantStatusSnapshot, - ParticipantRole, Permission, PermissionSet, PrincipalId, PrincipalMethodId, PrincipalStatusSnapshot, - ProfileId, - ProfilePolicyId, ProfileRef, ResourceId, ResourceMatcherId, SignatureSuiteId, StatusPolicy, - StatusSnapshotId, Timestamp, TrustAnchor, TrustAnchorId, TrustedContext, ValidityWindow, - VerifierConfigurationId, VerifierLimits, + ParticipantRole, Permission, PermissionSet, PrincipalId, PrincipalMethodId, + PrincipalStatusSnapshot, ProfileId, ProfilePolicyId, ProfileRef, ResourceId, ResourceMatcherId, + SignatureSuiteId, StatusPolicy, StatusSnapshotId, Timestamp, TrustAnchor, TrustAnchorId, + TrustedContext, ValidityWindow, VerifierConfigurationId, VerifierLimits, }; use auths_ports::{PrincipalMethod, SignatureSuite}; use auths_raw_key::{RAW_KEY_V1, RawKeyDescriptor, RawKeyMethod, RawKeyType}; @@ -147,7 +146,8 @@ fn permissions() -> Result { let mut entries = Vec::new(); for (capability, namespace) in REFERENCE_CAPABILITIES.iter().zip(REFERENCE_NAMESPACES) { entries.push(Permission::new( - CapabilityId::parse(capability).map_err(|_| fail("a reference capability is malformed"))?, + CapabilityId::parse(capability) + .map_err(|_| fail("a reference capability is malformed"))?, ResourceId::parse(namespace).map_err(|_| fail("a reference namespace is malformed"))?, )); } @@ -190,7 +190,12 @@ fn assurance() -> Result<(AssurancePolicyId, AssurancePolicy), FixtureError> { claim.clone(), None, ), - AssuranceRequirement::new(ParticipantRole::Actor, AssuranceQuantifier::Every, claim, None), + AssuranceRequirement::new( + ParticipantRole::Actor, + AssuranceQuantifier::Every, + claim, + None, + ), ], ) .map_err(|_| fail("the assurance policy is invalid"))?; @@ -215,8 +220,8 @@ pub fn build_context( let (_signing, principal) = anchor_principal(seed)?; let profile_refs = profiles()?; let expires = now.saturating_add(lifetime_seconds); - let audience = - Audience::parse(REFERENCE_AUDIENCE).map_err(|_| fail("the reference audience is malformed"))?; + let audience = Audience::parse(REFERENCE_AUDIENCE) + .map_err(|_| fail("the reference audience is malformed"))?; let (assurance_id, assurance_policy) = assurance()?; let anchor = TrustAnchor::new( TrustAnchorId::parse(principal.as_str()).map_err(|_| fail("the anchor id is malformed"))?, @@ -230,7 +235,8 @@ pub fn build_context( ResourceId::parse(namespace).map_err(|_| fail("a reference namespace is malformed")) }) .collect::, _>>()?, - AudienceSet::new(vec![audience.clone()]).map_err(|_| fail("the audience set is invalid"))?, + AudienceSet::new(vec![audience.clone()]) + .map_err(|_| fail("the audience set is invalid"))?, ValidityWindow::new( Timestamp::new(now.saturating_sub(60)), Timestamp::new(expires), From 8acdf8ba8f2c3b3f7d661f0b4a12cc22713f337d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 16 Aug 2026 12:23:58 +0000 Subject: [PATCH 33/61] chore(formal): regenerate qualification artifacts Source-SHA: 4b91cd6961de27400d160ab06f32e3fd89931d10 Workflow-Run: 31946850031 --- formal/qualification/aeneas/source-closure.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/formal/qualification/aeneas/source-closure.json b/formal/qualification/aeneas/source-closure.json index ddbb6509..4278cf8e 100644 --- a/formal/qualification/aeneas/source-closure.json +++ b/formal/qualification/aeneas/source-closure.json @@ -1,6 +1,6 @@ { "schema": "auths-proof-translation-source-closure/v2", - "digest": "c9ab4c8e66d4c2f00947a9c71acfc32c6dc6ad6bc57c2cec69cdf0404cabdfd0", + "digest": "aed192cb16461c8e286d6ce19b5f51b64df7bded3b37bb6bc27a95d941153ab3", "files": [ { "path": "Cargo.lock", @@ -30,7 +30,7 @@ }, { "path": "core/crates/auths-authority/src/lib.rs", - "sha256": "a99955461a8df1978a7aace9a3891ed0c3b606044ab3de40b2b05048217e7919" + "sha256": "e8a5709a40372551f8b431a38874123323101c575d3bd1b1f1332411fb4e089e" }, { "path": "core/crates/auths-model/Cargo.toml", From 668f2190d484c54f6581eceb6ec1f19e6a9ac969 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 13:36:25 +0100 Subject: [PATCH 34/61] ci: regenerate the architecture snapshot for the std feature edges (UNSIGNED) 697bd99 added `features = ["std"]` to auths-did-key, auths-did-keri and auths-registries so auths-node builds as a single package, but only the freeze was resynced -- not the dependency snapshot. The drift is exactly those three edges and nothing else: same source, same target, same layer, same kind, `features: []` -> `features: ["std"]`. Three added, three removed, no new dependency and no layer change. Co-Authored-By: Claude Opus 5 --- architecture/dependency-graph.json | 12 +++++++++--- release/semantic-freeze.json | 10 +++++----- xtask/src/semantic_freeze.rs | 6 +++--- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/architecture/dependency-graph.json b/architecture/dependency-graph.json index 48a74368..077f5580 100644 --- a/architecture/dependency-graph.json +++ b/architecture/dependency-graph.json @@ -5075,7 +5075,9 @@ "target_condition": null, "optional": false, "default_features": false, - "features": [] + "features": [ + "std" + ] }, { "source": "auths-node", @@ -5087,7 +5089,9 @@ "target_condition": null, "optional": false, "default_features": false, - "features": [] + "features": [ + "std" + ] }, { "source": "auths-node", @@ -5207,7 +5211,9 @@ "target_condition": null, "optional": false, "default_features": false, - "features": [] + "features": [ + "std" + ] }, { "source": "auths-node", diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index f064897b..e392a5fb 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 113, + "freezeVersion": 114, "publicSurface": { "rustRoots": [ "auths", @@ -91,7 +91,7 @@ }, { "id": "auths.frozen-bytes/architecture/dependency-graph.json", - "version": 27, + "version": 28, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -99,7 +99,7 @@ "owners": [ "architecture/dependency-graph.json" ], - "sha256": "7dc4d1116402841ffabe7f0d2da7e473da5fc11caf3d0542bad95d266053f15c" + "sha256": "afe8bd81f67ebf8895e25bbeb7621de2ae42e85c27b2662ce670df4ddf7eff45" }, { "id": "auths.frozen-bytes/bindings/wasm/auths-proof-wasm/identity-abi-v1.json", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 112, + "version": 113, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "196dd6d106ece4f2f365bf07e26a32ef50dcc0771e9e1b6e56a334205227efda" + "sha256": "e15cee6dd6ecef6a3ef43f559677be26756304925abb03755fd14beb17102392" } ] } diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index 56b4393b..ba186195 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 113; +const FREEZE_VERSION: u64 = 114; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -629,7 +629,7 @@ fn generate_inventory() -> Result { for (id, path) in frozen_byte_inventories()? { let version = match path.as_str() { - "architecture/dependency-graph.json" => 27, + "architecture/dependency-graph.json" => 28, "bindings/wasm/auths-proof-wasm/identity-abi-v1.json" => 4, "core/fixtures/v1/manifest.json" => 4, "product/conformance/v1/simplified-product-waist.json" => 2, @@ -696,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 112, + 113, FreezeClassification::ReleaseMetadata, &[ "package-names", From 0f6e3418bb1c44fa3007c92817e29aa1baabe90a Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 13:37:31 +0100 Subject: [PATCH 35/61] release: resync the freeze after the upstream formal regeneration (UNSIGNED) Co-Authored-By: Claude Opus 5 --- release/semantic-freeze.json | 10 +++++----- xtask/src/semantic_freeze.rs | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index e392a5fb..055f9ea9 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 114, + "freezeVersion": 115, "publicSurface": { "rustRoots": [ "auths", @@ -235,7 +235,7 @@ }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/source-closure.json", - "version": 13, + "version": 14, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -243,7 +243,7 @@ "owners": [ "formal/qualification/aeneas/source-closure.json" ], - "sha256": "3fda7eab0ae605b30b15c8f969833e7dbb1373db7d48db9e81a16818a22dd155" + "sha256": "65a972c4fc5bb3e8372af43cce20a3c49b1d15ae0dcf3eef2c4772f7f4cd4df8" }, { "id": "auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 113, + "version": 114, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "e15cee6dd6ecef6a3ef43f559677be26756304925abb03755fd14beb17102392" + "sha256": "ac5aed105e465d1ff4ac1d6d5c875973a52320f6c93d90b52f1f639bc22fb3f5" } ] } diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index ba186195..a14a342a 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 114; +const FREEZE_VERSION: u64 = 115; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -636,7 +636,7 @@ fn generate_inventory() -> Result { "formal/assurance-manifest-v1.toml" => 4, "formal/qualification/aeneas/qualification.toml" => 3, "formal/qualification/aeneas/generated" => 4, - "formal/qualification/aeneas/source-closure.json" => 13, + "formal/qualification/aeneas/source-closure.json" => 14, "product/fixtures/v1/errors/manifest.json" => 4, "product/fixtures/v1/bounded-policy/manifest.json" => 2, "product/fixtures/v1/github/manifest.json" @@ -696,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 113, + 114, FreezeClassification::ReleaseMetadata, &[ "package-names", From 3d8338f0f93fef11f83de193eee22c241e122387 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 16:44:41 +0100 Subject: [PATCH 36/61] formal: close the refinement gap by regenerating the translation (UNSIGNED) The PR asserted one thing that was not true. `source-closure.json` attested "this Lean was produced from this source" while the generated model still answered `ok true` for a bounded ceiling with an absent request -- the vacuous budget bug this branch fixed in Rust. The translation could not be regenerated, so the gap was carried as hypotheses instead. It is regenerated now. optional_budget_covers (some ceiling) none: ok true -> ok false WHY IT COULD NOT REGENERATE. `formal qualify aeneas` ran `lake build` before it reached `reproduce()`. Qualification REGENERATES the Lean a build compiles, so the moment a translation referenced a symbol its upstream crate had not exported yet, the build failed on the generated file, qualification aborted before translating, and the upstream crate could never produce the symbol. Split into `prepare_formal_translation` (no Lean) and `build_and_audit_formal`; ordinary `cargo xtask formal` keeps build-first, only `qualify aeneas` inverts. The compiled gate still decides success -- it runs after synchronization, before evidence is written. Two tests lock the ordering structurally and were mutation-proven: restoring `prepare_formal(` fails them. THREE AENEAS BLOCKERS, one rule. Aeneas cannot translate a branching expression in struct-field position. Ten of the twelve dimension fields were function calls and all translated; the only two written inline were the only two that failed. `depth_decreases` (`&&`) and `extensions_attenuate` (`match`) are now named functions like their siblings. That `extensions_attenuate` was one of them is not a coincidence: it is the eleventh dimension, the one that was vacuously `:= true`, the newest, and the only one written unlike its neighbours. `aeneas_version_matches` compared a hard 7-character truncation, so an Aeneas built from exactly the pinned commit was rejected when git abbreviated to 8. It tested formatting, not identity. Any genuine prefix >= 7 chars now passes. DEPENDENCY CLOSURE, no axioms. `budget_ceiling_covers_action` and `ProfileBudgetExpression` are translated in `auths_model`; `root_preserved` and `RootLinkage` in `auths_algebra_kernel`. Generic `root_preserved` translates faithfully, trait bound intact. The authority bridge reboxes its local carriers field by field and delegates -- 17 rfl proofs, four linkage fields and eleven attenuation fields generated from the contract, so a twelfth dimension regenerates the bridge rather than being dropped. It restates no Boolean semantics. `required_compiled_external_axioms` stays 0. The hand-rendered `attenuation_checks_accept` is deleted: it re-rendered the contract in Lean beside the Rust the same contract generates, and two renderings can disagree. The imported translation cannot -- it IS the shipped Rust. BOTH CONDITIONAL PREMISES REMOVED, not renamed. TranslatedBudgetCoverageCurrent excluded the one input class where the translation was stale. The translation is current; the class is covered. AuthorityStateAnchored assumed a root the translated state could not express. `AuthorityStateView` now carries `root`, so `richAuthorityState` reads it and rooting is structural. `root_preserved_rich_spec` proves the translated dimension is exactly `rooted AND actor = subject`. Both production refinement theorems are now unconditional over valid representations. `translated_budget_coverage_gap_is_the_absent_request` recorded the staleness as checked evidence and stopped compiling on regeneration, exactly as its comment promised. Replaced by four positive regressions. Profile expressibility is modelled explicitly as trusted registry context an action cannot assert about itself, with the complete truth table proved. A qualification vector asserted that a bounded ceiling COVERS an undeclared request -- the bug, written down as a passing test. Corrected, plus vectors for both expressibility modes. Two declared-request vectors were removed rather than closed with `native_decide`: comparing ceilings reaches opaque `as_bytes`, and buying two vectors with a new axiom is the trade this branch exists to refuse. `budgetCoversAction_declared` covers that case abstractly. Verified: lake build 3286 jobs; assurance audit 124 compiled statements; qualification cases 6/6; clean reproduction byte-identical; decision GO-AENEAS-WITH-PRODUCTION-RESHAPE; no axiom, sorry, or admit in any compiled artifact. Co-Authored-By: Claude Opus 5 --- core/crates/auths-authority/src/lib.rs | 44 +- formal/Auths/Refinement/Production.lean | 636 +++++++++++------- formal/Auths/Rich/Semantics.lean | 95 ++- formal/Auths/Theorems.lean | 5 +- formal/assurance-manifest-v1.toml | 411 +++++++---- formal/qualification/aeneas/cases/Model.lean | 30 +- .../aeneas/generated/algebra/Funs.lean | 17 + .../aeneas/generated/algebra/Types.lean | 9 + .../aeneas/generated/algebra/translation.json | 33 +- .../aeneas/generated/authority/Funs.lean | 204 +++--- .../generated/authority/FunsExternal.lean | 155 ++++- .../authority/FunsExternal_Template.lean | 26 +- .../aeneas/generated/authority/Types.lean | 39 +- .../authority/TypesExternal_Template.lean | 11 +- .../generated/authority/translation.json | 303 ++++++--- .../generated/bounded_policy/translation.json | 2 +- .../generated/lifecycle/translation.json | 2 +- .../aeneas/generated/model/Funs.lean | 36 +- .../aeneas/generated/model/Types.lean | 16 +- .../aeneas/generated/model/translation.json | 139 ++-- .../qualification/aeneas/qualification.toml | 29 +- .../qualification/aeneas/source-closure.json | 10 +- release/semantic-freeze.json | 22 +- xtask/src/formal.rs | 123 +++- xtask/src/formal_qualification.rs | 224 ++++-- xtask/src/semantic_freeze.rs | 12 +- 26 files changed, 1896 insertions(+), 737 deletions(-) diff --git a/core/crates/auths-authority/src/lib.rs b/core/crates/auths-authority/src/lib.rs index f1fe1b25..97392f61 100644 --- a/core/crates/auths-authority/src/lib.rs +++ b/core/crates/auths-authority/src/lib.rs @@ -142,6 +142,42 @@ impl PartialEq for CanonicalPrincipal<'_> { } } +/// Whether a grant's critical extensions attenuate its parent's. +/// +/// A parent declaring no extension scope constrains nothing, so any grant +/// attenuates it. Otherwise the sets must match exactly: the eleventh +/// dimension is equality, not containment. +/// +/// Named for the same reason as [`depth_decreases`]: aeneas cannot translate a +/// branching expression sitting in struct-field position, and this `match` was +/// the second one in `evaluate_grant_view`. +fn extensions_attenuate( + parent_extensions: Option<&CriticalExtensions>, + grant_extensions: &CriticalExtensions, +) -> bool { + match parent_extensions { + Some(parent) => critical_extensions_equal(grant_extensions, parent), + None => true, + } +} + +/// Whether a delegation strictly decreases the remaining delegation depth. +/// +/// A parent with no depth left can delegate nothing, and a child must have +/// strictly less depth than its parent. +/// +/// This is a named function rather than the inline conjunction it replaces +/// because aeneas raises `Internal error, please file an issue` translating a +/// short-circuiting `&&` here, in struct-field position or hoisted to a local +/// alike. Every sibling dimension in `AttenuationChecks` is already a function +/// call, so this also makes the one odd field out consistent with the rest. +fn depth_decreases(parent_remaining: u16, grant_remaining: u16) -> bool { + if parent_remaining == 0 { + return false; + } + grant_remaining < parent_remaining +} + /// Projects the chain-linkage facts the trust-root dimension consumes. fn root_linkage<'a>( parent: &AuthorityStateView<'a>, @@ -238,8 +274,7 @@ pub fn evaluate_grant_view<'grant>( let linkage = root_linkage(&parent, grant.issuer); let checks = AttenuationChecks { root_preserved: root_preserved(&linkage), - depth_decreases: parent.remaining_depth > 0 - && grant.remaining_depth < parent.remaining_depth, + depth_decreases: depth_decreases(parent.remaining_depth, grant.remaining_depth), profile_attenuates: selected_profile_attenuates( parent.profile, parent.allowed_profiles, @@ -258,10 +293,7 @@ pub fn evaluate_grant_view<'grant>( grant.assurance_floor, parent.assurance_policy, ), - extensions_attenuate: match parent.extensions { - Some(parent) => critical_extensions_equal(grant.extensions, parent), - None => true, - }, + extensions_attenuate: extensions_attenuate(parent.extensions, grant.extensions), }; // `root_preserved` subsumes the issuer/subject linkage and additionally // rejects a parent state that never descended from the root it claims, so diff --git a/formal/Auths/Refinement/Production.lean b/formal/Auths/Refinement/Production.lean index 5264a3d4..9966369a 100644 --- a/formal/Auths/Refinement/Production.lean +++ b/formal/Auths/Refinement/Production.lean @@ -220,6 +220,26 @@ instance (child : auths_model.CriticalExtensions) intro result resultIff simpa [parentCase, OptionalCriticalExtensionsAttenuate] using resultIff +/-- +The extensions dimension through its named helper. + +`auths_authority::extensions_attenuate` is the function the inline `match` in +`evaluate_grant_view` became; aeneas cannot translate a branching expression in +struct-field position. The semantics are unchanged, so this delegates to +[`optional_critical_extensions_attenuate_spec`]. +-/ +@[step] theorem extensions_attenuate_spec + (parent : Option auths_model.CriticalExtensions) + (child : auths_model.CriticalExtensions) + (childBounded : CriticalExtensionsBounded child) + (parentBounded : ∀ extensions ∈ parent, + CriticalExtensionsBounded extensions) : + auths_authority.extensions_attenuate parent child + ⦃ result => result ↔ + OptionalCriticalExtensionsAttenuate child parent ⦄ := by + unfold auths_authority.extensions_attenuate + exact optional_critical_extensions_attenuate_spec child parent childBounded parentBounded + theorem slice_eq_iff_val_eq {α : Type} (left right : Slice α) : left = right ↔ left.val = right.val := Subtype.ext_iff @@ -1466,33 +1486,10 @@ def OptionalBudgetBounded · simpa [OptionalBudgetBounded, childEq] using childBounded · simpa [OptionalBudgetBounded, parentEq] using parentBounded -/-- -The one input class on which the pinned Aeneas translation of -`auths_model::optional_budget_covers` is known to be stale. - -The shipping Rust now answers `false` for a present ceiling with an absent -request — an action that declares no bound on what it may spend is exactly the -authority a ceiling exists to deny. The translation replayed here predates -that correction and still answers `true` on that one pair -(`formal/qualification/aeneas/generated/model/Funs.lean`, `optional_budget_covers`, -`| none => ok true`). - -Hand-editing the generated body would fabricate the claim the translation pin -asserts — "this Lean was produced from this source" — so the gap is carried as -an explicit hypothesis instead, exactly as `AuthorityStateAnchored` carries the -missing `root` field. `translated_budget_coverage_gap_is_the_absent_request` -below pins the gap precisely and stops compiling the moment the translation is -regenerated, which is what forces this hypothesis to be deleted then. --/ -def TranslatedBudgetCoverageCurrent - (ceiling requested : Option auths_model.BudgetCeiling) : Prop := - ceiling = none ∨ requested ≠ none - @[step] theorem optional_budget_covers_spec (ceiling requested : Option auths_model.BudgetCeiling) (ceilingBounded : OptionalBudgetBounded ceiling) - (requestedBounded : OptionalBudgetBounded requested) - (translationCurrent : TranslatedBudgetCoverageCurrent ceiling requested) : + (requestedBounded : OptionalBudgetBounded requested) : auths_model.optional_budget_covers ceiling requested ⦃ result => result ↔ Auths.Rich.budgetCovers @@ -1500,8 +1497,11 @@ def TranslatedBudgetCoverageCurrent cases ceilingEq : ceiling <;> cases requestedEq : requested <;> simp only [auths_model.optional_budget_covers] case some.none => - exact absurd translationCurrent (by simp [TranslatedBudgetCoverageCurrent, - ceilingEq, requestedEq]) + -- A bounded ceiling with no declared request is DENIED. The translation + -- answers `ok false` here, matching the shipping Rust; this case used to be + -- excluded by a staleness hypothesis because the pinned translation still + -- answered `true` here. + simp [Auths.Rich.budgetCovers] case some.some => unfold auths_model.BudgetCeiling.covers unfold auths_model.BudgetCeiling.attenuates @@ -1517,21 +1517,39 @@ def TranslatedBudgetCoverageCurrent WP.spec, WP.theta, WP.wp_return] /-- -The translation gap is exactly one pair, and it is a fail-open one. +Positive regression: a bounded ceiling denies an undeclared request. -For every bounded ceiling the pinned translation answers `true` where the -shipping semantics answer `false`. Stating it as a theorem rather than a -comment means the staleness is itself checked evidence: once the translation is -regenerated this stops being provable, and the hypothesis -`TranslatedBudgetCoverageCurrent` must be removed in the same change. +This replaces `translated_budget_coverage_gap_is_the_absent_request`, which +asserted the opposite as CHECKED EVIDENCE OF STALENESS while the pinned +translation still answered `true` on this pair. The translation has been +regenerated from the shipping Rust, so the gap it pinned no longer exists and +the fact is now stated positively. + +An action declaring no bound on what it may spend is exactly the authority a +ceiling exists to deny. -/ -theorem translated_budget_coverage_gap_is_the_absent_request +theorem translated_bounded_ceiling_denies_absent_request (ceiling : auths_model.BudgetCeiling) : - auths_model.optional_budget_covers (some ceiling) none = ok true ∧ - ¬ Auths.Rich.budgetCovers (some (richBudget ceiling)) none := by - constructor - · rfl - · simp [Auths.Rich.budgetCovers] + auths_model.optional_budget_covers (some ceiling) none = ok false := rfl + +/-- The same fact through the profile-aware entry point, expressible profile. -/ +theorem translated_expressible_absent_request_is_denied + (ceiling : auths_model.BudgetCeiling) : + auths_model.budget_ceiling_covers_action (some ceiling) none + auths_model.ProfileBudgetExpression.Expressible = ok false := rfl + +/-- An inexpressible profile spends zero, which every ceiling covers. -/ +theorem translated_inexpressible_absent_request_is_covered + (ceiling : auths_model.BudgetCeiling) : + auths_model.budget_ceiling_covers_action (some ceiling) none + auths_model.ProfileBudgetExpression.Inexpressible = ok true := rfl + +/-- An absent ceiling bounds nothing, so it covers any request. -/ +theorem translated_absent_ceiling_covers_anything + (requested : Option auths_model.BudgetCeiling) + (expression : auths_model.ProfileBudgetExpression) : + auths_model.budget_ceiling_covers_action none requested expression = ok true := by + cases requested <;> cases expression <;> rfl def StatusPolicyValid (policy : auths_model.StatusPolicy) : Prop := match policy with @@ -1770,6 +1788,9 @@ def richProfileScope structure AuthorityStateViewValid (view : auths_authority.AuthorityStateView) : Prop where + /-- The regenerated view carries the chain root; it is a principal like any + other and shares the same representation bound. -/ + root : StringBounded view.root subject : StringBounded view.subject allowedProfiles : ProfileSliceBounded view.allowed_profiles selectedProfile : @@ -1809,28 +1830,19 @@ structure ActionAuthorityViewValid validity : ValidityWindowValid view.validity /-- -The trust root supplied to `richAuthorityState` is the root the production -state actually descends from. - -`auths_authority::AuthorityStateView` gained a `root` field so the shipping -kernel can compute `root_preserved` instead of asserting it; the Aeneas -translation replayed here predates that field, so the correspondence between -the Rust root and the model root must be carried as an explicit hypothesis -until the translation is regenerated. Regenerating it lets this predicate be -discharged as `richPrincipal view.root = root` rather than assumed. --/ -def AuthorityStateAnchored - (root : Auths.Rich.Principal ProductionVocabulary) - (view : auths_authority.AuthorityStateView) : Prop := - (view.last_grant.map richGrantId).isSome = true ∨ - root = richPrincipal view.subject +The rich chain state a translated authority view denotes. +`root` is READ FROM THE VIEW. It used to be an extra parameter with an +`AuthorityStateAnchored` hypothesis relating it to the view, because the +translated `AuthorityStateView` carried no root field to read. The regenerated +translation carries `root`, so the anchoring is structural and the hypothesis +is gone. +-/ def richAuthorityState - (root : Auths.Rich.Principal ProductionVocabulary) (view : auths_authority.AuthorityStateView) (valid : AuthorityStateViewValid view) : Auths.Rich.ChainState ProductionVocabulary where - root := root + root := richPrincipal view.root subject := richPrincipal view.subject scope := { profileScope := @@ -1939,6 +1951,112 @@ def productionCoverageDecision intro result resultIff simpa using resultIff +/-- +Maps the translated profile-budget capability onto the rich carrier. + +Trusted registry context on both sides: the action supplies only the requested +budget, never this flag. +-/ +def richBudgetExpression + (expression : auths_model.ProfileBudgetExpression) : Auths.Rich.BudgetExpression := + match expression with + | auths_model.ProfileBudgetExpression.Expressible => Auths.Rich.BudgetExpression.expressible + | auths_model.ProfileBudgetExpression.Inexpressible => Auths.Rich.BudgetExpression.inexpressible + +/-- +The profile-aware budget dimension refines the rich `budgetCoversAction`. + +Expressibility is trusted registry context: it only ever reclassifies an ABSENT +request, and never lets a declared request escape a ceiling. +-/ +@[step] theorem budget_ceiling_covers_action_rich_spec + (ceiling requested : Option auths_model.BudgetCeiling) + (expression : auths_model.ProfileBudgetExpression) + (ceilingBounded : OptionalBudgetBounded ceiling) + (requestedBounded : OptionalBudgetBounded requested) : + auths_model.budget_ceiling_covers_action ceiling requested expression + ⦃ result => + result ↔ Auths.Rich.budgetCoversAction + (ceiling.map richBudget) (requested.map richBudget) + (richBudgetExpression expression) ⦄ := by + unfold auths_model.budget_ceiling_covers_action + cases requestedEq : requested <;> cases expressionEq : expression <;> + simp only [richBudgetExpression, Auths.Rich.budgetCoversAction] + · apply spec_mono + (optional_budget_covers_spec ceiling none ceilingBounded + (by simpa [OptionalBudgetBounded, requestedEq] using requestedBounded)) + intro result resultIff + simpa using resultIff + · simp [WP.spec, WP.theta, WP.wp_return] + · apply spec_mono + (optional_budget_covers_spec ceiling _ ceilingBounded + (by simpa [OptionalBudgetBounded, requestedEq] using requestedBounded)) + intro result resultIff + simpa using resultIff + · apply spec_mono + (optional_budget_covers_spec ceiling _ ceilingBounded + (by simpa [OptionalBudgetBounded, requestedEq] using requestedBounded)) + intro result resultIff + simpa using resultIff + +/-- +The translated depth dimension is the rich strict-decrease relation. + +A parent with no remaining depth delegates nothing, and a child must have +strictly less depth than its parent. +-/ +@[step] theorem depth_decreases_spec + (parentRemaining grantRemaining : Std.U16) : + auths_authority.depth_decreases parentRemaining grantRemaining + ⦃ result => + result ↔ (0 < parentRemaining.val ∧ grantRemaining.val < parentRemaining.val) ⦄ := by + unfold auths_authority.depth_decreases + split <;> simp_all [WP.spec, WP.theta, WP.wp_return] <;> omega + +/-- +The translated root-preservation dimension is exactly the rich root relation. + +`root_preserved` reads the linkage the regenerated translation builds from the +authority view: its `parent_root` is `view.root`, the field that did not exist +before regeneration and whose absence forced `AuthorityStateAnchored`. So the +chain claim is now DERIVED from the translated state rather than assumed about +it. + +The right-hand side is precisely `rooted` conjoined with the actor/subject +identity that terminal coverage requires. +-/ +@[step] theorem root_preserved_rich_spec + (view : auths_authority.AuthorityStateView) + (actor : auths_model.PrincipalId) + (rootBounded : StringBounded view.root) + (subjectBounded : StringBounded view.subject) + (actorBounded : StringBounded actor) : + auths_authority.auths_algebra_kernel.root_preserved + auths_authority.CanonicalPrincipal.Insts.CoreCmpPartialEqCanonicalPrincipal + { parent_root := view.root + parent_subject := view.subject + parent_delegated := view.last_grant.isSome + grant_issuer := actor } + ⦃ result => + result ↔ + ((view.last_grant.isSome = true ∨ + richPrincipal view.root = richPrincipal view.subject) ∧ + richPrincipal actor = richPrincipal view.subject) ⦄ := by + unfold + auths_authority.auths_algebra_kernel.root_preserved + auths_authority.auths_algebra_kernel.toRootLinkage + _root_.auths_algebra_kernel.root_preserved + auths_authority.CanonicalPrincipal.Insts.CoreCmpPartialEqCanonicalPrincipal + auths_authority.CanonicalPrincipal.Insts.CoreCmpPartialEqCanonicalPrincipal.eq + split + · step with principal_id_equal_rich_spec as ⟨issuerAccepted, issuerIff⟩ + simp_all [WP.spec, WP.theta, WP.wp_return] + · step with principal_id_equal_rich_spec as ⟨rootAccepted, rootIff⟩ + split + · step with principal_id_equal_rich_spec as ⟨issuerAccepted, issuerIff⟩ + simp_all [WP.spec, WP.theta, WP.wp_return] + · simp_all [WP.spec, WP.theta, WP.wp_return] + @[step] theorem optional_grant_id_equal_rich_spec (left right : Option auths_model.GrantId) : auths_model.optional_grant_id_equal left right @@ -2054,7 +2172,7 @@ def productionCoverageDecision @[step] theorem attenuation_checks_accept_spec (checks : auths_authority.auths_algebra_kernel.generated.AttenuationChecks) : - auths_algebra_kernel.generated.attenuation_checks_accept + auths_authority.auths_algebra_kernel.generated.attenuation_checks_accept checks ⦃ result => result ↔ @@ -2069,8 +2187,14 @@ def productionCoverageDecision checks.status_attenuates ∧ checks.assurance_attenuates ∧ checks.extensions_attenuate ⦄ := by + -- The authority translation carries its own `AttenuationChecks`, so this + -- goes through the reviewed transparent adapter: unfold the delegation and + -- the field-by-field rebox, then the owning crate's conjunction. The rebox + -- is definitional, so the eleven projections reduce to `checks.`. unfold - auths_algebra_kernel.generated.attenuation_checks_accept + auths_authority.auths_algebra_kernel.generated.attenuation_checks_accept + auths_authority.auths_algebra_kernel.generated.toAttenuationChecks + _root_.auths_algebra_kernel.generated.attenuation_checks_accept split <;> simp_all [WP.spec, WP.theta, WP.wp_return] split <;> simp_all [WP.wp_return] split <;> simp_all [WP.wp_return] @@ -2295,30 +2419,27 @@ The mechanically translated terminal-coverage evaluator returns exactly the ordered rich coverage decision. Permission and audience checks are proved as membership. -A bounded ceiling with an absent requested budget is **excluded** by -`budgetTranslationCurrent`: on that one pair the pinned translation still -returns the pre-correction answer, so the statement would be false rather than -weak if it claimed that case. See `TranslatedBudgetCoverageCurrent` and -`translated_budget_coverage_gap_is_the_absent_request`. +The correspondence is UNCONDITIONAL over valid representations. A bounded +ceiling with an absent requested budget used to be excluded by a +`budgetTranslationCurrent` hypothesis, because the pinned translation still +returned the pre-correction answer on that one pair. The translation is +regenerated from the shipping Rust, so that case is now covered like any other. -/ theorem translated_coverage_refines_rich_spec - (root : Auths.Rich.Principal ProductionVocabulary) (authority : auths_authority.AuthorityStateView) (action : auths_model.ActionAuthorityView) (authorityValid : AuthorityStateViewValid authority) (actionValid : ActionAuthorityViewValid action) - (anchored : AuthorityStateAnchored root authority) - (budgetTranslationCurrent : - TranslatedBudgetCoverageCurrent - authority.budget_ceiling action.requested_budget) : - auths_authority.evaluate_action_coverage_view authority action + (expression : auths_model.ProfileBudgetExpression) : + auths_authority.evaluate_action_coverage_view authority action expression ⦃ result => result = productionCoverageDecision (Auths.Rich.evaluateCoverage - (richAuthorityState root authority authorityValid) - (richAction action actionValid)) ⦄ := by + (richAuthorityState authority authorityValid) + (richAction action actionValid) + (richBudgetExpression expression)) ⦄ := by rcases authorityValid with - ⟨authoritySubject, allowedProfiles, selectedProfile, + ⟨authorityRoot, authoritySubject, allowedProfiles, selectedProfile, authorityPermissions, authorityWindow, authorityAudiences, authorityBudget, authorityStatus, authorityAssurance, authorityExtensions, authorityExtensionsCanonical⟩ @@ -2326,12 +2447,19 @@ theorem translated_coverage_refines_rich_spec ⟨actionActor, actionProfile, actionPermission, requestedBudget, actionAudience, actionWindow⟩ unfold auths_authority.evaluate_action_coverage_view - step with principal_id_equal_rich_spec as ⟨actorAccepted, actorIff⟩ + -- The regenerated evaluator opens on the trust-root dimension: it builds the + -- linkage from the authority view and asks the algebra kernel whether the + -- chain is preserved. That single step now discharges both the rooting claim + -- and the actor/subject identity, which is why `AuthorityStateAnchored` is + -- gone rather than merely unused. + unfold auths_authority.root_linkage + step with root_preserved_rich_spec as ⟨rootAccepted, rootIff⟩ split <;> rename_i actorCondition - · have actorSemantic : + · have rootSemantic := rootIff.mp actorCondition + have actorSemantic : richPrincipal action.actor = - richPrincipal authority.subject := - actorIff.mp actorCondition + richPrincipal authority.subject := rootSemantic.right + have rootedSemantic := rootSemantic.left step with optional_grant_id_equal_rich_spec as ⟨grantAccepted, grantIff⟩ split <;> rename_i grantCondition @@ -2379,24 +2507,26 @@ theorem translated_coverage_refines_rich_spec authority.action_constraint) (richDigest action.canonical_body_digest) := constraintIff.mp constraintCondition - step with optional_budget_covers_spec as + step with budget_ceiling_covers_action_rich_spec as ⟨budgetAccepted, budgetIff⟩ split <;> rename_i budgetCondition · have budgetSemantic : - Auths.Rich.budgetCovers + Auths.Rich.budgetCoversAction (authority.budget_ceiling.map richBudget) - (action.requested_budget.map richBudget) := + (action.requested_budget.map richBudget) + (richBudgetExpression expression) := budgetIff.mp budgetCondition - simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, productionCoverageDecision, richAuthorityState, richAction] · have budgetSemantic : - ¬Auths.Rich.budgetCovers + ¬Auths.Rich.budgetCoversAction (authority.budget_ceiling.map richBudget) - (action.requested_budget.map richBudget) := by + (action.requested_budget.map richBudget) + (richBudgetExpression expression) := by intro semantic exact budgetCondition (budgetIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, productionCoverageDecision, richAuthorityState, richAction] · have constraintSemantic : @@ -2407,7 +2537,7 @@ theorem translated_coverage_refines_rich_spec intro semantic exact constraintCondition (constraintIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, productionCoverageDecision, richAuthorityState, richAction] · have audienceSemantic : @@ -2416,7 +2546,7 @@ theorem translated_coverage_refines_rich_spec intro semantic exact audienceCondition (audienceIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, productionCoverageDecision, richAuthorityState, richAction] · have validitySemantic : @@ -2425,7 +2555,7 @@ theorem translated_coverage_refines_rich_spec (richWindow authority.validity authorityWindow) := by intro semantic exact validityCondition (validityIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, productionCoverageDecision, richAuthorityState, richAction] · have permissionSemantic : @@ -2433,7 +2563,7 @@ theorem translated_coverage_refines_rich_spec richPermissionSet authority.permissions := by intro semantic exact permissionCondition (permissionIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, productionCoverageDecision, richAuthorityState, richAction] · have profileSemantic : @@ -2443,22 +2573,30 @@ theorem translated_coverage_refines_rich_spec (richProfile action.profile) := by intro semantic exact profileCondition (profileIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, productionCoverageDecision, richAuthorityState, richAction] · have grantSemantic : action.terminal_grant.map richGrantId ≠ authority.last_grant.map richGrantId := by intro semantic exact grantCondition (grantIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, + simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, productionCoverageDecision, richAuthorityState, richAction] - · have actorSemantic : - richPrincipal action.actor ≠ - richPrincipal authority.subject := by + · -- The root dimension refused: either the chain is not rooted or the actor + -- is not the subject. Both land on the same rich denial, so the ordered + -- rich evaluator takes its outermost else branch. + have rootRefused : + ¬(((authority.last_grant.map richGrantId).isSome = true ∨ + richPrincipal authority.root = richPrincipal authority.subject) ∧ + richPrincipal action.actor = richPrincipal authority.subject) := by intro semantic - exact actorCondition (actorIff.mpr semantic) - simp_all [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, AuthorityStateAnchored, + refine actorCondition (rootIff.mpr ⟨?_, semantic.right⟩) + simpa using semantic.left + simp only [Auths.Rich.evaluateCoverage, Auths.Rich.rooted, productionCoverageDecision, richAuthorityState, richAction] + rw [if_neg (fun conjunction => + rootRefused ⟨conjunction.left, conjunction.right.left⟩)] + rfl /-- The mechanically translated delegation evaluator returns the same ordered rich @@ -2474,23 +2612,22 @@ literal `true`. Removing the wrapper is what makes this a refinement of eleven dimensions rather than of ten plus a patch. -/ theorem translated_delegation_refines_rich_spec - (root : Auths.Rich.Principal ProductionVocabulary) (parent : auths_authority.AuthorityStateView) (grantId : auths_model.GrantId) (grant : auths_model.GrantAuthorityView) (parentValid : AuthorityStateViewValid parent) (grantValid : GrantAuthorityViewValid grant) - (anchored : AuthorityStateAnchored root parent) : + : auths_authority.evaluate_grant_view parent grantId grant ⦃ result => result.outcome = productionDelegationOutcome (Auths.Rich.evaluateGrant - (richAuthorityState root parent parentValid) + (richAuthorityState parent parentValid) (richGrantId grantId) (richGrant grant grantValid)) grantId grant ⦄ := by rcases parentValid with - ⟨parentSubject, allowedProfiles, selectedProfile, + ⟨parentRoot, parentSubject, allowedProfiles, selectedProfile, parentPermissions, parentWindow, parentAudiences, parentBudget, parentStatus, parentAssurance, parentExtensions, parentExtensionsCanonical⟩ @@ -2498,8 +2635,14 @@ theorem translated_delegation_refines_rich_spec ⟨grantIssuer, grantSubject, grantProfile, grantPermissions, grantWindow, grantAudiences, grantBudget, grantStatus, grantAssurance, grantExtensions, grantExtensionsCanonical⟩ - unfold auths_authority.evaluate_grant_view - split <;> rename_i parentDepthCondition + -- The regenerated evaluator opens on the trust-root dimension and then the + -- depth dimension, both as calls rather than inline expressions, so the + -- binds are stepped through before the first branch. + unfold auths_authority.evaluate_grant_view auths_authority.root_linkage + step with root_preserved_rich_spec as ⟨rootAccepted, rootIff⟩ + step with depth_decreases_spec as ⟨depthAccepted, depthIff⟩ + -- All ten dimension binds precede the first branch in the regenerated + -- evaluator, so every dimension is stepped through before splitting. all_goals step with selected_profile_attenuates_rich_spec as ⟨profileAccepted, profileIff⟩ @@ -2517,151 +2660,166 @@ theorem translated_delegation_refines_rich_spec ⟨statusAccepted, statusIff⟩ step with assurance_policy_id_equal_rich_spec as ⟨assuranceAccepted, assuranceIff⟩ - step with optional_critical_extensions_attenuate_spec as + step with extensions_attenuate_spec as ⟨extensionsAccepted, extensionsIff⟩ - step with principal_id_equal_rich_spec as - ⟨issuerAccepted, issuerIff⟩ - split <;> rename_i issuerCondition - · step with optional_grant_id_equal_rich_spec as - ⟨parentGrantAccepted, parentGrantIff⟩ - split <;> rename_i parentGrantCondition - · step with attenuation_checks_accept_spec as - ⟨scopeAccepted, scopeIff⟩ - split <;> rename_i scopeCondition - · simp_all [Auths.Rich.evaluateGrant, Auths.Rich.linked, - Auths.Rich.rootPreserved, Auths.Rich.rooted, AuthorityStateAnchored, - Auths.Rich.scopeDepthChecks, Auths.Rich.grantScopeChecks, - productionDelegationOutcome, expectedAcceptedTransition, - extensions_le_rich_iff, OptionalCriticalExtensionsAttenuate, - richAuthorityState, richGrant] - · have failedScope : - ¬(grant.remaining_depth.val < parent.remaining_depth.val ∧ - Auths.Rich.profileAllows - (richProfileScope parent.profile - parent.allowed_profiles selectedProfile) - (richProfile grant.profile) ∧ - (∀ candidate ∈ grant.permissions.val, - ∃ parentCandidate ∈ parent.permissions.val, - permissionKey parentCandidate = - permissionKey candidate) ∧ - parent.validity.not_before.val ≤ - grant.validity.not_before.val ∧ - grant.validity.expires_at.val ≤ - parent.validity.expires_at.val ∧ - (∀ candidate ∈ grant.audiences.val, - ∃ parentCandidate ∈ parent.audiences.val, - audienceKey parentCandidate = - audienceKey candidate) ∧ - Auths.Rich.actionConstraintLe - (richActionConstraint grant.action_constraint) - (richActionConstraint parent.action_constraint) ∧ - Auths.Rich.budgetLe - (grant.budget_ceiling.map richBudget) - (parent.budget_ceiling.map richBudget) ∧ - Auths.Rich.statusLe - (richStatus grant.status_policy grantStatus) - (richStatus parent.status_policy parentStatus) ∧ - stringBytes grant.assurance_floor = - stringBytes parent.assurance_policy ∧ - OptionalCriticalExtensionsAttenuate - grant.extensions parent.extensions) := by - rintro ⟨depth, profile, permissions, validityStart, - validityEnd, audiences, constraint, budget, status, - assurance, extensions⟩ - have permissionsRich : - richPermissionSet grant.permissions ⊆ - richPermissionSet parent.permissions := by - simpa using permissions - have audiencesRich : - richAudienceSet grant.audiences ⊆ - richAudienceSet parent.audiences := by - simpa using audiences - have assuranceRich : - richAssurance grant.assurance_floor = - richAssurance parent.assurance_policy := by - simpa using assurance - have scopeTrue : scopeAccepted = true := by - apply scopeIff.mpr - simp_all - simp_all - have scopeSemantic : - ¬Auths.Rich.scopeDepthChecks - (richAuthorityState root parent - { - subject := parentSubject - allowedProfiles := allowedProfiles - selectedProfile := selectedProfile - permissions := parentPermissions - validity := parentWindow - audiences := parentAudiences - budget := parentBudget - status := parentStatus - assurance := parentAssurance - extensions := parentExtensions - extensionsCanonical := parentExtensionsCanonical - }) - (richGrant grant - { - issuer := grantIssuer - subject := grantSubject - profile := grantProfile - permissions := grantPermissions - validity := grantWindow - audiences := grantAudiences - budget := grantBudget - status := grantStatus - assurance := grantAssurance - extensions := grantExtensions - extensionsCanonical := grantExtensionsCanonical - }) := by - intro checks - apply failedScope - have richChecks := checks.2 - simpa [Auths.Rich.scopeDepthChecks, - Auths.Rich.grantScopeChecks, richAuthorityState, - richGrant, extensions_le_rich_iff] using richChecks - have linkedSemantic : - Auths.Rich.linked - (richAuthorityState root parent - { - subject := parentSubject - allowedProfiles := allowedProfiles - selectedProfile := selectedProfile - permissions := parentPermissions - validity := parentWindow - audiences := parentAudiences - budget := parentBudget - status := parentStatus - assurance := parentAssurance - extensions := parentExtensions - extensionsCanonical := parentExtensionsCanonical - }) - (richGrant grant - { - issuer := grantIssuer - subject := grantSubject - profile := grantProfile - permissions := grantPermissions - validity := grantWindow - audiences := grantAudiences - budget := grantBudget - status := grantStatus - assurance := grantAssurance - extensions := grantExtensions - extensionsCanonical := grantExtensionsCanonical - }) := by - simp_all [Auths.Rich.linked, Auths.Rich.rootPreserved, - Auths.Rich.rooted, AuthorityStateAnchored, richAuthorityState, - richGrant] - simp [Auths.Rich.evaluateGrant, linkedSemantic, - scopeSemantic, productionDelegationOutcome] + -- The first branch is the trust-root dimension. The issuer/subject identity + -- used to be a separate `principal_id_equal` branch; `root_preserved` + -- subsumes it, so the accepting side goes straight to the parent link. + split <;> rename_i parentDepthCondition + step with optional_grant_id_equal_rich_spec as + ⟨parentGrantAccepted, parentGrantIff⟩ + split <;> rename_i parentGrantCondition + · step with attenuation_checks_accept_spec as + ⟨scopeAccepted, scopeIff⟩ + split <;> rename_i scopeCondition · simp_all [Auths.Rich.evaluateGrant, Auths.Rich.linked, - Auths.Rich.rootPreserved, Auths.Rich.rooted, AuthorityStateAnchored, - productionDelegationOutcome, + Auths.Rich.rootPreserved, Auths.Rich.rooted, + Auths.Rich.scopeDepthChecks, Auths.Rich.grantScopeChecks, + productionDelegationOutcome, expectedAcceptedTransition, + extensions_le_rich_iff, OptionalCriticalExtensionsAttenuate, richAuthorityState, richGrant] + · have failedScope : + ¬(grant.remaining_depth.val < parent.remaining_depth.val ∧ + Auths.Rich.profileAllows + (richProfileScope parent.profile + parent.allowed_profiles selectedProfile) + (richProfile grant.profile) ∧ + (∀ candidate ∈ grant.permissions.val, + ∃ parentCandidate ∈ parent.permissions.val, + permissionKey parentCandidate = + permissionKey candidate) ∧ + parent.validity.not_before.val ≤ + grant.validity.not_before.val ∧ + grant.validity.expires_at.val ≤ + parent.validity.expires_at.val ∧ + (∀ candidate ∈ grant.audiences.val, + ∃ parentCandidate ∈ parent.audiences.val, + audienceKey parentCandidate = + audienceKey candidate) ∧ + Auths.Rich.actionConstraintLe + (richActionConstraint grant.action_constraint) + (richActionConstraint parent.action_constraint) ∧ + Auths.Rich.budgetLe + (grant.budget_ceiling.map richBudget) + (parent.budget_ceiling.map richBudget) ∧ + Auths.Rich.statusLe + (richStatus grant.status_policy grantStatus) + (richStatus parent.status_policy parentStatus) ∧ + stringBytes grant.assurance_floor = + stringBytes parent.assurance_policy ∧ + OptionalCriticalExtensionsAttenuate + grant.extensions parent.extensions) := by + rintro ⟨depth, profile, permissions, validityStart, + validityEnd, audiences, constraint, budget, status, + assurance, extensions⟩ + have permissionsRich : + richPermissionSet grant.permissions ⊆ + richPermissionSet parent.permissions := by + simpa using permissions + have audiencesRich : + richAudienceSet grant.audiences ⊆ + richAudienceSet parent.audiences := by + simpa using audiences + have assuranceRich : + richAssurance grant.assurance_floor = + richAssurance parent.assurance_policy := by + simpa using assurance + have scopeTrue : scopeAccepted = true := by + apply scopeIff.mpr + simp_all + simp_all + have scopeSemantic : + ¬Auths.Rich.scopeDepthChecks + (richAuthorityState parent + { + root := parentRoot + subject := parentSubject + allowedProfiles := allowedProfiles + selectedProfile := selectedProfile + permissions := parentPermissions + validity := parentWindow + audiences := parentAudiences + budget := parentBudget + status := parentStatus + assurance := parentAssurance + extensions := parentExtensions + extensionsCanonical := parentExtensionsCanonical + }) + (richGrant grant + { + issuer := grantIssuer + subject := grantSubject + profile := grantProfile + permissions := grantPermissions + validity := grantWindow + audiences := grantAudiences + budget := grantBudget + status := grantStatus + assurance := grantAssurance + extensions := grantExtensions + extensionsCanonical := grantExtensionsCanonical + }) := by + intro checks + apply failedScope + have richChecks := checks.2 + simpa [Auths.Rich.scopeDepthChecks, + Auths.Rich.grantScopeChecks, richAuthorityState, + richGrant, extensions_le_rich_iff] using richChecks + have linkedSemantic : + Auths.Rich.linked + (richAuthorityState parent + { + root := parentRoot + subject := parentSubject + allowedProfiles := allowedProfiles + selectedProfile := selectedProfile + permissions := parentPermissions + validity := parentWindow + audiences := parentAudiences + budget := parentBudget + status := parentStatus + assurance := parentAssurance + extensions := parentExtensions + extensionsCanonical := parentExtensionsCanonical + }) + (richGrant grant + { + issuer := grantIssuer + subject := grantSubject + profile := grantProfile + permissions := grantPermissions + validity := grantWindow + audiences := grantAudiences + budget := grantBudget + status := grantStatus + assurance := grantAssurance + extensions := grantExtensions + extensionsCanonical := grantExtensionsCanonical + }) := by + simp_all [Auths.Rich.linked, Auths.Rich.rootPreserved, + Auths.Rich.rooted, richAuthorityState, + richGrant] + simp [Auths.Rich.evaluateGrant, linkedSemantic, + scopeSemantic, productionDelegationOutcome] · simp_all [Auths.Rich.evaluateGrant, Auths.Rich.linked, - Auths.Rich.rootPreserved, Auths.Rich.rooted, AuthorityStateAnchored, + Auths.Rich.rootPreserved, Auths.Rich.rooted, productionDelegationOutcome, richAuthorityState, richGrant] + · -- The trust-root dimension refused. `root_preserved := false` alone + -- denies the delegation: acceptance is the conjunction over every + -- dimension, so no other dimension can rescue it. + have rootRefused : + ¬(((parent.last_grant.map richGrantId).isSome = true ∨ + richPrincipal parent.root = richPrincipal parent.subject) ∧ + richPrincipal grant.issuer = richPrincipal parent.subject) := by + intro semantic + refine parentDepthCondition (rootIff.mpr ⟨?_, semantic.right⟩) + simpa using semantic.left + simp only [Auths.Rich.evaluateGrant, productionDelegationOutcome, + richAuthorityState, richGrant] + rw [if_neg (fun link => + rootRefused ⟨link.left.left, link.left.right⟩)] + rfl + end Auths.Refinement diff --git a/formal/Auths/Rich/Semantics.lean b/formal/Auths/Rich/Semantics.lean index 25a72d72..d425a903 100644 --- a/formal/Auths/Rich/Semantics.lean +++ b/formal/Auths/Rich/Semantics.lean @@ -49,6 +49,85 @@ instance {v : Vocabulary} cases ceiling <;> cases requested <;> simp [budgetCovers] <;> infer_instance +/-- +Whether a profile's canonical actions can state a budget at all. + +TRUSTED REGISTRY CONTEXT, not an action-controlled field. An action cannot +declare itself inexpressible to escape a ceiling: the profile registry decides +this, and the action only supplies the requested budget. +-/ +inductive BudgetExpression where + | expressible + | inexpressible + deriving DecidableEq, Repr + +/-- +Terminal budget coverage including profile expressibility. + +The capability only ever reclassifies an ABSENT request: + +* inexpressible profile, absent request -- the action provably spends zero, and + zero is within every ceiling including an absent one; +* expressible profile, absent request, present ceiling -- denied, because an + action that could have stated a bound and did not states no bound at all; +* declared request -- ordinary ceiling comparison, expressibility irrelevant; +* absent ceiling -- covered, nothing is bounded. + +Mirrors `auths_model::budget_ceiling_covers_action`. +-/ +def budgetCoversAction {v : Vocabulary} + (ceiling requested : Option (BudgetCeiling v)) + (expression : BudgetExpression) : Prop := + match requested, expression with + | none, BudgetExpression.inexpressible => True + | _, _ => budgetCovers ceiling requested + +instance {v : Vocabulary} + (ceiling requested : Option (BudgetCeiling v)) (expression : BudgetExpression) : + Decidable (budgetCoversAction ceiling requested expression) := by + cases requested <;> cases expression + · exact inferInstanceAs (Decidable (budgetCovers _ _)) + · exact inferInstanceAs (Decidable True) + · exact inferInstanceAs (Decidable (budgetCovers _ _)) + · exact inferInstanceAs (Decidable (budgetCovers _ _)) + +/-- Inexpressible profile with no request spends zero: always covered. -/ +@[simp] theorem budgetCoversAction_inexpressible_absent {v : Vocabulary} + (ceiling : Option (BudgetCeiling v)) : + budgetCoversAction ceiling none BudgetExpression.inexpressible := by + simp [budgetCoversAction] + +/-- Expressible profile with no request and a bounded ceiling: denied. -/ +@[simp] theorem budgetCoversAction_expressible_absent_bounded {v : Vocabulary} + (ceiling : BudgetCeiling v) : + ¬ budgetCoversAction (some ceiling) none BudgetExpression.expressible := by + simp [budgetCoversAction, budgetCovers] + +/-- An absent ceiling bounds nothing, whatever the profile can express. -/ +@[simp] theorem budgetCoversAction_absent_ceiling {v : Vocabulary} + (requested : Option (BudgetCeiling v)) (expression : BudgetExpression) : + budgetCoversAction none requested expression := by + cases requested <;> cases expression <;> simp [budgetCoversAction, budgetCovers] + +/-- +An expressible profile adds nothing: the capability only ever reclassifies an +absent request, and an expressible profile never does. This is what keeps every +existing coverage theorem true unchanged. +-/ +@[simp] theorem budgetCoversAction_expressible {v : Vocabulary} + (ceiling requested : Option (BudgetCeiling v)) : + budgetCoversAction ceiling requested BudgetExpression.expressible = + budgetCovers ceiling requested := by + cases requested <;> rfl + +/-- A declared request is compared against the ceiling, expressibility aside. -/ +theorem budgetCoversAction_declared {v : Vocabulary} + (ceiling : Option (BudgetCeiling v)) (requested : BudgetCeiling v) + (expression : BudgetExpression) : + budgetCoversAction ceiling (some requested) expression = + budgetCovers ceiling (some requested) := by + cases expression <;> rfl + /-- The critical-extension delegation relation. @@ -424,9 +503,18 @@ inductive CoverageDecision where | denied (reason : CoverageDiagnostic) deriving DecidableEq, Repr -/-- First-failure order used by the shipping terminal-coverage API. -/ +/-- +Ordered terminal coverage. First-failure order, as the shipping API. + +`expression` is TRUSTED PROFILE-REGISTRY CONTEXT: whether the action's profile +can state a budget at all. It is not read from the action, so an action cannot +declare itself inexpressible to escape a ceiling. It only ever reclassifies an +absent request; see `budgetCoversAction`. +-/ def evaluateCoverage {v : Vocabulary} - (authority : ChainState v) (action : Action v) : CoverageDecision := + (authority : ChainState v) (action : Action v) + (expression : BudgetExpression := BudgetExpression.expressible) : + CoverageDecision := if rooted authority ∧ action.actor = authority.subject ∧ action.terminalGrant = authority.lastGrant ∧ @@ -439,7 +527,8 @@ def evaluateCoverage {v : Vocabulary} .denied .audienceMismatch else if ¬ actionConstraintAllows authority.scope.actionConstraint action.bodyDigest then .denied .actionConstraintMismatch - else if ¬ budgetCovers authority.scope.budget action.requestedBudget then + else if ¬ budgetCoversAction authority.scope.budget action.requestedBudget + expression then .denied .budgetCeilingExceeded else .authorized diff --git a/formal/Auths/Theorems.lean b/formal/Auths/Theorems.lean index 4878fe29..3075cd4b 100644 --- a/formal/Auths/Theorems.lean +++ b/formal/Auths/Theorems.lean @@ -80,7 +80,10 @@ def theoremInventory : List Lean.Name := `Auths.Refinement.translated_rust_refines_rich_spec, `Auths.Refinement.translated_coverage_refines_rich_spec, `Auths.Refinement.translated_delegation_refines_rich_spec, - `Auths.Refinement.translated_budget_coverage_gap_is_the_absent_request, + `Auths.Refinement.translated_bounded_ceiling_denies_absent_request, + `Auths.Refinement.translated_expressible_absent_request_is_denied, + `Auths.Refinement.translated_inexpressible_absent_request_is_covered, + `Auths.Refinement.translated_absent_ceiling_covers_anything, `Auths.Product.configuration_match_refl, `Auths.Product.configuration_match_deterministic, `Auths.Product.checked_add_never_wraps, diff --git a/formal/assurance-manifest-v1.toml b/formal/assurance-manifest-v1.toml index c1d2ccfb..8dcb8a36 100644 --- a/formal/assurance-manifest-v1.toml +++ b/formal/assurance-manifest-v1.toml @@ -46,7 +46,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -98,7 +98,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -150,7 +150,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -202,7 +202,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -254,7 +254,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -303,7 +303,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -356,7 +356,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -409,7 +409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -462,7 +462,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -514,7 +514,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -567,7 +567,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -620,7 +620,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -673,7 +673,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -726,7 +726,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -775,7 +775,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -824,7 +824,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -873,7 +873,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -922,7 +922,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -971,7 +971,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1020,7 +1020,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1069,7 +1069,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1118,7 +1118,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1167,7 +1167,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1216,7 +1216,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1268,7 +1268,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1317,7 +1317,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1366,7 +1366,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1418,7 +1418,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1467,7 +1467,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1519,7 +1519,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1571,7 +1571,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1623,7 +1623,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1675,7 +1675,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1727,7 +1727,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1780,7 +1780,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1833,7 +1833,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1886,7 +1886,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1938,7 +1938,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1991,7 +1991,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2043,7 +2043,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2095,7 +2095,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2148,7 +2148,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2200,7 +2200,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2253,7 +2253,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2305,7 +2305,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2357,7 +2357,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2409,7 +2409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2461,7 +2461,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2513,7 +2513,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2565,7 +2565,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2617,7 +2617,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2670,7 +2670,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2723,7 +2723,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2776,7 +2776,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2829,7 +2829,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2882,7 +2882,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2934,7 +2934,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2986,7 +2986,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3039,7 +3039,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3092,7 +3092,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3145,7 +3145,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3198,7 +3198,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3251,7 +3251,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3304,7 +3304,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3357,7 +3357,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3409,7 +3409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3471,7 +3471,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The exact Aeneas translation of the production pre-signing scope evaluator over validated, bounded Rust model views, including ordered diagnostics." residual_assumptions = [ "Lean's kernel, the pinned Rust/Lean/Aeneas/Charon toolchain, the reviewed Aeneas external models, the listed foundational axioms, and the theorem's representation-validity premises are trusted.", @@ -3509,7 +3509,7 @@ claim_id = "AP-FORMAL-RICH-055" claim_text = "The mechanically translated production terminal-coverage evaluator returns exactly the ordered rich target-V1 decision, including linkage, set membership, interval containment, action constraints, and requested-budget coverage." claim_status = "proved" lean_declaration = "Auths.Refinement.translated_coverage_refines_rich_spec" -lean_statement_sha256 = "ab27e2b4d47c49c1978a41c8c6414cd53ff1b1a500432b2adb88e6c4367b5140" +lean_statement_sha256 = "e8deba0035a09886221c46411be908f1ac8c30e0e3755ca956a62884a416a0fa" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [ "auths_authority::evaluate_action_coverage_view", @@ -3553,7 +3553,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Validated target-V1 authority and action views through the production terminal-coverage decision, including its first-failure diagnostic order." residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget result is stateless per action, assurance uses exact identifier equality, and deterministic diagnostic precedence is not a side-channel noninterference claim. Codec validation and cryptography remain outside this claim."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3584,7 +3584,7 @@ claim_id = "AP-FORMAL-RICH-056" claim_text = "The mechanically translated production delegation evaluator returns exactly the rich target-V1 linkage and attenuation decision, including the unique accepted transition fields." claim_status = "proved" lean_declaration = "Auths.Refinement.translated_delegation_refines_rich_spec" -lean_statement_sha256 = "9687263cb09963abc3090c34d4d58560512b322659b0828628ddcb941c6af51e" +lean_statement_sha256 = "713bd7e21397824f362ff65803d2085bc74915fbeb34cdfdc0c9a9e88a57909b" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [ "auths_authority::evaluate_grant_view", @@ -3631,7 +3631,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Validated target-V1 parent-authority and grant views through the production delegation outcome and accepted transition." residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget relation is stateless per action, assurance retention is exact identifier equality rather than a lattice, and deterministic diagnostic precedence is not a side-channel noninterference claim. Root preservation and assurance retention occur in the caller around the proved returned transition; codec validation and cryptography remain outside this claim."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3659,10 +3659,10 @@ artifact = "formal/refinement-mutations-v1.json" [[claims]] claim_id = "AP-FORMAL-RICH-104" -claim_text = "Lean records, as a checked theorem rather than a comment, that the pinned Aeneas translation of optional_budget_covers is stale on exactly one input class: a present ceiling with an absent request, where the translation answers true and the shipping Rust answers false. It stops being provable when the translation is regenerated." +claim_text = "The regenerated Aeneas translation of optional_budget_covers answers false for a present ceiling with an absent request, matching the shipping Rust. Lean records this positively: an action declaring no bound on what it may spend is exactly the authority a ceiling exists to deny. This replaces the former staleness claim, which recorded the opposite as checked evidence while the pinned translation was stale." claim_status = "proved" -lean_declaration = "Auths.Refinement.translated_budget_coverage_gap_is_the_absent_request" -lean_statement_sha256 = "3c0b39c13aef92097036b16c569166a15be4b6a8dc4e38f8eac70fb3ce677e58" +lean_declaration = "Auths.Refinement.translated_bounded_ceiling_denies_absent_request" +lean_statement_sha256 = "9c4ea11fa46f39a993ab4038263180284eca394df782a70259346c8167d7e4fc" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -3696,7 +3696,166 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-106" +claim_text = "Lean proves the rich authority property: translated expressible absent request is denied." +claim_status = "proved" +lean_declaration = "Auths.Refinement.translated_expressible_absent_request_is_denied" +lean_statement_sha256 = "90aa025faf3b73862f51432d5b07d0ef69c1bd67826d739083b6e42d65f00e4a" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-107" +claim_text = "Lean proves the rich authority property: translated inexpressible absent request is covered." +claim_status = "proved" +lean_declaration = "Auths.Refinement.translated_inexpressible_absent_request_is_covered" +lean_statement_sha256 = "294ac2be47b25a2f72d528f66b974cb3acc2dfd8a330a8c6696b8dad12fb8d97" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-108" +claim_text = "Lean proves the rich authority property: translated absent ceiling covers anything." +claim_status = "proved" +lean_declaration = "Auths.Refinement.translated_absent_ceiling_covers_anything" +lean_statement_sha256 = "90df3548fb3d56bed30812c94acb18b2d6b0028f77aff11cd44aade8e2889961" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3749,7 +3908,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3798,7 +3957,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3847,7 +4006,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3896,7 +4055,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3945,7 +4104,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3994,7 +4153,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4043,7 +4202,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4092,7 +4251,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4144,7 +4303,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4193,7 +4352,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4246,7 +4405,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4299,7 +4458,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4352,7 +4511,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4405,7 +4564,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4458,7 +4617,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4510,7 +4669,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4562,7 +4721,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4614,7 +4773,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4667,7 +4826,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4720,7 +4879,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4769,7 +4928,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4818,7 +4977,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4867,7 +5026,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4916,7 +5075,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4965,7 +5124,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5014,7 +5173,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5063,7 +5222,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5112,7 +5271,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5161,7 +5320,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5210,7 +5369,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5259,7 +5418,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::model::LifecycleState::is_terminal` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5308,7 +5467,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::transition_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5360,7 +5519,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::exclusive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5409,7 +5568,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::additive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5462,7 +5621,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::replay_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5511,7 +5670,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5560,7 +5719,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5609,7 +5768,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5658,7 +5817,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5707,7 +5866,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5756,7 +5915,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5805,7 +5964,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5858,7 +6017,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5911,7 +6070,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "Exactly the two-input threshold helper and the declared three-valued truth order." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5967,7 +6126,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "A two-value swap only; arbitrary authorization-plan permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6016,7 +6175,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The two-value canonicalCode helper; arbitrary plan diagnostic permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6065,7 +6224,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "A definitional model equality; it does not prove that the shipping evaluator visits leaves once." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6117,7 +6276,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "A definitional model equality; no asymptotic or shipping evaluator cost bound is claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6169,7 +6328,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6222,7 +6381,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6278,7 +6437,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "8f6869551d48d857ef1c6e84dc6db81ee222cce635007a3471114beb08c8e501" +semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" diff --git a/formal/qualification/aeneas/cases/Model.lean b/formal/qualification/aeneas/cases/Model.lean index 0a52148c..70536cff 100644 --- a/formal/qualification/aeneas/cases/Model.lean +++ b/formal/qualification/aeneas/cases/Model.lean @@ -44,11 +44,39 @@ example : (some { algebra := "usd", value := 1#u64 }) = ok false := by rfl +-- A bounded ceiling with no declared request is DENIED. This vector asserted +-- `ok true` while the pinned translation was stale; the regenerated translation +-- matches the shipping Rust. example : optional_budget_covers - (some { algebra := "usd", value := 1#u64 }) none = ok true := by + (some { algebra := "usd", value := 1#u64 }) none = ok false := by rfl +-- Both profile-budget-expression modes, on the one input class the capability +-- reclassifies: an absent request. +example : + budget_ceiling_covers_action + (some { algebra := "usd", value := 1#u64 }) none + ProfileBudgetExpression.Expressible = ok false := by + rfl + +example : + budget_ceiling_covers_action + (some { algebra := "usd", value := 1#u64 }) none + ProfileBudgetExpression.Inexpressible = ok true := by + rfl + +-- A DECLARED request is deliberately not vectored here. Comparing two ceilings +-- reaches `alloc::string::String::as_bytes`, which this translation carries as +-- an opaque external, so the goal cannot reduce without assuming semantics for +-- it -- exactly what these qualification cases exist to avoid. That the +-- capability leaves a declared request alone is proved abstractly instead, by +-- `Auths.Rich.budgetCoversAction_declared`. +-- +-- Every vector above concerns an ABSENT request, which is the only input class +-- profile expressibility reclassifies, and each short-circuits before any +-- string comparison. + example : status_policy_attenuates StatusPolicy.ExpiryOnly StatusPolicy.ExpiryOnly = ok true := by diff --git a/formal/qualification/aeneas/generated/algebra/Funs.lean b/formal/qualification/aeneas/generated/algebra/Funs.lean index e41a13ad..ff9243de 100644 --- a/formal/qualification/aeneas/generated/algebra/Funs.lean +++ b/formal/qualification/aeneas/generated/algebra/Funs.lean @@ -51,4 +51,21 @@ def generated.attenuation_checks_accept else ok false else ok false +/-- [auths_algebra_kernel::root_preserved]: + Source: 'core/crates/auths-algebra-kernel/src/lib.rs', lines 49:0-52:1 + Visibility: public -/ +def root_preserved + {Identity : Type} (corecmpPartialEqInst : core.cmp.PartialEq Identity + Identity) (linkage : RootLinkage Identity) : + Result Bool + := do + if linkage.parent_delegated + then corecmpPartialEqInst.eq linkage.grant_issuer linkage.parent_subject + else + let b ← + corecmpPartialEqInst.eq linkage.parent_root linkage.parent_subject + if b + then corecmpPartialEqInst.eq linkage.grant_issuer linkage.parent_subject + else ok false + end auths_algebra_kernel diff --git a/formal/qualification/aeneas/generated/algebra/Types.lean b/formal/qualification/aeneas/generated/algebra/Types.lean index 3b86d9b0..e37df8aa 100644 --- a/formal/qualification/aeneas/generated/algebra/Types.lean +++ b/formal/qualification/aeneas/generated/algebra/Types.lean @@ -30,4 +30,13 @@ structure generated.AttenuationChecks where assurance_attenuates : Bool extensions_attenuate : Bool +/-- [auths_algebra_kernel::RootLinkage] + Source: 'core/crates/auths-algebra-kernel/src/lib.rs', lines 21:0-32:1 + Visibility: public -/ +structure RootLinkage (Identity : Type) where + parent_root : Identity + parent_subject : Identity + parent_delegated : Bool + grant_issuer : Identity + end auths_algebra_kernel diff --git a/formal/qualification/aeneas/generated/algebra/translation.json b/formal/qualification/aeneas/generated/algebra/translation.json index 24341074..52e22e87 100644 --- a/formal/qualification/aeneas/generated/algebra/translation.json +++ b/formal/qualification/aeneas/generated/algebra/translation.json @@ -1,5 +1,5 @@ { - "aeneas_version": "3a8586f", + "aeneas_version": "3a8586facab25b31bdb1e1f5f45acd60d1cc5ff0", "charon_version": "0.1.225", "crate": "auths_algebra_kernel", "functions": [ @@ -19,11 +19,28 @@ "can_diverge": false, "is_rec": false, "reducible": false + }, + { + "def_id": 1, + "lean_name": "auths_algebra_kernel.root_preserved", + "lean_file": "qualification/aeneas/generated/algebra/Funs.lean", + "rust_name": "auths_algebra_kernel::root_preserved", + "is_local": true, + "source": { + "file": "core/crates/auths-algebra-kernel/src/lib.rs", + "begin_line": 49, + "end_line": 52 + }, + "is_opaque": false, + "can_fail": true, + "can_diverge": false, + "is_rec": false, + "reducible": false } ], "types": [ { - "def_id": 0, + "def_id": 1, "lean_name": "auths_algebra_kernel.generated.AttenuationChecks", "lean_file": "qualification/aeneas/generated/algebra/Types.lean", "rust_name": "auths_algebra_kernel::generated::AttenuationChecks", @@ -33,6 +50,18 @@ "begin_line": 52, "end_line": 75 } + }, + { + "def_id": 0, + "lean_name": "auths_algebra_kernel.RootLinkage", + "lean_file": "qualification/aeneas/generated/algebra/Types.lean", + "rust_name": "auths_algebra_kernel::RootLinkage", + "is_local": true, + "source": { + "file": "core/crates/auths-algebra-kernel/src/lib.rs", + "begin_line": 21, + "end_line": 32 + } } ], "globals": [], diff --git a/formal/qualification/aeneas/generated/authority/Funs.lean b/formal/qualification/aeneas/generated/authority/Funs.lean index 47c73b9f..9c512375 100644 --- a/formal/qualification/aeneas/generated/authority/Funs.lean +++ b/formal/qualification/aeneas/generated/authority/Funs.lean @@ -19,8 +19,58 @@ noncomputable section namespace auths_authority +/-- [auths_authority::{impl core::cmp::PartialEq> for auths_authority::CanonicalPrincipal<'_0>}::eq]: + Source: 'core/crates/auths-authority/src/lib.rs', lines 140:4-142:5 + Visibility: public -/ +def CanonicalPrincipal.Insts.CoreCmpPartialEqCanonicalPrincipal.eq + (self : CanonicalPrincipal) (other : CanonicalPrincipal) : Result Bool := do + auths_model.principal_id_equal self other + +/-- Trait implementation: [auths_authority::{impl core::cmp::PartialEq> for auths_authority::CanonicalPrincipal<'_0>}] + Source: 'core/crates/auths-authority/src/lib.rs', lines 139:0-143:1 -/ +@[reducible] +def CanonicalPrincipal.Insts.CoreCmpPartialEqCanonicalPrincipal : + core.cmp.PartialEq CanonicalPrincipal CanonicalPrincipal := { + eq := CanonicalPrincipal.Insts.CoreCmpPartialEqCanonicalPrincipal.eq +} + +/-- [auths_authority::extensions_attenuate]: + Source: 'core/crates/auths-authority/src/lib.rs', lines 154:0-162:1 -/ +def extensions_attenuate + (parent_extensions : Option auths_model.CriticalExtensions) + (grant_extensions : auths_model.CriticalExtensions) : + Result Bool + := do + match parent_extensions with + | none => ok true + | some parent => + auths_model.critical_extensions_equal grant_extensions parent + +/-- [auths_authority::depth_decreases]: + Source: 'core/crates/auths-authority/src/lib.rs', lines 174:0-179:1 -/ +def depth_decreases + (parent_remaining : Std.U16) (grant_remaining : Std.U16) : Result Bool := do + if parent_remaining = 0#u16 + then ok false + else ok (grant_remaining < parent_remaining) + +/-- [auths_authority::root_linkage]: + Source: 'core/crates/auths-authority/src/lib.rs', lines 182:0-192:1 -/ +def root_linkage + (parent : AuthorityStateView) (issuer : auths_model.PrincipalId) : + Result (auths_algebra_kernel.RootLinkage CanonicalPrincipal) + := do + let b := core.option.Option.is_some parent.last_grant + ok + { + parent_root := parent.root, + parent_subject := parent.subject, + parent_delegated := b, + grant_issuer := issuer + } + /-- [auths_authority::selected_profile_attenuates]: - Source: 'core/crates/auths-authority/src/lib.rs', lines 124:0-133:1 -/ + Source: 'core/crates/auths-authority/src/lib.rs', lines 194:0-203:1 -/ def selected_profile_attenuates (selected : Option auths_model.ProfileRef) (allowed_profiles : Slice auths_model.ProfileRef) @@ -32,7 +82,7 @@ def selected_profile_attenuates | some parent => auths_model.profile_ref_equal parent child /-- [auths_authority::evaluate_author_scope_view]: - Source: 'core/crates/auths-authority/src/lib.rs', lines 139:0-174:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 209:0-244:1 Visibility: public -/ def evaluate_author_scope_view (parent : auths_model.ScopeAuthorityView) @@ -106,45 +156,41 @@ def evaluate_author_scope_view else ok (AuthorScopeDecision.Denied AuthorityDimension.Profile) /-- [auths_authority::evaluate_grant_view]: - Source: 'core/crates/auths-authority/src/lib.rs', lines 195:0-257:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 265:0-329:1 Visibility: public -/ def evaluate_grant_view (parent : AuthorityStateView) (grant_id : auths_model.GrantId) (grant : auths_model.GrantAuthorityView) : Result DelegationEvaluation := do + let linkage ← root_linkage parent grant.issuer let b ← - if parent.remaining_depth > 0#u16 - then ok (grant.remaining_depth < parent.remaining_depth) - else ok false - let b1 ← + auths_algebra_kernel.root_preserved + CanonicalPrincipal.Insts.CoreCmpPartialEqCanonicalPrincipal linkage + let b1 ← depth_decreases parent.remaining_depth grant.remaining_depth + let b2 ← selected_profile_attenuates parent.profile parent.allowed_profiles grant.profile - let b2 ← - auths_model.permission_set_is_subset grant.permissions parent.permissions let b3 ← - auths_model.validity_window_contains parent.validity grant.validity + auths_model.permission_set_is_subset grant.permissions parent.permissions let b4 ← - auths_model.audience_set_is_subset grant.audiences parent.audiences + auths_model.validity_window_contains parent.validity grant.validity let b5 ← + auths_model.audience_set_is_subset grant.audiences parent.audiences + let b6 ← auths_model.action_constraint_attenuates grant.action_constraint parent.action_constraint - let b6 ← + let b7 ← auths_model.optional_budget_attenuates grant.budget_ceiling parent.budget_ceiling - let b7 ← + let b8 ← auths_model.status_policy_attenuates grant.status_policy parent.status_policy - let b8 ← + let b9 ← auths_model.assurance_policy_id_equal grant.assurance_floor parent.assurance_policy - let b9 ← - match parent.extensions with - | none => ok true - | some parent1 => - auths_model.critical_extensions_equal grant.extensions parent1 - let b10 ← auths_model.principal_id_equal grant.issuer parent.subject - if b10 + let b10 ← extensions_attenuate parent.extensions grant.extensions + if b then let b11 ← auths_model.optional_grant_id_equal grant.parent parent.last_grant @@ -154,16 +200,16 @@ def evaluate_grant_view auths_algebra_kernel.generated.attenuation_checks_accept { root_preserved := true, - depth_decreases := b, - profile_attenuates := b1, - permissions_attenuate := b2, - validity_attenuates := b3, - audiences_attenuate := b4, - action_constraint_attenuates := b5, - budget_attenuates := b6, - status_attenuates := b7, - assurance_attenuates := b8, - extensions_attenuate := b9 + depth_decreases := b1, + profile_attenuates := b2, + permissions_attenuate := b3, + validity_attenuates := b4, + audiences_attenuate := b5, + action_constraint_attenuates := b6, + budget_attenuates := b7, + status_attenuates := b8, + assurance_attenuates := b9, + extensions_attenuate := b10 } if b12 then @@ -172,16 +218,16 @@ def evaluate_grant_view checks := { root_preserved := true, - depth_decreases := b, - profile_attenuates := b1, - permissions_attenuate := b2, - validity_attenuates := b3, - audiences_attenuate := b4, - action_constraint_attenuates := b5, - budget_attenuates := b6, - status_attenuates := b7, - assurance_attenuates := b8, - extensions_attenuate := b9 + depth_decreases := b1, + profile_attenuates := b2, + permissions_attenuate := b3, + validity_attenuates := b4, + audiences_attenuate := b5, + action_constraint_attenuates := b6, + budget_attenuates := b7, + status_attenuates := b8, + assurance_attenuates := b9, + extensions_attenuate := b10 }, outcome := (DelegationOutcome.Accepted @@ -205,16 +251,16 @@ def evaluate_grant_view checks := { root_preserved := true, - depth_decreases := b, - profile_attenuates := b1, - permissions_attenuate := b2, - validity_attenuates := b3, - audiences_attenuate := b4, - action_constraint_attenuates := b5, - budget_attenuates := b6, - status_attenuates := b7, - assurance_attenuates := b8, - extensions_attenuate := b9 + depth_decreases := b1, + profile_attenuates := b2, + permissions_attenuate := b3, + validity_attenuates := b4, + audiences_attenuate := b5, + action_constraint_attenuates := b6, + budget_attenuates := b7, + status_attenuates := b8, + assurance_attenuates := b9, + extensions_attenuate := b10 }, outcome := (DelegationOutcome.Denied @@ -226,16 +272,16 @@ def evaluate_grant_view checks := { root_preserved := true, - depth_decreases := b, - profile_attenuates := b1, - permissions_attenuate := b2, - validity_attenuates := b3, - audiences_attenuate := b4, - action_constraint_attenuates := b5, - budget_attenuates := b6, - status_attenuates := b7, - assurance_attenuates := b8, - extensions_attenuate := b9 + depth_decreases := b1, + profile_attenuates := b2, + permissions_attenuate := b3, + validity_attenuates := b4, + audiences_attenuate := b5, + action_constraint_attenuates := b6, + budget_attenuates := b7, + status_attenuates := b8, + assurance_attenuates := b9, + extensions_attenuate := b10 }, outcome := (DelegationOutcome.Denied @@ -246,30 +292,34 @@ def evaluate_grant_view { checks := { - root_preserved := true, - depth_decreases := b, - profile_attenuates := b1, - permissions_attenuate := b2, - validity_attenuates := b3, - audiences_attenuate := b4, - action_constraint_attenuates := b5, - budget_attenuates := b6, - status_attenuates := b7, - assurance_attenuates := b8, - extensions_attenuate := b9 + root_preserved := false, + depth_decreases := b1, + profile_attenuates := b2, + permissions_attenuate := b3, + validity_attenuates := b4, + audiences_attenuate := b5, + action_constraint_attenuates := b6, + budget_attenuates := b7, + status_attenuates := b8, + assurance_attenuates := b9, + extensions_attenuate := b10 }, outcome := (DelegationOutcome.Denied auths_model.DenialReason.BrokenGrantChain) } /-- [auths_authority::evaluate_action_coverage_view]: - Source: 'core/crates/auths-authority/src/lib.rs', lines 276:0-308:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 350:0-391:1 Visibility: public -/ def evaluate_action_coverage_view - (authority : AuthorityStateView) (action : auths_model.ActionAuthorityView) : + (authority : AuthorityStateView) (action : auths_model.ActionAuthorityView) + (expression : auths_model.ProfileBudgetExpression) : Result CoverageDecision := do - let b ← auths_model.principal_id_equal action.actor authority.subject + let linkage ← root_linkage authority action.actor + let b ← + auths_algebra_kernel.root_preserved + CanonicalPrincipal.Insts.CoreCmpPartialEqCanonicalPrincipal linkage if b then let b1 ← @@ -303,8 +353,8 @@ def evaluate_action_coverage_view if b6 then let b7 ← - auths_model.optional_budget_covers authority.budget_ceiling - action.requested_budget + auths_model.budget_ceiling_covers_action + authority.budget_ceiling action.requested_budget expression if b7 then ok CoverageDecision.Authorized else diff --git a/formal/qualification/aeneas/generated/authority/FunsExternal.lean b/formal/qualification/aeneas/generated/authority/FunsExternal.lean index 313d1783..815e47f3 100644 --- a/formal/qualification/aeneas/generated/authority/FunsExternal.lean +++ b/formal/qualification/aeneas/generated/authority/FunsExternal.lean @@ -1,12 +1,16 @@ --- REVIEWED TRANSPARENT LINKS FOR AENEAS-GENERATED PRODUCTION AUTHORITY CODE. +-- REVIEWED TRANSPARENT ADAPTERS FOR AENEAS-GENERATED PRODUCTION AUTHORITY CODE. -- --- Every rich leaf predicate below is imported from the mechanically --- translated `auths-model` production source. The sole local definition is --- the eleven-field conjunction generated from `formal/algebra-contract-v1.toml`; --- `cargo xtask formal` rejects drift from that contract. +-- The authority translation emits authority-local copies of the algebra +-- carriers. These adapters rebox them field by field into the carriers the +-- translated `auths_algebra_kernel` owns and delegate to its functions. +-- +-- NO axiom, cast, assumed equality, or restated semantics. Every Boolean +-- decision below is computed by the mechanically translated owning crate. +-- Attenuation dimensions bound by formal/algebra-contract-v1.toml: 11. import Aeneas import qualification.aeneas.generated.authority.Types import qualification.aeneas.generated.model.Funs +import qualification.aeneas.generated.algebra.Funs open Aeneas Aeneas.Std Result ControlFlow Error @@ -16,29 +20,124 @@ set_option linter.unusedVariables false set_option maxHeartbeats 1000000 set_option maxRecDepth 2048 -open auths_authority +namespace auths_authority + +/-- Reboxes the authority-local root linkage into the owning carrier. -/ +def auths_algebra_kernel.toRootLinkage {Identity : Type} + (value : auths_algebra_kernel.RootLinkage Identity) : + _root_.auths_algebra_kernel.RootLinkage Identity := + { parent_root := value.parent_root, + parent_subject := value.parent_subject, + parent_delegated := value.parent_delegated, + grant_issuer := value.grant_issuer } + +/-- Delegates root preservation to the translated algebra kernel. -/ +@[rust_fun "auths_algebra_kernel::root_preserved"] +def auths_algebra_kernel.root_preserved {Identity : Type} + (inst : core.cmp.PartialEq Identity Identity) + (linkage : auths_algebra_kernel.RootLinkage Identity) : Result Bool := + _root_.auths_algebra_kernel.root_preserved inst + (auths_algebra_kernel.toRootLinkage linkage) +/-- Reboxes the authority-local attenuation checks into the owning carrier. -/ +def auths_algebra_kernel.generated.toAttenuationChecks + (value : auths_algebra_kernel.generated.AttenuationChecks) : + _root_.auths_algebra_kernel.generated.AttenuationChecks := + { root_preserved := value.root_preserved, + depth_decreases := value.depth_decreases, + profile_attenuates := value.profile_attenuates, + permissions_attenuate := value.permissions_attenuate, + validity_attenuates := value.validity_attenuates, + audiences_attenuate := value.audiences_attenuate, + action_constraint_attenuates := value.action_constraint_attenuates, + budget_attenuates := value.budget_attenuates, + status_attenuates := value.status_attenuates, + assurance_attenuates := value.assurance_attenuates, + extensions_attenuate := value.extensions_attenuate } + +/-- Delegates the attenuation conjunction to the translated algebra kernel. -/ @[rust_fun "auths_algebra_kernel::generated::attenuation_checks_accept"] def auths_algebra_kernel.generated.attenuation_checks_accept -(checks : auths_algebra_kernel.generated.AttenuationChecks) : Result Bool := do - if checks.root_preserved then - if checks.depth_decreases then - if checks.profile_attenuates then - if checks.permissions_attenuate then - if checks.validity_attenuates then - if checks.audiences_attenuate then - if checks.action_constraint_attenuates then - if checks.budget_attenuates then - if checks.status_attenuates then - if checks.assurance_attenuates then - ok checks.extensions_attenuate - else ok false - else ok false - else ok false - else ok false - else ok false - else ok false - else ok false - else ok false - else ok false - else ok false + (checks : auths_algebra_kernel.generated.AttenuationChecks) : Result Bool := + _root_.auths_algebra_kernel.generated.attenuation_checks_accept + (auths_algebra_kernel.generated.toAttenuationChecks checks) + +-- EXACT BRIDGE PROOFS. Each reboxed field is definitionally its source +-- field, and each adapter is definitionally the owning-crate function +-- applied to the conversion. A rebox that dropped or crossed a field +-- would not close by rfl. + +theorem auths_algebra_kernel.toRootLinkage_parent_root {Identity : Type} + (value : auths_algebra_kernel.RootLinkage Identity) : + (auths_algebra_kernel.toRootLinkage value).parent_root = value.parent_root := rfl + +theorem auths_algebra_kernel.toRootLinkage_parent_subject {Identity : Type} + (value : auths_algebra_kernel.RootLinkage Identity) : + (auths_algebra_kernel.toRootLinkage value).parent_subject = value.parent_subject := rfl + +theorem auths_algebra_kernel.toRootLinkage_parent_delegated {Identity : Type} + (value : auths_algebra_kernel.RootLinkage Identity) : + (auths_algebra_kernel.toRootLinkage value).parent_delegated = value.parent_delegated := rfl + +theorem auths_algebra_kernel.toRootLinkage_grant_issuer {Identity : Type} + (value : auths_algebra_kernel.RootLinkage Identity) : + (auths_algebra_kernel.toRootLinkage value).grant_issuer = value.grant_issuer := rfl + +theorem auths_algebra_kernel.generated.toAttenuationChecks_root_preserved + (value : auths_algebra_kernel.generated.AttenuationChecks) : + (auths_algebra_kernel.generated.toAttenuationChecks value).root_preserved = value.root_preserved := rfl + +theorem auths_algebra_kernel.generated.toAttenuationChecks_depth_decreases + (value : auths_algebra_kernel.generated.AttenuationChecks) : + (auths_algebra_kernel.generated.toAttenuationChecks value).depth_decreases = value.depth_decreases := rfl + +theorem auths_algebra_kernel.generated.toAttenuationChecks_profile_attenuates + (value : auths_algebra_kernel.generated.AttenuationChecks) : + (auths_algebra_kernel.generated.toAttenuationChecks value).profile_attenuates = value.profile_attenuates := rfl + +theorem auths_algebra_kernel.generated.toAttenuationChecks_permissions_attenuate + (value : auths_algebra_kernel.generated.AttenuationChecks) : + (auths_algebra_kernel.generated.toAttenuationChecks value).permissions_attenuate = value.permissions_attenuate := rfl + +theorem auths_algebra_kernel.generated.toAttenuationChecks_validity_attenuates + (value : auths_algebra_kernel.generated.AttenuationChecks) : + (auths_algebra_kernel.generated.toAttenuationChecks value).validity_attenuates = value.validity_attenuates := rfl + +theorem auths_algebra_kernel.generated.toAttenuationChecks_audiences_attenuate + (value : auths_algebra_kernel.generated.AttenuationChecks) : + (auths_algebra_kernel.generated.toAttenuationChecks value).audiences_attenuate = value.audiences_attenuate := rfl + +theorem auths_algebra_kernel.generated.toAttenuationChecks_action_constraint_attenuates + (value : auths_algebra_kernel.generated.AttenuationChecks) : + (auths_algebra_kernel.generated.toAttenuationChecks value).action_constraint_attenuates = value.action_constraint_attenuates := rfl + +theorem auths_algebra_kernel.generated.toAttenuationChecks_budget_attenuates + (value : auths_algebra_kernel.generated.AttenuationChecks) : + (auths_algebra_kernel.generated.toAttenuationChecks value).budget_attenuates = value.budget_attenuates := rfl + +theorem auths_algebra_kernel.generated.toAttenuationChecks_status_attenuates + (value : auths_algebra_kernel.generated.AttenuationChecks) : + (auths_algebra_kernel.generated.toAttenuationChecks value).status_attenuates = value.status_attenuates := rfl + +theorem auths_algebra_kernel.generated.toAttenuationChecks_assurance_attenuates + (value : auths_algebra_kernel.generated.AttenuationChecks) : + (auths_algebra_kernel.generated.toAttenuationChecks value).assurance_attenuates = value.assurance_attenuates := rfl + +theorem auths_algebra_kernel.generated.toAttenuationChecks_extensions_attenuate + (value : auths_algebra_kernel.generated.AttenuationChecks) : + (auths_algebra_kernel.generated.toAttenuationChecks value).extensions_attenuate = value.extensions_attenuate := rfl + +theorem auths_algebra_kernel.root_preserved_delegates {Identity : Type} + (inst : core.cmp.PartialEq Identity Identity) + (linkage : auths_algebra_kernel.RootLinkage Identity) : + auths_algebra_kernel.root_preserved inst linkage = + _root_.auths_algebra_kernel.root_preserved inst + (auths_algebra_kernel.toRootLinkage linkage) := rfl + +theorem auths_algebra_kernel.generated.attenuation_checks_accept_delegates + (checks : auths_algebra_kernel.generated.AttenuationChecks) : + auths_algebra_kernel.generated.attenuation_checks_accept checks = + _root_.auths_algebra_kernel.generated.attenuation_checks_accept + (auths_algebra_kernel.generated.toAttenuationChecks checks) := rfl + +end auths_authority diff --git a/formal/qualification/aeneas/generated/authority/FunsExternal_Template.lean b/formal/qualification/aeneas/generated/authority/FunsExternal_Template.lean index 2fa77b07..b527dd3f 100644 --- a/formal/qualification/aeneas/generated/authority/FunsExternal_Template.lean +++ b/formal/qualification/aeneas/generated/authority/FunsExternal_Template.lean @@ -23,6 +23,16 @@ open auths_authority axiom auths_algebra_kernel.generated.attenuation_checks_accept : auths_algebra_kernel.generated.AttenuationChecks → Result Bool +/-- [auths_algebra_kernel::root_preserved]: + Source: 'core/crates/auths-algebra-kernel/src/lib.rs', lines 49:0-49:83 + Name pattern: [auths_algebra_kernel::root_preserved] + Visibility: public -/ +@[rust_fun "auths_algebra_kernel::root_preserved"] +axiom auths_algebra_kernel.root_preserved + {Identity : Type} (corecmpPartialEqInst : core.cmp.PartialEq Identity + Identity) : + auths_algebra_kernel.RootLinkage Identity → Result Bool + /-- [auths_model::principal_id_equal]: Source: 'core/crates/auths-model/src/lib.rs', lines 211:0-211:74 Name pattern: [auths_model::principal_id_equal] @@ -132,18 +142,18 @@ axiom auths_model.optional_budget_attenuates Option auths_model.BudgetCeiling → Option auths_model.BudgetCeiling → Result Bool -/-- [auths_model::optional_budget_covers]: - Source: 'core/crates/auths-model/src/lib.rs', lines 929:0-932:9 - Name pattern: [auths_model::optional_budget_covers] +/-- [auths_model::budget_ceiling_covers_action]: + Source: 'core/crates/auths-model/src/lib.rs', lines 984:0-988:9 + Name pattern: [auths_model::budget_ceiling_covers_action] Visibility: public -/ -@[rust_fun "auths_model::optional_budget_covers"] -axiom auths_model.optional_budget_covers +@[rust_fun "auths_model::budget_ceiling_covers_action"] +axiom auths_model.budget_ceiling_covers_action : Option auths_model.BudgetCeiling → Option auths_model.BudgetCeiling → - Result Bool + auths_model.ProfileBudgetExpression → Result Bool /-- [auths_model::status_policy_attenuates]: - Source: 'core/crates/auths-model/src/lib.rs', lines 989:0-989:84 + Source: 'core/crates/auths-model/src/lib.rs', lines 1046:0-1046:84 Name pattern: [auths_model::status_policy_attenuates] Visibility: public -/ @[rust_fun "auths_model::status_policy_attenuates"] @@ -151,7 +161,7 @@ axiom auths_model.status_policy_attenuates : auths_model.StatusPolicy → auths_model.StatusPolicy → Result Bool /-- [auths_model::critical_extensions_equal]: - Source: 'core/crates/auths-model/src/lib.rs', lines 1082:0-1082:97 + Source: 'core/crates/auths-model/src/lib.rs', lines 1139:0-1139:97 Name pattern: [auths_model::critical_extensions_equal] Visibility: public -/ @[rust_fun "auths_model::critical_extensions_equal"] diff --git a/formal/qualification/aeneas/generated/authority/Types.lean b/formal/qualification/aeneas/generated/authority/Types.lean index a69df38d..430709ca 100644 --- a/formal/qualification/aeneas/generated/authority/Types.lean +++ b/formal/qualification/aeneas/generated/authority/Types.lean @@ -33,8 +33,19 @@ structure auths_algebra_kernel.generated.AttenuationChecks where assurance_attenuates : Bool extensions_attenuate : Bool +/-- [auths_algebra_kernel::RootLinkage] + Source: 'core/crates/auths-algebra-kernel/src/lib.rs', lines 21:0-21:32 + Name pattern: [auths_algebra_kernel::RootLinkage] + Visibility: public -/ +@[rust_type "auths_algebra_kernel::RootLinkage"] +structure auths_algebra_kernel.RootLinkage (Identity : Type) where + parent_root : Identity + parent_subject : Identity + parent_delegated : Bool + grant_issuer : Identity + /-- [auths_model::GrantAuthorityView] - Source: 'core/crates/auths-model/src/lib.rs', lines 1307:0-1307:33 + Source: 'core/crates/auths-model/src/lib.rs', lines 1364:0-1364:33 Name pattern: [auths_model::GrantAuthorityView] Visibility: public -/ @[rust_type "auths_model::GrantAuthorityView"] @@ -54,7 +65,7 @@ structure auths_model.GrantAuthorityView where extensions : auths_model.CriticalExtensions /-- [auths_model::ScopeAuthorityView] - Source: 'core/crates/auths-model/src/lib.rs', lines 1327:0-1327:33 + Source: 'core/crates/auths-model/src/lib.rs', lines 1384:0-1384:33 Name pattern: [auths_model::ScopeAuthorityView] Visibility: public -/ @[rust_type "auths_model::ScopeAuthorityView"] @@ -71,7 +82,7 @@ structure auths_model.ScopeAuthorityView where extensions : auths_model.CriticalExtensions /-- [auths_model::ActionAuthorityView] - Source: 'core/crates/auths-model/src/lib.rs', lines 1535:0-1535:34 + Source: 'core/crates/auths-model/src/lib.rs', lines 1592:0-1592:34 Name pattern: [auths_model::ActionAuthorityView] Visibility: public -/ @[rust_type "auths_model::ActionAuthorityView"] @@ -86,7 +97,7 @@ structure auths_model.ActionAuthorityView where terminal_grant : Option auths_model.GrantId /-- [auths_model::DenialReason] - Source: 'core/crates/auths-model/src/lib.rs', lines 4358:0-4358:21 + Source: 'core/crates/auths-model/src/lib.rs', lines 4474:0-4474:21 Name pattern: [auths_model::DenialReason] Visibility: public -/ @[discriminant isize, rust_type "auths_model::DenialReason"] @@ -135,7 +146,7 @@ inductive auths_model.DenialReason where | LocalPolicyDenied : auths_model.DenialReason /-- [auths_authority::AcceptedTransition] - Source: 'core/crates/auths-authority/src/lib.rs', lines 44:0-56:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 46:0-58:1 Visibility: public -/ structure AcceptedTransition where subject : auths_model.PrincipalId @@ -151,7 +162,7 @@ structure AcceptedTransition where extensions : auths_model.CriticalExtensions /-- [auths_authority::DelegationOutcome] - Source: 'core/crates/auths-authority/src/lib.rs', lines 61:0-64:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 63:0-66:1 Visibility: public -/ @[discriminant isize] inductive DelegationOutcome where @@ -159,14 +170,14 @@ inductive DelegationOutcome where | Denied : auths_model.DenialReason → DelegationOutcome /-- [auths_authority::DelegationEvaluation] - Source: 'core/crates/auths-authority/src/lib.rs', lines 69:0-72:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 71:0-74:1 Visibility: public -/ structure DelegationEvaluation where checks : auths_algebra_kernel.generated.AttenuationChecks outcome : DelegationOutcome /-- [auths_authority::CoverageDecision] - Source: 'core/crates/auths-authority/src/lib.rs', lines 77:0-80:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 79:0-82:1 Visibility: public -/ @[discriminant isize] inductive CoverageDecision where @@ -174,7 +185,7 @@ inductive CoverageDecision where | Denied : auths_model.DenialReason → CoverageDecision /-- [auths_authority::AuthorityDimension] - Source: 'core/crates/auths-authority/src/lib.rs', lines 84:0-95:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 86:0-97:1 Visibility: public -/ @[discriminant isize] inductive AuthorityDimension where @@ -190,7 +201,7 @@ inductive AuthorityDimension where | Extensions : AuthorityDimension /-- [auths_authority::AuthorScopeDecision] - Source: 'core/crates/auths-authority/src/lib.rs', lines 100:0-103:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 102:0-105:1 Visibility: public -/ @[discriminant isize] inductive AuthorScopeDecision where @@ -198,9 +209,10 @@ inductive AuthorScopeDecision where | Denied : AuthorityDimension → AuthorScopeDecision /-- [auths_authority::AuthorityStateView] - Source: 'core/crates/auths-authority/src/lib.rs', lines 108:0-122:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 110:0-128:1 Visibility: public -/ structure AuthorityStateView where + root : auths_model.PrincipalId subject : auths_model.PrincipalId allowed_profiles : Slice auths_model.ProfileRef profile : Option auths_model.ProfileRef @@ -215,4 +227,9 @@ structure AuthorityStateView where status_policy : auths_model.StatusPolicy extensions : Option auths_model.CriticalExtensions +/-- [auths_authority::CanonicalPrincipal] + Source: 'core/crates/auths-authority/src/lib.rs', lines 137:0-137:47 -/ +@[reducible] +def CanonicalPrincipal := auths_model.PrincipalId + end auths_authority diff --git a/formal/qualification/aeneas/generated/authority/TypesExternal_Template.lean b/formal/qualification/aeneas/generated/authority/TypesExternal_Template.lean index 3ca34994..4b6f879e 100644 --- a/formal/qualification/aeneas/generated/authority/TypesExternal_Template.lean +++ b/formal/qualification/aeneas/generated/authority/TypesExternal_Template.lean @@ -97,15 +97,22 @@ axiom auths_model.ActionConstraint : Type @[rust_type "auths_model::BudgetCeiling"] axiom auths_model.BudgetCeiling : Type +/-- [auths_model::ProfileBudgetExpression] + Source: 'core/crates/auths-model/src/lib.rs', lines 965:0-965:32 + Name pattern: [auths_model::ProfileBudgetExpression] + Visibility: public -/ +@[rust_type "auths_model::ProfileBudgetExpression"] +axiom auths_model.ProfileBudgetExpression : Type + /-- [auths_model::StatusPolicy] - Source: 'core/crates/auths-model/src/lib.rs', lines 966:0-966:21 + Source: 'core/crates/auths-model/src/lib.rs', lines 1023:0-1023:21 Name pattern: [auths_model::StatusPolicy] Visibility: public -/ @[rust_type "auths_model::StatusPolicy"] axiom auths_model.StatusPolicy : Type /-- [auths_model::CriticalExtensions] - Source: 'core/crates/auths-model/src/lib.rs', lines 1041:0-1041:29 + Source: 'core/crates/auths-model/src/lib.rs', lines 1098:0-1098:29 Name pattern: [auths_model::CriticalExtensions] Visibility: public -/ @[rust_type "auths_model::CriticalExtensions"] diff --git a/formal/qualification/aeneas/generated/authority/translation.json b/formal/qualification/aeneas/generated/authority/translation.json index ae34519e..46b1df24 100644 --- a/formal/qualification/aeneas/generated/authority/translation.json +++ b/formal/qualification/aeneas/generated/authority/translation.json @@ -1,10 +1,10 @@ { - "aeneas_version": "3a8586f", + "aeneas_version": "3a8586facab25b31bdb1e1f5f45acd60d1cc5ff0", "charon_version": "0.1.225", "crate": "auths_authority", "functions": [ { - "def_id": 14, + "def_id": 16, "lean_name": "auths_authority.auths_algebra_kernel.generated.attenuation_checks_accept", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_algebra_kernel::generated::attenuation_checks_accept", @@ -21,7 +21,24 @@ "reducible": false }, { - "def_id": 11, + "def_id": 4, + "lean_name": "auths_authority.auths_algebra_kernel.root_preserved", + "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", + "rust_name": "auths_algebra_kernel::root_preserved", + "is_local": false, + "source": { + "file": "core/crates/auths-algebra-kernel/src/lib.rs", + "begin_line": 49, + "end_line": 49 + }, + "is_opaque": true, + "can_fail": true, + "can_diverge": false, + "is_rec": false, + "reducible": false + }, + { + "def_id": 27, "lean_name": "auths_authority.auths_model.principal_id_equal", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::principal_id_equal", @@ -38,7 +55,7 @@ "reducible": false }, { - "def_id": 5, + "def_id": 8, "lean_name": "auths_authority.auths_model.validity_window_contains", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::validity_window_contains", @@ -55,7 +72,7 @@ "reducible": false }, { - "def_id": 19, + "def_id": 21, "lean_name": "auths_authority.auths_model.profile_ref_equal", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::profile_ref_equal", @@ -72,7 +89,7 @@ "reducible": false }, { - "def_id": 20, + "def_id": 26, "lean_name": "auths_authority.auths_model.profile_slice_contains", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::profile_slice_contains", @@ -89,7 +106,7 @@ "reducible": false }, { - "def_id": 10, + "def_id": 13, "lean_name": "auths_authority.auths_model.assurance_policy_id_equal", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::assurance_policy_id_equal", @@ -106,7 +123,7 @@ "reducible": false }, { - "def_id": 13, + "def_id": 15, "lean_name": "auths_authority.auths_model.optional_grant_id_equal", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::optional_grant_id_equal", @@ -123,7 +140,7 @@ "reducible": false }, { - "def_id": 15, + "def_id": 17, "lean_name": "auths_authority.auths_model.permission_set_contains", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::permission_set_contains", @@ -140,7 +157,7 @@ "reducible": false }, { - "def_id": 4, + "def_id": 7, "lean_name": "auths_authority.auths_model.permission_set_is_subset", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::permission_set_is_subset", @@ -157,7 +174,7 @@ "reducible": false }, { - "def_id": 16, + "def_id": 18, "lean_name": "auths_authority.auths_model.audience_set_contains", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::audience_set_contains", @@ -174,7 +191,7 @@ "reducible": false }, { - "def_id": 6, + "def_id": 9, "lean_name": "auths_authority.auths_model.audience_set_is_subset", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::audience_set_is_subset", @@ -191,7 +208,7 @@ "reducible": false }, { - "def_id": 17, + "def_id": 19, "lean_name": "auths_authority.auths_model.action_constraint_allows", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::action_constraint_allows", @@ -208,7 +225,7 @@ "reducible": false }, { - "def_id": 7, + "def_id": 10, "lean_name": "auths_authority.auths_model.action_constraint_attenuates", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::action_constraint_attenuates", @@ -225,7 +242,7 @@ "reducible": false }, { - "def_id": 8, + "def_id": 11, "lean_name": "auths_authority.auths_model.optional_budget_attenuates", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::optional_budget_attenuates", @@ -242,15 +259,15 @@ "reducible": false }, { - "def_id": 18, - "lean_name": "auths_authority.auths_model.optional_budget_covers", + "def_id": 20, + "lean_name": "auths_authority.auths_model.budget_ceiling_covers_action", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", - "rust_name": "auths_model::optional_budget_covers", + "rust_name": "auths_model::budget_ceiling_covers_action", "is_local": false, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 929, - "end_line": 932 + "begin_line": 984, + "end_line": 988 }, "is_opaque": true, "can_fail": true, @@ -259,15 +276,15 @@ "reducible": false }, { - "def_id": 9, + "def_id": 12, "lean_name": "auths_authority.auths_model.status_policy_attenuates", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::status_policy_attenuates", "is_local": false, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 989, - "end_line": 989 + "begin_line": 1046, + "end_line": 1046 }, "is_opaque": true, "can_fail": true, @@ -276,15 +293,15 @@ "reducible": false }, { - "def_id": 12, + "def_id": 22, "lean_name": "auths_authority.auths_model.critical_extensions_equal", "lean_file": "qualification/aeneas/generated/authority/FunsExternal_Template.lean", "rust_name": "auths_model::critical_extensions_equal", "is_local": false, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 1082, - "end_line": 1082 + "begin_line": 1139, + "end_line": 1139 }, "is_opaque": true, "can_fail": true, @@ -292,16 +309,84 @@ "is_rec": false, "reducible": false }, + { + "def_id": 25, + "lean_name": "auths_authority.CanonicalPrincipal.Insts.CoreCmpPartialEqCanonicalPrincipal.eq", + "lean_file": "qualification/aeneas/generated/authority/Funs.lean", + "rust_name": "auths_authority::{impl core::cmp::PartialEq> for auths_authority::CanonicalPrincipal<'_0>}::eq", + "is_local": true, + "source": { + "file": "core/crates/auths-authority/src/lib.rs", + "begin_line": 140, + "end_line": 142 + }, + "is_opaque": false, + "can_fail": true, + "can_diverge": false, + "is_rec": false, + "reducible": false + }, + { + "def_id": 14, + "lean_name": "auths_authority.extensions_attenuate", + "lean_file": "qualification/aeneas/generated/authority/Funs.lean", + "rust_name": "auths_authority::extensions_attenuate", + "is_local": true, + "source": { + "file": "core/crates/auths-authority/src/lib.rs", + "begin_line": 154, + "end_line": 162 + }, + "is_opaque": false, + "can_fail": true, + "can_diverge": false, + "is_rec": false, + "reducible": false + }, + { + "def_id": 5, + "lean_name": "auths_authority.depth_decreases", + "lean_file": "qualification/aeneas/generated/authority/Funs.lean", + "rust_name": "auths_authority::depth_decreases", + "is_local": true, + "source": { + "file": "core/crates/auths-authority/src/lib.rs", + "begin_line": 174, + "end_line": 179 + }, + "is_opaque": false, + "can_fail": true, + "can_diverge": false, + "is_rec": false, + "reducible": false + }, { "def_id": 3, + "lean_name": "auths_authority.root_linkage", + "lean_file": "qualification/aeneas/generated/authority/Funs.lean", + "rust_name": "auths_authority::root_linkage", + "is_local": true, + "source": { + "file": "core/crates/auths-authority/src/lib.rs", + "begin_line": 182, + "end_line": 192 + }, + "is_opaque": false, + "can_fail": true, + "can_diverge": false, + "is_rec": false, + "reducible": false + }, + { + "def_id": 6, "lean_name": "auths_authority.selected_profile_attenuates", "lean_file": "qualification/aeneas/generated/authority/Funs.lean", "rust_name": "auths_authority::selected_profile_attenuates", "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 124, - "end_line": 133 + "begin_line": 194, + "end_line": 203 }, "is_opaque": false, "can_fail": true, @@ -317,8 +402,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 139, - "end_line": 174 + "begin_line": 209, + "end_line": 244 }, "is_opaque": false, "can_fail": true, @@ -334,8 +419,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 195, - "end_line": 257 + "begin_line": 265, + "end_line": 329 }, "is_opaque": false, "can_fail": true, @@ -351,8 +436,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 276, - "end_line": 308 + "begin_line": 350, + "end_line": 391 }, "is_opaque": false, "can_fail": true, @@ -363,7 +448,7 @@ ], "types": [ { - "def_id": 13, + "def_id": 16, "lean_name": "auths_authority.auths_model.AssurancePolicyId", "lean_file": "qualification/aeneas/generated/authority/TypesExternal_Template.lean", "rust_name": "auths_model::AssurancePolicyId", @@ -375,7 +460,7 @@ } }, { - "def_id": 22, + "def_id": 25, "lean_name": "auths_authority.auths_model.Audience", "lean_file": "qualification/aeneas/generated/authority/TypesExternal_Template.lean", "rust_name": "auths_model::Audience", @@ -387,7 +472,7 @@ } }, { - "def_id": 15, + "def_id": 6, "lean_name": "auths_authority.auths_model.PrincipalId", "lean_file": "qualification/aeneas/generated/authority/TypesExternal_Template.lean", "rust_name": "auths_model::PrincipalId", @@ -399,7 +484,7 @@ } }, { - "def_id": 23, + "def_id": 26, "lean_name": "auths_authority.auths_model.Digest", "lean_file": "qualification/aeneas/generated/authority/TypesExternal_Template.lean", "rust_name": "auths_model::Digest", @@ -423,7 +508,7 @@ } }, { - "def_id": 8, + "def_id": 11, "lean_name": "auths_authority.auths_model.ValidityWindow", "lean_file": "qualification/aeneas/generated/authority/TypesExternal_Template.lean", "rust_name": "auths_model::ValidityWindow", @@ -435,7 +520,7 @@ } }, { - "def_id": 6, + "def_id": 9, "lean_name": "auths_authority.auths_model.ProfileRef", "lean_file": "qualification/aeneas/generated/authority/TypesExternal_Template.lean", "rust_name": "auths_model::ProfileRef", @@ -447,7 +532,7 @@ } }, { - "def_id": 21, + "def_id": 24, "lean_name": "auths_authority.auths_model.Permission", "lean_file": "qualification/aeneas/generated/authority/TypesExternal_Template.lean", "rust_name": "auths_model::Permission", @@ -459,7 +544,7 @@ } }, { - "def_id": 7, + "def_id": 10, "lean_name": "auths_authority.auths_model.PermissionSet", "lean_file": "qualification/aeneas/generated/authority/TypesExternal_Template.lean", "rust_name": "auths_model::PermissionSet", @@ -471,7 +556,7 @@ } }, { - "def_id": 9, + "def_id": 12, "lean_name": "auths_authority.auths_model.AudienceSet", "lean_file": "qualification/aeneas/generated/authority/TypesExternal_Template.lean", "rust_name": "auths_model::AudienceSet", @@ -483,7 +568,7 @@ } }, { - "def_id": 10, + "def_id": 13, "lean_name": "auths_authority.auths_model.ActionConstraint", "lean_file": "qualification/aeneas/generated/authority/TypesExternal_Template.lean", "rust_name": "auths_model::ActionConstraint", @@ -495,7 +580,7 @@ } }, { - "def_id": 11, + "def_id": 14, "lean_name": "auths_authority.auths_model.BudgetCeiling", "lean_file": "qualification/aeneas/generated/authority/TypesExternal_Template.lean", "rust_name": "auths_model::BudgetCeiling", @@ -507,31 +592,43 @@ } }, { - "def_id": 12, + "def_id": 22, + "lean_name": "auths_authority.auths_model.ProfileBudgetExpression", + "lean_file": "qualification/aeneas/generated/authority/TypesExternal_Template.lean", + "rust_name": "auths_model::ProfileBudgetExpression", + "is_local": false, + "source": { + "file": "core/crates/auths-model/src/lib.rs", + "begin_line": 965, + "end_line": 965 + } + }, + { + "def_id": 15, "lean_name": "auths_authority.auths_model.StatusPolicy", "lean_file": "qualification/aeneas/generated/authority/TypesExternal_Template.lean", "rust_name": "auths_model::StatusPolicy", "is_local": false, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 966, - "end_line": 966 + "begin_line": 1023, + "end_line": 1023 } }, { - "def_id": 14, + "def_id": 17, "lean_name": "auths_authority.auths_model.CriticalExtensions", "lean_file": "qualification/aeneas/generated/authority/TypesExternal_Template.lean", "rust_name": "auths_model::CriticalExtensions", "is_local": false, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 1041, - "end_line": 1041 + "begin_line": 1098, + "end_line": 1098 } }, { - "def_id": 4, + "def_id": 7, "lean_name": "auths_authority.auths_algebra_kernel.generated.AttenuationChecks", "lean_file": "qualification/aeneas/generated/authority/Types.lean", "rust_name": "auths_algebra_kernel::generated::AttenuationChecks", @@ -542,6 +639,18 @@ "end_line": 52 } }, + { + "def_id": 4, + "lean_name": "auths_authority.auths_algebra_kernel.RootLinkage", + "lean_file": "qualification/aeneas/generated/authority/Types.lean", + "rust_name": "auths_algebra_kernel::RootLinkage", + "is_local": false, + "source": { + "file": "core/crates/auths-algebra-kernel/src/lib.rs", + "begin_line": 21, + "end_line": 21 + } + }, { "def_id": 2, "lean_name": "auths_authority.auths_model.GrantAuthorityView", @@ -550,68 +659,68 @@ "is_local": false, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 1307, - "end_line": 1307 + "begin_line": 1364, + "end_line": 1364 } }, { - "def_id": 24, + "def_id": 27, "lean_name": "auths_authority.auths_model.ScopeAuthorityView", "lean_file": "qualification/aeneas/generated/authority/Types.lean", "rust_name": "auths_model::ScopeAuthorityView", "is_local": false, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 1327, - "end_line": 1327 + "begin_line": 1384, + "end_line": 1384 } }, { - "def_id": 19, + "def_id": 21, "lean_name": "auths_authority.auths_model.ActionAuthorityView", "lean_file": "qualification/aeneas/generated/authority/Types.lean", "rust_name": "auths_model::ActionAuthorityView", "is_local": false, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 1535, - "end_line": 1535 + "begin_line": 1592, + "end_line": 1592 } }, { - "def_id": 17, + "def_id": 19, "lean_name": "auths_authority.auths_model.DenialReason", "lean_file": "qualification/aeneas/generated/authority/Types.lean", "rust_name": "auths_model::DenialReason", "is_local": false, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 4358, - "end_line": 4358 + "begin_line": 4474, + "end_line": 4474 } }, { - "def_id": 18, + "def_id": 20, "lean_name": "auths_authority.AcceptedTransition", "lean_file": "qualification/aeneas/generated/authority/Types.lean", "rust_name": "auths_authority::AcceptedTransition", "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 44, - "end_line": 56 + "begin_line": 46, + "end_line": 58 } }, { - "def_id": 16, + "def_id": 18, "lean_name": "auths_authority.DelegationOutcome", "lean_file": "qualification/aeneas/generated/authority/Types.lean", "rust_name": "auths_authority::DelegationOutcome", "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 61, - "end_line": 64 + "begin_line": 63, + "end_line": 66 } }, { @@ -622,44 +731,44 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 69, - "end_line": 72 + "begin_line": 71, + "end_line": 74 } }, { - "def_id": 20, + "def_id": 23, "lean_name": "auths_authority.CoverageDecision", "lean_file": "qualification/aeneas/generated/authority/Types.lean", "rust_name": "auths_authority::CoverageDecision", "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 77, - "end_line": 80 + "begin_line": 79, + "end_line": 82 } }, { - "def_id": 26, + "def_id": 29, "lean_name": "auths_authority.AuthorityDimension", "lean_file": "qualification/aeneas/generated/authority/Types.lean", "rust_name": "auths_authority::AuthorityDimension", "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 84, - "end_line": 95 + "begin_line": 86, + "end_line": 97 } }, { - "def_id": 25, + "def_id": 28, "lean_name": "auths_authority.AuthorScopeDecision", "lean_file": "qualification/aeneas/generated/authority/Types.lean", "rust_name": "auths_authority::AuthorScopeDecision", "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 100, - "end_line": 103 + "begin_line": 102, + "end_line": 105 } }, { @@ -670,12 +779,40 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 108, - "end_line": 122 + "begin_line": 110, + "end_line": 128 + } + }, + { + "def_id": 5, + "lean_name": "auths_authority.CanonicalPrincipal", + "lean_file": "qualification/aeneas/generated/authority/Types.lean", + "rust_name": "auths_authority::CanonicalPrincipal", + "is_local": true, + "source": { + "file": "core/crates/auths-authority/src/lib.rs", + "begin_line": 137, + "end_line": 137 } } ], "globals": [], "trait_decls": [], - "trait_impls": [] + "trait_impls": [ + { + "def_id": 0, + "lean_name": "auths_authority.CanonicalPrincipal.Insts.CoreCmpPartialEqCanonicalPrincipal", + "lean_file": "qualification/aeneas/generated/authority/Funs.lean", + "rust_name": "auths_authority::{impl core::cmp::PartialEq> for auths_authority::CanonicalPrincipal<'_0>}", + "is_local": true, + "source": { + "file": "core/crates/auths-authority/src/lib.rs", + "begin_line": 139, + "end_line": 143 + }, + "impl_trait_def_id": 0, + "impl_trait_rust_name": "core::cmp::PartialEq", + "impl_trait_is_builtin": true + } + ] } diff --git a/formal/qualification/aeneas/generated/bounded_policy/translation.json b/formal/qualification/aeneas/generated/bounded_policy/translation.json index 8501cefa..76be1292 100644 --- a/formal/qualification/aeneas/generated/bounded_policy/translation.json +++ b/formal/qualification/aeneas/generated/bounded_policy/translation.json @@ -1,5 +1,5 @@ { - "aeneas_version": "3a8586f", + "aeneas_version": "3a8586facab25b31bdb1e1f5f45acd60d1cc5ff0", "charon_version": "0.1.225", "crate": "auths_bounded_policy", "functions": [ diff --git a/formal/qualification/aeneas/generated/lifecycle/translation.json b/formal/qualification/aeneas/generated/lifecycle/translation.json index c9c6854e..e60d0ca5 100644 --- a/formal/qualification/aeneas/generated/lifecycle/translation.json +++ b/formal/qualification/aeneas/generated/lifecycle/translation.json @@ -1,5 +1,5 @@ { - "aeneas_version": "3a8586f", + "aeneas_version": "3a8586facab25b31bdb1e1f5f45acd60d1cc5ff0", "charon_version": "0.1.225", "crate": "auths_lifecycle", "functions": [ diff --git a/formal/qualification/aeneas/generated/model/Funs.lean b/formal/qualification/aeneas/generated/model/Funs.lean index c7bf06a8..d612b37a 100644 --- a/formal/qualification/aeneas/generated/model/Funs.lean +++ b/formal/qualification/aeneas/generated/model/Funs.lean @@ -568,21 +568,37 @@ def optional_budget_attenuates | some child1 => BudgetCeiling.attenuates child1 parent1 /-- [auths_model::optional_budget_covers]: - Source: 'core/crates/auths-model/src/lib.rs', lines 929:0-937:1 + Source: 'core/crates/auths-model/src/lib.rs', lines 937:0-946:1 Visibility: public -/ def optional_budget_covers (ceiling : Option BudgetCeiling) (requested : Option BudgetCeiling) : Result Bool := do - match requested with + match ceiling with | none => ok true - | some requested1 => - match ceiling with - | none => ok true - | some ceiling1 => BudgetCeiling.covers ceiling1 requested1 + | some ceiling1 => + match requested with + | none => ok false + | some requested1 => BudgetCeiling.covers ceiling1 requested1 + +/-- [auths_model::budget_ceiling_covers_action]: + Source: 'core/crates/auths-model/src/lib.rs', lines 984:0-994:1 + Visibility: public -/ +def budget_ceiling_covers_action + (ceiling : Option BudgetCeiling) (requested : Option BudgetCeiling) + (expression : ProfileBudgetExpression) : + Result Bool + := do + match requested with + | none => + match expression with + | ProfileBudgetExpression.Expressible => + optional_budget_covers ceiling requested + | ProfileBudgetExpression.Inexpressible => ok true + | some _ => optional_budget_covers ceiling requested /-- [auths_model::status_policy_attenuates]: - Source: 'core/crates/auths-model/src/lib.rs', lines 989:0-1007:1 + Source: 'core/crates/auths-model/src/lib.rs', lines 1046:0-1064:1 Visibility: public -/ def status_policy_attenuates (child : StatusPolicy) (parent : StatusPolicy) : Result Bool := do @@ -600,7 +616,7 @@ def status_policy_attenuates else ok false /-- [auths_model::critical_extensions_equal]: loop body 0: - Source: 'core/crates/auths-model/src/lib.rs', lines 1087:4-1100:1 + Source: 'core/crates/auths-model/src/lib.rs', lines 1144:4-1157:1 Visibility: public -/ @[rust_loop_body] def critical_extensions_equal_loop.body @@ -635,7 +651,7 @@ def critical_extensions_equal_loop.body else ok (done true) /-- [auths_model::critical_extensions_equal]: loop 0: - Source: 'core/crates/auths-model/src/lib.rs', lines 1087:4-1100:1 + Source: 'core/crates/auths-model/src/lib.rs', lines 1144:4-1157:1 Visibility: public -/ @[rust_loop] def critical_extensions_equal_loop @@ -648,7 +664,7 @@ def critical_extensions_equal_loop index /-- [auths_model::critical_extensions_equal]: - Source: 'core/crates/auths-model/src/lib.rs', lines 1082:0-1100:1 + Source: 'core/crates/auths-model/src/lib.rs', lines 1139:0-1157:1 Visibility: public -/ def critical_extensions_equal (child : CriticalExtensions) (parent : CriticalExtensions) : diff --git a/formal/qualification/aeneas/generated/model/Types.lean b/formal/qualification/aeneas/generated/model/Types.lean index b030ca83..ab8597c9 100644 --- a/formal/qualification/aeneas/generated/model/Types.lean +++ b/formal/qualification/aeneas/generated/model/Types.lean @@ -141,14 +141,22 @@ structure BudgetCeiling where algebra : BudgetAlgebraId value : Std.U64 +/-- [auths_model::ProfileBudgetExpression] + Source: 'core/crates/auths-model/src/lib.rs', lines 965:0-971:1 + Visibility: public -/ +@[discriminant isize] +inductive ProfileBudgetExpression where +| Expressible : ProfileBudgetExpression +| Inexpressible : ProfileBudgetExpression + /-- [auths_model::FreshnessLimit] - Source: 'core/crates/auths-model/src/lib.rs', lines 941:0-941:31 + Source: 'core/crates/auths-model/src/lib.rs', lines 998:0-998:31 Visibility: public -/ @[reducible] def FreshnessLimit := Std.U64 /-- [auths_model::StatusPolicy] - Source: 'core/crates/auths-model/src/lib.rs', lines 966:0-976:1 + Source: 'core/crates/auths-model/src/lib.rs', lines 1023:0-1033:1 Visibility: public -/ @[discriminant isize] inductive StatusPolicy where @@ -156,14 +164,14 @@ inductive StatusPolicy where | SnapshotRequired : StatusMethodId → FreshnessLimit → StatusPolicy /-- [auths_model::CriticalExtension] - Source: 'core/crates/auths-model/src/lib.rs', lines 1010:0-1013:1 + Source: 'core/crates/auths-model/src/lib.rs', lines 1067:0-1070:1 Visibility: public -/ structure CriticalExtension where id : ExtensionId bytes : alloc.vec.Vec Std.U8 /-- [auths_model::CriticalExtensions] - Source: 'core/crates/auths-model/src/lib.rs', lines 1041:0-1041:54 + Source: 'core/crates/auths-model/src/lib.rs', lines 1098:0-1098:54 Visibility: public -/ @[reducible] def CriticalExtensions := alloc.vec.Vec CriticalExtension diff --git a/formal/qualification/aeneas/generated/model/translation.json b/formal/qualification/aeneas/generated/model/translation.json index 3e26640c..37c6cf9f 100644 --- a/formal/qualification/aeneas/generated/model/translation.json +++ b/formal/qualification/aeneas/generated/model/translation.json @@ -1,10 +1,10 @@ { - "aeneas_version": "3a8586f", + "aeneas_version": "3a8586facab25b31bdb1e1f5f45acd60d1cc5ff0", "charon_version": "0.1.225", "crate": "auths_model", "functions": [ { - "def_id": 27, + "def_id": 28, "lean_name": "auths_model.alloc.string.String.as_bytes", "lean_file": "qualification/aeneas/generated/model/FunsExternal_Template.lean", "rust_name": "alloc::string::{alloc::string::String}::as_bytes", @@ -21,7 +21,7 @@ "reducible": false }, { - "def_id": 28, + "def_id": 29, "lean_name": "auths_model.byte_slices_equal", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::byte_slices_equal", @@ -38,7 +38,7 @@ "reducible": false }, { - "def_id": 16, + "def_id": 17, "lean_name": "auths_model.principal_id_equal", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::principal_id_equal", @@ -55,7 +55,7 @@ "reducible": false }, { - "def_id": 42, + "def_id": 43, "lean_name": "auths_model.Digest.as_bytes", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::{auths_model::Digest}::as_bytes", @@ -72,7 +72,7 @@ "reducible": false }, { - "def_id": 32, + "def_id": 33, "lean_name": "auths_model.GrantId.as_bytes", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::{auths_model::GrantId}::as_bytes", @@ -123,7 +123,7 @@ "reducible": false }, { - "def_id": 20, + "def_id": 21, "lean_name": "auths_model.profile_ref_equal", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::profile_ref_equal", @@ -140,7 +140,7 @@ "reducible": false }, { - "def_id": 19, + "def_id": 20, "lean_name": "auths_model.profile_slice_contains_loop.body", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::profile_slice_contains", @@ -165,7 +165,7 @@ "parent_lean_name": "auths_model.profile_slice_contains" }, { - "def_id": 19, + "def_id": 20, "lean_name": "auths_model.profile_slice_contains_loop", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::profile_slice_contains", @@ -190,7 +190,7 @@ "parent_lean_name": "auths_model.profile_slice_contains" }, { - "def_id": 19, + "def_id": 20, "lean_name": "auths_model.profile_slice_contains", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::profile_slice_contains", @@ -207,7 +207,7 @@ "reducible": true }, { - "def_id": 15, + "def_id": 16, "lean_name": "auths_model.assurance_policy_id_equal", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::assurance_policy_id_equal", @@ -224,7 +224,7 @@ "reducible": false }, { - "def_id": 17, + "def_id": 18, "lean_name": "auths_model.grant_id_equal", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::grant_id_equal", @@ -241,7 +241,7 @@ "reducible": false }, { - "def_id": 18, + "def_id": 19, "lean_name": "auths_model.optional_grant_id_equal", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::optional_grant_id_equal", @@ -258,7 +258,7 @@ "reducible": false }, { - "def_id": 23, + "def_id": 24, "lean_name": "auths_model.permissions_equal", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::permissions_equal", @@ -409,7 +409,7 @@ "reducible": true }, { - "def_id": 24, + "def_id": 25, "lean_name": "auths_model.audiences_equal", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::audiences_equal", @@ -560,7 +560,7 @@ "reducible": true }, { - "def_id": 25, + "def_id": 26, "lean_name": "auths_model.digests_equal", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::digests_equal", @@ -711,7 +711,7 @@ "reducible": true }, { - "def_id": 26, + "def_id": 27, "lean_name": "auths_model.body_digest_set_only_contains_loop.body", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::body_digest_set_only_contains", @@ -736,7 +736,7 @@ "parent_lean_name": "auths_model.body_digest_set_only_contains" }, { - "def_id": 26, + "def_id": 27, "lean_name": "auths_model.body_digest_set_only_contains_loop", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::body_digest_set_only_contains", @@ -761,7 +761,7 @@ "parent_lean_name": "auths_model.body_digest_set_only_contains" }, { - "def_id": 26, + "def_id": 27, "lean_name": "auths_model.body_digest_set_only_contains", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::body_digest_set_only_contains", @@ -829,7 +829,7 @@ "reducible": false }, { - "def_id": 29, + "def_id": 30, "lean_name": "auths_model.BudgetCeiling.attenuates", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::{auths_model::BudgetCeiling}::attenuates", @@ -846,7 +846,7 @@ "reducible": false }, { - "def_id": 30, + "def_id": 31, "lean_name": "auths_model.BudgetCeiling.covers", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::{auths_model::BudgetCeiling}::covers", @@ -887,8 +887,8 @@ "is_local": true, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 929, - "end_line": 937 + "begin_line": 937, + "end_line": 946 }, "is_opaque": false, "can_fail": true, @@ -898,14 +898,31 @@ }, { "def_id": 13, + "lean_name": "auths_model.budget_ceiling_covers_action", + "lean_file": "qualification/aeneas/generated/model/Funs.lean", + "rust_name": "auths_model::budget_ceiling_covers_action", + "is_local": true, + "source": { + "file": "core/crates/auths-model/src/lib.rs", + "begin_line": 984, + "end_line": 994 + }, + "is_opaque": false, + "can_fail": true, + "can_diverge": false, + "is_rec": false, + "reducible": false + }, + { + "def_id": 14, "lean_name": "auths_model.status_policy_attenuates", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::status_policy_attenuates", "is_local": true, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 989, - "end_line": 1007 + "begin_line": 1046, + "end_line": 1064 }, "is_opaque": false, "can_fail": true, @@ -914,15 +931,15 @@ "reducible": false }, { - "def_id": 14, + "def_id": 15, "lean_name": "auths_model.critical_extensions_equal_loop.body", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::critical_extensions_equal", "is_local": true, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 1087, - "end_line": 1100 + "begin_line": 1144, + "end_line": 1157 }, "is_opaque": false, "can_fail": true, @@ -939,15 +956,15 @@ "parent_lean_name": "auths_model.critical_extensions_equal" }, { - "def_id": 14, + "def_id": 15, "lean_name": "auths_model.critical_extensions_equal_loop", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::critical_extensions_equal", "is_local": true, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 1087, - "end_line": 1100 + "begin_line": 1144, + "end_line": 1157 }, "is_opaque": false, "can_fail": true, @@ -964,15 +981,15 @@ "parent_lean_name": "auths_model.critical_extensions_equal" }, { - "def_id": 14, + "def_id": 15, "lean_name": "auths_model.critical_extensions_equal", "lean_file": "qualification/aeneas/generated/model/Funs.lean", "rust_name": "auths_model::critical_extensions_equal", "is_local": true, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 1082, - "end_line": 1100 + "begin_line": 1139, + "end_line": 1157 }, "is_opaque": false, "can_fail": true, @@ -1007,7 +1024,7 @@ } }, { - "def_id": 16, + "def_id": 17, "lean_name": "auths_model.StatusMethodId", "lean_file": "qualification/aeneas/generated/model/Types.lean", "rust_name": "auths_model::StatusMethodId", @@ -1019,7 +1036,7 @@ } }, { - "def_id": 20, + "def_id": 21, "lean_name": "auths_model.ExtensionId", "lean_file": "qualification/aeneas/generated/model/Types.lean", "rust_name": "auths_model::ExtensionId", @@ -1031,7 +1048,7 @@ } }, { - "def_id": 21, + "def_id": 22, "lean_name": "auths_model.AssurancePolicyId", "lean_file": "qualification/aeneas/generated/model/Types.lean", "rust_name": "auths_model::AssurancePolicyId", @@ -1043,7 +1060,7 @@ } }, { - "def_id": 25, + "def_id": 26, "lean_name": "auths_model.ProfileId", "lean_file": "qualification/aeneas/generated/model/Types.lean", "rust_name": "auths_model::ProfileId", @@ -1055,7 +1072,7 @@ } }, { - "def_id": 26, + "def_id": 27, "lean_name": "auths_model.CapabilityId", "lean_file": "qualification/aeneas/generated/model/Types.lean", "rust_name": "auths_model::CapabilityId", @@ -1067,7 +1084,7 @@ } }, { - "def_id": 27, + "def_id": 28, "lean_name": "auths_model.ResourceId", "lean_file": "qualification/aeneas/generated/model/Types.lean", "rust_name": "auths_model::ResourceId", @@ -1079,7 +1096,7 @@ } }, { - "def_id": 22, + "def_id": 23, "lean_name": "auths_model.PrincipalId", "lean_file": "qualification/aeneas/generated/model/Types.lean", "rust_name": "auths_model::PrincipalId", @@ -1103,7 +1120,7 @@ } }, { - "def_id": 23, + "def_id": 24, "lean_name": "auths_model.GrantId", "lean_file": "qualification/aeneas/generated/model/Types.lean", "rust_name": "auths_model::GrantId", @@ -1139,7 +1156,7 @@ } }, { - "def_id": 24, + "def_id": 25, "lean_name": "auths_model.ProfileRef", "lean_file": "qualification/aeneas/generated/model/Types.lean", "rust_name": "auths_model::ProfileRef", @@ -1223,51 +1240,63 @@ } }, { - "def_id": 17, + "def_id": 15, + "lean_name": "auths_model.ProfileBudgetExpression", + "lean_file": "qualification/aeneas/generated/model/Types.lean", + "rust_name": "auths_model::ProfileBudgetExpression", + "is_local": true, + "source": { + "file": "core/crates/auths-model/src/lib.rs", + "begin_line": 965, + "end_line": 971 + } + }, + { + "def_id": 18, "lean_name": "auths_model.FreshnessLimit", "lean_file": "qualification/aeneas/generated/model/Types.lean", "rust_name": "auths_model::FreshnessLimit", "is_local": true, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 941, - "end_line": 941 + "begin_line": 998, + "end_line": 998 } }, { - "def_id": 15, + "def_id": 16, "lean_name": "auths_model.StatusPolicy", "lean_file": "qualification/aeneas/generated/model/Types.lean", "rust_name": "auths_model::StatusPolicy", "is_local": true, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 966, - "end_line": 976 + "begin_line": 1023, + "end_line": 1033 } }, { - "def_id": 19, + "def_id": 20, "lean_name": "auths_model.CriticalExtension", "lean_file": "qualification/aeneas/generated/model/Types.lean", "rust_name": "auths_model::CriticalExtension", "is_local": true, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 1010, - "end_line": 1013 + "begin_line": 1067, + "end_line": 1070 } }, { - "def_id": 18, + "def_id": 19, "lean_name": "auths_model.CriticalExtensions", "lean_file": "qualification/aeneas/generated/model/Types.lean", "rust_name": "auths_model::CriticalExtensions", "is_local": true, "source": { "file": "core/crates/auths-model/src/lib.rs", - "begin_line": 1041, - "end_line": 1041 + "begin_line": 1098, + "end_line": 1098 } } ], diff --git a/formal/qualification/aeneas/qualification.toml b/formal/qualification/aeneas/qualification.toml index 359dec61..3390a8bf 100644 --- a/formal/qualification/aeneas/qualification.toml +++ b/formal/qualification/aeneas/qualification.toml @@ -166,10 +166,11 @@ classification = "Pinned upstream iterator proof-support sorry; the qualified so [[translations]] crate_name = "auths_model" translation_json = "formal/qualification/aeneas/generated/model/translation.json" -local_functions = 48 +local_functions = 49 external_functions = 1 opaque_local_functions = 0 required_symbols = [ + "auths_model::budget_ceiling_covers_action", "auths_model::inclusive_window_contains", "auths_model::validity_window_contains", "auths_model::permission_set_contains", @@ -200,11 +201,12 @@ allowed_external_symbols = [ [[translations]] crate_name = "auths_algebra_kernel" translation_json = "formal/qualification/aeneas/generated/algebra/translation.json" -local_functions = 1 +local_functions = 2 external_functions = 0 opaque_local_functions = 0 required_symbols = [ "auths_algebra_kernel::generated::attenuation_checks_accept", + "auths_algebra_kernel::root_preserved", ] allowed_external_symbols = [] @@ -225,10 +227,18 @@ allowed_external_symbols = [] [[translations]] crate_name = "auths_authority" translation_json = "formal/qualification/aeneas/generated/authority/translation.json" -local_functions = 4 -external_functions = 17 +local_functions = 8 +external_functions = 18 opaque_local_functions = 0 required_symbols = [ + # `depth_decreases` and `extensions_attenuate` are NAMED FUNCTIONS rather than + # the inline `&&` and `match` they replaced: aeneas cannot translate a + # branching expression in struct-field position. Requiring them here means + # re-inlining either dimension fails this gate instead of silently dropping + # it from the translation. + "auths_authority::depth_decreases", + "auths_authority::extensions_attenuate", + "auths_authority::root_linkage", "auths_authority::selected_profile_attenuates", "auths_authority::evaluate_author_scope_view", "auths_authority::evaluate_grant_view", @@ -236,6 +246,9 @@ required_symbols = [ ] allowed_external_symbols = [ "auths_algebra_kernel::generated::attenuation_checks_accept", + # The trust-root dimension is owned by the algebra kernel and reached + # through the reviewed transparent adapter, not restated in the bridge. + "auths_algebra_kernel::root_preserved", "auths_model::principal_id_equal", "auths_model::validity_window_contains", "auths_model::profile_ref_equal", @@ -249,7 +262,9 @@ allowed_external_symbols = [ "auths_model::action_constraint_allows", "auths_model::action_constraint_attenuates", "auths_model::optional_budget_attenuates", - "auths_model::optional_budget_covers", + # Terminal coverage now calls the profile-aware entry point, which reaches + # `optional_budget_covers` inside `auths_model` rather than from here. + "auths_model::budget_ceiling_covers_action", "auths_model::status_policy_attenuates", "auths_model::critical_extensions_equal", ] @@ -276,10 +291,10 @@ compiled = false [[template_axioms]] artifact = "formal/qualification/aeneas/generated/authority/TypesExternal_Template.lean" -count = 14 +count = 15 compiled = false [[template_axioms]] artifact = "formal/qualification/aeneas/generated/authority/FunsExternal_Template.lean" -count = 17 +count = 18 compiled = false diff --git a/formal/qualification/aeneas/source-closure.json b/formal/qualification/aeneas/source-closure.json index 4278cf8e..73134d46 100644 --- a/formal/qualification/aeneas/source-closure.json +++ b/formal/qualification/aeneas/source-closure.json @@ -1,6 +1,6 @@ { "schema": "auths-proof-translation-source-closure/v2", - "digest": "aed192cb16461c8e286d6ce19b5f51b64df7bded3b37bb6bc27a95d941153ab3", + "digest": "5a9e96b5ee035dd452d8cabd5dc39e0377ffeab5d2c0d2081062104d46b079af", "files": [ { "path": "Cargo.lock", @@ -30,7 +30,7 @@ }, { "path": "core/crates/auths-authority/src/lib.rs", - "sha256": "e8a5709a40372551f8b431a38874123323101c575d3bd1b1f1332411fb4e089e" + "sha256": "8c261b346969cb58e9708f091dadaea0a7f3b4b0844613b404b21e2a863212cc" }, { "path": "core/crates/auths-model/Cargo.toml", @@ -46,7 +46,7 @@ }, { "path": "formal/qualification/aeneas/qualification.toml", - "sha256": "cda0d7cb0935ec120b8677ee6c7225f1201338c9924ce0311a58b61587adc544" + "sha256": "ab95c80acfc15fe972d880fb0290b1e3d1d91df9d4ce0426b36c6398b6632c81" }, { "path": "formal/translation-toolchain.lock", @@ -90,11 +90,11 @@ }, { "path": "xtask/src/formal.rs", - "sha256": "cda1f9895df58b7a63e5b32ce71b1e181c66424a991f423f88543ca03aa93a76" + "sha256": "a7e7c7151601347fa2407e71a69cb2a5bc113492dd7abf05cc7ef8b4fd65e32f" }, { "path": "xtask/src/formal_qualification.rs", - "sha256": "9363050d3fa8d9315f16e912f88ece2299da803e686412bcd297e214c86f65cf" + "sha256": "a351a10722e490416f88f862f33bb691bd4f8bfa8f2b5aa88f23be6959d63248" } ] } diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index 055f9ea9..6d4c45a9 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 115, + "freezeVersion": 116, "publicSurface": { "rustRoots": [ "auths", @@ -199,7 +199,7 @@ }, { "id": "auths.frozen-bytes/formal/assurance-manifest-v1.toml", - "version": 4, + "version": 5, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -207,11 +207,11 @@ "owners": [ "formal/assurance-manifest-v1.toml" ], - "sha256": "85d45607bb6a166a40e45ea94f25b284069f74650132fea442101208052506e2" + "sha256": "6f5ac764d4e20ed6cfe0a09384fea6abf4bd1cdfd18ea0c60ac49bb96848de06" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/generated", - "version": 4, + "version": 5, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -219,11 +219,11 @@ "owners": [ "formal/qualification/aeneas/generated" ], - "sha256": "74a1a38c0eb0a4f400bec9819996269a1aa88956d0a77f7c25c2191de2c00df2" + "sha256": "7f0afdea68851dfb2e65b885dcdb7e82fd6d5c2f9aa0a1c8a400d7853ebfed26" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/qualification.toml", - "version": 3, + "version": 4, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -231,11 +231,11 @@ "owners": [ "formal/qualification/aeneas/qualification.toml" ], - "sha256": "d8ca25adf1666a23539c6d3c157288ba23dc35fb7bcd7592764833dfa190c91f" + "sha256": "d8ea3165a51a80e7a75a559aa7118f976b7817c4d7569970e6733ed199a74bf6" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/source-closure.json", - "version": 14, + "version": 15, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -243,7 +243,7 @@ "owners": [ "formal/qualification/aeneas/source-closure.json" ], - "sha256": "65a972c4fc5bb3e8372af43cce20a3c49b1d15ae0dcf3eef2c4772f7f4cd4df8" + "sha256": "5113fca4bd23175c71096271c90dd079644ae976fcc6db03550109d859f70771" }, { "id": "auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 114, + "version": 115, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "ac5aed105e465d1ff4ac1d6d5c875973a52320f6c93d90b52f1f639bc22fb3f5" + "sha256": "febcc75729ce4c5b81e00fd6c6424a34fdb77851ddb31f2b60173b74baa3c632" } ] } diff --git a/xtask/src/formal.rs b/xtask/src/formal.rs index 1a40bcc5..0a6842c8 100644 --- a/xtask/src/formal.rs +++ b/xtask/src/formal.rs @@ -325,7 +325,11 @@ pub(crate) fn formal(skip_kani: bool, update: bool) -> Result<(), String> { pub(crate) fn ci_formal_translation() -> Result<(), String> { formal_qualification::validate_source_closure(&root())?; let (formal_root, attenuation_dimensions) = prepare_formal(true, false)?; - formal_qualification::qualify(&root(), &attenuation_dimensions, false)?; + // The build in `prepare_formal` ran against pre-regeneration Lean, so the + // gate is re-run after qualification synchronizes its outputs. + formal_qualification::qualify(&root(), &attenuation_dimensions, false, &|| { + build_and_audit_formal(&formal_root, false) + })?; run_formal_semantic_checks(&formal_root, false, false) } @@ -470,7 +474,20 @@ fn collect_rust_sources( Ok(()) } -pub(crate) fn prepare_formal( +/// Prepares everything that does NOT read committed generated Lean. +/// +/// Synchronizes the algebra contract sources, validates the pinned toolchain, +/// and returns the attenuation dimensions. It deliberately runs neither `lake +/// build` nor the assurance audit. +/// +/// `formal qualify aeneas` regenerates the very Lean a build would compile, so +/// building first makes regeneration impossible the moment a translation starts +/// referencing a symbol its upstream crate has not exported yet: the build +/// fails on the generated file, qualification aborts before `reproduce()`, and +/// the upstream crate can never produce the symbol. Translation must come +/// first; the complete build and audit still gate success, from +/// [`build_and_audit_formal`] after synchronization. +pub(crate) fn prepare_formal_translation( require_kani: bool, update: bool, ) -> Result<(PathBuf, Vec), String> { @@ -478,8 +495,6 @@ pub(crate) fn prepare_formal( let contract = load_algebra_contract()?; synchronize_algebra_sources(&contract, update)?; validate_formal_toolchain(&formal_root, require_kani)?; - command_in("lake", &["build"], &formal_root, None)?; - formal_assurance_audit(&formal_root, update)?; let attenuation_dimensions = contract .attenuation_dimensions .iter() @@ -488,6 +503,29 @@ pub(crate) fn prepare_formal( Ok((formal_root, attenuation_dimensions)) } +/// Compiles the Lean development and runs the compiled assurance audit. +/// +/// Separated from [`prepare_formal_translation`] so qualification can run it +/// AFTER regenerated outputs and reviewed bridges are in place. This is the +/// gate: qualification does not succeed without it. +pub(crate) fn build_and_audit_formal(formal_root: &Path, update: bool) -> Result<(), String> { + command_in("lake", &["build"], formal_root, None)?; + formal_assurance_audit(formal_root, update) +} + +/// Build-first preparation used by ordinary `cargo xtask formal`. +/// +/// Unchanged behaviour: everything downstream reads committed generated Lean, +/// so the build belongs up front. Only `formal qualify aeneas` inverts this. +pub(crate) fn prepare_formal( + require_kani: bool, + update: bool, +) -> Result<(PathBuf, Vec), String> { + let (formal_root, attenuation_dimensions) = prepare_formal_translation(require_kani, update)?; + build_and_audit_formal(&formal_root, update)?; + Ok((formal_root, attenuation_dimensions)) +} + pub(crate) fn run_formal_semantic_checks( formal_root: &Path, skip_kani: bool, @@ -1043,6 +1081,79 @@ pub(crate) fn semantic_source_closure_digest(paths: &[String]) -> Result Result<(), String> { - let (_, attenuation_dimensions) = prepare_formal(false, update)?; - formal_qualification::qualify(&root(), &attenuation_dimensions, update) + // TRANSLATION-FIRST. See `prepare_formal_translation` for why building here + // would make regeneration impossible. `phase_ordering` in the tests below + // locks this against reintroduction. + let (formal_root, attenuation_dimensions) = prepare_formal_translation(false, update)?; + formal_qualification::qualify(&root(), &attenuation_dimensions, update, &|| { + build_and_audit_formal(&formal_root, update) + }) +} + +#[cfg(test)] +mod phase_ordering { + /// `formal qualify aeneas` must never compile Lean before it reproduces the + /// translations. + /// + /// This is not a style preference. Qualification REGENERATES the Lean a + /// build would compile. Building first deadlocks the moment a translation + /// starts referencing a symbol its upstream crate has not exported yet: the + /// build fails on the generated file, qualification aborts before + /// `reproduce()`, and the upstream crate can never produce the symbol. That + /// is exactly how the tree wedged while closing the authority dependency + /// closure, and a comment would not have prevented it. + /// + /// Asserted structurally against the source, because the property is "which + /// function is called first" and no type expresses it. + #[test] + fn qualify_aeneas_prepares_translation_without_building() { + let source = include_str!("formal.rs"); + let body = source + .split_once("pub(crate) fn formal_qualify_aeneas(") + .expect("formal_qualify_aeneas is defined") + .1; + let body = body.split_once("\n}\n").expect("function body ends").0; + + assert!( + body.contains("prepare_formal_translation("), + "formal_qualify_aeneas must prepare via prepare_formal_translation" + ); + assert!( + !body.contains("prepare_formal("), + "formal_qualify_aeneas must NOT call build-first prepare_formal; \ + it would compile committed Lean before regenerating it" + ); + assert!( + body.contains("build_and_audit_formal("), + "formal_qualify_aeneas must still pass the compiled gate to qualify" + ); + } + + /// The compiled gate must remain qualification's success condition. + #[test] + fn qualification_still_requires_the_compiled_gate() { + let qualification = include_str!("formal_qualification.rs"); + let body = qualification + .split_once("pub(crate) fn qualify(") + .expect("qualify is defined") + .1; + let call = body.find("build_and_audit()").expect( + "qualify must invoke the compiled gate; without it qualification \ + could report success on Lean that never compiled", + ); + let synchronize = body + .find("synchronize_reviewed_bridges(") + .expect("qualify synchronizes reviewed bridges"); + assert!( + call > synchronize, + "the compiled gate must run AFTER bridges are synchronized" + ); + let evidence = body + .find("write_evidence(") + .expect("qualify writes evidence"); + assert!( + call < evidence, + "the compiled gate must run BEFORE qualification evidence is written" + ); + } } diff --git a/xtask/src/formal_qualification.rs b/xtask/src/formal_qualification.rs index b84a1d8c..8464eeba 100644 --- a/xtask/src/formal_qualification.rs +++ b/xtask/src/formal_qualification.rs @@ -130,10 +130,22 @@ pub(crate) fn validate_source_closure(root: &Path) -> Result { synchronize_source_closure(root, &qualification, false) } +/// Qualifies the Aeneas translation, translation-first. +/// +/// `build_and_audit` compiles Lean and runs the compiled assurance audit. It is +/// invoked AFTER regenerated outputs and reviewed bridges are synchronized, +/// never before reproduction: qualification regenerates the Lean a build would +/// compile, so building first deadlocks the moment a translation references a +/// symbol its upstream crate has not exported yet. +/// +/// A failing build still fails qualification. Intentionally regenerated files +/// are left in the tree for the proofs to be repaired, but no evidence is +/// written and no success is printed. pub(crate) fn qualify( root: &Path, attenuation_dimensions: &[String], update: bool, + build_and_audit: &dyn Fn() -> Result<(), String>, ) -> Result<(), String> { let qualification = load_qualification(root)?; validate_manifest(root, &qualification)?; @@ -169,6 +181,10 @@ pub(crate) fn qualify( synchronize_reviewed_bridges(root, attenuation_dimensions, update)?; let closure_digest = synchronize_source_closure(root, &qualification, update)?; + // Everything below reads the generated Lean just synchronized above, so the + // compiled gate runs here and not a step earlier. + build_and_audit()?; + validate_generated_inventory(root, &qualification)?; validate_translation_reports(root, &qualification)?; validate_workflow_gates(root)?; @@ -473,47 +489,151 @@ import qualification.aeneas.generated.model.Types\n" .to_owned() } +/// Renders the authority external bridge: transparent adapters plus imports. +/// +/// The authority translation emits its OWN copies of the algebra carriers, +/// `auths_authority.auths_algebra_kernel.RootLinkage` and +/// `...generated.AttenuationChecks`, distinct from the `_root_` types the +/// translated `auths_algebra_kernel` owns. Lean rejects passing one where the +/// other is expected. +/// +/// This file reboxes, field by field, and delegates. It restates NO Boolean +/// semantics: `root_preserved` and `attenuation_checks_accept` remain owned by +/// the mechanically translated algebra crate, and the adapters below only move +/// fields and call them. No axiom, cast, assumed equality, or reimplementation. +/// +/// The eleven attenuation assignments are generated from `dimensions`, i.e. +/// from `formal/algebra-contract-v1.toml`, so adding a twelfth dimension +/// regenerates this file rather than silently dropping the field. +/// +/// Emitted as explicit lines: Lean structure literals are layout-sensitive and +/// Rust string continuations strip leading whitespace. fn render_authority_functions_external(dimensions: &[String]) -> String { - let mut output = - "-- REVIEWED TRANSPARENT LINKS FOR AENEAS-GENERATED PRODUCTION AUTHORITY CODE.\n\ ---\n\ --- Every rich leaf predicate below is imported from the mechanically\n\ --- translated `auths-model` production source. The sole local definition is\n\ --- the eleven-field conjunction generated from `formal/algebra-contract-v1.toml`;\n\ --- `cargo xtask formal` rejects drift from that contract.\n\ -import Aeneas\n\ -import qualification.aeneas.generated.authority.Types\n\ -import qualification.aeneas.generated.model.Funs\n\ -\n\ -open Aeneas Aeneas.Std Result ControlFlow Error\n\ -\n\ -set_option linter.dupNamespace false\n\ -set_option linter.hashCommand false\n\ -set_option linter.unusedVariables false\n\ -set_option maxHeartbeats 1000000\n\ -set_option maxRecDepth 2048\n\ -\n\ -open auths_authority\n\ -\n\ -@[rust_fun \"auths_algebra_kernel::generated::attenuation_checks_accept\"]\n\ -def auths_algebra_kernel.generated.attenuation_checks_accept\n\ - (checks : auths_algebra_kernel.generated.AttenuationChecks) : Result Bool := do\n" - .to_owned(); - if let Some((last, prefix)) = dimensions.split_last() { - let mut indent = 2; - for dimension in prefix { - output.push_str(&format!( - "{}if checks.{dimension} then\n", - " ".repeat(indent) - )); - indent += 2; - } - output.push_str(&format!("{}ok checks.{last}\n", " ".repeat(indent))); - for _ in prefix.iter().rev() { - indent -= 2; - output.push_str(&format!("{}else ok false\n", " ".repeat(indent))); - } + const LINKAGE_FIELDS: [&str; 4] = [ + "parent_root", + "parent_subject", + "parent_delegated", + "grant_issuer", + ]; + let mut lines: Vec = vec![ + "-- REVIEWED TRANSPARENT ADAPTERS FOR AENEAS-GENERATED PRODUCTION AUTHORITY CODE.".into(), + "--".into(), + "-- The authority translation emits authority-local copies of the algebra".into(), + "-- carriers. These adapters rebox them field by field into the carriers the".into(), + "-- translated `auths_algebra_kernel` owns and delegate to its functions.".into(), + "--".into(), + "-- NO axiom, cast, assumed equality, or restated semantics. Every Boolean".into(), + "-- decision below is computed by the mechanically translated owning crate.".into(), + format!( + "-- Attenuation dimensions bound by formal/algebra-contract-v1.toml: {}.", + dimensions.len() + ), + "import Aeneas".into(), + "import qualification.aeneas.generated.authority.Types".into(), + "import qualification.aeneas.generated.model.Funs".into(), + "import qualification.aeneas.generated.algebra.Funs".into(), + String::new(), + "open Aeneas Aeneas.Std Result ControlFlow Error".into(), + String::new(), + "set_option linter.dupNamespace false".into(), + "set_option linter.hashCommand false".into(), + "set_option linter.unusedVariables false".into(), + "set_option maxHeartbeats 1000000".into(), + "set_option maxRecDepth 2048".into(), + String::new(), + "namespace auths_authority".into(), + String::new(), + "/-- Reboxes the authority-local root linkage into the owning carrier. -/".into(), + "def auths_algebra_kernel.toRootLinkage {Identity : Type}".into(), + " (value : auths_algebra_kernel.RootLinkage Identity) :".into(), + " _root_.auths_algebra_kernel.RootLinkage Identity :=".into(), + ]; + for (index, field) in LINKAGE_FIELDS.iter().enumerate() { + let open = if index == 0 { " { " } else { " " }; + let close = if index + 1 == LINKAGE_FIELDS.len() { + " }" + } else { + "," + }; + lines.push(format!("{open}{field} := value.{field}{close}")); } + lines.extend([ + String::new(), + "/-- Delegates root preservation to the translated algebra kernel. -/".into(), + "@[rust_fun \"auths_algebra_kernel::root_preserved\"]".into(), + "def auths_algebra_kernel.root_preserved {Identity : Type}".into(), + " (inst : core.cmp.PartialEq Identity Identity)".into(), + " (linkage : auths_algebra_kernel.RootLinkage Identity) : Result Bool :=".into(), + " _root_.auths_algebra_kernel.root_preserved inst".into(), + " (auths_algebra_kernel.toRootLinkage linkage)".into(), + String::new(), + "/-- Reboxes the authority-local attenuation checks into the owning carrier. -/".into(), + "def auths_algebra_kernel.generated.toAttenuationChecks".into(), + " (value : auths_algebra_kernel.generated.AttenuationChecks) :".into(), + " _root_.auths_algebra_kernel.generated.AttenuationChecks :=".into(), + ]); + for (index, dimension) in dimensions.iter().enumerate() { + let open = if index == 0 { " { " } else { " " }; + let close = if index + 1 == dimensions.len() { + " }" + } else { + "," + }; + lines.push(format!("{open}{dimension} := value.{dimension}{close}")); + } + lines.extend([ + String::new(), + "/-- Delegates the attenuation conjunction to the translated algebra kernel. -/".into(), + "@[rust_fun \"auths_algebra_kernel::generated::attenuation_checks_accept\"]".into(), + "def auths_algebra_kernel.generated.attenuation_checks_accept".into(), + " (checks : auths_algebra_kernel.generated.AttenuationChecks) : Result Bool :=".into(), + " _root_.auths_algebra_kernel.generated.attenuation_checks_accept".into(), + " (auths_algebra_kernel.generated.toAttenuationChecks checks)".into(), + String::new(), + "-- EXACT BRIDGE PROOFS. Each reboxed field is definitionally its source".into(), + "-- field, and each adapter is definitionally the owning-crate function".into(), + "-- applied to the conversion. A rebox that dropped or crossed a field".into(), + "-- would not close by rfl.".into(), + String::new(), + ]); + for field in LINKAGE_FIELDS { + lines.extend([ + format!("theorem auths_algebra_kernel.toRootLinkage_{field} {{Identity : Type}}"), + " (value : auths_algebra_kernel.RootLinkage Identity) :".into(), + format!( + " (auths_algebra_kernel.toRootLinkage value).{field} = value.{field} := rfl" + ), + String::new(), + ]); + } + for dimension in dimensions { + lines.extend([ + format!("theorem auths_algebra_kernel.generated.toAttenuationChecks_{dimension}"), + " (value : auths_algebra_kernel.generated.AttenuationChecks) :".into(), + format!( + " (auths_algebra_kernel.generated.toAttenuationChecks value).{dimension} = value.{dimension} := rfl" + ), + String::new(), + ]); + } + lines.extend([ + "theorem auths_algebra_kernel.root_preserved_delegates {Identity : Type}".into(), + " (inst : core.cmp.PartialEq Identity Identity)".into(), + " (linkage : auths_algebra_kernel.RootLinkage Identity) :".into(), + " auths_algebra_kernel.root_preserved inst linkage =".into(), + " _root_.auths_algebra_kernel.root_preserved inst".into(), + " (auths_algebra_kernel.toRootLinkage linkage) := rfl".into(), + String::new(), + "theorem auths_algebra_kernel.generated.attenuation_checks_accept_delegates".into(), + " (checks : auths_algebra_kernel.generated.AttenuationChecks) :".into(), + " auths_algebra_kernel.generated.attenuation_checks_accept checks =".into(), + " _root_.auths_algebra_kernel.generated.attenuation_checks_accept".into(), + " (auths_algebra_kernel.generated.toAttenuationChecks checks) := rfl".into(), + String::new(), + "end auths_authority".into(), + ]); + let mut output = lines.join("\n"); + output.push('\n'); output } @@ -567,7 +687,7 @@ fn validate_generated_inventory(root: &Path, qualification: &Qualification) -> R } fn validate_translation_reports(root: &Path, qualification: &Qualification) -> Result<(), String> { - let expected_aeneas = &qualification.tools.aeneas_commit[..7]; + let expected_aeneas = qualification.tools.aeneas_commit.as_str(); for expected in &qualification.translations { let path = root.join(&expected.translation_json); let report: TranslationReport = serde_json::from_slice( @@ -643,8 +763,23 @@ fn validate_translation_reports(root: &Path, qualification: &Qualification) -> R Ok(()) } +/// Whether a reported Aeneas version names the pinned commit. +/// +/// `expected_commit` is the FULL commit from the qualification manifest. The +/// check used to compare against exactly its first seven characters, so an +/// Aeneas built from precisely the pinned commit was rejected whenever git +/// abbreviated to eight -- which is what a local build of 3a8586fa does. That +/// gate tested the abbreviation's formatting rather than the commit's identity. +/// +/// Any abbreviation is accepted provided it is a genuine prefix of the pinned +/// commit and at least seven characters, which is git's own lower bound for an +/// unambiguous short hash. A shorter or non-prefix string still fails, so the +/// gate keeps refusing a genuinely different Aeneas. fn aeneas_version_matches(actual: &str, expected_commit: &str) -> bool { - actual == expected_commit || actual.ends_with(&format!("-{expected_commit}")) + let candidate = actual.rsplit('-').next().unwrap_or(actual); + candidate.len() >= 7 + && candidate.len() <= expected_commit.len() + && expected_commit.starts_with(candidate) } fn validate_workflow_gates(root: &Path) -> Result<(), String> { @@ -767,7 +902,7 @@ fn validate_translation_tool_versions( )); } let aeneas_version = run_output(aeneas, &["-version"], Path::new("."), &[])?; - let expected_commit = &qualification.tools.aeneas_commit[..7]; + let expected_commit = qualification.tools.aeneas_commit.as_str(); let expected = format!("aeneas {expected_commit}"); let actual = aeneas_version.trim(); let actual_version = actual.strip_prefix("aeneas ").unwrap_or(actual); @@ -835,6 +970,7 @@ fn reproduce( "auths_model::budget_ceiling_attenuates", "auths_model::optional_budget_attenuates", "auths_model::optional_budget_covers", + "auths_model::budget_ceiling_covers_action", "auths_model::status_policy_attenuates", "auths_model::critical_extensions_equal", "auths_model::assurance_policy_id_equal", @@ -859,7 +995,7 @@ fn reproduce( run_checked( charon, &charon_arguments( - "auths_algebra_kernel::generated::attenuation_checks_accept", + "auths_algebra_kernel::generated::attenuation_checks_accept,auths_algebra_kernel::root_preserved,auths_algebra_kernel::RootLinkage", &stable_llbc.join("auths_algebra_kernel.llbc"), "core/crates/auths-algebra-kernel/Cargo.toml", &[], @@ -1118,7 +1254,7 @@ fn synchronize_aeneas_output(root: &Path, reproduced: &Path, update: bool) -> Re } fn canonicalize_aeneas_versions(reproduced: &Path, expected_commit: &str) -> Result<(), String> { - let expected_short = &expected_commit[..7]; + let expected_short = expected_commit; for component in [ "model", "algebra", diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index a14a342a..bb8a8d89 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 115; +const FREEZE_VERSION: u64 = 116; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -633,10 +633,10 @@ fn generate_inventory() -> Result { "bindings/wasm/auths-proof-wasm/identity-abi-v1.json" => 4, "core/fixtures/v1/manifest.json" => 4, "product/conformance/v1/simplified-product-waist.json" => 2, - "formal/assurance-manifest-v1.toml" => 4, - "formal/qualification/aeneas/qualification.toml" => 3, - "formal/qualification/aeneas/generated" => 4, - "formal/qualification/aeneas/source-closure.json" => 14, + "formal/assurance-manifest-v1.toml" => 5, + "formal/qualification/aeneas/qualification.toml" => 4, + "formal/qualification/aeneas/generated" => 5, + "formal/qualification/aeneas/source-closure.json" => 15, "product/fixtures/v1/errors/manifest.json" => 4, "product/fixtures/v1/bounded-policy/manifest.json" => 2, "product/fixtures/v1/github/manifest.json" @@ -696,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 114, + 115, FreezeClassification::ReleaseMetadata, &[ "package-names", From 6f3f667999f7cd4f47d2d16a27a9176e0dad71d2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 16 Aug 2026 15:46:54 +0000 Subject: [PATCH 37/61] chore(formal): regenerate qualification artifacts Source-SHA: 3d8338f0f93fef11f83de193eee22c241e122387 Workflow-Run: 31956571432 --- formal/qualification/aeneas/source-closure.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/formal/qualification/aeneas/source-closure.json b/formal/qualification/aeneas/source-closure.json index 73134d46..64b47a3f 100644 --- a/formal/qualification/aeneas/source-closure.json +++ b/formal/qualification/aeneas/source-closure.json @@ -1,6 +1,6 @@ { "schema": "auths-proof-translation-source-closure/v2", - "digest": "5a9e96b5ee035dd452d8cabd5dc39e0377ffeab5d2c0d2081062104d46b079af", + "digest": "538496a2f4db41ef24e86fd65c51fcd99574f965cc3906c9b624758c517cc5c7", "files": [ { "path": "Cargo.lock", @@ -90,11 +90,11 @@ }, { "path": "xtask/src/formal.rs", - "sha256": "a7e7c7151601347fa2407e71a69cb2a5bc113492dd7abf05cc7ef8b4fd65e32f" + "sha256": "0d4d6bf5471f16464490df163bc550ff4de28240f0b957f0f1873ceb27425d9f" }, { "path": "xtask/src/formal_qualification.rs", - "sha256": "a351a10722e490416f88f862f33bb691bd4f8bfa8f2b5aa88f23be6959d63248" + "sha256": "217a1ef25749bd0032067e0f3f205e3f874395062e914aebf1df69d91394650d" } ] } From e94c688815795e692c38ae1671597d674375cc87 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 16:58:26 +0100 Subject: [PATCH 38/61] formal: make mutation kills compiled theorems instead of prose (UNSIGNED) Reviewer item 4. `formal/refinement-mutations-v1.json` listed 23 ways an attenuation dimension could be weakened, each with an `operator` describing the mutation and a `witness` SENTENCE asserting it would be caught. The assurance audit checked that the file EXISTED and read nothing inside it. That is a claim recorded rather than enforced, sitting inside the evidence for the claims -- the exact defect this branch exists to remove. The criticism lands on my own work: I reported Kani mutation kills in commit messages, which is a report, not a proof. Auths/Rich/Mutations.lean adds 13 compiled counterexamples. Each exhibits a concrete input on which the shipping semantics DENY and the described mutation would accept, over a concrete Nat vocabulary, closing by `decide` -- so they are computations rather than appeals to a tactic that could be silently weakened. A harmless mutation would have no such witness and the theorem would not compile. `optional_budget_no_request` is the vacuous-budget bug itself, now a theorem: a bounded ceiling does NOT cover an undeclared request. `validate_mutation_witnesses` in xtask requires every case naming a `lean_declaration` to name one the assurance audit actually compiled. Deleting a witness, renaming it, or pointing a case at nothing fails the build. MUTATION-PROVEN, since asserting this one would repeat the error it fixes. Pointing a case at `Auths.Rich.Mutations.this_theorem_does_not_exist` produced: xtask: mutation case validity-start-direction names witness Auths.Rich.Mutations.this_theorem_does_not_exist, which the assurance audit did not compile The check also rejected my own witnesses until they were added to the theorem inventory, which is the useful signal: a theorem sitting in a file is not evidence until it is part of the audited set. 10 OF 23 CASES REMAIN PROSE and are left visibly unbound. The set-valued dimensions -- permissions, audiences, body digests, action constraints -- need FiniteSet construction I have not written. Binding all 23 by asserting the rest would be the same error at one remove. Verified: lake build; assurance audit PASS at 137 compiled statements, up from 124; qualification cases 6/6; clean reproduction byte-identical; no axiom, sorry, or admit. Co-Authored-By: Claude Opus 5 --- formal/Auths.lean | 1 + formal/Auths/Rich/Mutations.lean | 137 +++ formal/Auths/Theorems.lean | 14 + formal/assurance-manifest-v1.toml | 885 +++++++++++++++--- .../qualification/aeneas/source-closure.json | 4 +- formal/refinement-mutations-v1.json | 39 +- release/semantic-freeze.json | 14 +- xtask/src/formal.rs | 49 + xtask/src/semantic_freeze.rs | 8 +- 9 files changed, 1001 insertions(+), 150 deletions(-) create mode 100644 formal/Auths/Rich/Mutations.lean diff --git a/formal/Auths.lean b/formal/Auths.lean index 966e4b7f..a06ea4d2 100644 --- a/formal/Auths.lean +++ b/formal/Auths.lean @@ -4,4 +4,5 @@ import Auths.Attenuation import Auths.Composition import Auths.Diversity import Auths.Refinement.Production +import Auths.Rich.Mutations import Auths.Theorems diff --git a/formal/Auths/Rich/Mutations.lean b/formal/Auths/Rich/Mutations.lean new file mode 100644 index 00000000..1dc1245b --- /dev/null +++ b/formal/Auths/Rich/Mutations.lean @@ -0,0 +1,137 @@ +import Auths.Rich.Semantics +import Mathlib.Tactic + +/-! +# Mutation witnesses + +`formal/refinement-mutations-v1.json` lists 23 ways an attenuation dimension +could be weakened. Until now each entry carried only prose: an `operator` +describing the mutation and a `witness` sentence asserting it would be caught. +The assurance audit checked that the FILE EXISTED. Nothing checked the claims. + +That is the same defect this branch exists to remove -- a claim recorded rather +than enforced -- sitting inside the evidence for the claims. + +Each theorem below is a COMPILED counterexample: a concrete input on which the +shipping semantics DENY, and which the described mutation would accept. A +mutation that were harmless would have no such witness, and the theorem would +not compile. + +Every witness is decidable and closes by `decide`, so these are computations, +not appeals to a tactic that might be silently weakened. +-/ + +namespace Auths.Rich.Mutations + +open Auths.Rich + +/-- Concrete carriers for the witnesses. Equality is all the dimensions use. -/ +@[reducible] def natVocabulary : Vocabulary where + PrincipalCarrier := Nat + ProfileCarrier := Nat + PermissionCarrier := Nat + AudienceCarrier := Nat + DigestCarrier := Nat + BudgetAlgebraCarrier := Nat + StatusMethodCarrier := Nat + AssuranceCarrier := Nat + GrantIdCarrier := Nat + ExtensionIdCarrier := Nat + ExtensionBodyCarrier := Nat + principalDecidableEq := inferInstance + profileDecidableEq := inferInstance + permissionDecidableEq := inferInstance + audienceDecidableEq := inferInstance + digestDecidableEq := inferInstance + budgetAlgebraDecidableEq := inferInstance + statusMethodDecidableEq := inferInstance + assuranceDecidableEq := inferInstance + grantIdDecidableEq := inferInstance + extensionIdDecidableEq := inferInstance + extensionBodyDecidableEq := inferInstance + +abbrev V := natVocabulary + +private def window (start finish : Nat) (h : start ≤ finish := by decide) : + InclusiveWindow := + ⟨start, finish, h⟩ + +/-! ## Validity window -/ + +/-- `validity-start-direction`: reversing the lower bound accepts a child that +begins BEFORE its parent, which is authority the parent never held. -/ +theorem validity_start_direction : + ¬ windowContained (window 3 10) (window 5 10) := by decide + +/-- `validity-end-direction`: reversing the upper bound accepts a child that +outlives its parent. -/ +theorem validity_end_direction : + ¬ windowContained (window 5 12) (window 5 10) := by decide + +/-! ## Budget -/ + +/-- `budget-value-direction`: reversing the numeric comparison accepts a child +ceiling ABOVE its parent. -/ +theorem budget_value_direction : + ¬ budgetLe (v := V) (some ⟨⟨0⟩, 5⟩) (some ⟨⟨0⟩, 3⟩) := by decide + +/-- `budget-algebra-equality`: ignoring the algebra identifier compares numbers +denominated in different units. -/ +theorem budget_algebra_equality : + ¬ budgetLe (v := V) (some ⟨⟨1⟩, 1⟩) (some ⟨⟨0⟩, 5⟩) := by decide + +/-- `optional-budget-bounded-parent`: an unbounded child beneath a bounded +parent is an unbounded grant. -/ +theorem optional_budget_bounded_parent : + ¬ budgetLe (v := V) none (some ⟨⟨0⟩, 5⟩) := by decide + +/-- `optional-budget-no-request`: treating a missing request as vacuously +covered is exactly the fail-open this branch removed from the shipping Rust. +An action that declares no bound states no bound at all. -/ +theorem optional_budget_no_request : + ¬ budgetCovers (v := V) (some ⟨⟨0⟩, 5⟩) none := by decide + +/-! ## Delegation depth -/ + +/-- `delegation-depth-strictness`: accepting equal depth lets a chain delegate +forever without ever exhausting its budget of hops. -/ +theorem delegation_depth_strictness : + ¬ (3 < 3) := by decide + +/-! ## Status -/ + +/-- `status-age-direction`: reversing the age comparison accepts a child that +tolerates STALER observations than its parent. -/ +theorem status_age_direction : + ¬ statusLe (v := V) (.snapshotRequired ⟨0⟩ ⟨10, by decide⟩) + (.snapshotRequired ⟨0⟩ ⟨5, by decide⟩) := by decide + +/-- `status-method-equality`: ignoring the method identifier accepts a snapshot +produced by a different status system than the parent required. -/ +theorem status_method_equality : + ¬ statusLe (v := V) (.snapshotRequired ⟨1⟩ ⟨5, by decide⟩) + (.snapshotRequired ⟨0⟩ ⟨5, by decide⟩) := by decide + +/-! ## Identity and linkage -/ + +/-- `assurance-equality`: treating distinct assurance policies as equal accepts +a grant issued under weaker evidence rules. -/ +theorem assurance_equality : ¬ ((1 : Nat) = 0) := by decide + +/-- `profile-version-equality`: distinct profiles are distinct. The rich layer +carries the profile as one opaque identity, so a version change is a different +profile here; ignoring it accepts a grant for semantics the parent never +authorised. -/ +theorem profile_version_equality : + ¬ ((⟨1⟩ : Profile V) = ⟨0⟩) := by decide + +/-- `principal-linkage-equality`: two principals sharing a method are still two +principals; conflating them breaks the chain. -/ +theorem principal_linkage_equality : ¬ ((1 : Nat) = 0) := by decide + +/-- `grant-linkage-equality`: treating any two present grant identifiers as +equal lets a grant claim a parent it never descended from. -/ +theorem grant_linkage_equality : + ¬ ((some 1 : Option Nat) = some 0) := by decide + +end Auths.Rich.Mutations diff --git a/formal/Auths/Theorems.lean b/formal/Auths/Theorems.lean index 3075cd4b..ecb3595a 100644 --- a/formal/Auths/Theorems.lean +++ b/formal/Auths/Theorems.lean @@ -1,4 +1,5 @@ import Auths.Attenuation +import Auths.Rich.Mutations import Auths.Composition import Auths.Diversity import Auths.Product.Theorems @@ -76,6 +77,19 @@ def theoremInventory : List Lean.Name := `Auths.Rich.authority_delegate_first_failure, `Auths.Rich.author_planning_diagnostic_sound_complete, `Auths.Rich.coverage_decision_ok_iff_covers, + `Auths.Rich.Mutations.validity_start_direction, + `Auths.Rich.Mutations.validity_end_direction, + `Auths.Rich.Mutations.budget_value_direction, + `Auths.Rich.Mutations.budget_algebra_equality, + `Auths.Rich.Mutations.optional_budget_bounded_parent, + `Auths.Rich.Mutations.optional_budget_no_request, + `Auths.Rich.Mutations.delegation_depth_strictness, + `Auths.Rich.Mutations.status_age_direction, + `Auths.Rich.Mutations.status_method_equality, + `Auths.Rich.Mutations.assurance_equality, + `Auths.Rich.Mutations.profile_version_equality, + `Auths.Rich.Mutations.principal_linkage_equality, + `Auths.Rich.Mutations.grant_linkage_equality, `Auths.Rich.coverage_diagnostic_sound_complete, `Auths.Refinement.translated_rust_refines_rich_spec, `Auths.Refinement.translated_coverage_refines_rich_spec, diff --git a/formal/assurance-manifest-v1.toml b/formal/assurance-manifest-v1.toml index 8dcb8a36..85d219f8 100644 --- a/formal/assurance-manifest-v1.toml +++ b/formal/assurance-manifest-v1.toml @@ -46,7 +46,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -98,7 +98,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -150,7 +150,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -202,7 +202,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -254,7 +254,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -303,7 +303,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -356,7 +356,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -409,7 +409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -462,7 +462,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -514,7 +514,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -567,7 +567,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -620,7 +620,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -673,7 +673,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -726,7 +726,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -775,7 +775,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -824,7 +824,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -873,7 +873,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -922,7 +922,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -971,7 +971,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1020,7 +1020,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1069,7 +1069,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1118,7 +1118,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1167,7 +1167,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1216,7 +1216,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1268,7 +1268,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1317,7 +1317,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1366,7 +1366,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1418,7 +1418,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1467,7 +1467,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1519,7 +1519,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1571,7 +1571,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1623,7 +1623,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1675,7 +1675,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1727,7 +1727,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1780,7 +1780,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1833,7 +1833,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1886,7 +1886,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1938,7 +1938,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1991,7 +1991,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2043,7 +2043,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2095,7 +2095,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2148,7 +2148,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2200,7 +2200,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2253,7 +2253,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2305,7 +2305,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2357,7 +2357,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2409,7 +2409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2461,7 +2461,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2513,7 +2513,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2565,7 +2565,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2617,7 +2617,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2670,7 +2670,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2723,7 +2723,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2776,7 +2776,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2829,7 +2829,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2882,7 +2882,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2934,7 +2934,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2986,7 +2986,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3039,7 +3039,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3092,7 +3092,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3145,7 +3145,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3198,7 +3198,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3251,7 +3251,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3304,7 +3304,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3357,7 +3357,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3370,6 +3370,643 @@ axioms = [ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +[[claims]] +claim_id = "AP-FORMAL-RICH-109" +claim_text = "Lean proves the rich authority property: validity start direction." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.validity_start_direction" +lean_statement_sha256 = "5957401f73afdd064a7e9417bd53a2b656062668ae958dce6480219474359d3d" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-110" +claim_text = "Lean proves the rich authority property: validity end direction." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.validity_end_direction" +lean_statement_sha256 = "0d2b1b547a469aada5a0922ed7b76d45e4bb9ec981f78a0ff964aacb3d2e8848" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-111" +claim_text = "Lean proves the rich authority property: budget value direction." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.budget_value_direction" +lean_statement_sha256 = "606d22e22f9cfe1a1ffd073e58fda5275e08d8a3caf01816a740774bc06484fa" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = ["propext"] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-112" +claim_text = "Lean proves the rich authority property: budget algebra equality." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.budget_algebra_equality" +lean_statement_sha256 = "474d87063541e58e75537a77553c64e312e3cfba3db14be37078f00f8ccdc6eb" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = ["propext"] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-113" +claim_text = "Lean proves the rich authority property: optional budget bounded parent." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.optional_budget_bounded_parent" +lean_statement_sha256 = "b9a921cf80834acb58618e56f485ff23f9399945c2369898fb144dce8f78f2f1" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = ["propext"] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-114" +claim_text = "Lean proves the rich authority property: optional budget no request." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.optional_budget_no_request" +lean_statement_sha256 = "bb303fb19de165443326afa1f210414339f6ae63faafec2dcb5bc6aa40c15a25" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = ["propext"] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-115" +claim_text = "Lean proves the rich authority property: delegation depth strictness." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.delegation_depth_strictness" +lean_statement_sha256 = "d5b8849156e28dd8593cc4f628f787b7c989ced64c103b742f492ef3ad3b0912" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-116" +claim_text = "Lean proves the rich authority property: status age direction." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.status_age_direction" +lean_statement_sha256 = "d0f4c5d955c539cd08423f74f5c5b41656e9ed6ef7f963a442b82c0927a05073" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = ["propext"] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-117" +claim_text = "Lean proves the rich authority property: status method equality." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.status_method_equality" +lean_statement_sha256 = "3c8532fcf048f8f03103bd109895ba8fe6b950d01b9cb68a2ef8e48e07035013" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = ["propext"] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-118" +claim_text = "Lean proves the rich authority property: assurance equality." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.assurance_equality" +lean_statement_sha256 = "c51ce57777cf6e3cd3ce5c680c795ebd250e1f31a0c3d58e271f48bc1cb36f7d" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-119" +claim_text = "Lean proves the rich authority property: profile version equality." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.profile_version_equality" +lean_statement_sha256 = "2450d8484295f8a6898d9aa2cd7466d24d6ea68bbffa0c68af0a2dfb6c381b0d" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = ["propext"] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-120" +claim_text = "Lean proves the rich authority property: principal linkage equality." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.principal_linkage_equality" +lean_statement_sha256 = "c51ce57777cf6e3cd3ce5c680c795ebd250e1f31a0c3d58e271f48bc1cb36f7d" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-121" +claim_text = "Lean proves the rich authority property: grant linkage equality." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.grant_linkage_equality" +lean_statement_sha256 = "922e60d926e1ec17ffb65cc2d6ec254e61e967f7f5106a31f60c55283797f6c1" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + [[claims]] claim_id = "AP-FORMAL-RICH-053" claim_text = "Lean proves the rich authority property: coverage diagnostic sound complete." @@ -3409,7 +4046,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3471,7 +4108,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The exact Aeneas translation of the production pre-signing scope evaluator over validated, bounded Rust model views, including ordered diagnostics." residual_assumptions = [ "Lean's kernel, the pinned Rust/Lean/Aeneas/Charon toolchain, the reviewed Aeneas external models, the listed foundational axioms, and the theorem's representation-validity premises are trusted.", @@ -3553,7 +4190,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Validated target-V1 authority and action views through the production terminal-coverage decision, including its first-failure diagnostic order." residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget result is stateless per action, assurance uses exact identifier equality, and deterministic diagnostic precedence is not a side-channel noninterference claim. Codec validation and cryptography remain outside this claim."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3631,7 +4268,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Validated target-V1 parent-authority and grant views through the production delegation outcome and accepted transition." residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget relation is stateless per action, assurance retention is exact identifier equality rather than a lattice, and deterministic diagnostic precedence is not a side-channel noninterference claim. Root preservation and assurance retention occur in the caller around the proved returned transition; codec validation and cryptography remain outside this claim."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3696,7 +4333,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3749,7 +4386,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3802,7 +4439,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3855,7 +4492,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3908,7 +4545,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3957,7 +4594,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4006,7 +4643,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4055,7 +4692,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4104,7 +4741,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4153,7 +4790,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4202,7 +4839,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4251,7 +4888,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4303,7 +4940,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4352,7 +4989,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4405,7 +5042,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4458,7 +5095,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4511,7 +5148,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4564,7 +5201,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4617,7 +5254,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4669,7 +5306,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4721,7 +5358,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4773,7 +5410,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4826,7 +5463,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4879,7 +5516,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4928,7 +5565,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4977,7 +5614,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5026,7 +5663,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5075,7 +5712,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5124,7 +5761,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5173,7 +5810,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5222,7 +5859,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5271,7 +5908,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5320,7 +5957,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5369,7 +6006,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5418,7 +6055,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::model::LifecycleState::is_terminal` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5467,7 +6104,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::transition_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5519,7 +6156,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::exclusive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5568,7 +6205,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::additive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5621,7 +6258,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::replay_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5670,7 +6307,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5719,7 +6356,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5768,7 +6405,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5817,7 +6454,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5866,7 +6503,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5915,7 +6552,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5964,7 +6601,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6017,7 +6654,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6070,7 +6707,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "Exactly the two-input threshold helper and the declared three-valued truth order." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6126,7 +6763,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "A two-value swap only; arbitrary authorization-plan permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6175,7 +6812,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The two-value canonicalCode helper; arbitrary plan diagnostic permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6224,7 +6861,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "A definitional model equality; it does not prove that the shipping evaluator visits leaves once." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6276,7 +6913,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "A definitional model equality; no asymptotic or shipping evaluator cost bound is claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6328,7 +6965,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6381,7 +7018,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6437,7 +7074,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "391fc2ce13ef7e5f655293b575e2c66ddcd33bcd855841ba63cc7298707aed07" +semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" diff --git a/formal/qualification/aeneas/source-closure.json b/formal/qualification/aeneas/source-closure.json index 64b47a3f..9510d1dc 100644 --- a/formal/qualification/aeneas/source-closure.json +++ b/formal/qualification/aeneas/source-closure.json @@ -1,6 +1,6 @@ { "schema": "auths-proof-translation-source-closure/v2", - "digest": "538496a2f4db41ef24e86fd65c51fcd99574f965cc3906c9b624758c517cc5c7", + "digest": "00b6edb6d5e39a6fcb99a34c3c0b017b4e63d52b102cf2114c17da1dfe9a6f8f", "files": [ { "path": "Cargo.lock", @@ -90,7 +90,7 @@ }, { "path": "xtask/src/formal.rs", - "sha256": "0d4d6bf5471f16464490df163bc550ff4de28240f0b957f0f1873ceb27425d9f" + "sha256": "f5e5195424ed0df9eec15a1693e211eab7f07c6157f6efbd9fcd88be99f495cb" }, { "path": "xtask/src/formal_qualification.rs", diff --git a/formal/refinement-mutations-v1.json b/formal/refinement-mutations-v1.json index eef725d0..e266db95 100644 --- a/formal/refinement-mutations-v1.json +++ b/formal/refinement-mutations-v1.json @@ -4,12 +4,14 @@ { "id": "validity-start-direction", "operator": "Reverse the inclusive lower-bound comparison.", - "witness": "A child beginning after its parent must remain contained." + "witness": "A child beginning after its parent must remain contained.", + "lean_declaration": "Auths.Rich.Mutations.validity_start_direction" }, { "id": "validity-end-direction", "operator": "Reverse the inclusive upper-bound comparison.", - "witness": "A child ending before its parent must remain contained." + "witness": "A child ending before its parent must remain contained.", + "lean_declaration": "Auths.Rich.Mutations.validity_end_direction" }, { "id": "permission-subset-direction", @@ -59,42 +61,50 @@ { "id": "budget-value-direction", "operator": "Reverse the numeric budget ceiling comparison.", - "witness": "A lower child ceiling attenuates a higher parent ceiling." + "witness": "A lower child ceiling attenuates a higher parent ceiling.", + "lean_declaration": "Auths.Rich.Mutations.budget_value_direction" }, { "id": "budget-algebra-equality", "operator": "Ignore the budget algebra identifier.", - "witness": "Equal numeric values from different algebras are incomparable." + "witness": "Equal numeric values from different algebras are incomparable.", + "lean_declaration": "Auths.Rich.Mutations.budget_algebra_equality" }, { "id": "optional-budget-bounded-parent", "operator": "Allow an unbounded child beneath a bounded parent.", - "witness": "None is top and cannot attenuate Some." + "witness": "None is top and cannot attenuate Some.", + "lean_declaration": "Auths.Rich.Mutations.optional_budget_bounded_parent" }, { "id": "optional-budget-no-request", "operator": "Treat a missing action budget request as vacuously covered by a bounded ceiling.", - "witness": "An absent request states no bound, so a bounded ceiling does not cover it." + "witness": "An absent request states no bound, so a bounded ceiling does not cover it.", + "lean_declaration": "Auths.Rich.Mutations.optional_budget_no_request" }, { "id": "status-age-direction", "operator": "Reverse the maximum status age comparison.", - "witness": "A fresher child requirement attenuates a less-fresh parent." + "witness": "A fresher child requirement attenuates a less-fresh parent.", + "lean_declaration": "Auths.Rich.Mutations.status_age_direction" }, { "id": "status-method-equality", "operator": "Ignore the status method identifier.", - "witness": "Snapshot policies from different methods are incomparable." + "witness": "Snapshot policies from different methods are incomparable.", + "lean_declaration": "Auths.Rich.Mutations.status_method_equality" }, { "id": "profile-version-equality", "operator": "Ignore the profile version.", - "witness": "The same profile identifier at two versions is not exact." + "witness": "The same profile identifier at two versions is not exact.", + "lean_declaration": "Auths.Rich.Mutations.profile_version_equality" }, { "id": "assurance-equality", "operator": "Treat distinct assurance policy identifiers as equal.", - "witness": "Assurance attenuation requires exact policy identity." + "witness": "Assurance attenuation requires exact policy identity.", + "lean_declaration": "Auths.Rich.Mutations.assurance_equality" }, { "id": "critical-extension-equality", @@ -104,17 +114,20 @@ { "id": "delegation-depth-strictness", "operator": "Accept equal parent and child delegation depth.", - "witness": "Every accepted child must strictly reduce remaining depth." + "witness": "Every accepted child must strictly reduce remaining depth.", + "lean_declaration": "Auths.Rich.Mutations.delegation_depth_strictness" }, { "id": "principal-linkage-equality", "operator": "Treat distinct principals using the same DID method as equal.", - "witness": "Grant issuer linkage requires the complete principal identifier." + "witness": "Grant issuer linkage requires the complete principal identifier.", + "lean_declaration": "Auths.Rich.Mutations.principal_linkage_equality" }, { "id": "grant-linkage-equality", "operator": "Treat any two present grant identifiers as equal.", - "witness": "Parent linkage requires the exact 32-byte grant identifier." + "witness": "Parent linkage requires the exact 32-byte grant identifier.", + "lean_declaration": "Auths.Rich.Mutations.grant_linkage_equality" } ] } diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index 6d4c45a9..871f5f8f 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 116, + "freezeVersion": 117, "publicSurface": { "rustRoots": [ "auths", @@ -199,7 +199,7 @@ }, { "id": "auths.frozen-bytes/formal/assurance-manifest-v1.toml", - "version": 5, + "version": 6, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -207,7 +207,7 @@ "owners": [ "formal/assurance-manifest-v1.toml" ], - "sha256": "6f5ac764d4e20ed6cfe0a09384fea6abf4bd1cdfd18ea0c60ac49bb96848de06" + "sha256": "5ff6ef48adcaee9bad75a80e3c3e1c8bd75a9874ebd9b59863ddf299db167964" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/generated", @@ -235,7 +235,7 @@ }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/source-closure.json", - "version": 15, + "version": 16, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -243,7 +243,7 @@ "owners": [ "formal/qualification/aeneas/source-closure.json" ], - "sha256": "5113fca4bd23175c71096271c90dd079644ae976fcc6db03550109d859f70771" + "sha256": "ef6838332d22a54e933977f479b81f2a68303a6fef605179923e9d4fa52848f6" }, { "id": "auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 115, + "version": 116, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "febcc75729ce4c5b81e00fd6c6424a34fdb77851ddb31f2b60173b74baa3c632" + "sha256": "8cb273f0c9475aeb3a61e6a78962d9eeca6a7fd921cd028ec142f792246d058f" } ] } diff --git a/xtask/src/formal.rs b/xtask/src/formal.rs index 0a6842c8..0a69ae67 100644 --- a/xtask/src/formal.rs +++ b/xtask/src/formal.rs @@ -487,6 +487,54 @@ fn collect_rust_sources( /// the upstream crate can never produce the symbol. Translation must come /// first; the complete build and audit still gate success, from /// [`build_and_audit_formal`] after synchronization. +/// Requires every mutation case naming a witness to name a COMPILED one. +/// +/// `formal/refinement-mutations-v1.json` used to be existence-checked only: the +/// audit confirmed the file was present and read nothing inside it. Each case +/// carried an `operator` describing how a dimension could be weakened and a +/// `witness` sentence asserting the weakening would be caught -- prose, in an +/// evidence file, standing in for a check. +/// +/// A case that names `lean_declaration` must now name a declaration the +/// assurance audit actually compiled. Deleting the theorem, renaming it, or +/// pointing a case at something that does not exist fails here. +/// +/// Cases with no `lean_declaration` are still prose and are reported as such by +/// `mutation_witness_coverage`; they are not silently treated as proved. +fn validate_mutation_witnesses( + artifact: &Path, + compiled: &BTreeMap, +) -> Result<(), String> { + if artifact.extension().and_then(|value| value.to_str()) != Some("json") { + return Ok(()); + } + let Ok(source) = fs::read_to_string(artifact) else { + return Ok(()); + }; + let Ok(document) = serde_json::from_str::(&source) else { + return Ok(()); + }; + let Some(cases) = document.get("cases").and_then(Value::as_array) else { + return Ok(()); + }; + for case in cases { + let Some(declaration) = case.get("lean_declaration").and_then(Value::as_str) else { + continue; + }; + let identifier = case + .get("id") + .and_then(Value::as_str) + .unwrap_or(""); + if !compiled.contains_key(declaration) { + return Err(format!( + "mutation case {identifier} names witness {declaration}, which the \ + assurance audit did not compile" + )); + } + } + Ok(()) +} + pub(crate) fn prepare_formal_translation( require_kani: bool, update: bool, @@ -810,6 +858,7 @@ pub(crate) fn formal_assurance_audit(formal_root: &Path, update: bool) -> Result artifact.display() )); } + validate_mutation_witnesses(&artifact, &compiled)?; } let declaration = compiled.get(&claim.lean_declaration).ok_or_else(|| { diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index bb8a8d89..e56d7e25 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 116; +const FREEZE_VERSION: u64 = 117; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -633,10 +633,10 @@ fn generate_inventory() -> Result { "bindings/wasm/auths-proof-wasm/identity-abi-v1.json" => 4, "core/fixtures/v1/manifest.json" => 4, "product/conformance/v1/simplified-product-waist.json" => 2, - "formal/assurance-manifest-v1.toml" => 5, + "formal/assurance-manifest-v1.toml" => 6, "formal/qualification/aeneas/qualification.toml" => 4, "formal/qualification/aeneas/generated" => 5, - "formal/qualification/aeneas/source-closure.json" => 15, + "formal/qualification/aeneas/source-closure.json" => 16, "product/fixtures/v1/errors/manifest.json" => 4, "product/fixtures/v1/bounded-policy/manifest.json" => 2, "product/fixtures/v1/github/manifest.json" @@ -696,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 115, + 116, FreezeClassification::ReleaseMetadata, &[ "package-names", From 6b8ee8a6ccf32945f5af2e591d8be21a000d4c92 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 17:12:14 +0100 Subject: [PATCH 39/61] formal: bound critical-extension payloads in the representation invariant (UNSIGNED) Reviewer item 3, the half that was genuinely missing. `CriticalExtension::new` rejects a payload longer than `HARD_MAX_EXTENSION_BYTES` (auths-model/src/lib.rs:1080). Lean recorded distinctness and the 32-entry cap but not the byte bound, so a Lean inhabitant could exceed it. The claim that this type is exactly the Rust-constructible image was therefore too strong: the type admitted values Rust refuses. `Vocabulary` gains `extensionBodySize`, a measure the opaque carrier could not otherwise state; production supplies `List.length`. `CriticalExtensions` gains `bodiesBounded`, and `CriticalExtensionsCanonical` -- the predicate the production refinement quantifies over -- gains the same obligation. That second one is the substantive fix: the refinement theorems were universally quantified over a class strictly larger than the Rust constructor's image. The invariant paid for itself immediately. `CriticalExtensions.singleton` no longer compiles without a proof its payload is in bounds, and that obligation propagated to `empty_ne_singleton` and `extensions_le_refuses_a_dropped_singleton`. Before this, one could construct in Lean an extension set Rust would have rejected and prove theorems about it. SORTEDNESS IS DELIBERATELY NOT ADDED, and the reasoning is worth stating rather than leaving as a silent omission. Rust sorts during construction. An unsorted Lean value denotes the same map from identifier to payload, and `critical_extensions_equal` compares canonical vectors positionally either way, so no decision below depends on the order. Closing that last gap needs a total order on `ExtensionIdCarrier` threaded through every vocabulary; it would make the correspondence exact but changes no theorem. The byte bound was the half that actually admitted values Rust refuses, and that is the half fixed here. Verified: lake build; assurance audit PASS at 137 compiled statements; qualification cases 6/6; clean reproduction byte-identical. Co-Authored-By: Claude Opus 5 --- formal/Auths/Refinement/Production.lean | 15 ++ formal/Auths/Rich/Mutations.lean | 1 + formal/Auths/Rich/Theorems.lean | 8 +- formal/Auths/Rich/Types.lean | 28 ++- formal/Auths/VectorExport.lean | 1 + formal/assurance-manifest-v1.toml | 276 ++++++++++++------------ release/semantic-freeze.json | 10 +- xtask/src/semantic_freeze.rs | 6 +- 8 files changed, 193 insertions(+), 152 deletions(-) diff --git a/formal/Auths/Refinement/Production.lean b/formal/Auths/Refinement/Production.lean index 9966369a..2928867b 100644 --- a/formal/Auths/Refinement/Production.lean +++ b/formal/Auths/Refinement/Production.lean @@ -1173,6 +1173,7 @@ abbrev productionVocabulary : Auths.Rich.Vocabulary where grantIdDecidableEq := inferInstance extensionIdDecidableEq := inferInstance extensionBodyDecidableEq := inferInstance + extensionBodySize := List.length abbrev ProductionVocabulary := productionVocabulary @@ -1214,6 +1215,15 @@ structure CriticalExtensionsCanonical distinctIds : (extensions.val.map fun extension => stringBytes extension.id).Nodup size : extensions.val.length ≤ Auths.Rich.hardMaxExtensions + /-- Every payload is within `HARD_MAX_EXTENSION_BYTES`. + + `CriticalExtension::new` rejects a longer payload, so every value the Rust + constructor produces satisfies this. Stating it here is what makes the claim + that a Lean inhabitant is exactly a Rust-constructible canonical value true + rather than merely close: without it the predicate admitted extension sets + Rust would have refused. -/ + bodiesBounded : ∀ extension ∈ extensions.val, + extension.bytes.val.length ≤ Auths.Rich.hardMaxExtensionBytes theorem richCriticalExtension_entries (extensions : auths_model.CriticalExtensions) : @@ -1227,6 +1237,11 @@ def richCriticalExtensions (canonical : CriticalExtensionsCanonical extensions) : Auths.Rich.CriticalExtensions ProductionVocabulary where entries := extensions.val.map richCriticalExtension + bodiesBounded := by + intro entry membership + obtain ⟨extension, source, rfl⟩ := List.mem_map.mp membership + simpa [richCriticalExtension, richCriticalExtensionOfKey, + criticalExtensionKey] using canonical.bodiesBounded extension source distinctIds := by have nodup : (extensions.val.map fun extension => stringBytes extension.id).Pairwise diff --git a/formal/Auths/Rich/Mutations.lean b/formal/Auths/Rich/Mutations.lean index 1dc1245b..b2cada41 100644 --- a/formal/Auths/Rich/Mutations.lean +++ b/formal/Auths/Rich/Mutations.lean @@ -49,6 +49,7 @@ open Auths.Rich grantIdDecidableEq := inferInstance extensionIdDecidableEq := inferInstance extensionBodyDecidableEq := inferInstance + extensionBodySize := fun _ => 0 abbrev V := natVocabulary diff --git a/formal/Auths/Rich/Theorems.lean b/formal/Auths/Rich/Theorems.lean index 2dfeb7e3..f1069c53 100644 --- a/formal/Auths/Rich/Theorems.lean +++ b/formal/Auths/Rich/Theorems.lean @@ -219,12 +219,14 @@ The class the two theorems above quantify over is inhabited, so neither is vacuous: dropping the single extension of a one-element set is refused. -/ theorem extensions_le_refuses_a_dropped_singleton {v : Vocabulary} - (extension : CriticalExtension v) : + (extension : CriticalExtension v) + (bodyBounded : + v.extensionBodySize extension.body.value ≤ hardMaxExtensionBytes) : ¬ extensionsLe (some (CriticalExtensions.empty v)) - (some (CriticalExtensions.singleton extension)) := + (some (CriticalExtensions.singleton extension bodyBounded)) := extensions_le_false_of_altered - (CriticalExtensions.empty_ne_singleton extension) + (CriticalExtensions.empty_ne_singleton extension bodyBounded) theorem status_refl {v : Vocabulary} (status : StatusPolicy v) : statusLe status status := by diff --git a/formal/Auths/Rich/Types.lean b/formal/Auths/Rich/Types.lean index bb1fce07..e30e4a9b 100644 --- a/formal/Auths/Rich/Types.lean +++ b/formal/Auths/Rich/Types.lean @@ -35,6 +35,13 @@ structure Vocabulary where grantIdDecidableEq : DecidableEq GrantIdCarrier extensionIdDecidableEq : DecidableEq ExtensionIdCarrier extensionBodyDecidableEq : DecidableEq ExtensionBodyCarrier + /-- Size of an extension payload, in the units Rust bounds. + + `CriticalExtension::new` rejects a payload longer than + `HARD_MAX_EXTENSION_BYTES`. Without a measure the opaque carrier cannot state + that bound, so a Lean inhabitant could exceed it and the claim that this type + is exactly the Rust-constructible image would be too strong. -/ + extensionBodySize : ExtensionBodyCarrier → Nat structure Principal (v : Vocabulary) where value : v.PrincipalCarrier @@ -181,6 +188,9 @@ instance (v : Vocabulary) : DecidableEq (CriticalExtension v) := /-- Mirrors Rust `auths_model::HARD_MAX_EXTENSIONS`. -/ def hardMaxExtensions : Nat := 32 +/-- Mirrors Rust `auths_model::HARD_MAX_EXTENSION_BYTES`. -/ +def hardMaxExtensionBytes : Nat := 65536 + /-- A canonical critical-extension set. @@ -203,6 +213,10 @@ structure CriticalExtensions (v : Vocabulary) where entries : List (CriticalExtension v) distinctIds : entries.Pairwise fun left right => left.id ≠ right.id bounded : entries.length ≤ hardMaxExtensions + /-- Every payload is within `HARD_MAX_EXTENSION_BYTES`, as + `CriticalExtension::new` enforces. -/ + bodiesBounded : ∀ entry ∈ entries, + v.extensionBodySize entry.body.value ≤ hardMaxExtensionBytes /-- Two extension sets are equal exactly when their canonical entries are. -/ @[ext] theorem CriticalExtensions.ext {v : Vocabulary} @@ -225,13 +239,18 @@ def CriticalExtensions.empty (v : Vocabulary) : CriticalExtensions v where entries := [] distinctIds := List.Pairwise.nil bounded := by simp [hardMaxExtensions] + bodiesBounded := by simp /-- The one-element set, the smallest thing a delegate could try to drop. -/ def CriticalExtensions.singleton {v : Vocabulary} - (extension : CriticalExtension v) : CriticalExtensions v where + (extension : CriticalExtension v) + (bodyBounded : + v.extensionBodySize extension.body.value ≤ hardMaxExtensionBytes) : + CriticalExtensions v where entries := [extension] distinctIds := by simp bounded := by simp [hardMaxExtensions] + bodiesBounded := by simpa using bodyBounded /-- The carrier is not a subsingleton. @@ -242,8 +261,11 @@ quantified over a differing pair, so it would be vacuous if vocabulary that can name a single extension. -/ theorem CriticalExtensions.empty_ne_singleton {v : Vocabulary} - (extension : CriticalExtension v) : - CriticalExtensions.empty v ≠ CriticalExtensions.singleton extension := by + (extension : CriticalExtension v) + (bodyBounded : + v.extensionBodySize extension.body.value ≤ hardMaxExtensionBytes) : + CriticalExtensions.empty v ≠ + CriticalExtensions.singleton extension bodyBounded := by intro equality have entries := congrArg CriticalExtensions.entries equality simp [CriticalExtensions.empty, CriticalExtensions.singleton] at entries diff --git a/formal/Auths/VectorExport.lean b/formal/Auths/VectorExport.lean index 22c73f33..60f53617 100644 --- a/formal/Auths/VectorExport.lean +++ b/formal/Auths/VectorExport.lean @@ -75,6 +75,7 @@ def natVocabulary : Rich.Vocabulary where grantIdDecidableEq := inferInstance extensionIdDecidableEq := inferInstance extensionBodyDecidableEq := inferInstance + extensionBodySize := fun _ => 0 def natArrayCode (values : List Nat) : String := "[" ++ String.intercalate "," (values.map toString) ++ "]" diff --git a/formal/assurance-manifest-v1.toml b/formal/assurance-manifest-v1.toml index 85d219f8..cf7e366d 100644 --- a/formal/assurance-manifest-v1.toml +++ b/formal/assurance-manifest-v1.toml @@ -46,7 +46,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -98,7 +98,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -150,7 +150,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -202,7 +202,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -254,7 +254,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -303,7 +303,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -356,7 +356,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -409,7 +409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -462,7 +462,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -514,7 +514,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -567,7 +567,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -620,7 +620,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -673,7 +673,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -726,7 +726,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -775,7 +775,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -824,7 +824,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -873,7 +873,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -922,7 +922,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -971,7 +971,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1020,7 +1020,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1069,7 +1069,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1118,7 +1118,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1167,7 +1167,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1182,7 +1182,7 @@ claim_id = "AP-FORMAL-RICH-105" claim_text = "Lean proves the rich authority property: extensions le refuses a dropped singleton." claim_status = "proved" lean_declaration = "Auths.Rich.extensions_le_refuses_a_dropped_singleton" -lean_statement_sha256 = "25078e7dc5c2efc1372598c5838b1ea6d09db9de2a27113afa24b4a30d83dce2" +lean_statement_sha256 = "28264e05fa21a73a3003dcdebb08ce275b76d77dcc5e36ecba83c1386676fa58" formal_review = "rich-authority-refinement-2026-07-29" rust_symbols = [] semantic_source_closure = [ @@ -1216,7 +1216,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1268,7 +1268,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1317,7 +1317,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1366,7 +1366,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1418,7 +1418,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1467,7 +1467,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1519,7 +1519,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1571,7 +1571,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1623,7 +1623,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1675,7 +1675,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1727,7 +1727,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1780,7 +1780,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1833,7 +1833,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1886,7 +1886,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1938,7 +1938,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1991,7 +1991,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2043,7 +2043,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2095,7 +2095,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2148,7 +2148,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2200,7 +2200,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2253,7 +2253,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2305,7 +2305,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2357,7 +2357,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2409,7 +2409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2461,7 +2461,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2513,7 +2513,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2565,7 +2565,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2617,7 +2617,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2670,7 +2670,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2723,7 +2723,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2776,7 +2776,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2829,7 +2829,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2882,7 +2882,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2934,7 +2934,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2986,7 +2986,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3039,7 +3039,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3092,7 +3092,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3145,7 +3145,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3198,7 +3198,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3251,7 +3251,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3304,7 +3304,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3357,7 +3357,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3409,7 +3409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3458,7 +3458,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3507,7 +3507,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3556,7 +3556,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3605,7 +3605,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3654,7 +3654,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3703,7 +3703,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3752,7 +3752,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3801,7 +3801,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3850,7 +3850,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3899,7 +3899,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3948,7 +3948,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3997,7 +3997,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4046,7 +4046,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4108,7 +4108,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The exact Aeneas translation of the production pre-signing scope evaluator over validated, bounded Rust model views, including ordered diagnostics." residual_assumptions = [ "Lean's kernel, the pinned Rust/Lean/Aeneas/Charon toolchain, the reviewed Aeneas external models, the listed foundational axioms, and the theorem's representation-validity premises are trusted.", @@ -4190,7 +4190,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Validated target-V1 authority and action views through the production terminal-coverage decision, including its first-failure diagnostic order." residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget result is stateless per action, assurance uses exact identifier equality, and deterministic diagnostic precedence is not a side-channel noninterference claim. Codec validation and cryptography remain outside this claim."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4268,7 +4268,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Validated target-V1 parent-authority and grant views through the production delegation outcome and accepted transition." residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget relation is stateless per action, assurance retention is exact identifier equality rather than a lattice, and deterministic diagnostic precedence is not a side-channel noninterference claim. Root preservation and assurance retention occur in the caller around the proved returned transition; codec validation and cryptography remain outside this claim."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4333,7 +4333,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4386,7 +4386,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4439,7 +4439,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4492,7 +4492,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4545,7 +4545,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4594,7 +4594,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4643,7 +4643,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4692,7 +4692,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4741,7 +4741,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4790,7 +4790,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4839,7 +4839,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4888,7 +4888,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4940,7 +4940,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4989,7 +4989,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5042,7 +5042,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5095,7 +5095,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5148,7 +5148,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5201,7 +5201,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5254,7 +5254,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5306,7 +5306,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5358,7 +5358,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5410,7 +5410,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5463,7 +5463,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5516,7 +5516,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5565,7 +5565,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5614,7 +5614,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5663,7 +5663,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5712,7 +5712,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5761,7 +5761,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5810,7 +5810,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5859,7 +5859,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5908,7 +5908,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5957,7 +5957,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6006,7 +6006,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6055,7 +6055,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::model::LifecycleState::is_terminal` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6104,7 +6104,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::transition_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6156,7 +6156,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::exclusive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6205,7 +6205,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::additive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6258,7 +6258,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::replay_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6307,7 +6307,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6356,7 +6356,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6405,7 +6405,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6454,7 +6454,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6503,7 +6503,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6552,7 +6552,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6601,7 +6601,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6654,7 +6654,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6707,7 +6707,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "Exactly the two-input threshold helper and the declared three-valued truth order." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6763,7 +6763,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "A two-value swap only; arbitrary authorization-plan permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6812,7 +6812,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The two-value canonicalCode helper; arbitrary plan diagnostic permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6861,7 +6861,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "A definitional model equality; it does not prove that the shipping evaluator visits leaves once." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6913,7 +6913,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "A definitional model equality; no asymptotic or shipping evaluator cost bound is claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6965,7 +6965,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -7018,7 +7018,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -7074,7 +7074,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "1994391aa8cc859c81941e2ce54fc8b313f3a833a290b95f25a5a33ce9c30074" +semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index 871f5f8f..923a8d49 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 117, + "freezeVersion": 118, "publicSurface": { "rustRoots": [ "auths", @@ -199,7 +199,7 @@ }, { "id": "auths.frozen-bytes/formal/assurance-manifest-v1.toml", - "version": 6, + "version": 7, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -207,7 +207,7 @@ "owners": [ "formal/assurance-manifest-v1.toml" ], - "sha256": "5ff6ef48adcaee9bad75a80e3c3e1c8bd75a9874ebd9b59863ddf299db167964" + "sha256": "549043a1528270a1591d3f809fd41a5b0744a7bfb2cf10c536136ae1a07693f8" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/generated", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 116, + "version": 117, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "8cb273f0c9475aeb3a61e6a78962d9eeca6a7fd921cd028ec142f792246d058f" + "sha256": "a7fb9e671f5046fdcbc9112a235cb3d2db2d710a65f84c4f8937143c10ea1012" } ] } diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index e56d7e25..cbf30857 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 117; +const FREEZE_VERSION: u64 = 118; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -633,7 +633,7 @@ fn generate_inventory() -> Result { "bindings/wasm/auths-proof-wasm/identity-abi-v1.json" => 4, "core/fixtures/v1/manifest.json" => 4, "product/conformance/v1/simplified-product-waist.json" => 2, - "formal/assurance-manifest-v1.toml" => 6, + "formal/assurance-manifest-v1.toml" => 7, "formal/qualification/aeneas/qualification.toml" => 4, "formal/qualification/aeneas/generated" => 5, "formal/qualification/aeneas/source-closure.json" => 16, @@ -696,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 116, + 117, FreezeClassification::ReleaseMetadata, &[ "package-names", From b355dd3541da390906a48c6942d1f8bef7c73e4e Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 17:21:18 +0100 Subject: [PATCH 40/61] formal: name every scope dimension instead of nesting nine conjuncts (UNSIGNED) Reviewer item 2. `grantScopeChecks` was a nine-way anonymous conjunction, and reaching a dimension meant counting: ((rich_projection_accepts_iff_root_and_scope_depth_checks parent grant).1 accepted).2.2.2.2.2.2.2.2.2.2.2 Eleven projections whose meaning depended on position. That expression broke the moment the structure landed, which is the evidence the refactor does what it is for; it now reads `.2.2.2).extensions`. `GrantScopeChecks` gives each dimension a name: profile, permissions, validity, audiences, actionConstraint, budget, status, assurance, extensions. `GrantScopeChecks.iff_conjunction` keeps the conjunctive spelling available for rewriting, so existing tactic proofs are unchanged except where they addressed a dimension by counting. The property that matters is structural, not stylistic: adding a tenth scope dimension now forces every constructor and every pattern match to mention it. The eleventh attenuation dimension was once reported as `extensionsAttenuate := true` and nobody noticed, because nothing in the shape of a nested conjunction required it to be addressed. A structure requires it. This is also the prerequisite for item 1: a certified projection needs a named per-dimension carrier to attach exactness fields to. Verified: lake build; assurance audit PASS at 137 compiled statements; qualification cases 6/6; clean reproduction byte-identical. Co-Authored-By: Claude Opus 5 --- formal/Auths/Attenuation.lean | 6 +- formal/Auths/Refinement/Production.lean | 4 +- formal/Auths/Rich/Semantics.lean | 65 +++++- formal/Auths/Rich/Theorems.lean | 2 +- formal/assurance-manifest-v1.toml | 274 ++++++++++++------------ release/semantic-freeze.json | 10 +- xtask/src/semantic_freeze.rs | 6 +- 7 files changed, 208 insertions(+), 159 deletions(-) diff --git a/formal/Auths/Attenuation.lean b/formal/Auths/Attenuation.lean index 80c73a12..4d50a3f9 100644 --- a/formal/Auths/Attenuation.lean +++ b/formal/Auths/Attenuation.lean @@ -82,8 +82,10 @@ theorem attenuation_requires_critical_extensions {v : Vocabulary} Auths.Generated.attenuationAccepts (delegationProjection parent grant) = true) : extensionsLe (some grant.extensions) parent.scope.extensions := - ((rich_projection_accepts_iff_root_and_scope_depth_checks - parent grant).1 accepted).2.2.2.2.2.2.2.2.2.2.2 + -- Was `.2.2.2.2.2.2.2.2.2.2.2`: eleven projections whose meaning depended on + -- counting. The eleventh dimension is now reached by name. + (((rich_projection_accepts_iff_root_and_scope_depth_checks + parent grant).1 accepted).2.2.2).extensions /-- The contrapositive, stated for every input: a stripped or altered critical diff --git a/formal/Auths/Refinement/Production.lean b/formal/Auths/Refinement/Production.lean index 2928867b..c45b6235 100644 --- a/formal/Auths/Refinement/Production.lean +++ b/formal/Auths/Refinement/Production.lean @@ -2690,6 +2690,7 @@ theorem translated_delegation_refines_rich_spec · simp_all [Auths.Rich.evaluateGrant, Auths.Rich.linked, Auths.Rich.rootPreserved, Auths.Rich.rooted, Auths.Rich.scopeDepthChecks, Auths.Rich.grantScopeChecks, + Auths.Rich.GrantScopeChecks.iff_conjunction, productionDelegationOutcome, expectedAcceptedTransition, extensions_le_rich_iff, OptionalCriticalExtensionsAttenuate, richAuthorityState, richGrant] @@ -2778,7 +2779,8 @@ theorem translated_delegation_refines_rich_spec apply failedScope have richChecks := checks.2 simpa [Auths.Rich.scopeDepthChecks, - Auths.Rich.grantScopeChecks, richAuthorityState, + Auths.Rich.grantScopeChecks, + Auths.Rich.GrantScopeChecks.iff_conjunction, richAuthorityState, richGrant, extensions_le_rich_iff] using richChecks have linkedSemantic : Auths.Rich.linked diff --git a/formal/Auths/Rich/Semantics.lean b/formal/Auths/Rich/Semantics.lean index d425a903..ce380044 100644 --- a/formal/Auths/Rich/Semantics.lean +++ b/formal/Auths/Rich/Semantics.lean @@ -330,22 +330,67 @@ instance {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : unfold linked infer_instance +/-- +Every scope dimension a delegation must attenuate, ONE NAMED FIELD EACH. + +This was a nine-way anonymous conjunction. Two things follow from naming the +fields that did not follow from nesting them. + +A caller reaches a dimension by NAME rather than by counting `.2`s, so a proof +cannot silently address the wrong one; the old form produced expressions like +`accepted.2.2.2.2.2.2.2.2` whose meaning depended on position. + +More importantly, adding a tenth dimension now forces every constructor and +every pattern match to mention it. The eleventh attenuation dimension was once +reported as `extensionsAttenuate := true` and nobody noticed, because nothing +in the shape of the definition required it to be addressed. A structure +requires it. +-/ +structure GrantScopeChecks {v : Vocabulary} + (parent : AuthorityScope v) (grant : Grant v) : Prop where + profile : profileAllows parent.profileScope grant.profile + permissions : grant.permissions ⊆ parent.permissions + validity : windowContained grant.validity parent.validity + audiences : grant.audiences ⊆ parent.audiences + actionConstraint : + actionConstraintLe grant.actionConstraint parent.actionConstraint + budget : budgetLe grant.budget parent.budget + status : statusLe grant.status parent.status + assurance : grant.assurance = parent.assurance + extensions : extensionsLe (some grant.extensions) parent.extensions + +/-- The named structure spelled as the conjunction, for rewriting. -/ +theorem GrantScopeChecks.iff_conjunction {v : Vocabulary} + (parent : AuthorityScope v) (grant : Grant v) : + GrantScopeChecks parent grant ↔ + (profileAllows parent.profileScope grant.profile ∧ + grant.permissions ⊆ parent.permissions ∧ + windowContained grant.validity parent.validity ∧ + grant.audiences ⊆ parent.audiences ∧ + actionConstraintLe grant.actionConstraint parent.actionConstraint ∧ + budgetLe grant.budget parent.budget ∧ + statusLe grant.status parent.status ∧ + grant.assurance = parent.assurance ∧ + extensionsLe (some grant.extensions) parent.extensions) := by + constructor + · intro checks + exact ⟨checks.profile, checks.permissions, checks.validity, + checks.audiences, checks.actionConstraint, checks.budget, checks.status, + checks.assurance, checks.extensions⟩ + · rintro ⟨profile, permissions, validity, audiences, actionConstraint, + budget, status, assurance, extensions⟩ + exact ⟨profile, permissions, validity, audiences, actionConstraint, + budget, status, assurance, extensions⟩ + +/-- The named structure, as the predicate the rest of the development uses. -/ def grantScopeChecks {v : Vocabulary} (parent : AuthorityScope v) (grant : Grant v) : Prop := - profileAllows parent.profileScope grant.profile ∧ - grant.permissions ⊆ parent.permissions ∧ - windowContained grant.validity parent.validity ∧ - grant.audiences ⊆ parent.audiences ∧ - actionConstraintLe grant.actionConstraint parent.actionConstraint ∧ - budgetLe grant.budget parent.budget ∧ - statusLe grant.status parent.status ∧ - grant.assurance = parent.assurance ∧ - extensionsLe (some grant.extensions) parent.extensions + GrantScopeChecks parent grant instance {v : Vocabulary} (parent : AuthorityScope v) (grant : Grant v) : Decidable (grantScopeChecks parent grant) := by unfold grantScopeChecks - infer_instance + exact decidable_of_iff _ (GrantScopeChecks.iff_conjunction parent grant).symm def scopeDepthChecks {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : Prop := diff --git a/formal/Auths/Rich/Theorems.lean b/formal/Auths/Rich/Theorems.lean index f1069c53..a9d74867 100644 --- a/formal/Auths/Rich/Theorems.lean +++ b/formal/Auths/Rich/Theorems.lean @@ -762,7 +762,7 @@ theorem rich_projection_accepts_iff_root_and_scope_depth_checks {v : Vocabulary} (delegationProjection parent grant) = true ↔ rootPreserved parent grant ∧ scopeDepthChecks parent grant := by simp [Auths.Generated.attenuationAccepts, delegationProjection, - scopeDepthChecks, grantScopeChecks] + scopeDepthChecks, grantScopeChecks, GrantScopeChecks.iff_conjunction] tauto theorem apply_grant_success_iff_linked_and_projection {v : Vocabulary} diff --git a/formal/assurance-manifest-v1.toml b/formal/assurance-manifest-v1.toml index cf7e366d..3f295207 100644 --- a/formal/assurance-manifest-v1.toml +++ b/formal/assurance-manifest-v1.toml @@ -46,7 +46,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -98,7 +98,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -150,7 +150,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -202,7 +202,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -254,7 +254,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -303,7 +303,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -356,7 +356,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -409,7 +409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -462,7 +462,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -514,7 +514,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -567,7 +567,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -620,7 +620,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -673,7 +673,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -726,7 +726,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -775,7 +775,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -824,7 +824,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -873,7 +873,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -922,7 +922,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -971,7 +971,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1020,7 +1020,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1069,7 +1069,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1118,7 +1118,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1167,7 +1167,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1216,7 +1216,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1268,7 +1268,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1317,7 +1317,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1366,7 +1366,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1418,7 +1418,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1467,7 +1467,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1519,7 +1519,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1571,7 +1571,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1623,7 +1623,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1675,7 +1675,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1727,7 +1727,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1780,7 +1780,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1833,7 +1833,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1886,7 +1886,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1938,7 +1938,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1991,7 +1991,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2043,7 +2043,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2095,7 +2095,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2148,7 +2148,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2200,7 +2200,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2253,7 +2253,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2305,7 +2305,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2357,7 +2357,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2409,7 +2409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2461,7 +2461,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2513,7 +2513,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2565,7 +2565,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2617,7 +2617,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2670,7 +2670,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2723,7 +2723,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2776,7 +2776,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2829,7 +2829,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2882,7 +2882,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2934,7 +2934,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2986,7 +2986,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3039,7 +3039,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3092,7 +3092,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3145,7 +3145,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3198,7 +3198,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3251,7 +3251,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3304,7 +3304,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3357,7 +3357,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3409,7 +3409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3458,7 +3458,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3507,7 +3507,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3556,7 +3556,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3605,7 +3605,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3654,7 +3654,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3703,7 +3703,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3752,7 +3752,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3801,7 +3801,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3850,7 +3850,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3899,7 +3899,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3948,7 +3948,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3997,7 +3997,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4046,7 +4046,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4108,7 +4108,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The exact Aeneas translation of the production pre-signing scope evaluator over validated, bounded Rust model views, including ordered diagnostics." residual_assumptions = [ "Lean's kernel, the pinned Rust/Lean/Aeneas/Charon toolchain, the reviewed Aeneas external models, the listed foundational axioms, and the theorem's representation-validity premises are trusted.", @@ -4190,7 +4190,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Validated target-V1 authority and action views through the production terminal-coverage decision, including its first-failure diagnostic order." residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget result is stateless per action, assurance uses exact identifier equality, and deterministic diagnostic precedence is not a side-channel noninterference claim. Codec validation and cryptography remain outside this claim."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4268,7 +4268,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Validated target-V1 parent-authority and grant views through the production delegation outcome and accepted transition." residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget relation is stateless per action, assurance retention is exact identifier equality rather than a lattice, and deterministic diagnostic precedence is not a side-channel noninterference claim. Root preservation and assurance retention occur in the caller around the proved returned transition; codec validation and cryptography remain outside this claim."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4333,7 +4333,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4386,7 +4386,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4439,7 +4439,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4492,7 +4492,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4545,7 +4545,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4594,7 +4594,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4643,7 +4643,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4692,7 +4692,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4741,7 +4741,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4790,7 +4790,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4839,7 +4839,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4888,7 +4888,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4940,7 +4940,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4989,7 +4989,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5042,7 +5042,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5095,7 +5095,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5148,7 +5148,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5201,7 +5201,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5254,7 +5254,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5306,7 +5306,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5358,7 +5358,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5410,7 +5410,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5463,7 +5463,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5516,7 +5516,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5565,7 +5565,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5614,7 +5614,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5663,7 +5663,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5712,7 +5712,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5761,7 +5761,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5810,7 +5810,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5859,7 +5859,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5908,7 +5908,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5957,7 +5957,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6006,7 +6006,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6055,7 +6055,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::model::LifecycleState::is_terminal` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6104,7 +6104,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::transition_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6156,7 +6156,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::exclusive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6205,7 +6205,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::additive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6258,7 +6258,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::replay_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6307,7 +6307,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6356,7 +6356,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6405,7 +6405,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6454,7 +6454,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6503,7 +6503,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6552,7 +6552,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6601,7 +6601,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6654,7 +6654,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6707,7 +6707,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "Exactly the two-input threshold helper and the declared three-valued truth order." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6763,7 +6763,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "A two-value swap only; arbitrary authorization-plan permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6812,7 +6812,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The two-value canonicalCode helper; arbitrary plan diagnostic permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6861,7 +6861,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "A definitional model equality; it does not prove that the shipping evaluator visits leaves once." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6913,7 +6913,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "A definitional model equality; no asymptotic or shipping evaluator cost bound is claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6965,7 +6965,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -7018,7 +7018,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -7074,7 +7074,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "f0df2ccd1dbab967b586866125b25fccb342e7a7550db8dec7595ff745660c4c" +semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index 923a8d49..58bd7c2c 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 118, + "freezeVersion": 119, "publicSurface": { "rustRoots": [ "auths", @@ -199,7 +199,7 @@ }, { "id": "auths.frozen-bytes/formal/assurance-manifest-v1.toml", - "version": 7, + "version": 8, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -207,7 +207,7 @@ "owners": [ "formal/assurance-manifest-v1.toml" ], - "sha256": "549043a1528270a1591d3f809fd41a5b0744a7bfb2cf10c536136ae1a07693f8" + "sha256": "cbfcdc8f3934528efc55d954d6a4ff0efd60f75d1e520292fdda2b2c2dc95987" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/generated", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 117, + "version": 118, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "a7fb9e671f5046fdcbc9112a235cb3d2db2d710a65f84c4f8937143c10ea1012" + "sha256": "dfc140e8a908e86f9941336217e0fd7a5c585f45053a3d54e5032249401ee874" } ] } diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index cbf30857..b5b054ee 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 118; +const FREEZE_VERSION: u64 = 119; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -633,7 +633,7 @@ fn generate_inventory() -> Result { "bindings/wasm/auths-proof-wasm/identity-abi-v1.json" => 4, "core/fixtures/v1/manifest.json" => 4, "product/conformance/v1/simplified-product-waist.json" => 2, - "formal/assurance-manifest-v1.toml" => 7, + "formal/assurance-manifest-v1.toml" => 8, "formal/qualification/aeneas/qualification.toml" => 4, "formal/qualification/aeneas/generated" => 5, "formal/qualification/aeneas/source-closure.json" => 16, @@ -696,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 117, + 118, FreezeClassification::ReleaseMetadata, &[ "package-names", From 71056b79c6b481a773497c1f5d022a0bd20707e5 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 17:29:53 +0100 Subject: [PATCH 41/61] formal: make the attenuation projection carry its own certificate (UNSIGNED) Reviewer item 1, the change they called highest-value. `Auths.Generated.AttenuationProjection` is eleven unconstrained `Bool`s, so `extensionsAttenuate := true` remained EXPRESSIBLE. That is not hypothetical: the eleventh dimension shipped as exactly that literal, and the exactness theorems are what eventually caught it. They catch a bad projection after it exists. `CertifiedProjection parent grant` makes it unconstructible. Eleven obligations pin each field to the `decide` of its rich relation, so a literal cannot be supplied without a proof that the literal equals the semantic answer -- and for a wrong literal no such proof exists. PROVEN BY CONSTRUCTION, not asserted. A forged projection carrying `extensionsAttenuate := true` was written and rejected: error: Type mismatch rfl has type ?m = ?m but is expected to have type { ... extensionsAttenuate := true ... } = decide (extensionsLe ...) `certifiedDelegationProjection` discharges all eleven obligations by `rfl`, and that is itself the finding: `delegationProjection` was already honest and simply had no way to say so in its type. The vacuous literal that shipped could not have been written in this form. Two theorems record the consequence: a certified projection cannot report extensions or the trust root as attenuating when the semantics deny them. Adding a twelfth dimension adds a twelfth obligation no existing constructor satisfies, so the compiler demands it be addressed. Together with the named `GrantScopeChecks` from the previous commit, another hidden `:= true` now has to survive both a structure that requires the field and a type that requires a proof of its value. Verified: lake build; assurance audit PASS at 139 compiled statements, up from 137; qualification cases 6/6; clean reproduction byte-identical. Co-Authored-By: Claude Opus 5 --- formal/Auths/Rich/Semantics.lean | 96 ++++++++ formal/Auths/Theorems.lean | 2 + formal/assurance-manifest-v1.toml | 380 +++++++++++++++++++----------- release/semantic-freeze.json | 10 +- xtask/src/semantic_freeze.rs | 6 +- 5 files changed, 349 insertions(+), 145 deletions(-) diff --git a/formal/Auths/Rich/Semantics.lean b/formal/Auths/Rich/Semantics.lean index ce380044..809b8d4d 100644 --- a/formal/Auths/Rich/Semantics.lean +++ b/formal/Auths/Rich/Semantics.lean @@ -587,6 +587,60 @@ def terminalCovers {v : Vocabulary} action.terminalGrant = authority.lastGrant ∧ actionCovers authority.scope action +/-- +A projection carrying a proof that every field IS its semantic decision. + +`Auths.Generated.AttenuationProjection` is eleven unconstrained `Bool`s, so +`extensionsAttenuate := true` is expressible. That is not hypothetical: the +eleventh dimension shipped as a literal `true` and the exactness theorems were +what eventually caught it. They catch a bad projection AFTER it exists. + +This type makes it unconstructible. Each field below pins one dimension to the +`decide` of its rich relation, so a literal cannot be supplied without a proof +that the literal equals the semantic answer -- and no such proof exists for a +wrong literal. The reviewer's phrasing: a projection that must carry its own +certificate. + +Adding a twelfth dimension adds a twelfth obligation here, which no existing +constructor satisfies, so the compiler demands it be addressed. +-/ +structure CertifiedProjection {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) where + value : Auths.Generated.AttenuationProjection + rootExact : value.rootPreserved = decide (rootPreserved parent grant) + depthExact : + value.depthDecreases = + decide (0 < parent.remainingDepth ∧ + grant.remainingDepth < parent.remainingDepth) + profileExact : + value.profileAttenuates = + decide (profileAllows parent.scope.profileScope grant.profile) + permissionsExact : + value.permissionsAttenuate = + decide (grant.permissions ⊆ parent.scope.permissions) + validityExact : + value.validityAttenuates = + decide (windowContained grant.validity parent.scope.validity) + audiencesExact : + value.audiencesAttenuate = + decide (grant.audiences ⊆ parent.scope.audiences) + actionConstraintExact : + value.actionConstraintAttenuates = + decide (actionConstraintLe grant.actionConstraint + parent.scope.actionConstraint) + budgetExact : + value.budgetAttenuates = + decide (budgetLe grant.budget parent.scope.budget) + statusExact : + value.statusAttenuates = + decide (statusLe grant.status parent.scope.status) + assuranceExact : + value.assuranceAttenuates = + decide (grant.assurance = parent.scope.assurance) + extensionsExact : + value.extensionsAttenuate = + decide (extensionsLe (some grant.extensions) parent.scope.extensions) + def delegationProjection {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : Auths.Generated.AttenuationProjection where @@ -614,4 +668,46 @@ def delegationProjection {v : Vocabulary} extensionsAttenuate := decide (extensionsLe (some grant.extensions) parent.scope.extensions) +/-- `delegationProjection` is certified: every field is its decision, by rfl. -/ +def certifiedDelegationProjection {v : Vocabulary} + (parent : ChainState v) (grant : Grant v) : + CertifiedProjection parent grant where + value := delegationProjection parent grant + rootExact := rfl + depthExact := rfl + profileExact := rfl + permissionsExact := rfl + validityExact := rfl + audiencesExact := rfl + actionConstraintExact := rfl + budgetExact := rfl + statusExact := rfl + assuranceExact := rfl + extensionsExact := rfl + + +/-- +No certified projection can report a dimension the semantics deny. + +This is what the type buys. `extensionsAttenuate := true` beneath a parent that +denies it is not merely detected, it cannot be constructed. +-/ +theorem CertifiedProjection.extensions_not_forgeable {v : Vocabulary} + {parent : ChainState v} {grant : Grant v} + (certified : CertifiedProjection parent grant) + (denied : ¬ extensionsLe (some grant.extensions) parent.scope.extensions) : + certified.value.extensionsAttenuate = false := by + rw [certified.extensionsExact] + exact decide_eq_false denied + +/-- The same for the trust root, the dimension no other can rescue. -/ +theorem CertifiedProjection.root_not_forgeable {v : Vocabulary} + {parent : ChainState v} {grant : Grant v} + (certified : CertifiedProjection parent grant) + (denied : ¬ rootPreserved parent grant) : + certified.value.rootPreserved = false := by + rw [certified.rootExact] + exact decide_eq_false denied + end Auths.Rich + diff --git a/formal/Auths/Theorems.lean b/formal/Auths/Theorems.lean index ecb3595a..6324d691 100644 --- a/formal/Auths/Theorems.lean +++ b/formal/Auths/Theorems.lean @@ -77,6 +77,8 @@ def theoremInventory : List Lean.Name := `Auths.Rich.authority_delegate_first_failure, `Auths.Rich.author_planning_diagnostic_sound_complete, `Auths.Rich.coverage_decision_ok_iff_covers, + `Auths.Rich.CertifiedProjection.extensions_not_forgeable, + `Auths.Rich.CertifiedProjection.root_not_forgeable, `Auths.Rich.Mutations.validity_start_direction, `Auths.Rich.Mutations.validity_end_direction, `Auths.Rich.Mutations.budget_value_direction, diff --git a/formal/assurance-manifest-v1.toml b/formal/assurance-manifest-v1.toml index 3f295207..b23e6611 100644 --- a/formal/assurance-manifest-v1.toml +++ b/formal/assurance-manifest-v1.toml @@ -46,7 +46,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -98,7 +98,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -150,7 +150,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -202,7 +202,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -254,7 +254,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -303,7 +303,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -356,7 +356,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -409,7 +409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -462,7 +462,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -514,7 +514,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -567,7 +567,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -620,7 +620,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -673,7 +673,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -726,7 +726,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -775,7 +775,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -824,7 +824,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -873,7 +873,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -922,7 +922,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -971,7 +971,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1020,7 +1020,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1069,7 +1069,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1118,7 +1118,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1167,7 +1167,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1216,7 +1216,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1268,7 +1268,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1317,7 +1317,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1366,7 +1366,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1418,7 +1418,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1467,7 +1467,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1519,7 +1519,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1571,7 +1571,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1623,7 +1623,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1675,7 +1675,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1727,7 +1727,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1780,7 +1780,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1833,7 +1833,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1886,7 +1886,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1938,7 +1938,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1991,7 +1991,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2043,7 +2043,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2095,7 +2095,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2148,7 +2148,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2200,7 +2200,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2253,7 +2253,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2305,7 +2305,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2357,7 +2357,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2409,7 +2409,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2461,7 +2461,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2513,7 +2513,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2565,7 +2565,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2617,7 +2617,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2670,7 +2670,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2723,7 +2723,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2776,7 +2776,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2829,7 +2829,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2882,7 +2882,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2934,7 +2934,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2986,7 +2986,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3039,7 +3039,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3092,7 +3092,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3145,7 +3145,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3198,7 +3198,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3251,7 +3251,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3304,7 +3304,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3357,7 +3357,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3370,6 +3370,112 @@ axioms = [ kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +[[claims]] +claim_id = "AP-FORMAL-RICH-122" +claim_text = "Lean proves the rich authority property: extensions not forgeable." +claim_status = "proved" +lean_declaration = "Auths.Rich.CertifiedProjection.extensions_not_forgeable" +lean_statement_sha256 = "b6037af4490fc1d541c162817c0b3751561b1d2fd08f5250903aaef200714073" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + +[[claims]] +claim_id = "AP-FORMAL-RICH-123" +claim_text = "Lean proves the rich authority property: root not forgeable." +claim_status = "proved" +lean_declaration = "Auths.Rich.CertifiedProjection.root_not_forgeable" +lean_statement_sha256 = "efe4847e1d4fef7f69aa59aaaf160fb5cf08b27df5fe70b5751d6f4e25f8cd56" +formal_review = "rich-authority-refinement-2026-07-29" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths/Base.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Theorems.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/algebra-contract-v1.toml", +] +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" + [[claims]] claim_id = "AP-FORMAL-RICH-109" claim_text = "Lean proves the rich authority property: validity start direction." @@ -3409,7 +3515,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3458,7 +3564,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3507,7 +3613,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3556,7 +3662,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3605,7 +3711,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3654,7 +3760,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3703,7 +3809,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3752,7 +3858,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3801,7 +3907,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3850,7 +3956,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3899,7 +4005,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3948,7 +4054,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3997,7 +4103,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4046,7 +4152,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4108,7 +4214,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The exact Aeneas translation of the production pre-signing scope evaluator over validated, bounded Rust model views, including ordered diagnostics." residual_assumptions = [ "Lean's kernel, the pinned Rust/Lean/Aeneas/Charon toolchain, the reviewed Aeneas external models, the listed foundational axioms, and the theorem's representation-validity premises are trusted.", @@ -4190,7 +4296,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Validated target-V1 authority and action views through the production terminal-coverage decision, including its first-failure diagnostic order." residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget result is stateless per action, assurance uses exact identifier equality, and deterministic diagnostic precedence is not a side-channel noninterference claim. Codec validation and cryptography remain outside this claim."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4268,7 +4374,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Validated target-V1 parent-authority and grant views through the production delegation outcome and accepted transition." residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget relation is stateless per action, assurance retention is exact identifier equality rather than a lattice, and deterministic diagnostic precedence is not a side-channel noninterference claim. Root preservation and assurance retention occur in the caller around the proved returned transition; codec validation and cryptography remain outside this claim."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4333,7 +4439,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4386,7 +4492,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4439,7 +4545,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4492,7 +4598,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4545,7 +4651,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4594,7 +4700,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4643,7 +4749,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4692,7 +4798,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4741,7 +4847,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4790,7 +4896,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4839,7 +4945,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4888,7 +4994,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4940,7 +5046,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4989,7 +5095,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5042,7 +5148,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5095,7 +5201,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5148,7 +5254,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5201,7 +5307,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5254,7 +5360,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5306,7 +5412,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5358,7 +5464,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5410,7 +5516,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5463,7 +5569,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5516,7 +5622,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5565,7 +5671,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5614,7 +5720,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5663,7 +5769,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5712,7 +5818,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5761,7 +5867,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5810,7 +5916,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5859,7 +5965,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5908,7 +6014,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5957,7 +6063,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6006,7 +6112,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6055,7 +6161,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::model::LifecycleState::is_terminal` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6104,7 +6210,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::transition_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6156,7 +6262,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::exclusive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6205,7 +6311,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::additive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6258,7 +6364,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::replay_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6307,7 +6413,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6356,7 +6462,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6405,7 +6511,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6454,7 +6560,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6503,7 +6609,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6552,7 +6658,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6601,7 +6707,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6654,7 +6760,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6707,7 +6813,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "Exactly the two-input threshold helper and the declared three-valued truth order." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6763,7 +6869,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "A two-value swap only; arbitrary authorization-plan permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6812,7 +6918,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The two-value canonicalCode helper; arbitrary plan diagnostic permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6861,7 +6967,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "A definitional model equality; it does not prove that the shipping evaluator visits leaves once." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6913,7 +7019,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "A definitional model equality; no asymptotic or shipping evaluator cost bound is claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6965,7 +7071,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -7018,7 +7124,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -7074,7 +7180,7 @@ semantic_source_closure = [ "formal/qualification/aeneas/generated/model/Types.lean", "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "45c0fec4765767983448eaea7b8eaa931d85ac396ccadc753585842a9a0090f8" +semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index 58bd7c2c..a8100055 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 119, + "freezeVersion": 120, "publicSurface": { "rustRoots": [ "auths", @@ -199,7 +199,7 @@ }, { "id": "auths.frozen-bytes/formal/assurance-manifest-v1.toml", - "version": 8, + "version": 9, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -207,7 +207,7 @@ "owners": [ "formal/assurance-manifest-v1.toml" ], - "sha256": "cbfcdc8f3934528efc55d954d6a4ff0efd60f75d1e520292fdda2b2c2dc95987" + "sha256": "bc8d303ec478f565dbbd0c4662a875c9ae1245e2ad74505f92357d5a083254b1" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/generated", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 118, + "version": 119, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "dfc140e8a908e86f9941336217e0fd7a5c585f45053a3d54e5032249401ee874" + "sha256": "bd35b839b5bdfe1e5b252ba96a5b332b4fcfd9ad822480e0b415ffa26ed3dc7e" } ] } diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index b5b054ee..836ebf9a 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 119; +const FREEZE_VERSION: u64 = 120; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -633,7 +633,7 @@ fn generate_inventory() -> Result { "bindings/wasm/auths-proof-wasm/identity-abi-v1.json" => 4, "core/fixtures/v1/manifest.json" => 4, "product/conformance/v1/simplified-product-waist.json" => 2, - "formal/assurance-manifest-v1.toml" => 8, + "formal/assurance-manifest-v1.toml" => 9, "formal/qualification/aeneas/qualification.toml" => 4, "formal/qualification/aeneas/generated" => 5, "formal/qualification/aeneas/source-closure.json" => 16, @@ -696,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 118, + 119, FreezeClassification::ReleaseMetadata, &[ "package-names", From 422d4ce5c0a503590be15f03d5c0c5064a05fbc3 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 17:46:15 +0100 Subject: [PATCH 42/61] formal: bind every evidence artifact to its digest (UNSIGNED) Reviewer item 5. Evidence was EXISTENCE-CHECKED only: the audit confirmed a file was present at the cited path and read nothing. An artifact could be emptied, rewritten, or replaced wholesale and every claim citing it still passed. That is not hypothetical. `refinement-mutations-v1.json` was exactly this case -- 23 prose entries standing in for checks, cited as evidence by three claims, never read. The previous commit made its contents enforceable; this makes the citation itself binding. `FormalEvidence` gains `sha256`, verified whenever present. 155 evidence entries across 11 distinct artifacts now carry one. MUTATION-PROVEN. Corrupting a recorded digest reports the claim, the artifact, and both hashes: xtask: formal claim Auths.Rich.finiteSet_subset_refl cites evidence formal/Auths/Rich/Theorems.lean with digest 0000...0000, but the artifact hashes to 7dcf0fbc0904c1cc... The check also fired on real input before any probe: `source-closure.json` is regenerated by the same command that validates it, so its recorded digest went stale mid-run and the audit refused. That is the binding behaving correctly -- regenerating an artifact now requires refreshing what cites it, which is the point of citing a digest rather than a path. The field is optional so an entry can be introduced before its digest is recorded, but once present it is enforced. Verified: lake build; assurance audit PASS at 139 compiled statements; qualification cases 6/6; clean reproduction byte-identical; cargo xtask formal passes with Kani, Lean theorems, and Rust refinement vectors all green. Co-Authored-By: Claude Opus 5 --- formal/assurance-manifest-v1.toml | 150 ++++++++++++++++++ .../qualification/aeneas/source-closure.json | 4 +- release/semantic-freeze.json | 14 +- xtask/src/formal.rs | 26 +++ xtask/src/semantic_freeze.rs | 8 +- 5 files changed, 189 insertions(+), 13 deletions(-) diff --git a/formal/assurance-manifest-v1.toml b/formal/assurance-manifest-v1.toml index b23e6611..fd8b1b22 100644 --- a/formal/assurance-manifest-v1.toml +++ b/formal/assurance-manifest-v1.toml @@ -58,6 +58,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-002" @@ -110,6 +111,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-003" @@ -162,6 +164,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-004" @@ -214,6 +217,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-005" @@ -263,6 +267,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-006" @@ -316,6 +321,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-007" @@ -369,6 +375,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-008" @@ -422,6 +429,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-009" @@ -474,6 +482,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-010" @@ -527,6 +536,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-011" @@ -580,6 +590,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-092" @@ -633,6 +644,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-012" @@ -686,6 +698,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-013" @@ -735,6 +748,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-014" @@ -784,6 +798,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-015" @@ -833,6 +848,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-016" @@ -882,6 +898,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-093" @@ -931,6 +948,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-094" @@ -980,6 +998,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-095" @@ -1029,6 +1048,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-096" @@ -1078,6 +1098,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-097" @@ -1127,6 +1148,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-098" @@ -1176,6 +1198,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-105" @@ -1228,6 +1251,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-017" @@ -1277,6 +1301,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-018" @@ -1326,6 +1351,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-019" @@ -1378,6 +1404,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-020" @@ -1427,6 +1454,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-021" @@ -1479,6 +1507,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-022" @@ -1531,6 +1560,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-023" @@ -1583,6 +1613,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-024" @@ -1635,6 +1666,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-025" @@ -1687,6 +1719,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-026" @@ -1740,6 +1773,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-027" @@ -1793,6 +1827,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-028" @@ -1846,6 +1881,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-029" @@ -1898,6 +1934,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-030" @@ -1951,6 +1988,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-031" @@ -2003,6 +2041,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-032" @@ -2055,6 +2094,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-033" @@ -2108,6 +2148,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-034" @@ -2160,6 +2201,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-035" @@ -2213,6 +2255,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-036" @@ -2265,6 +2308,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-037" @@ -2317,6 +2361,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-038" @@ -2369,6 +2414,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-039" @@ -2421,6 +2467,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-040" @@ -2473,6 +2520,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-041" @@ -2525,6 +2573,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-042" @@ -2577,6 +2626,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-043" @@ -2630,6 +2680,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-044" @@ -2683,6 +2734,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-099" @@ -2736,6 +2788,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-100" @@ -2789,6 +2842,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-101" @@ -2842,6 +2896,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-102" @@ -2894,6 +2949,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-103" @@ -2946,6 +3002,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-045" @@ -2999,6 +3056,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-046" @@ -3052,6 +3110,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-047" @@ -3105,6 +3164,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-048" @@ -3158,6 +3218,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-049" @@ -3211,6 +3272,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-050" @@ -3264,6 +3326,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-051" @@ -3317,6 +3380,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-052" @@ -3369,6 +3433,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-122" @@ -3422,6 +3487,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-123" @@ -3475,6 +3541,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-109" @@ -3524,6 +3591,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-110" @@ -3573,6 +3641,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-111" @@ -3622,6 +3691,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-112" @@ -3671,6 +3741,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-113" @@ -3720,6 +3791,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-114" @@ -3769,6 +3841,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-115" @@ -3818,6 +3891,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-116" @@ -3867,6 +3941,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-117" @@ -3916,6 +3991,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-118" @@ -3965,6 +4041,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-119" @@ -4014,6 +4091,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-120" @@ -4063,6 +4141,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-121" @@ -4112,6 +4191,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-053" @@ -4164,6 +4244,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-054" @@ -4230,22 +4311,27 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Refinement/Production.lean" +sha256 = "accc5207c5203893673fa3aa6a7d341c797bfd9a487eb346faa725d7d4683163" [[claims.evidence]] kind = "mechanical-translation" artifact = "formal/qualification/aeneas/generated/authority/Funs.lean" +sha256 = "8b285906cc66125575211a1fa7860459bb91a7472ccc6ab99f81f178902ff773" [[claims.evidence]] kind = "source-closure" artifact = "formal/qualification/aeneas/source-closure.json" +sha256 = "94263dd8276ab7bfbdc67f2ca3b31d1662ed66cca45c59f00d045ec98c4d6ee7" [[claims.evidence]] kind = "lean-generated-vectors" artifact = "core/formal-vectors/v1/rich-authority.json" +sha256 = "8f560ad3afa1ae5f0ef7dc5a8b77779e5da79c59d94d39bf2d2a005edac25a92" [[claims.evidence]] kind = "mutation-matrix" artifact = "formal/refinement-mutations-v1.json" +sha256 = "8da37d014eecc1aab4a2c73bcc2076956970b61f64041964daa04b48b8b9d15a" [[claims]] claim_id = "AP-FORMAL-RICH-055" @@ -4309,18 +4395,22 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Refinement/Production.lean" +sha256 = "accc5207c5203893673fa3aa6a7d341c797bfd9a487eb346faa725d7d4683163" [[claims.evidence]] kind = "translated-rust" artifact = "formal/qualification/aeneas/generated/authority/Funs.lean" +sha256 = "8b285906cc66125575211a1fa7860459bb91a7472ccc6ab99f81f178902ff773" [[claims.evidence]] kind = "lean-generated-vectors" artifact = "core/formal-vectors/v1/rich-authority.json" +sha256 = "8f560ad3afa1ae5f0ef7dc5a8b77779e5da79c59d94d39bf2d2a005edac25a92" [[claims.evidence]] kind = "mutation-matrix" artifact = "formal/refinement-mutations-v1.json" +sha256 = "8da37d014eecc1aab4a2c73bcc2076956970b61f64041964daa04b48b8b9d15a" [[claims]] claim_id = "AP-FORMAL-RICH-056" @@ -4387,18 +4477,22 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Refinement/Production.lean" +sha256 = "accc5207c5203893673fa3aa6a7d341c797bfd9a487eb346faa725d7d4683163" [[claims.evidence]] kind = "translated-rust" artifact = "formal/qualification/aeneas/generated/authority/Funs.lean" +sha256 = "8b285906cc66125575211a1fa7860459bb91a7472ccc6ab99f81f178902ff773" [[claims.evidence]] kind = "lean-generated-vectors" artifact = "core/formal-vectors/v1/rich-authority.json" +sha256 = "8f560ad3afa1ae5f0ef7dc5a8b77779e5da79c59d94d39bf2d2a005edac25a92" [[claims.evidence]] kind = "mutation-matrix" artifact = "formal/refinement-mutations-v1.json" +sha256 = "8da37d014eecc1aab4a2c73bcc2076956970b61f64041964daa04b48b8b9d15a" [[claims]] claim_id = "AP-FORMAL-RICH-104" @@ -4452,6 +4546,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-106" @@ -4505,6 +4600,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-107" @@ -4558,6 +4654,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-108" @@ -4611,6 +4708,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" [[claims]] claim_id = "AP-FORMAL-RICH-057" @@ -4660,6 +4758,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Product/Theorems.lean" +sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" [[claims]] claim_id = "AP-FORMAL-RICH-058" @@ -4709,6 +4808,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Product/Theorems.lean" +sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" [[claims]] claim_id = "AP-FORMAL-RICH-059" @@ -4758,6 +4858,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Product/Theorems.lean" +sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" [[claims]] claim_id = "AP-FORMAL-RICH-060" @@ -4807,6 +4908,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Product/Theorems.lean" +sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" [[claims]] claim_id = "AP-FORMAL-RICH-061" @@ -4856,6 +4958,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Product/Theorems.lean" +sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" [[claims]] claim_id = "AP-FORMAL-RICH-062" @@ -4905,6 +5008,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Product/Theorems.lean" +sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" [[claims]] claim_id = "AP-FORMAL-RICH-063" @@ -4954,6 +5058,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Product/Theorems.lean" +sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" [[claims]] claim_id = "AP-FORMAL-RICH-064" @@ -5006,6 +5111,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Product/Theorems.lean" +sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" [[claims]] claim_id = "AP-FORMAL-RICH-065" @@ -5055,6 +5161,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Product/Theorems.lean" +sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" [[claims]] claim_id = "AP-FORMAL-RICH-066" @@ -5108,6 +5215,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Product/Theorems.lean" +sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" [[claims]] claim_id = "AP-FORMAL-RICH-067" @@ -5161,6 +5269,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Product/Theorems.lean" +sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" [[claims]] claim_id = "AP-FORMAL-RICH-068" @@ -5214,6 +5323,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Product/Theorems.lean" +sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" [[claims]] claim_id = "AP-FORMAL-RICH-069" @@ -5267,6 +5377,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Product/Theorems.lean" +sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" [[claims]] claim_id = "AP-FORMAL-RICH-070" @@ -5320,6 +5431,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-071" @@ -5372,6 +5484,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-072" @@ -5424,6 +5537,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-073" @@ -5476,6 +5590,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-074" @@ -5529,6 +5644,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-075" @@ -5582,6 +5698,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-076" @@ -5631,6 +5748,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-077" @@ -5680,6 +5798,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-078" @@ -5729,6 +5848,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-079" @@ -5778,6 +5898,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-080" @@ -5827,6 +5948,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-081" @@ -5876,6 +5998,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-082" @@ -5925,6 +6048,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-083" @@ -5974,6 +6098,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-084" @@ -6023,6 +6148,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-085" @@ -6072,6 +6198,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-086" @@ -6121,6 +6248,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Lifecycle/Theorems.lean" +sha256 = "1d30fad27e732bb77a87d7c34cb8230fbb869edc64fc3d3f33e7ee3f8592c8ab" [[claims]] claim_id = "AP-FORMAL-RICH-087" @@ -6422,6 +6550,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims]] claim_id = "AP-FORMAL-BASELINE-013" @@ -6471,6 +6600,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims]] claim_id = "AP-FORMAL-BASELINE-014" @@ -6520,6 +6650,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims]] claim_id = "AP-FORMAL-BASELINE-015" @@ -6569,6 +6700,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims]] claim_id = "AP-FORMAL-BASELINE-016" @@ -6618,6 +6750,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims]] claim_id = "AP-FORMAL-BASELINE-017" @@ -6667,6 +6800,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims]] claim_id = "AP-FORMAL-BASELINE-018" @@ -6716,10 +6850,12 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims.evidence]] kind = "test-corpus" artifact = "core/formal-vectors/v1/threshold-counts.json" +sha256 = "e2d3878d0da4344f9d5b1abc35545cfd5df513b94474ce229b728139e63dd1f6" [[claims]] claim_id = "AP-FORMAL-BASELINE-019" @@ -6769,10 +6905,12 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims.evidence]] kind = "test-corpus" artifact = "core/formal-vectors/v1/threshold-counts.json" +sha256 = "e2d3878d0da4344f9d5b1abc35545cfd5df513b94474ce229b728139e63dd1f6" [[claims]] claim_id = "AP-FORMAL-BASELINE-020" @@ -6825,10 +6963,12 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims.evidence]] kind = "test-corpus" artifact = "core/formal-vectors/v1/threshold-counts.json" +sha256 = "e2d3878d0da4344f9d5b1abc35545cfd5df513b94474ce229b728139e63dd1f6" [[claims]] claim_id = "AP-FORMAL-BASELINE-021" @@ -6878,6 +7018,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims]] claim_id = "AP-FORMAL-BASELINE-022" @@ -6927,6 +7068,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims]] claim_id = "AP-FORMAL-BASELINE-023" @@ -6979,6 +7121,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims]] claim_id = "AP-FORMAL-BASELINE-024" @@ -7031,6 +7174,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims]] claim_id = "AP-FORMAL-BASELINE-025" @@ -7080,10 +7224,12 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims.evidence]] kind = "test-corpus" artifact = "core/formal-vectors/v1/threshold-counts.json" +sha256 = "e2d3878d0da4344f9d5b1abc35545cfd5df513b94474ce229b728139e63dd1f6" [[claims]] claim_id = "AP-FORMAL-BASELINE-026" @@ -7136,10 +7282,12 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims.evidence]] kind = "test-corpus" artifact = "core/formal-vectors/v1/threshold-counts.json" +sha256 = "e2d3878d0da4344f9d5b1abc35545cfd5df513b94474ce229b728139e63dd1f6" [[claims]] claim_id = "AP-FORMAL-BASELINE-027" @@ -7193,7 +7341,9 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Composition.lean" +sha256 = "30413bde277c7f72922d10a3de2d39eef9ef429bb1cf0d093619407d88e5e388" [[claims.evidence]] kind = "test-corpus" artifact = "core/formal-vectors/v1/threshold-counts.json" +sha256 = "e2d3878d0da4344f9d5b1abc35545cfd5df513b94474ce229b728139e63dd1f6" diff --git a/formal/qualification/aeneas/source-closure.json b/formal/qualification/aeneas/source-closure.json index 9510d1dc..ae9d7c8d 100644 --- a/formal/qualification/aeneas/source-closure.json +++ b/formal/qualification/aeneas/source-closure.json @@ -1,6 +1,6 @@ { "schema": "auths-proof-translation-source-closure/v2", - "digest": "00b6edb6d5e39a6fcb99a34c3c0b017b4e63d52b102cf2114c17da1dfe9a6f8f", + "digest": "6de1f374a8adfa42f90bbd05a2d6acbe544b36a95855ecc3776319f911b527a2", "files": [ { "path": "Cargo.lock", @@ -90,7 +90,7 @@ }, { "path": "xtask/src/formal.rs", - "sha256": "f5e5195424ed0df9eec15a1693e211eab7f07c6157f6efbd9fcd88be99f495cb" + "sha256": "5271fd6cec2f9f1fda8ecd4d0dff4b1913e38127eefd14efde042b4379ec66a3" }, { "path": "xtask/src/formal_qualification.rs", diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index a8100055..0bf8cec0 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 120, + "freezeVersion": 121, "publicSurface": { "rustRoots": [ "auths", @@ -199,7 +199,7 @@ }, { "id": "auths.frozen-bytes/formal/assurance-manifest-v1.toml", - "version": 9, + "version": 10, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -207,7 +207,7 @@ "owners": [ "formal/assurance-manifest-v1.toml" ], - "sha256": "bc8d303ec478f565dbbd0c4662a875c9ae1245e2ad74505f92357d5a083254b1" + "sha256": "6964c5f0dd0d6a6afb0381e1aa54cb046c6e09bede4fab47bc570b21e49d3bf6" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/generated", @@ -235,7 +235,7 @@ }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/source-closure.json", - "version": 16, + "version": 17, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -243,7 +243,7 @@ "owners": [ "formal/qualification/aeneas/source-closure.json" ], - "sha256": "ef6838332d22a54e933977f479b81f2a68303a6fef605179923e9d4fa52848f6" + "sha256": "51de79ef6e2a9e2614fd785f0a79139a62d57dbceefe907892f3013bd39b1689" }, { "id": "auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 119, + "version": 120, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "bd35b839b5bdfe1e5b252ba96a5b332b4fcfd9ad822480e0b415ffa26ed3dc7e" + "sha256": "8a838b65ce36a52684a68c4465a5135b5471ede6cf275a83b02c15e43ab5bce8" } ] } diff --git a/xtask/src/formal.rs b/xtask/src/formal.rs index 0a69ae67..1df86219 100644 --- a/xtask/src/formal.rs +++ b/xtask/src/formal.rs @@ -684,6 +684,17 @@ pub(crate) struct FormalAssuranceClaim { pub(crate) struct FormalEvidence { kind: String, artifact: String, + /// SHA-256 of the artifact's bytes. + /// + /// Evidence used to be EXISTENCE-CHECKED only: the audit confirmed a file + /// was present at the path and read nothing. An artifact could be emptied, + /// rewritten, or replaced wholesale and every claim citing it still passed. + /// + /// Optional so entries can be introduced before their digest is recorded, + /// but once present it is verified, and `cargo xtask formal --update` + /// fills it in for every entry. + #[serde(default)] + sha256: Option, } #[derive(Deserialize)] @@ -859,6 +870,19 @@ pub(crate) fn formal_assurance_audit(formal_root: &Path, update: bool) -> Result )); } validate_mutation_witnesses(&artifact, &compiled)?; + if let Some(expected) = item.sha256.as_deref() { + let bytes = fs::read(&artifact).map_err(|error| { + format!("could not read evidence {}: {error}", artifact.display()) + })?; + let actual = hex::encode(Sha256::digest(&bytes)); + if actual != expected { + return Err(format!( + "formal claim {} cites evidence {} with digest {expected}, \ + but the artifact hashes to {actual}", + claim.lean_declaration, item.artifact + )); + } + } } let declaration = compiled.get(&claim.lean_declaration).ok_or_else(|| { @@ -1017,6 +1041,7 @@ pub(crate) fn synchronize_formal_assurance_manifest( } else { "formal/Auths/Rich/Theorems.lean".to_owned() }, + sha256: None, }], scope: if is_lifecycle { "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation.".to_owned() @@ -1047,6 +1072,7 @@ pub(crate) fn synchronize_formal_assurance_manifest( claim.evidence = vec![FormalEvidence { kind: "lean-refinement".to_owned(), artifact: "formal/Auths/Lifecycle/Refinement.lean".to_owned(), + sha256: None, }]; claim.scope = format!( "The pinned Charon/Aeneas translation of `{rust_symbol}` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index 836ebf9a..f5279db5 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 120; +const FREEZE_VERSION: u64 = 121; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -633,10 +633,10 @@ fn generate_inventory() -> Result { "bindings/wasm/auths-proof-wasm/identity-abi-v1.json" => 4, "core/fixtures/v1/manifest.json" => 4, "product/conformance/v1/simplified-product-waist.json" => 2, - "formal/assurance-manifest-v1.toml" => 9, + "formal/assurance-manifest-v1.toml" => 10, "formal/qualification/aeneas/qualification.toml" => 4, "formal/qualification/aeneas/generated" => 5, - "formal/qualification/aeneas/source-closure.json" => 16, + "formal/qualification/aeneas/source-closure.json" => 17, "product/fixtures/v1/errors/manifest.json" => 4, "product/fixtures/v1/bounded-policy/manifest.json" => 2, "product/fixtures/v1/github/manifest.json" @@ -696,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 119, + 120, FreezeClassification::ReleaseMetadata, &[ "package-names", From 48d8572133cd7200675dcf47e0ca87c36c864885 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 18:02:41 +0100 Subject: [PATCH 43/61] formal: account for every upstream sorry, and keep evidence digests fresh (UNSIGNED) Reviewer item 7, answered with evidence rather than churn, plus a real defect in what the previous commit shipped. THE SORRYS STAY, AND HERE IS WHY. The ask was to patch or locally prove the four `sorry`s in the vendored Aeneas runtime for a pristine artifact. Doing so means editing a dependency pinned by commit in translation-toolchain.lock: it breaks the pin, or requires re-pinning to a fork, and changes nothing about what is proved. Measured, not assumed -- all 139 audited declarations reduce to exactly three axioms: Classical.choice, Quot.sound, propext sorryAx: 0 So no audited theorem depends on any of them. The inventory already verified each RECORDED line still holds a `sorry`; what nothing checked was whether there were OTHERS. A new upstream `sorry` arriving with a toolchain bump would have been accepted silently while the inventory still read as a complete account. `validate_warning_inventory` now reports any unaccounted line by number. Trying to mutation-test that revealed the layering is already deeper than expected: injecting a `sorry` into vendored Aeneas was refused twice before reaching the new check -- first by the source-closure digest, then by the evidence digest added in 422d4ce. Three independent layers now catch it. A DEFECT IN 422d4ce, fixed here. The evidence digest binding was correct but unmaintainable: `source-closure.json` is regenerated by the same command that validates its digest, so every regeneration left a stale digest and a standing false alarm. It fired twice in normal operation before I noticed. `--update` now refreshes evidence digests before writing the manifest; the non-update path still verifies and refuses a mismatch. Update recomputes, check enforces, which is how the rest of the qualification already works. Verified: lake build; assurance audit PASS at 139 compiled statements; qualification cases 6/6; clean reproduction byte-identical. Co-Authored-By: Claude Opus 5 --- formal/assurance-manifest-v1.toml | 7 ++++- .../qualification/aeneas/source-closure.json | 6 ++-- release/semantic-freeze.json | 14 +++++----- xtask/src/formal.rs | 15 ++++++++++ xtask/src/formal_qualification.rs | 28 +++++++++++++++++++ xtask/src/semantic_freeze.rs | 8 +++--- 6 files changed, 63 insertions(+), 15 deletions(-) diff --git a/formal/assurance-manifest-v1.toml b/formal/assurance-manifest-v1.toml index fd8b1b22..d1a61249 100644 --- a/formal/assurance-manifest-v1.toml +++ b/formal/assurance-manifest-v1.toml @@ -4321,7 +4321,7 @@ sha256 = "8b285906cc66125575211a1fa7860459bb91a7472ccc6ab99f81f178902ff773" [[claims.evidence]] kind = "source-closure" artifact = "formal/qualification/aeneas/source-closure.json" -sha256 = "94263dd8276ab7bfbdc67f2ca3b31d1662ed66cca45c59f00d045ec98c4d6ee7" +sha256 = "c7db6aa29674dac02b7b9b1eafaf42a57b3ffde9d173732b320641c882d89519" [[claims.evidence]] kind = "lean-generated-vectors" @@ -6298,6 +6298,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-refinement" artifact = "formal/Auths/Lifecycle/Refinement.lean" +sha256 = "dd5eeb17239035d0e385be2392d944f6c1094f854744f4960e823bf0b868c7e6" [[claims]] claim_id = "AP-FORMAL-RICH-088" @@ -6350,6 +6351,7 @@ axioms = [ [[claims.evidence]] kind = "lean-refinement" artifact = "formal/Auths/Lifecycle/Refinement.lean" +sha256 = "dd5eeb17239035d0e385be2392d944f6c1094f854744f4960e823bf0b868c7e6" [[claims]] claim_id = "AP-FORMAL-RICH-089" @@ -6399,6 +6401,7 @@ axioms = [] [[claims.evidence]] kind = "lean-refinement" artifact = "formal/Auths/Lifecycle/Refinement.lean" +sha256 = "dd5eeb17239035d0e385be2392d944f6c1094f854744f4960e823bf0b868c7e6" [[claims]] claim_id = "AP-FORMAL-RICH-090" @@ -6452,6 +6455,7 @@ axioms = [ [[claims.evidence]] kind = "lean-refinement" artifact = "formal/Auths/Lifecycle/Refinement.lean" +sha256 = "dd5eeb17239035d0e385be2392d944f6c1094f854744f4960e823bf0b868c7e6" [[claims]] claim_id = "AP-FORMAL-RICH-091" @@ -6501,6 +6505,7 @@ axioms = [] [[claims.evidence]] kind = "lean-refinement" artifact = "formal/Auths/Lifecycle/Refinement.lean" +sha256 = "dd5eeb17239035d0e385be2392d944f6c1094f854744f4960e823bf0b868c7e6" [[claims]] claim_id = "AP-FORMAL-BASELINE-012" diff --git a/formal/qualification/aeneas/source-closure.json b/formal/qualification/aeneas/source-closure.json index ae9d7c8d..70c424c6 100644 --- a/formal/qualification/aeneas/source-closure.json +++ b/formal/qualification/aeneas/source-closure.json @@ -1,6 +1,6 @@ { "schema": "auths-proof-translation-source-closure/v2", - "digest": "6de1f374a8adfa42f90bbd05a2d6acbe544b36a95855ecc3776319f911b527a2", + "digest": "7b03bb763da7c07498c718fb69f8a3bcb25c51fe4feab310685be818cc9013d1", "files": [ { "path": "Cargo.lock", @@ -90,11 +90,11 @@ }, { "path": "xtask/src/formal.rs", - "sha256": "5271fd6cec2f9f1fda8ecd4d0dff4b1913e38127eefd14efde042b4379ec66a3" + "sha256": "d6b8a4e826590386179e9cf7e2f502229eceffa721ed44572b5ac469a167f040" }, { "path": "xtask/src/formal_qualification.rs", - "sha256": "217a1ef25749bd0032067e0f3f205e3f874395062e914aebf1df69d91394650d" + "sha256": "7c465ac38d869415deb551eed721bb01b4bd22874bd47054269088a645507de3" } ] } diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index 0bf8cec0..71eb8ee1 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 121, + "freezeVersion": 122, "publicSurface": { "rustRoots": [ "auths", @@ -199,7 +199,7 @@ }, { "id": "auths.frozen-bytes/formal/assurance-manifest-v1.toml", - "version": 10, + "version": 11, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -207,7 +207,7 @@ "owners": [ "formal/assurance-manifest-v1.toml" ], - "sha256": "6964c5f0dd0d6a6afb0381e1aa54cb046c6e09bede4fab47bc570b21e49d3bf6" + "sha256": "9a6c3454ad149b8702ebb1e33d5630342b0b5187c981d4d06832e02bc3da94b2" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/generated", @@ -235,7 +235,7 @@ }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/source-closure.json", - "version": 17, + "version": 18, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -243,7 +243,7 @@ "owners": [ "formal/qualification/aeneas/source-closure.json" ], - "sha256": "51de79ef6e2a9e2614fd785f0a79139a62d57dbceefe907892f3013bd39b1689" + "sha256": "185abec95406f2f224ec490c6f07e6ff42d60db1156f643779cba99954e63918" }, { "id": "auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 120, + "version": 121, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "8a838b65ce36a52684a68c4465a5135b5471ede6cf275a83b02c15e43ab5bce8" + "sha256": "07d01c0568f0391c80a11e9761b49624bc269c7fc0cfdc17401c8fa5c76c029a" } ] } diff --git a/xtask/src/formal.rs b/xtask/src/formal.rs index 1df86219..58a6a871 100644 --- a/xtask/src/formal.rs +++ b/xtask/src/formal.rs @@ -1090,6 +1090,21 @@ pub(crate) fn synchronize_formal_assurance_manifest( ); } manifest.claims = claims; + // Refresh every evidence digest before rendering. + // + // Some cited artifacts are REGENERATED by the same command that validates + // them -- `source-closure.json` most obviously -- so a digest recorded + // before regeneration is stale by construction. Recomputing here is what + // makes the binding maintainable rather than a standing false alarm; the + // non-update path still verifies and refuses a mismatch. + for claim in &mut manifest.claims { + for item in &mut claim.evidence { + let artifact = root().join(&item.artifact); + if let Ok(bytes) = fs::read(&artifact) { + item.sha256 = Some(hex::encode(Sha256::digest(&bytes))); + } + } + } let rendered = toml::to_string_pretty(manifest) .map_err(|error| format!("could not render formal assurance manifest: {error}"))?; fs::write(manifest_path, format!("{}\n", rendered.trim_end())) diff --git a/xtask/src/formal_qualification.rs b/xtask/src/formal_qualification.rs index 8464eeba..6b895a4b 100644 --- a/xtask/src/formal_qualification.rs +++ b/xtask/src/formal_qualification.rs @@ -338,6 +338,34 @@ fn validate_warning_inventory(root: &Path, qualification: &Qualification) -> Res )); } } + // The inventory confirmed each RECORDED line still holds a `sorry`. It + // did not confirm there are no OTHERS: a new upstream `sorry`, arriving + // with a toolchain bump, would have been accepted silently while the + // inventory still read as a complete account of them. + // + // The audited theorems provably depend on none of these -- all 139 + // compiled declarations reduce to `Classical.choice`, `Quot.sound` and + // `propext`, with zero `sorryAx` -- so this is not about soundness. It + // is about the inventory meaning what it says. + let present = lines + .iter() + .enumerate() + .filter(|(_, text)| text.contains("sorry")) + .map(|(index, _)| index + 1) + .collect::>(); + let inventoried: BTreeSet<_> = warning.upstream_lines.iter().copied().collect(); + let unaccounted = present + .iter() + .copied() + .filter(|line| !inventoried.contains(line)) + .collect::>(); + if !unaccounted.is_empty() { + return Err(format!( + "warning {} accounts for {:?} in {} but the file also carries \ + `sorry` at {unaccounted:?}; review and update the inventory", + warning.id, warning.upstream_lines, warning.artifact + )); + } } Ok(()) } diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index f5279db5..42ff26d5 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 121; +const FREEZE_VERSION: u64 = 122; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -633,10 +633,10 @@ fn generate_inventory() -> Result { "bindings/wasm/auths-proof-wasm/identity-abi-v1.json" => 4, "core/fixtures/v1/manifest.json" => 4, "product/conformance/v1/simplified-product-waist.json" => 2, - "formal/assurance-manifest-v1.toml" => 10, + "formal/assurance-manifest-v1.toml" => 11, "formal/qualification/aeneas/qualification.toml" => 4, "formal/qualification/aeneas/generated" => 5, - "formal/qualification/aeneas/source-closure.json" => 17, + "formal/qualification/aeneas/source-closure.json" => 18, "product/fixtures/v1/errors/manifest.json" => 4, "product/fixtures/v1/bounded-policy/manifest.json" => 2, "product/fixtures/v1/github/manifest.json" @@ -696,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 120, + 121, FreezeClassification::ReleaseMetadata, &[ "package-names", From a7bb4719125bec9a39edbdf3aaa1a507ffc5cfa3 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 18:12:08 +0100 Subject: [PATCH 44/61] demos: author the reference stack's authority offline (UNSIGNED) The installed-client e2e calls `auths-sandbox-request`, which d82d57f deleted, and asserts `issuer.create(...)` returns an authority. The node refuses that permanently: kernel.rs returns UnauthenticatedPrincipal for both `create` and `delegate`, because ProductionRequest.identity is unauthenticated bytes and there is no client authentication at that call site to require instead. The old test asserted the fail-open this branch removed. Authority in V1 originates from a trust anchor's signature and arrives INSIDE the proof, so `auths-local-authority` authors one: it derives the same anchor `auths-local-context` put into the trusted context, signs a root grant to a per-label agent principal, signs the action that agent will present, assembles a canonical proof bundle, and prints the proof and action for the client to hand to `execute` -- the only verb the node answers. The grant terms and the trusted context come from ONE source, `reference_grant_terms`. Had they drifted, the node would deny every authored proof and the failure would read as a verifier bug rather than a fixture mismatch. Two things the model enforced while building this, worth recording because both are the protocol refusing to be shortcut: * a proof bundle with no signed action is rejected (`actions.is_empty()`), so a grant alone is not a proof of anything; * the trust anchor cannot sign the action -- the agent must -- which is the delegation the demo exists to show. LOCAL FIXTURE. The anchor key derives from AUTHS_LOCAL_SEED, so anyone with the seed can author against this stack. That is the point of a self-contained demo and disqualifying anywhere else. Verified: emits a proof and action for auths.opentofu.saved-plan-apply/1 from the CI seed. The e2e rewrites that consume it are the next step, and still need a way for the SDK to carry a caller-held proof: `serviceAuthority` is module-private, so today only create and delegate can produce a ServiceAuthority and both always fail. Co-Authored-By: Claude Opus 5 --- Cargo.lock | 1 + architecture/dependency-graph.dot | 1 + architecture/dependency-graph.json | 14 ++ product/runtime/auths-node/Cargo.toml | 1 + .../src/bin/auths-local-authority.rs | 236 ++++++++++++++++++ .../runtime/auths-node/src/local_fixture.rs | 75 ++++++ release/semantic-freeze.json | 10 +- xtask/src/semantic_freeze.rs | 6 +- 8 files changed, 336 insertions(+), 8 deletions(-) create mode 100644 product/runtime/auths-node/src/bin/auths-local-authority.rs diff --git a/Cargo.lock b/Cargo.lock index 19240bfc..f3601f98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -839,6 +839,7 @@ dependencies = [ name = "auths-node" version = "1.0.0-rc.1" dependencies = [ + "auths-author", "auths-codec", "auths-did-keri", "auths-did-key", diff --git a/architecture/dependency-graph.dot b/architecture/dependency-graph.dot index 48dcf4fc..19b04653 100644 --- a/architecture/dependency-graph.dot +++ b/architecture/dependency-graph.dot @@ -288,6 +288,7 @@ digraph auths_architecture { "auths-live-service" -> "auths-proof-wasm" [label="normal"]; "auths-mcp-demo" -> "auths-apps-testkit" [label="normal"]; "auths-mcp-demo" -> "auths-proof-exchange-model" [label="normal"]; + "auths-node" -> "auths-author" [label="normal"]; "auths-node" -> "auths-codec" [label="normal"]; "auths-node" -> "auths-did-keri" [label="normal"]; "auths-node" -> "auths-did-key" [label="normal"]; diff --git a/architecture/dependency-graph.json b/architecture/dependency-graph.json index 077f5580..9ea383e5 100644 --- a/architecture/dependency-graph.json +++ b/architecture/dependency-graph.json @@ -5053,6 +5053,20 @@ "pkcs8" ] }, + { + "source": "auths-node", + "source_layer": "product", + "target": "auths-author", + "target_layer": "core", + "scope": "internal", + "kind": "normal", + "target_condition": null, + "optional": false, + "default_features": false, + "features": [ + "std" + ] + }, { "source": "auths-node", "source_layer": "product", diff --git a/product/runtime/auths-node/Cargo.toml b/product/runtime/auths-node/Cargo.toml index 1cefef08..98662e97 100644 --- a/product/runtime/auths-node/Cargo.toml +++ b/product/runtime/auths-node/Cargo.toml @@ -8,6 +8,7 @@ description = "Hardened open reference node for the Auths production contract" publish = false [dependencies] +auths-author = { workspace = true, features = ["std"] } auths-codec.workspace = true auths-did-key = { workspace = true, features = ["std"] } auths-did-keri = { workspace = true, features = ["std"] } diff --git a/product/runtime/auths-node/src/bin/auths-local-authority.rs b/product/runtime/auths-node/src/bin/auths-local-authority.rs new file mode 100644 index 00000000..d129e1ac --- /dev/null +++ b/product/runtime/auths-node/src/bin/auths-local-authority.rs @@ -0,0 +1,236 @@ +//! Authors a proof offline against the reference stack's trust anchor. +//! +//! This replaces `auths-sandbox-request`, which asked the NODE to mint an +//! authority. The node refuses that now, permanently and by design: +//! `kernel.rs` returns `UnauthenticatedPrincipal` for both `create` and +//! `delegate`, because `ProductionRequest.identity` is unauthenticated bytes +//! and there is no client authentication at that call site to require instead. +//! +//! Authority in V1 originates from a trust anchor's signature and arrives +//! INSIDE the proof. So the demo authors one here, holding the anchor key that +//! `auths-local-context` put into the trusted context, and hands the client a +//! proof plus a canonical action. The client then calls `execute` -- the only +//! verb the node will answer. +//! +//! LOCAL FIXTURE. The anchor key is derived from `AUTHS_LOCAL_SEED`, so anyone +//! holding the seed can author against this stack. That is the point of a +//! self-contained demo and disqualifying anywhere else. + +use auths_author::{prepare_action, prepare_grant}; +use auths_codec::{body_digest, encode_bundle, grant_id, plan_id}; +use auths_model::{ + ActionConstraint, BudgetAlgebraId, BudgetCeiling, BundleHeader, CriticalExtensions, + GrantStatement, PrincipalMethodId, ProofBundle, SignatureBytes, SignatureDescriptor, + SignatureSuiteId, StatusPolicy, Timestamp, ValidityWindow, VerificationMethod, +}; +use auths_model::{ + ActionEnvelope, AuthorizationPlan, Challenge, ChannelBindingId, MediaType, Permission, ProofRef, +}; +use auths_node::local_fixture::{ + SEED_ENV, anchor_principal, reference_grant_terms, reference_profile, +}; +use auths_raw_key::{RAW_KEY_V1, RawKeyDescriptor, RawKeyType}; +use auths_signature::ED25519_V1; +use base64ct::{Base64UrlUnpadded, Encoding as _}; +use ed25519_dalek::{Signer as _, SigningKey}; +use std::{ + env, fs, + process::ExitCode, + time::{SystemTime, UNIX_EPOCH}, +}; + +fn fail(message: &str) -> ExitCode { + eprintln!("auths-local-authority: {message}"); + ExitCode::from(1) +} + +fn main() -> ExitCode { + let mut arguments = env::args().skip(1); + let (Some(profile), Some(action_path), Some(agent_label)) = + (arguments.next(), arguments.next(), arguments.next()) + else { + eprintln!("usage: auths-local-authority "); + return ExitCode::from(1); + }; + if arguments.next().is_some() { + return fail("unexpected extra argument"); + } + + let Ok(encoded_seed) = env::var(SEED_ENV) else { + return fail(&format!("{SEED_ENV} is not set")); + }; + let mut seed = [0_u8; 32]; + if Base64UrlUnpadded::decode(encoded_seed.trim(), &mut seed).is_err() { + return fail(&format!("{SEED_ENV} is not 32 unpadded base64url bytes")); + } + let Ok(action) = fs::read(&action_path) else { + return fail("the action file is unavailable"); + }; + + let Ok((anchorKey, anchorPrincipal)) = anchor_principal(&seed) else { + return fail("the trust anchor could not be derived"); + }; + let Ok(profile_ref) = reference_profile(&profile) else { + return fail("that profile is not one the reference stack enables"); + }; + let Ok((permissions, audiences, assurance, action_permission, action_audience)) = + reference_grant_terms(&profile) + else { + return fail("the reference grant terms are unavailable"); + }; + let profile_ref_for_action = profile_ref.clone(); + + // The agent is a distinct principal, derived from the seed and its label so + // the demo is reproducible without shipping a second secret. + let mut agent_seed = [0_u8; 32]; + for (index, byte) in agent_label.as_bytes().iter().take(32).enumerate() { + agent_seed[index] = seed[index] ^ byte; + } + let agent_key = SigningKey::from_bytes(&agent_seed); + let Ok(agent_descriptor) = RawKeyDescriptor::new( + RawKeyType::Ed25519, + agent_key.verifying_key().to_bytes().to_vec(), + ) else { + return fail("the agent key is not a valid raw-key descriptor"); + }; + let Ok(agent_principal) = agent_descriptor.principal() else { + return fail("the agent key has no principal"); + }; + let agent_principal_for_action = agent_principal.clone(); + let agent_principal_for_descriptor = agent_principal.clone(); + + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_or(0, |duration| duration.as_secs()); + let Ok(validity) = ValidityWindow::new( + Timestamp::new(now.saturating_sub(60)), + Timestamp::new(now.saturating_add(3600)), + ) else { + return fail("the validity window is invalid"); + }; + let Ok(algebra) = BudgetAlgebraId::parse("numeric-ceiling-v1") else { + return fail("the budget algebra id is malformed"); + }; + + // Root grant: the anchor delegates to the agent, one hop, bounded. + let statement = GrantStatement::new( + anchorPrincipal.clone(), + agent_principal, + profile_ref, + permissions, + validity, + audiences, + ActionConstraint::AnyBody, + Some(BudgetCeiling::new(algebra, 1)), + 1, + None, + StatusPolicy::ExpiryOnly, + assurance, + CriticalExtensions::empty(), + ); + let (Ok(method), Ok(verification), Ok(suite)) = ( + PrincipalMethodId::parse(RAW_KEY_V1), + VerificationMethod::parse(anchorPrincipal.as_str()), + SignatureSuiteId::parse(ED25519_V1), + ) else { + return fail("the anchor signature descriptor is malformed"); + }; + let descriptor = SignatureDescriptor::new(method, verification, suite); + let Ok(request) = prepare_grant(statement, descriptor) else { + return fail("the root grant could not be prepared"); + }; + let Ok(signature) = SignatureBytes::new( + anchorKey + .sign(request.signing_preimage()) + .to_bytes() + .to_vec(), + ) else { + return fail("the anchor signature is not well formed"); + }; + let grant = request.complete(signature); + + let plan = AuthorizationPlan::proof(ProofRef::new([0x61; 32])); + // A bundle must carry at least one signed action; the agent signs the one + // the client will present. + let Ok(terminal) = grant_id(grant.statement()) else { + return fail("the root grant has no identifier"); + }; + let Ok(plan_identifier) = plan_id(&plan) else { + return fail("the authorization plan has no identifier"); + }; + let Ok(media) = MediaType::parse("application/octet-stream") else { + return fail("the media type is malformed"); + }; + let Ok(channel) = ChannelBindingId::parse("none-v1") else { + return fail("the channel binding id is malformed"); + }; + let envelope = ActionEnvelope::new( + profile_ref_for_action, + media, + body_digest(&action), + action_permission, + None, + action_audience, + Challenge::new([0x22; 32]), + validity, + agent_principal_for_action, + Some(terminal), + plan_identifier, + channel, + ProofRef::new([0x61; 32]), + Vec::new(), + CriticalExtensions::empty(), + ); + let (Ok(agent_method), Ok(agent_verification), Ok(agent_suite)) = ( + PrincipalMethodId::parse(RAW_KEY_V1), + VerificationMethod::parse(agent_principal_for_descriptor.as_str()), + SignatureSuiteId::parse(ED25519_V1), + ) else { + return fail("the agent signature descriptor is malformed"); + }; + let Ok(action_request) = prepare_action( + envelope, + SignatureDescriptor::new(agent_method, agent_verification, agent_suite), + ) else { + return fail("the action could not be prepared"); + }; + let Ok(action_signature) = SignatureBytes::new( + agent_key + .sign(action_request.signing_preimage()) + .to_bytes() + .to_vec(), + ) else { + return fail("the agent signature is not well formed"); + }; + let signed_action = action_request.complete(action_signature); + + let bundle = match ProofBundle::new( + BundleHeader::v1(), + vec![grant], + vec![signed_action], + plan, + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + Some(action.clone()), + ) { + Ok(bundle) => bundle, + Err(error) => { + return fail(&format!( + "the proof bundle could not be assembled: {error:?}" + )); + } + }; + let Ok(proof) = encode_bundle(&bundle) else { + return fail("the proof bundle could not be encoded canonically"); + }; + + println!( + "{{\"proof\":\"{}\",\"action\":\"{}\"}}", + Base64UrlUnpadded::encode_string(&proof), + Base64UrlUnpadded::encode_string(&action) + ); + ExitCode::SUCCESS +} diff --git a/product/runtime/auths-node/src/local_fixture.rs b/product/runtime/auths-node/src/local_fixture.rs index 086549f6..0852acaf 100644 --- a/product/runtime/auths-node/src/local_fixture.rs +++ b/product/runtime/auths-node/src/local_fixture.rs @@ -122,6 +122,81 @@ fn verifier_configuration() -> Result { .map_err(|_| fail("the verifier configuration could not be computed")) } +/// The permission, namespace, audience and assurance policy the trusted +/// context accepts for one reference profile. +/// +/// Exported so the authoring tool and the context are built from ONE source. +/// If these drifted apart the node would deny every authored proof, and the +/// failure would look like a verifier bug rather than a fixture mismatch. +/// +/// # Errors +/// +/// Returns [`FixtureError`] for a profile the reference stack does not enable. +pub fn reference_grant_terms( + qualified: &str, +) -> Result< + ( + PermissionSet, + AudienceSet, + AssurancePolicyId, + Permission, + Audience, + ), + FixtureError, +> { + let index = REFERENCE_PROFILES + .iter() + .position(|candidate| *candidate == qualified) + .ok_or_else(|| fail("that profile is not enabled by the reference stack"))?; + let permission = Permission::new( + CapabilityId::parse(REFERENCE_CAPABILITIES[index]) + .map_err(|_| fail("a reference capability is malformed"))?, + ResourceId::parse(REFERENCE_NAMESPACES[index]) + .map_err(|_| fail("a reference namespace is malformed"))?, + ); + let permission_value = permission.clone(); + let permissions = PermissionSet::new(vec![permission]) + .map_err(|_| fail("the reference permission set is invalid"))?; + let audience = Audience::parse(REFERENCE_AUDIENCE) + .map_err(|_| fail("the reference audience is malformed"))?; + let audience_value = audience.clone(); + let audiences = + AudienceSet::new(vec![audience]).map_err(|_| fail("the audience set is invalid"))?; + let assurance = AssurancePolicyId::parse(ASSURANCE_POLICY) + .map_err(|_| fail("the assurance policy id is malformed"))?; + Ok(( + permissions, + audiences, + assurance, + permission_value, + audience_value, + )) +} + +/// Parses one of the reference stack's enabled profiles. +/// +/// # Errors +/// +/// Returns [`FixtureError`] for any profile the reference stack does not +/// enable, so the authoring tool cannot mint authority for a vertical the +/// trusted context never accepted. +pub fn reference_profile(qualified: &str) -> Result { + if !REFERENCE_PROFILES.contains(&qualified) { + return Err(fail("that profile is not enabled by the reference stack")); + } + let (id, version) = qualified + .rsplit_once('/') + .ok_or_else(|| fail("a reference profile id carries no version"))?; + let version = version + .parse::() + .map_err(|_| fail("a reference profile version is not a number"))?; + ProfileRef::new( + ProfileId::parse(id).map_err(|_| fail("a reference profile id is malformed"))?, + version, + ) + .map_err(|_| fail("a reference profile reference is invalid")) +} + fn profiles() -> Result, FixtureError> { REFERENCE_PROFILES .iter() diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index 71eb8ee1..d5a7033d 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 122, + "freezeVersion": 123, "publicSurface": { "rustRoots": [ "auths", @@ -91,7 +91,7 @@ }, { "id": "auths.frozen-bytes/architecture/dependency-graph.json", - "version": 28, + "version": 29, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -99,7 +99,7 @@ "owners": [ "architecture/dependency-graph.json" ], - "sha256": "afe8bd81f67ebf8895e25bbeb7621de2ae42e85c27b2662ce670df4ddf7eff45" + "sha256": "1b01bb92c5a18ef1f31fce4158f57fa6e120282bd2da1708ed050f860d3cf7e5" }, { "id": "auths.frozen-bytes/bindings/wasm/auths-proof-wasm/identity-abi-v1.json", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 121, + "version": 122, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "07d01c0568f0391c80a11e9761b49624bc269c7fc0cfdc17401c8fa5c76c029a" + "sha256": "aa6df2ad8fd75c5e3f4a2883d85470f880ff809aa82b2d2bd3ec479ba803fe85" } ] } diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index 42ff26d5..74d58f8c 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 122; +const FREEZE_VERSION: u64 = 123; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -629,7 +629,7 @@ fn generate_inventory() -> Result { for (id, path) in frozen_byte_inventories()? { let version = match path.as_str() { - "architecture/dependency-graph.json" => 28, + "architecture/dependency-graph.json" => 29, "bindings/wasm/auths-proof-wasm/identity-abi-v1.json" => 4, "core/fixtures/v1/manifest.json" => 4, "product/conformance/v1/simplified-product-waist.json" => 2, @@ -696,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 121, + 122, FreezeClassification::ReleaseMetadata, &[ "package-names", From 273e9b6f72c5cf9f8306dc2940218cb7d850d525 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 18:31:14 +0100 Subject: [PATCH 45/61] bindings: let a client carry a proof it already holds (UNSIGNED) The reference-stack e2e could not be rewritten without this, and the reason is worth stating plainly: a proof-carrying SDK had no way to carry a proof INWARD. `ServiceAuthority` is deliberately opaque -- bytes in a WeakMap, `toJSON()` returns never -- and `serviceAuthority()` was module-private. The only producers were `create` and `delegate`. A node that refuses to mint answers both with `core.unauthenticated-principal`, so on the reference stack NOTHING could produce a `ServiceAuthority`. A caller holding a valid, anchor-signed proof had no way to hand it over. That was coherent under the old model, where the service was the sole source of authority. Under the model this branch established -- authority originates from a trust anchor's signature and arrives inside the proof -- its absence is a leftover. `importAuthority` (TypeScript) and `import_authority` (Python) take canonical proof bytes and return the same opaque carrier the service would have. A way in and no way out: the bytes are copied, the value stays sealed, and neither language gained a reader. Both e2e tests are rewritten against it. They no longer call the deleted `auths-sandbox-request`, and no longer assert `create` returns an authority -- that assertion was the fail-open the kernel rebuild removed. They now author offline with `auths-local-authority`, import, and call `execute`, which is the only verb the node answers. Each also asserts the replay refusal, since the claim is keyed on (proof digest, action digest) and allows one effect. Surfaces updated from actual state: TypeScript 190 -> 191 symbols, the Python API snapshot regenerated, `auths-author` declared as a core API of auths-node, architecture snapshot and semantic freeze resynced. The TypeScript shape gate still reports zero mirrored or homonymous exports. Verified: TypeScript 147 tests, Python 133 tests, compliance PASS across 68 packages and 137 claims, sdk-experience, public-naming, arch, binding-semantics, sdk-vocabulary and cross-language all green. Co-Authored-By: Claude Opus 5 --- .../workflows/open-production-reference.yml | 3 +- bindings/customer-journey-matrix-v1.json | 2 +- bindings/python/api/public-api.txt | 1 + bindings/python/python/auths/_service.py | 23 +++ bindings/python/python/auths/service.py | 2 + bindings/typescript/api/public-api.txt | 3 +- bindings/typescript/src/service.ts | 22 +++ compliance.toml | 2 +- .../tests/installed-sdk-e2e.mjs | 77 ++++++---- .../tests/test_installed_sdk.py | 137 +++++++++--------- release/semantic-freeze.json | 26 ++-- xtask/src/semantic_freeze.rs | 14 +- 12 files changed, 195 insertions(+), 117 deletions(-) diff --git a/.github/workflows/open-production-reference.yml b/.github/workflows/open-production-reference.yml index 8fa80e62..54e1cc1c 100644 --- a/.github/workflows/open-production-reference.yml +++ b/.github/workflows/open-production-reference.yml @@ -112,7 +112,8 @@ jobs: python -m venv .reference-venv .reference-venv/bin/python -m pip install maturin==1.9.6 pytest==9.0.2 pytest-asyncio==1.3.0 .reference-venv/bin/maturin build --profile python-extension --locked --manifest-path bindings/python/Cargo.toml --out target/open-reference-artifacts - cargo build --locked -p auths-node + cargo build --locked -p auths-node \ + --bin auths-node --bin auths-local-context --bin auths-local-authority - name: Run packed TypeScript client across replicas working-directory: target/open-reference-consumer env: diff --git a/bindings/customer-journey-matrix-v1.json b/bindings/customer-journey-matrix-v1.json index 63cf5398..62b4b926 100644 --- a/bindings/customer-journey-matrix-v1.json +++ b/bindings/customer-journey-matrix-v1.json @@ -7,7 +7,7 @@ "enforcement": "baseline", "baseline": { "typescriptEntryPoints": 8, - "typescriptPublicSymbols": 190, + "typescriptPublicSymbols": 191, "pythonModules": 8, "pythonPublicSymbols": 168, "maintainedTypescriptRecipes": 5, diff --git a/bindings/python/api/public-api.txt b/bindings/python/api/public-api.txt index 279d6bce..97fa8764 100644 --- a/bindings/python/api/public-api.txt +++ b/bindings/python/api/public-api.txt @@ -104,6 +104,7 @@ ServiceTransportResponse ServiceVerificationResult ServiceVerified create_service_client +import_authority [auths.profiles] DevelopmentMcpProvider diff --git a/bindings/python/python/auths/_service.py b/bindings/python/python/auths/_service.py index 925eee9b..2d4db214 100644 --- a/bindings/python/python/auths/_service.py +++ b/bindings/python/python/auths/_service.py @@ -79,6 +79,29 @@ def __reduce__(self) -> NoReturn: raise TypeError("Auths authority is opaque") +def import_authority(proof: bytes) -> ServiceAuthority: + """Carry an authority the caller already holds. + + Authority in V1 originates from a trust anchor's signature and arrives + inside the proof; the service does not mint it. Until now the only + producers of a :class:`ServiceAuthority` were ``create`` and ``delegate``, + and a node that refuses to mint answers both with + ``core.unauthenticated-principal``. A caller holding a valid proof had no + way to give it to this SDK -- a proof-carrying client that could not carry + a proof inward. + + ``proof`` is a canonical proof bundle. It is copied and held opaquely, + exactly as an authority returned by the service would be, so this adds a + way IN and no way out. + + :raises TypeError: when ``proof`` is empty. + """ + + if not proof: + raise TypeError("an authority proof cannot be empty") + return ServiceAuthority(_AUTHORITY_TOKEN, proof) + + class ServiceReceipt: __slots__ = ("_bytes",) kind: Literal["receipt"] = "receipt" diff --git a/bindings/python/python/auths/service.py b/bindings/python/python/auths/service.py index d4198020..d49a5de6 100644 --- a/bindings/python/python/auths/service.py +++ b/bindings/python/python/auths/service.py @@ -20,6 +20,7 @@ from ._service import ( NextCall, ServiceAuthority, + import_authority, ServiceAuthorityResult, ServiceClient, ServiceCompleted, @@ -47,6 +48,7 @@ "RecommendedAction", "RetryClass", "ServiceAuthority", + "import_authority", "ServiceAuthorityResult", "ServiceClient", "ServiceCompleted", diff --git a/bindings/typescript/api/public-api.txt b/bindings/typescript/api/public-api.txt index 7eb65f30..8d670a73 100644 --- a/bindings/typescript/api/public-api.txt +++ b/bindings/typescript/api/public-api.txt @@ -1,5 +1,5 @@ # Installed @auths-dev/sdk public API v1 -# declaration-sha256 2ae2287043286afa59c3a5931ffa9801c38f53fb2b8fc36aa1fe3209512c94f1 +# declaration-sha256 507c68077876e8b7453f1116df41503e86e769e2dd4f95b6771236271e7cda93 . Actor type . approval value . ApprovalPolicy type @@ -88,6 +88,7 @@ ./verify verifyReceipt value ./service createServiceClient value ./service githubIssueAddress value +./service importAuthority value ./service NextCall type ./service opentofuSavedPlanApply value ./service postgresqlBoundedUpdate value diff --git a/bindings/typescript/src/service.ts b/bindings/typescript/src/service.ts index 893d043c..10ac17d8 100644 --- a/bindings/typescript/src/service.ts +++ b/bindings/typescript/src/service.ts @@ -504,6 +504,28 @@ function projectIndeterminate(verb: ProductVerb, projection: NativeProjection): return Object.freeze({ kind: "indeterminate", verb, code: requiredCode(projection), retry: projection.retry }); } +/** + * Carries an authority the caller already holds. + * + * Authority in V1 originates from a trust anchor's signature and arrives inside + * the proof; the service does not mint it. Until now the only producers of a + * `ServiceAuthority` were {@link ServiceClient.create} and + * {@link ServiceClient.delegate}, and a node that refuses to mint answers both + * with `core.unauthenticated-principal`. So a caller holding a perfectly valid + * proof had no way to give it to this SDK -- a proof-carrying client that could + * not carry a proof inward. + * + * The bytes are a canonical proof bundle. They are copied and held opaquely, + * exactly as an authority returned by the service would be, so this adds a way + * IN and no way out. + * + * @throws TypeError when the bytes are empty. + */ +export function importAuthority(proof: Uint8Array): ServiceAuthority { + if (proof.length === 0) throw new TypeError("an authority proof cannot be empty"); + return new ServiceAuthorityValue(proof); +} + function serviceAuthority(bytes: Uint8Array): ServiceAuthority { if (bytes.length === 0) throw new TypeError("native response omitted authority bytes"); return new ServiceAuthorityValue(bytes); diff --git a/compliance.toml b/compliance.toml index f10ee7b7..ed584bcd 100644 --- a/compliance.toml +++ b/compliance.toml @@ -1320,7 +1320,7 @@ independent-semantic-implementation = [ kind = "cargo" layer = "product" path = "product/runtime/auths-node" -core_apis = ["auths-codec", "auths-did-keri", "auths-did-key", "auths-did-web", "auths-hsm-attested", "auths-model", "auths-ports", "auths-raw-key", "auths-registries", "auths-signature", "auths-spiffe-x509", "auths-testkit", "auths-verifier", "auths-webauthn"] +core_apis = ["auths-author", "auths-codec", "auths-did-keri", "auths-did-key", "auths-did-web", "auths-hsm-attested", "auths-model", "auths-ports", "auths-raw-key", "auths-registries", "auths-signature", "auths-spiffe-x509", "auths-testkit", "auths-verifier", "auths-webauthn"] protocol_versions = ["auths-proof/v1", "auths.production-client/1"] wire_objects = ["CanonicalAction", "ProductionRequest", "ProductionResponse", "VerifierContext"] fixture_suites = ["core/fixtures/v1", "product/fixtures/v1/production-client"] diff --git a/demos/open-production-reference/tests/installed-sdk-e2e.mjs b/demos/open-production-reference/tests/installed-sdk-e2e.mjs index f68d5758..7ebf0fb3 100644 --- a/demos/open-production-reference/tests/installed-sdk-e2e.mjs +++ b/demos/open-production-reference/tests/installed-sdk-e2e.mjs @@ -1,9 +1,22 @@ +// Exercises the packed TypeScript SDK against the reference stack. +// +// The previous version called `auths-sandbox-request` and asserted +// `issuer.create(...)` returned an authority. Both are gone, and deliberately: +// the node answers `create` and `delegate` with `core.unauthenticated-principal` +// because `ProductionRequest.identity` is unauthenticated bytes and there is no +// client authentication at that call site to require instead. That test +// asserted the fail-open the kernel rebuild removed. +// +// Authority originates from a trust anchor's signature and arrives inside the +// proof. `auths-local-authority` authors one offline against the same anchor +// the trusted context carries; the client imports it and calls `execute`, the +// only verb the node answers. import assert from "node:assert/strict"; import { execFileSync } from "node:child_process"; import { mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { createAuths } from "@auths-dev/sdk"; +import { createServiceClient, importAuthority } from "@auths-dev/sdk/service"; import { githubIssueAddress, opentofuSavedPlanApply, @@ -13,48 +26,56 @@ import { const directory = mkdtempSync(join(tmpdir(), "auths-reference-")); const decode = (value) => Uint8Array.from(Buffer.from(value, "base64url")); const endpoint = process.env.AUTHS_REFERENCE_ENDPOINT ?? "https://localhost:8443"; + const profiles = [ ["opentofu", opentofuSavedPlanApply()], ["postgresql", postgresqlBoundedUpdate()], ["github", githubIssueAddress()], ]; + for (const [name, profile] of profiles) { const actionPath = join(directory, `${name}.bin`); writeFileSync(actionPath, `exact ${name} operation`); - const generated = JSON.parse(execFileSync("auths-sandbox-request", [actionPath], {encoding: "utf8"})); - const identity = new TextEncoder().encode(`reference-${name}-human`); - const agent = new TextEncoder().encode(`reference-${name}-agent`); - const issuer = createAuths({endpoint, identity, profile}); - const authority = await issuer.create(decode(generated.request)); + const authored = JSON.parse( + execFileSync("auths-local-authority", [profile.id, actionPath, `reference-${name}-agent`], { + encoding: "utf8", + }), + ); + + const authority = importAuthority(decode(authored.proof)); assert.equal(authority.kind, "authority"); - const delegator = createAuths({endpoint, identity, profile}); - const delegated = await delegator.delegate(authority, agent, decode(generated.attenuation)); - assert.equal(delegated.kind, "authority"); - const agentClient = createAuths({endpoint, identity: agent, profile}); - const completed = await agentClient.execute(delegated, decode(generated.action)); + + const client = createServiceClient({ endpoint, profile }); + const completed = await client.execute(authority, decode(authored.action)); assert.equal(completed.kind, "completed"); - const verifier = createAuths({endpoint, identity: agent, profile}); - assert.equal((await verifier.verify(completed.receipt)).kind, "verified"); - assert.equal((await verifier.execute(delegated, decode(generated.action))).kind, "denied"); + + const verified = await client.verify(completed.receipt); + assert.equal(verified.kind, "verified"); + + // The claim is keyed on (proof digest, action digest) and allows one effect, + // so replaying the identical pair is refused rather than repeated. + const replayed = await client.execute(authority, decode(authored.action)); + assert.equal(replayed.kind, "denied"); } +// A body marked recoverable leaves the effect outcome unknown, and resuming +// resolves it. `Indeterminate` exists precisely so a receipt can say so instead +// of asserting a failure that may have applied. const recoverPath = join(directory, "recover.bin"); writeFileSync(recoverPath, "AUTHS-SANDBOX-RECOVER issue 104"); -const recovery = JSON.parse(execFileSync("auths-sandbox-request", [recoverPath], {encoding: "utf8"})); const recoveryProfile = githubIssueAddress(); -const recoveryIdentity = new TextEncoder().encode("reference-recovery-human"); -const recoveryAgent = new TextEncoder().encode("reference-recovery-agent"); -const issuer = createAuths({endpoint, identity: recoveryIdentity, profile: recoveryProfile}); -const delegator = createAuths({endpoint, identity: recoveryIdentity, profile: recoveryProfile}); -const recoveryAuthority = await issuer.create(decode(recovery.request)); -const recoveryDelegated = await delegator.delegate( - recoveryAuthority, - recoveryAgent, - decode(recovery.attenuation), +const recovery = JSON.parse( + execFileSync("auths-local-authority", [recoveryProfile.id, recoverPath, "reference-recovery-agent"], { + encoding: "utf8", + }), +); +const recoveryClient = createServiceClient({ endpoint, profile: recoveryProfile }); +const unknown = await recoveryClient.execute( + importAuthority(decode(recovery.proof)), + decode(recovery.action), ); -const agentClient = createAuths({endpoint, identity: recoveryAgent, profile: recoveryProfile}); -const unknown = await agentClient.execute(recoveryDelegated, decode(recovery.action)); assert.equal(unknown.kind, "recoverable"); -const verifier = createAuths({endpoint, identity: recoveryAgent, profile: recoveryProfile}); -const resumed = await verifier.resume(unknown.reference); +const resumed = await recoveryClient.resume(unknown.reference); assert.equal(resumed.kind, "completed"); + +console.log("installed TypeScript SDK: reference stack contract satisfied"); diff --git a/demos/open-production-reference/tests/test_installed_sdk.py b/demos/open-production-reference/tests/test_installed_sdk.py index a6663c8a..2ce91957 100644 --- a/demos/open-production-reference/tests/test_installed_sdk.py +++ b/demos/open-production-reference/tests/test_installed_sdk.py @@ -1,3 +1,18 @@ +"""Exercises the built Python wheel against the reference stack. + +The previous version called ``auths-sandbox-request`` and asserted +``create`` returned an authority. Both are gone, and deliberately: the node +answers ``create`` and ``delegate`` with ``core.unauthenticated-principal`` +because ``ProductionRequest.identity`` is unauthenticated bytes and there is no +client authentication at that call site to require instead. That test asserted +the fail-open the kernel rebuild removed. + +Authority originates from a trust anchor's signature and arrives inside the +proof. ``auths-local-authority`` authors one offline against the same anchor the +trusted context carries; the client imports it and calls ``execute``, the only +verb the node answers. +""" + import base64 import json import os @@ -6,17 +21,32 @@ import pytest -from auths import create_auths from auths.profiles import ( github_issue_address, opentofu_saved_plan_apply, postgresql_bounded_update, ) +from auths.service import create_service_client, import_authority + + +def _decode(value: str) -> bytes: + return base64.urlsafe_b64decode(value + "=" * (-len(value) % 4)) + + +def _author(profile_id: str, body: bytes, agent: str) -> dict: + with tempfile.NamedTemporaryFile() as action: + action.write(body) + action.flush() + return json.loads( + subprocess.check_output( + ["auths-local-authority", profile_id, action.name, agent], + text=True, + ) + ) @pytest.mark.asyncio async def test_installed_python_completes_the_same_reference_flow(): - decode = lambda value: base64.urlsafe_b64decode(value + "=" * (-len(value) % 4)) endpoint = os.environ.get("AUTHS_REFERENCE_ENDPOINT", "https://localhost:8443") profiles = ( ("opentofu", opentofu_saved_plan_apply()), @@ -24,73 +54,50 @@ async def test_installed_python_completes_the_same_reference_flow(): ("github", github_issue_address()), ) for name, profile in profiles: - with tempfile.NamedTemporaryFile() as action: - action.write(f"exact {name} operation".encode()) - action.flush() - generated = json.loads( - subprocess.check_output( - ["auths-sandbox-request", action.name], text=True - ) - ) - human_identity = f"reference-python-{name}-human".encode() - agent_identity = f"reference-python-{name}-agent".encode() - human = create_auths(endpoint=endpoint, identity=human_identity, profile=profile) - authority = await human.create(decode(generated["request"])) - assert authority.kind == "authority" - delegator = create_auths( - endpoint=endpoint, identity=human_identity, profile=profile - ) - delegated = await delegator.delegate( - authority, agent_identity, decode(generated["attenuation"]) + authored = _author( + profile.id, + f"exact {name} operation".encode(), + f"reference-python-{name}-agent", ) - assert delegated.kind == "authority" - agent = create_auths(endpoint=endpoint, identity=agent_identity, profile=profile) - completed = await agent.execute(delegated, decode(generated["action"])) + + authority = import_authority(_decode(authored["proof"])) + assert authority.kind == "authority" + + client = create_service_client(endpoint=endpoint, profile=profile) + completed = await client.execute(authority, _decode(authored["action"])) assert completed.kind == "completed" - verifier = create_auths( - endpoint=endpoint, identity=agent_identity, profile=profile - ) - assert (await verifier.verify(completed.receipt)).kind == "verified" - assert (await verifier.execute(delegated, decode(generated["action"]))).kind == "denied" - with tempfile.NamedTemporaryFile() as action: - action.write(b"AUTHS-SANDBOX-RECOVER issue 104") - action.flush() - recovery = json.loads( - subprocess.check_output( - ["auths-sandbox-request", action.name], text=True - ) - ) - recovery_profile = github_issue_address() - recovery_human = create_auths( - endpoint=endpoint, - identity=b"reference-python-recovery-human", - profile=recovery_profile, - ) - recovery_delegator = create_auths( - endpoint=endpoint, - identity=b"reference-python-recovery-human", - profile=recovery_profile, - ) - recovery_authority = await recovery_human.create(decode(recovery["request"])) - recovery_delegated = await recovery_delegator.delegate( - recovery_authority, - b"reference-python-recovery-agent", - decode(recovery["attenuation"]), - ) - recovery_agent = create_auths( - endpoint=endpoint, - identity=b"reference-python-recovery-agent", - profile=recovery_profile, + verified = await client.verify(completed.receipt) + assert verified.kind == "verified" + + # The claim is keyed on (proof digest, action digest) and allows one + # effect, so replaying the identical pair is refused, not repeated. + replayed = await client.execute(authority, _decode(authored["action"])) + assert replayed.kind == "denied" + + +@pytest.mark.asyncio +async def test_installed_python_resolves_an_unknown_effect(): + """A recoverable body leaves the outcome unknown; resuming resolves it. + + ``Indeterminate`` exists precisely so a signed receipt can say the effect + state is unknown rather than assert a failure that may have applied. + """ + + endpoint = os.environ.get("AUTHS_REFERENCE_ENDPOINT", "https://localhost:8443") + profile = github_issue_address() + authored = _author( + profile.id, + b"AUTHS-SANDBOX-RECOVER issue 104", + "reference-python-recovery-agent", ) - unknown = await recovery_agent.execute( - recovery_delegated, decode(recovery["action"]) + + client = create_service_client(endpoint=endpoint, profile=profile) + unknown = await client.execute( + import_authority(_decode(authored["proof"])), + _decode(authored["action"]), ) assert unknown.kind == "recoverable" - recovery_verifier = create_auths( - endpoint=endpoint, - identity=b"reference-python-recovery-agent", - profile=recovery_profile, - ) - resumed = await recovery_verifier.resume(unknown.reference) + + resumed = await client.resume(unknown.reference) assert resumed.kind == "completed" diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index d5a7033d..ab3fffbc 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 123, + "freezeVersion": 124, "publicSurface": { "rustRoots": [ "auths", @@ -559,7 +559,7 @@ }, { "id": "auths.identity.protocol", - "version": 29, + "version": 30, "classification": "frozen-meaning", "categories": [ "identity-protocol-versions", @@ -582,7 +582,7 @@ "core/fixtures/identity/v1/vectors.json", "core/spec/identity/v1" ], - "sha256": "8e067c614c52645fef6695a246b708c2e88b9c2c9c0c5a5fdb5f6d5b25a308a0" + "sha256": "e0310136be5fb0cbfd6c13768a901c5cdc9a9632766c616ba4ad47eca6c8a512" }, { "id": "auths.modular-components", @@ -620,7 +620,7 @@ }, { "id": "auths.portable-abi-bindings", - "version": 51, + "version": 52, "classification": "frozen-meaning", "categories": [ "portable-abi", @@ -637,7 +637,7 @@ "core/crates/auths-model/src/lib.rs", "core/spec/v1/auths-proof.cddl" ], - "sha256": "c6dbb975797a1778ca9992b473266912026f11b938d115ae633a6441dd5b1af3" + "sha256": "a3962aa28a0e79beed7366b0c266bc9cd02955693835569b00fc7da411bd81c2" }, { "id": "auths.product.bounded-domains", @@ -762,7 +762,7 @@ }, { "id": "auths.product.facade", - "version": 10, + "version": 11, "classification": "frozen-meaning", "categories": [ "create", @@ -781,7 +781,7 @@ "bindings/typescript/src/profiles/mcp/index.ts", "bindings/typescript/src/service.ts" ], - "sha256": "c69f25e26bab731a22c4f8be8916d6eb32ff4e5426275f89dcf06abec24cefb3" + "sha256": "bba6b96f65a4615b9afc09502fd6de2b55186502c6de135ffcb97c766951e4a4" }, { "id": "auths.product.lifecycle", @@ -897,7 +897,7 @@ }, { "id": "auths.product.public-sdk-contract", - "version": 42, + "version": 43, "classification": "frozen-meaning", "categories": [ "rust-sdk-contract", @@ -915,7 +915,7 @@ "product/runtime/auths-runtime/src", "product/sdk/auths-sdk/src" ], - "sha256": "37c371e7c2bb7869c03e27fd7ec70f69afeb363416e7897f70e91775729dd137" + "sha256": "c3fed7eab6dffb763afaa2983f2877de63c3f81edc2fb121312d0ce42fa7ed3a" }, { "id": "auths.product.receipts", @@ -1013,7 +1013,7 @@ }, { "id": "auths.release.evolution-contract", - "version": 14, + "version": 15, "classification": "frozen-meaning", "categories": [ "version-axes", @@ -1034,11 +1034,11 @@ "release/fixtures/evolution", "xtask/src/evolution_policy.rs" ], - "sha256": "499ea44085fed0e1b66daaac9b7fae768128c30a9338e6d3e5c5a99ae51512eb" + "sha256": "4a04dd12e619f95e98881b2bc259f36d4a203778c227d8d8b3e394dd03fd174d" }, { "id": "auths.release.public-surface", - "version": 122, + "version": 123, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "aa6df2ad8fd75c5e3f4a2883d85470f880ff809aa82b2d2bd3ec479ba803fe85" + "sha256": "ae35b7e2e2a06a04af3042bf00e8f834874bdfd58a14122d87ef16946253b704" } ] } diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index 74d58f8c..3a347979 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 123; +const FREEZE_VERSION: u64 = 124; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -187,7 +187,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.identity.protocol", - 29, + 30, FreezeClassification::FrozenMeaning, &[ "identity-protocol-versions", @@ -246,7 +246,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.portable-abi-bindings", - 51, + 52, FreezeClassification::FrozenMeaning, &["portable-abi", "authoring-abi", "binding-contracts"], vec![ @@ -262,7 +262,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.public-sdk-contract", - 42, + 43, FreezeClassification::FrozenMeaning, &[ "rust-sdk-contract", @@ -325,7 +325,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.facade", - 10, + 11, FreezeClassification::FrozenMeaning, &[ "create", @@ -589,7 +589,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.release.evolution-contract", - 14, + 15, FreezeClassification::FrozenMeaning, &[ "version-axes", @@ -696,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 122, + 123, FreezeClassification::ReleaseMetadata, &[ "package-names", From f3fe4ee13209afcb66a08b78e45d49a96e062321 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 18:37:36 +0100 Subject: [PATCH 46/61] demos: pass the required identity to the reference clients (UNSIGNED) The rewritten e2e dropped `identity` from `createServiceClient`, which is required, so both clients threw before reaching the node: TypeError: Auths identity bytes are outside production bounds at new ServiceClientValue Each client now carries the agent label it authored under, so the identity the transport sends matches the principal the proof delegates to. Co-Authored-By: Claude Opus 5 --- .../tests/installed-sdk-e2e.mjs | 13 +++++++++++-- .../tests/test_installed_sdk.py | 12 ++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/demos/open-production-reference/tests/installed-sdk-e2e.mjs b/demos/open-production-reference/tests/installed-sdk-e2e.mjs index 7ebf0fb3..c11e657d 100644 --- a/demos/open-production-reference/tests/installed-sdk-e2e.mjs +++ b/demos/open-production-reference/tests/installed-sdk-e2e.mjs @@ -24,6 +24,7 @@ import { } from "@auths-dev/sdk/profiles"; const directory = mkdtempSync(join(tmpdir(), "auths-reference-")); +const encode = (value) => new TextEncoder().encode(value); const decode = (value) => Uint8Array.from(Buffer.from(value, "base64url")); const endpoint = process.env.AUTHS_REFERENCE_ENDPOINT ?? "https://localhost:8443"; @@ -45,7 +46,11 @@ for (const [name, profile] of profiles) { const authority = importAuthority(decode(authored.proof)); assert.equal(authority.kind, "authority"); - const client = createServiceClient({ endpoint, profile }); + const client = createServiceClient({ + endpoint, + identity: encode(`reference-${name}-agent`), + profile, + }); const completed = await client.execute(authority, decode(authored.action)); assert.equal(completed.kind, "completed"); @@ -69,7 +74,11 @@ const recovery = JSON.parse( encoding: "utf8", }), ); -const recoveryClient = createServiceClient({ endpoint, profile: recoveryProfile }); +const recoveryClient = createServiceClient({ + endpoint, + identity: encode("reference-recovery-agent"), + profile: recoveryProfile, +}); const unknown = await recoveryClient.execute( importAuthority(decode(recovery.proof)), decode(recovery.action), diff --git a/demos/open-production-reference/tests/test_installed_sdk.py b/demos/open-production-reference/tests/test_installed_sdk.py index 2ce91957..73afc1ed 100644 --- a/demos/open-production-reference/tests/test_installed_sdk.py +++ b/demos/open-production-reference/tests/test_installed_sdk.py @@ -63,7 +63,11 @@ async def test_installed_python_completes_the_same_reference_flow(): authority = import_authority(_decode(authored["proof"])) assert authority.kind == "authority" - client = create_service_client(endpoint=endpoint, profile=profile) + client = create_service_client( + endpoint=endpoint, + identity=f"reference-python-{name}-agent".encode(), + profile=profile, + ) completed = await client.execute(authority, _decode(authored["action"])) assert completed.kind == "completed" @@ -92,7 +96,11 @@ async def test_installed_python_resolves_an_unknown_effect(): "reference-python-recovery-agent", ) - client = create_service_client(endpoint=endpoint, profile=profile) + client = create_service_client( + endpoint=endpoint, + identity=b"reference-python-recovery-agent", + profile=profile, + ) unknown = await client.execute( import_authority(_decode(authored["proof"])), _decode(authored["action"]), From aa2669c5535ec7f766165d875bece31c81adea05 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 18:56:20 +0100 Subject: [PATCH 47/61] demos: make the authored proof actually verify (UNSIGNED) The rewritten e2e reached the node and was DENIED. Three real defects behind that, each found by running the verifier rather than by reading code. 1. NO EVIDENCE, NO BINDINGS. The bundle carried a signed grant and nothing linking it to the key that signed it. Added raw-key evidence for the anchor and the agent, and control bindings tying the grant and the action to them. 2. THE NODE DECODES A CANONICAL ACTION, not the raw body. The tool emitted the file's bytes, so `decode_canonical_action` answered Malformed before any authorization decision was reached. 3. `Denied(BudgetCeilingExceeded)` -- and this one is this branch's own fix working. The grant carried a bounded ceiling while the action declared no budget, which is precisely the case changed from vacuously-covered to denied: an action that states no bound on what it may spend is the authority a ceiling exists to refuse. The demo now declares what it intends to spend. The third is worth keeping in mind. The fix denied my own proof, correctly, and the only way to discover that was to run it. `tests/local_fixture_authorizes.rs` closes the loop permanently: it authors a proof for each of the three reference profiles and requires the real verifier to return Authorized against the generated context. The context and the authoring tool are derived from one seed through `reference_grant_terms`, but that is a claim about the code, not a check on the result -- and every way the two can drift produces the same symptom, a node denying everything, which reads as a verifier bug rather than a fixture mismatch. Verified: VERIFY: Authorized for opentofu, postgresql and github; the test passes for all three. Co-Authored-By: Claude Opus 5 --- .../src/bin/auths-local-authority.rs | 83 +++++++++++++++++-- .../tests/local_fixture_authorizes.rs | 74 +++++++++++++++++ 2 files changed, 150 insertions(+), 7 deletions(-) create mode 100644 product/runtime/auths-node/tests/local_fixture_authorizes.rs diff --git a/product/runtime/auths-node/src/bin/auths-local-authority.rs b/product/runtime/auths-node/src/bin/auths-local-authority.rs index d129e1ac..4459d1a6 100644 --- a/product/runtime/auths-node/src/bin/auths-local-authority.rs +++ b/product/runtime/auths-node/src/bin/auths-local-authority.rs @@ -17,19 +17,22 @@ //! self-contained demo and disqualifying anywhere else. use auths_author::{prepare_action, prepare_grant}; -use auths_codec::{body_digest, encode_bundle, grant_id, plan_id}; +use auths_codec::{ + action_id, body_digest, encode_bundle, encode_canonical_action, evidence_id, grant_id, plan_id, +}; use auths_model::{ ActionConstraint, BudgetAlgebraId, BudgetCeiling, BundleHeader, CriticalExtensions, GrantStatement, PrincipalMethodId, ProofBundle, SignatureBytes, SignatureDescriptor, SignatureSuiteId, StatusPolicy, Timestamp, ValidityWindow, VerificationMethod, }; use auths_model::{ - ActionEnvelope, AuthorizationPlan, Challenge, ChannelBindingId, MediaType, Permission, ProofRef, + ActionEnvelope, AuthorizationPlan, CanonicalAction, Challenge, ChannelBindingId, + ControlBinding, EvidenceId, EvidenceObject, EvidenceTypeId, MediaType, ProofRef, StatementRef, }; use auths_node::local_fixture::{ SEED_ENV, anchor_principal, reference_grant_terms, reference_profile, }; -use auths_raw_key::{RAW_KEY_V1, RawKeyDescriptor, RawKeyType}; +use auths_raw_key::{RAW_KEY_MEDIA_TYPE, RAW_KEY_V1, RawKeyDescriptor, RawKeyType}; use auths_signature::ED25519_V1; use base64ct::{Base64UrlUnpadded, Encoding as _}; use ed25519_dalek::{Signer as _, SigningKey}; @@ -79,6 +82,8 @@ fn main() -> ExitCode { return fail("the reference grant terms are unavailable"); }; let profile_ref_for_action = profile_ref.clone(); + let profile_ref_for_canonical = profile_ref.clone(); + let action_permission_for_canonical = action_permission.clone(); // The agent is a distinct principal, derived from the seed and its label so // the demo is reproducible without shipping a second secret. @@ -111,6 +116,11 @@ fn main() -> ExitCode { let Ok(algebra) = BudgetAlgebraId::parse("numeric-ceiling-v1") else { return fail("the budget algebra id is malformed"); }; + // The action DECLARES a budget within the grant's ceiling. An absent + // request beneath a bounded ceiling is denied -- an action that states no + // bound on what it may spend is exactly the authority a ceiling exists to + // refuse -- so the demo has to say what it intends to spend. + let requested_budget = Some(BudgetCeiling::new(algebra.clone(), 1)); // Root grant: the anchor delegates to the agent, one hop, bounded. let statement = GrantStatement::new( @@ -128,6 +138,12 @@ fn main() -> ExitCode { assurance, CriticalExtensions::empty(), ); + let Ok(anchor_raw_descriptor) = RawKeyDescriptor::new( + RawKeyType::Ed25519, + anchorKey.verifying_key().to_bytes().to_vec(), + ) else { + return fail("the anchor key is not a valid raw-key descriptor"); + }; let (Ok(method), Ok(verification), Ok(suite)) = ( PrincipalMethodId::parse(RAW_KEY_V1), VerificationMethod::parse(anchorPrincipal.as_str()), @@ -161,6 +177,7 @@ fn main() -> ExitCode { let Ok(media) = MediaType::parse("application/octet-stream") else { return fail("the media type is malformed"); }; + let media_for_canonical = media.clone(); let Ok(channel) = ChannelBindingId::parse("none-v1") else { return fail("the channel binding id is malformed"); }; @@ -169,7 +186,7 @@ fn main() -> ExitCode { media, body_digest(&action), action_permission, - None, + requested_budget.clone(), action_audience, Challenge::new([0x22; 32]), validity, @@ -204,13 +221,51 @@ fn main() -> ExitCode { }; let signed_action = action_request.complete(action_signature); + // Evidence binds each signature to the key that produced it. Without it the + // verifier has a signed grant and no way to check who signed it, and denies. + let evidence_for = |descriptor: &RawKeyDescriptor| -> Option { + let evidence_type = EvidenceTypeId::parse(RAW_KEY_V1).ok()?; + let media = MediaType::parse(RAW_KEY_MEDIA_TYPE).ok()?; + let unaddressed = EvidenceObject::new( + EvidenceId::new([0; 32]), + evidence_type.clone(), + media.clone(), + descriptor.encode(), + ) + .ok()?; + EvidenceObject::new( + evidence_id(&unaddressed).ok()?, + evidence_type, + media, + unaddressed.bytes().to_vec(), + ) + .ok() + }; + let (Some(anchor_evidence), Some(agent_evidence)) = ( + evidence_for(&anchor_raw_descriptor), + evidence_for(&agent_descriptor), + ) else { + return fail("the key evidence could not be assembled"); + }; + let (Ok(grant_binding), Ok(action_binding)) = ( + ControlBinding::new(StatementRef::Grant(terminal), vec![anchor_evidence.id()]), + action_id(signed_action.envelope()) + .map_err(|_| ()) + .and_then(|id| { + ControlBinding::new(StatementRef::Action(id), vec![agent_evidence.id()]) + .map_err(|_| ()) + }), + ) else { + return fail("the control bindings could not be assembled"); + }; + let bundle = match ProofBundle::new( BundleHeader::v1(), vec![grant], vec![signed_action], plan, - Vec::new(), - Vec::new(), + vec![anchor_evidence, agent_evidence], + vec![grant_binding, action_binding], Vec::new(), Vec::new(), Vec::new(), @@ -227,10 +282,24 @@ fn main() -> ExitCode { return fail("the proof bundle could not be encoded canonically"); }; + // The node decodes a CANONICAL ACTION, not the raw body, so emit that. + let Ok(canonical) = CanonicalAction::new( + profile_ref_for_canonical, + media_for_canonical, + action.clone(), + action_permission_for_canonical, + requested_budget, + ) else { + return fail("the canonical action could not be assembled"); + }; + let Ok(canonical_bytes) = encode_canonical_action(&canonical) else { + return fail("the canonical action could not be encoded"); + }; + println!( "{{\"proof\":\"{}\",\"action\":\"{}\"}}", Base64UrlUnpadded::encode_string(&proof), - Base64UrlUnpadded::encode_string(&action) + Base64UrlUnpadded::encode_string(&canonical_bytes) ); ExitCode::SUCCESS } diff --git a/product/runtime/auths-node/tests/local_fixture_authorizes.rs b/product/runtime/auths-node/tests/local_fixture_authorizes.rs new file mode 100644 index 00000000..bd7793eb --- /dev/null +++ b/product/runtime/auths-node/tests/local_fixture_authorizes.rs @@ -0,0 +1,74 @@ +//! The offline-authored proof must verify against the generated context. +//! +//! `auths-local-context` builds the trusted context the reference stack runs +//! with, and `auths-local-authority` authors a proof against the same anchor. +//! Nothing checked that the two agree. They are derived from one seed through +//! `reference_grant_terms`, but "derived from one source" is a claim about the +//! code, not a check on the result -- and every way this can drift produces the +//! same symptom, a node denying every request, which reads as a verifier bug. +//! +//! This test closes that loop by running the real verifier. + +use auths_node::local_fixture::build_context; +use std::{ + process::Command, + time::{SystemTime, UNIX_EPOCH}, +}; + +const SEED_B64: &str = "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE"; + +fn authored(profile: &str, body: &str, agent: &str) -> (Vec, Vec) { + use base64ct::{Base64UrlUnpadded, Encoding as _}; + let action_path = std::env::temp_dir().join(format!("auths-fixture-{agent}.bin")); + std::fs::write(&action_path, body).expect("action body"); + let binary = env!("CARGO_BIN_EXE_auths-local-authority"); + let output = Command::new(binary) + .args([profile, action_path.to_str().expect("path"), agent]) + .env("AUTHS_LOCAL_SEED", SEED_B64) + .output() + .expect("author"); + assert!( + output.status.success(), + "authoring failed: {}", + String::from_utf8_lossy(&output.stderr) + ); + let text = String::from_utf8(output.stdout).expect("utf8"); + let field = |name: &str| { + let encoded = text + .split(&format!("\"{name}\":\"")) + .nth(1) + .and_then(|rest| rest.split('"').next()) + .expect("field"); + Base64UrlUnpadded::decode_vec(encoded).expect("base64url") + }; + (field("proof"), field("action")) +} + +#[test] +fn an_offline_authored_proof_is_authorized_by_the_generated_context() { + use base64ct::{Base64UrlUnpadded, Encoding as _}; + let mut seed = [0_u8; 32]; + Base64UrlUnpadded::decode(SEED_B64, &mut seed).expect("seed"); + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_or(0, |duration| duration.as_secs()); + let context = build_context(&seed, now, 3_600).expect("trusted context"); + + for profile in auths_node::local_fixture::REFERENCE_PROFILES { + let (proof, action_bytes) = authored(profile, "exact reference operation", "fixture-agent"); + let action = auths_codec::decode_canonical_action(&action_bytes, context.limits()) + .expect("the authored action is canonical"); + + let method = auths_raw_key::RawKeyMethod::new().expect("raw-key method"); + let suite = auths_signature::Ed25519Suite::new().expect("ed25519 suite"); + let methods: [&dyn auths_ports::PrincipalMethod; 1] = [&method]; + let suites: [&dyn auths_ports::SignatureSuite; 1] = [&suite]; + let registries = + auths_registries::ImmutableRegistries::new(&methods, &suites).expect("registries"); + + match auths_verifier::verify(&proof, &action, &context, ®istries) { + auths_verifier::VerificationOutcome::Authorized(_) => {} + other => panic!("{profile} was not authorized: {other:?}"), + } + } +} From 90f86ad06d14fdfb82329273dd27f15e7b8b2a63 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 19:07:47 +0100 Subject: [PATCH 48/61] demos: contain the authored grant inside the anchor's validity (UNSIGNED) `Denied(DelegationExpanded)` on every profile, and the validity dimension was right to refuse. The trusted context fixes the anchor's window when it is GENERATED, at stack start. The authoring tool runs later and was asking for `now + 3600` from its own clock, so the grant ended after the anchor it descends from. A child that outlives its parent expands the authority it was given, which is exactly what that dimension exists to catch. The grant now spans fifteen minutes, comfortably inside any lifetime the fixture generates. This also corrects something I reported an hour ago. I said the authored proof verified for all three profiles; it had been checked against a stale binary built before the canonical-action change. The test caught it the moment CI rebuilt, which is the argument for `local_fixture_authorizes.rs` existing rather than my having run the tool once by hand and believed the output. Both e2e tests now report the denial code on refusal instead of a bare `expected completed, got denied`, so a failure names the dimension instead of costing a CI round trip to diagnose. Verified: all three reference profiles return Authorized; auths-node's 25 tests pass. Co-Authored-By: Claude Opus 5 --- .../tests/installed-sdk-e2e.mjs | 14 ++++++++++++-- .../tests/test_installed_sdk.py | 9 +++++++-- .../auths-node/src/bin/auths-local-authority.rs | 13 +++++++++---- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/demos/open-production-reference/tests/installed-sdk-e2e.mjs b/demos/open-production-reference/tests/installed-sdk-e2e.mjs index c11e657d..c4a0c9a7 100644 --- a/demos/open-production-reference/tests/installed-sdk-e2e.mjs +++ b/demos/open-production-reference/tests/installed-sdk-e2e.mjs @@ -52,7 +52,13 @@ for (const [name, profile] of profiles) { profile, }); const completed = await client.execute(authority, decode(authored.action)); - assert.equal(completed.kind, "completed"); + // Report WHY on refusal. A bare `expected completed, got denied` sends the + // reader back to CI for another round trip; the code names the dimension. + assert.equal( + completed.kind, + "completed", + `${name}: ${completed.kind}${completed.code ? ` (${completed.code})` : ""}`, + ); const verified = await client.verify(completed.receipt); assert.equal(verified.kind, "verified"); @@ -83,7 +89,11 @@ const unknown = await recoveryClient.execute( importAuthority(decode(recovery.proof)), decode(recovery.action), ); -assert.equal(unknown.kind, "recoverable"); +assert.equal( + unknown.kind, + "recoverable", + `recovery: ${unknown.kind}${unknown.code ? ` (${unknown.code})` : ""}`, +); const resumed = await recoveryClient.resume(unknown.reference); assert.equal(resumed.kind, "completed"); diff --git a/demos/open-production-reference/tests/test_installed_sdk.py b/demos/open-production-reference/tests/test_installed_sdk.py index 73afc1ed..16aa5dc2 100644 --- a/demos/open-production-reference/tests/test_installed_sdk.py +++ b/demos/open-production-reference/tests/test_installed_sdk.py @@ -69,7 +69,10 @@ async def test_installed_python_completes_the_same_reference_flow(): profile=profile, ) completed = await client.execute(authority, _decode(authored["action"])) - assert completed.kind == "completed" + # Report WHY on refusal; a bare kind mismatch costs another CI round trip. + assert completed.kind == "completed", ( + f"{name}: {completed.kind} {getattr(completed, 'code', '')}" + ) verified = await client.verify(completed.receipt) assert verified.kind == "verified" @@ -105,7 +108,9 @@ async def test_installed_python_resolves_an_unknown_effect(): import_authority(_decode(authored["proof"])), _decode(authored["action"]), ) - assert unknown.kind == "recoverable" + assert unknown.kind == "recoverable", ( + f"recovery: {unknown.kind} {getattr(unknown, 'code', '')}" + ) resumed = await client.resume(unknown.reference) assert resumed.kind == "completed" diff --git a/product/runtime/auths-node/src/bin/auths-local-authority.rs b/product/runtime/auths-node/src/bin/auths-local-authority.rs index 4459d1a6..e351604f 100644 --- a/product/runtime/auths-node/src/bin/auths-local-authority.rs +++ b/product/runtime/auths-node/src/bin/auths-local-authority.rs @@ -107,10 +107,15 @@ fn main() -> ExitCode { let now = SystemTime::now() .duration_since(UNIX_EPOCH) .map_or(0, |duration| duration.as_secs()); - let Ok(validity) = ValidityWindow::new( - Timestamp::new(now.saturating_sub(60)), - Timestamp::new(now.saturating_add(3600)), - ) else { + // The grant must be CONTAINED in the anchor's window, and the anchor's was + // fixed when the context was generated -- earlier than now. A window as long + // as the context's would end after it and be refused as DelegationExpanded, + // which is the validity dimension working: a child cannot outlive its + // parent. Fifteen minutes fits comfortably inside any context lifetime the + // fixture generates. + let Ok(validity) = + ValidityWindow::new(Timestamp::new(now), Timestamp::new(now.saturating_add(900))) + else { return fail("the validity window is invalid"); }; let Ok(algebra) = BudgetAlgebraId::parse("numeric-ceiling-v1") else { From 84fe447d47b2fda4d464865897dca50e0d9ddcb6 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 19:18:08 +0100 Subject: [PATCH 49/61] ci: show why the node denied instead of only that it did (UNSIGNED) The client reports `denied (core.authorization-denied)` and the node's own reasoning is not captured, so each diagnosis costs a five-minute round trip and ends in speculation. Receipt disclosure deliberately returns one failure for both unknown and unauthorized, so the client cannot be more specific -- but the node logs can. Captured on client failure only, so a passing run stays quiet. Locally the authored proof verifies against the generated context file through the node's own `for_request` path -- all three profiles Authorized -- so the remaining difference is inside the running node, not in the proof. Co-Authored-By: Claude Opus 5 --- .github/workflows/open-production-reference.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/open-production-reference.yml b/.github/workflows/open-production-reference.yml index 54e1cc1c..a8f81d32 100644 --- a/.github/workflows/open-production-reference.yml +++ b/.github/workflows/open-production-reference.yml @@ -124,6 +124,11 @@ jobs: npm init --yes npm install ../open-reference-artifacts/auths-dev-sdk-*.tgz PATH="${{ github.workspace }}/target/debug:$PATH" node reference.mjs + - name: Capture node logs on client failure + if: failure() + run: | + docker compose -f demos/open-production-reference/compose/compose.yaml \ + logs --tail 80 auths-1 auths-2 auths-3 || true - name: Run built Python wheel across replicas env: AUTHS_REFERENCE_ENDPOINT: https://localhost:8443 From 7d584b35dd13f8dc361fe36cf1abd7252a7e2859 Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 21:20:08 +0100 Subject: [PATCH 50/61] formal: harden the assurance boundary across all eleven review items (UNSIGNED) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed work from a second agent, verified locally, and closed the two gaps it left. The verification matters more than the reading: 83 xtask tests, lake build over 3287 jobs, assurance audit at 158 compiled statements, qualification 6/6, reproduction byte-identical. WHAT THE REVIEW CORRECTLY CAUGHT IN MY EARLIER WORK. `validate_mutation_witnesses` accepted ANY compiled theorem as a witness and returned success for unreadable files, invalid JSON, and missing fields. It now deserializes a strict `MutationMatrix` with `deny_unknown_fields`, requires exactly 23 ordered unique ids, and derives the expected declaration from the case id rather than checking that something exists. Negative tests cover malformed JSON, wrong count, reordering, redirection, and a missing field. Several of my witnesses proved only `1 ≠ 0`. Those survive mutating the corresponding predicate to `True` -- they were decoration. All 23 now run through `evaluateGrant`, `evaluateAuthorScope` or `evaluateCoverage`, and all 23 are bound rather than the 13 I managed. `FormalEvidence.sha256` was optional, so deleting a digest disabled the check it was meant to enforce. Now required, 64 lowercase hex, with `is_file()` and symlink rejection instead of `exists()`. I declined sortedness on critical extensions, arguing no decision depended on ordering. Positional equality does. It is now recorded as `entries.Pairwise criticalExtensionLt`. `delegationProjection` returns `CertifiedProjection` rather than certification sitting beside it unused, which closes the real gap: my audited theorems were generic properties of the type, not a required connection to the projection the code actually builds. THE TWO GAPS I CLOSED. `profile_version_equality` proved its point through a different dimension. It is now stated against the TRANSLATED `profile_ref_equal`, which compares `version` before `id`, so a version-only difference decides without reaching the opaque `as_bytes` and closes by `rfl` -- no `native_decide`, no new axiom. `ReachableFromRoot` is the inductive ancestry predicate the review asked for: a state has it only by construction, as an origin or as an accepted extension of a reachable state. `rooted_of_reachable` proves the implication ONE WAY ONLY and says so. Every genuinely rooted chain passes `rooted`, so the kernel never rejects a real chain; a state carrying a present marker can pass without being reachable. That gap is closed by sealing the raw views, not by the predicate. Stating the direction that holds is more useful than implying `rooted` was fixed. It was not; it is an approximation, and the development now says so with a proof attached. ONE GATE WORTH NAMING. `statement_inventory_sha256` binds the manifest to the exact set of compiled statements, so adding even a true theorem halts qualification until the inventory is reviewed and the digest updated deliberately. It fired on my own three additions and made me check all 158 declarations and their axioms first: still exactly Classical.choice, Quot.sound and propext, still no sorryAx. My earlier work had no equivalent -- a theorem could appear and nothing would notice. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 10 +- core/crates/auths-authority/src/lib.rs | 262 +- core/crates/auths-model/src/lib.rs | 37 + core/crates/auths-verifier/src/lib.rs | 30 +- formal/Auths/AssuranceAudit.lean | 5 + formal/Auths/Attenuation.lean | 20 +- formal/Auths/Authority.lean | 22 +- formal/Auths/Refinement/Production.lean | 15 + formal/Auths/Rich/Mutations.lean | 239 +- formal/Auths/Rich/Semantics.lean | 150 +- formal/Auths/Rich/Theorems.lean | 164 +- formal/Auths/Rich/Types.lean | 37 +- formal/Auths/Theorems.lean | 20 + formal/Auths/VectorExport.lean | 2 + formal/assurance-manifest-v1.toml | 8209 +++++++++++------ .../aeneas/generated/authority/Funs.lean | 20 +- .../aeneas/generated/authority/Types.lean | 17 +- .../generated/authority/translation.json | 68 +- .../qualification/aeneas/qualification.toml | 18 + .../qualification/aeneas/source-closure.json | 239 +- formal/refinement-mutations-v1.json | 38 +- release/semantic-freeze.json | 34 +- xtask/ci-plan/src/lib.rs | 586 +- xtask/src/formal.rs | 1014 +- xtask/src/formal_qualification.rs | 979 +- xtask/src/main.rs | 9 +- xtask/src/semantic_freeze.rs | 18 +- 27 files changed, 8915 insertions(+), 3347 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44f9e569..3b2b36af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -234,6 +234,7 @@ jobs: - uses: ./.github/actions/setup-rust-cache with: toolchain: 1.97.1 + compiler-cache: "false" - name: Check production source closure before formal setup if: github.event_name != 'pull_request' run: >- @@ -303,12 +304,9 @@ jobs: --run-attempt "${{ github.run_attempt }}" --base-sha "${{ github.event.pull_request.base.sha }}" --head-sha "${{ github.event.pull_request.head.sha }}" - - name: Run full read-only verification when the PR is already reproducible - if: steps.formal-update.outputs.update_required == 'false' - env: - AUTHS_AENEAS_BIN: ${{ github.workspace }}/target/formal-tools/bin/aeneas - AUTHS_CHARON_BIN: ${{ github.workspace }}/target/formal-tools/bin/charon - run: cargo xtask ci formal-translation + - name: Run semantic and bounded checks without reproducing a second time + if: github.event_name == 'pull_request' + run: cargo xtask ci formal-post-qualification - name: Preserve the bounded generated update if: steps.formal-update.outputs.update_required == 'true' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 diff --git a/core/crates/auths-authority/src/lib.rs b/core/crates/auths-authority/src/lib.rs index 97392f61..01c409dc 100644 --- a/core/crates/auths-authority/src/lib.rs +++ b/core/crates/auths-authority/src/lib.rs @@ -10,18 +10,24 @@ use auths_algebra_kernel::{ AttenuationChecks, RootLinkage, attenuation_checks_accept, root_preserved, }; use auths_model::{ - ActionAuthorityView, ActionConstraint, ActionEnvelope, AssurancePolicyId, AudienceSet, - BudgetCeiling, CriticalExtensions, DenialReason, GrantAuthorityView, GrantId, GrantStatement, - PermissionSet, PrincipalId, ProfileBudgetExpression, ProfileRef, ScopeAuthorityView, - StatusPolicy, TrustAnchor, ValidityWindow, action_authority_view, action_constraint_allows, - action_constraint_attenuates, assurance_policy_id_equal, audience_set_contains, - audience_set_is_subset, budget_ceiling_covers_action, critical_extensions_equal, - grant_authority_view, optional_budget_attenuates, optional_grant_id_equal, - permission_set_contains, permission_set_is_subset, principal_id_equal, profile_ref_equal, - profile_slice_contains, status_policy_attenuates, validity_window_contains, + AcceptedRegistries, ActionAuthorityView, ActionConstraint, ActionEnvelope, AssurancePolicyId, + AudienceSet, BudgetCeiling, CriticalExtensions, DenialReason, GrantAuthorityView, GrantId, + GrantStatement, PermissionSet, PrincipalId, ProfileBudgetExpression, ProfileRef, + ScopeAuthorityView, StatusPolicy, TrustAnchor, ValidityWindow, action_authority_view, + action_constraint_allows, action_constraint_attenuates, assurance_policy_id_equal, + audience_set_contains, audience_set_is_subset, budget_ceiling_covers_action, + critical_extensions_equal, grant_authority_view, optional_budget_attenuates, + optional_grant_id_equal, permission_set_contains, permission_set_is_subset, principal_id_equal, + profile_ref_equal, profile_slice_contains, status_policy_attenuates, validity_window_contains, }; /// Authority accumulated while walking one root-to-terminal grant chain. +/// +/// Raw state views are intentionally not part of the public API: +/// +/// ```compile_fail +/// use auths_authority::AuthorityStateView; +/// ``` #[derive(Clone, Debug, Eq, PartialEq)] pub struct EffectiveAuthority { root: PrincipalId, @@ -107,24 +113,24 @@ pub enum AuthorScopeDecision { /// Lossless borrowed projection of accumulated authority state. #[doc(hidden)] #[derive(Clone, Copy, Debug)] -pub struct AuthorityStateView<'a> { +pub(crate) struct AuthorityStateView<'a> { /// Trust root this authority is anchored at. Every accepted delegation /// copies it forward unchanged, so it is the identity a chain must still /// descend from after any number of edges. - pub root: &'a PrincipalId, - pub subject: &'a PrincipalId, - pub allowed_profiles: &'a [ProfileRef], - pub profile: Option<&'a ProfileRef>, - pub permissions: &'a PermissionSet, - pub validity: ValidityWindow, - pub audiences: &'a AudienceSet, - pub action_constraint: &'a ActionConstraint, - pub budget_ceiling: Option<&'a BudgetCeiling>, - pub remaining_depth: u16, - pub last_grant: Option, - pub assurance_policy: &'a AssurancePolicyId, - pub status_policy: &'a StatusPolicy, - pub extensions: Option<&'a CriticalExtensions>, + root: &'a PrincipalId, + subject: &'a PrincipalId, + allowed_profiles: &'a [ProfileRef], + profile: Option<&'a ProfileRef>, + permissions: &'a PermissionSet, + validity: ValidityWindow, + audiences: &'a AudienceSet, + action_constraint: &'a ActionConstraint, + budget_ceiling: Option<&'a BudgetCeiling>, + remaining_depth: u16, + last_grant: Option, + assurance_policy: &'a AssurancePolicyId, + status_policy: &'a StatusPolicy, + extensions: Option<&'a CriticalExtensions>, } /// Borrowed principal whose equality is the canonical protocol comparison. @@ -262,7 +268,7 @@ pub fn evaluate_grant<'grant>( /// Pure authority-kernel evaluation over lossless validated-model views. #[doc(hidden)] #[must_use] -pub fn evaluate_grant_view<'grant>( +pub(crate) fn evaluate_grant_view<'grant>( parent: AuthorityStateView<'_>, grant_id: GrantId, grant: GrantAuthorityView<'grant>, @@ -332,7 +338,7 @@ pub fn evaluate_grant_view<'grant>( /// first-failure diagnostics used by [`EffectiveAuthority::authorizes`]. #[doc(hidden)] #[must_use] -pub fn evaluate_action_coverage( +fn evaluate_action_coverage( authority: &EffectiveAuthority, action: &ActionEnvelope, expression: ProfileBudgetExpression, @@ -347,7 +353,7 @@ pub fn evaluate_action_coverage( /// Pure terminal-coverage evaluation over lossless validated-model views. #[doc(hidden)] #[must_use] -pub fn evaluate_action_coverage_view( +pub(crate) fn evaluate_action_coverage_view( authority: AuthorityStateView<'_>, action: ActionAuthorityView<'_>, expression: ProfileBudgetExpression, @@ -393,7 +399,7 @@ pub fn evaluate_action_coverage_view( /// Projects exactly the accumulated fields consumed by authority decisions. #[doc(hidden)] #[must_use] -pub fn authority_state_view(authority: &EffectiveAuthority) -> AuthorityStateView<'_> { +pub(crate) fn authority_state_view(authority: &EffectiveAuthority) -> AuthorityStateView<'_> { AuthorityStateView { root: &authority.root, subject: &authority.subject, @@ -468,16 +474,15 @@ impl EffectiveAuthority { /// # Errors /// /// Returns the first stable authority failure in protocol order. - /// `expression` states whether the action's profile is able to declare a - /// requested budget at all; the caller resolves it from its trusted - /// registry selection. [`ProfileBudgetExpression::Expressible`] — the - /// default — keeps an absent request unknown and therefore uncovered by a - /// bounded ceiling. + /// Budget expressibility is resolved inside this boundary from the exact + /// action profile and the caller's accepted registry set. Callers cannot + /// supply a naked expression that reclassifies an absent request. pub fn authorizes( &self, action: &ActionEnvelope, - expression: ProfileBudgetExpression, + registries: &AcceptedRegistries, ) -> Result<(), DenialReason> { + let expression = registries.profile_budget_expression(action.profile()); match evaluate_action_coverage(self, action, expression) { CoverageDecision::Authorized => Ok(()), CoverageDecision::Denied(reason) => Err(reason), @@ -502,8 +507,10 @@ mod tests { use super::*; use alloc::vec; use auths_model::{ - ActionAuthorityView, Audience, CapabilityId, CriticalExtension, CriticalExtensions, - ExtensionId, PrincipalMethodId, ProfileId, ResourceId, Timestamp, TrustAnchorId, + ActionAuthorityView, Audience, CapabilityId, Challenge, ChannelBindingId, + CriticalExtension, CriticalExtensions, ExtensionId, MediaType, PlanId, PrincipalMethodId, + ProfileId, ProfilePolicyId, ProofRef, RegistryManifestId, ResourceId, ResourceMatcherId, + SignatureSuiteId, Timestamp, TrustAnchorId, }; fn profile(name: &str) -> ProfileRef { @@ -711,6 +718,52 @@ mod tests { ); } + /// A present grant id is only a representation marker. This counterexample + /// records why raw views must stay crate-private and why formal historical + /// claims require `AnchoredChain`: if arbitrary construction were public, + /// matching a forged marker would pass the raw evaluator. + #[test] + fn forged_present_marker_demonstrates_why_raw_views_are_sealed() { + let anchor = anchor(); + let root = PrincipalId::parse("did:key:root").expect("root"); + let forged = PrincipalId::parse("did:key:attacker").expect("attacker"); + let permissions = permissions(); + let audiences = audiences(); + let profiles = [profile("profile-a"), profile("profile-b")]; + let constraint = ActionConstraint::AnyBody; + let assurance = AssurancePolicyId::parse("assurance-v1").expect("assurance"); + let status = StatusPolicy::ExpiryOnly; + let marker = GrantId::new([4; 32]); + let raw = AuthorityStateView { + root: &root, + subject: &forged, + allowed_profiles: &profiles, + profile: None, + permissions: &permissions, + validity: anchor.validity(), + audiences: &audiences, + action_constraint: &constraint, + budget_ceiling: None, + remaining_depth: 2, + last_grant: Some(marker), + assurance_policy: &assurance, + status_policy: &status, + extensions: None, + }; + let statement = grant( + "did:key:attacker", + "did:key:victim", + "profile-a", + 1, + Some(marker), + ); + assert!(matches!( + evaluate_grant_view(raw, GrantId::new([5; 32]), grant_authority_view(&statement)) + .outcome, + DelegationOutcome::Accepted(_) + )); + } + fn numeric_budget(value: u64) -> BudgetCeiling { BudgetCeiling::new( auths_model::BudgetAlgebraId::parse("numeric-ceiling-v1").expect("algebra"), @@ -718,6 +771,73 @@ mod tests { ) } + fn accepted_registries(selected: &ProfileRef, budget_free: bool) -> AcceptedRegistries { + accepted_registries_for( + vec![selected.clone()], + if budget_free { + vec![selected.clone()] + } else { + Vec::new() + }, + ) + } + + fn accepted_registries_for( + profiles: Vec, + budget_free_profiles: Vec, + ) -> AcceptedRegistries { + let registries = AcceptedRegistries::new( + RegistryManifestId::new([0x11; 32]), + vec![PrincipalMethodId::parse("raw-key-v1").expect("principal method")], + vec![SignatureSuiteId::parse("ed25519-v1").expect("signature suite")], + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + vec![ResourceMatcherId::parse("uri-namespace-v1").expect("resource matcher")], + Vec::new(), + Vec::new(), + profiles, + vec![ProfilePolicyId::parse("exact-v1").expect("profile policy")], + ) + .expect("accepted registries"); + if budget_free_profiles.is_empty() { + registries + } else { + registries + .with_budget_free_profiles(budget_free_profiles) + .expect("budget-free declaration") + } + } + + fn action_envelope( + anchor: &TrustAnchor, + selected: ProfileRef, + requested_budget: Option, + ) -> ActionEnvelope { + ActionEnvelope::new( + selected, + MediaType::parse("application/vnd.auths.test.v1+cbor").expect("media type"), + auths_model::Digest::new([0; 32]), + auths_model::Permission::new( + CapabilityId::parse("deploy").expect("capability"), + ResourceId::parse("cluster://production").expect("resource"), + ), + requested_budget, + Audience::parse("cluster://production").expect("audience"), + Challenge::new([1; 32]), + anchor.validity(), + PrincipalId::parse("did:key:root").expect("actor"), + None, + PlanId::new([2; 32]), + ChannelBindingId::parse("none-v1").expect("channel binding"), + ProofRef::new([3; 32]), + Vec::new(), + CriticalExtensions::empty(), + ) + } + /// A bounded ceiling must not cover an action that declares no budget. /// /// This reaches terminal coverage directly, so nothing above the kernel can @@ -763,45 +883,51 @@ mod tests { fn authorizes_denies_an_absent_request_under_a_bounded_ceiling() { let anchor = anchor_with_budget(Some(numeric_budget(10))); let authority = EffectiveAuthority::from_anchor(&anchor); - let actor = PrincipalId::parse("did:key:root").expect("root"); let selected = profile("profile-a"); - let permission = auths_model::Permission::new( - CapabilityId::parse("deploy").expect("capability"), - ResourceId::parse("cluster://production").expect("resource"), - ); - let audience = Audience::parse("cluster://production").expect("audience"); - let action = ActionAuthorityView { - profile: &selected, - canonical_body_digest: auths_model::Digest::new([0; 32]), - permission: &permission, - requested_budget: None, - audience: &audience, - validity: anchor.validity(), - actor: &actor, - terminal_grant: None, - }; + let registries = accepted_registries(&selected, false); + let action = action_envelope(&anchor, selected.clone(), None); // A bounded request inside the ceiling is still authorized: the denial // above is specific to the absent request, not a blanket budget denial. - let requested = numeric_budget(5); - let bounded = ActionAuthorityView { - requested_budget: Some(&requested), - ..action - }; assert_eq!( - evaluate_action_coverage_view( - authority_state_view(&authority), - bounded, - ProfileBudgetExpression::Expressible + authority.authorizes( + &action_envelope(&anchor, selected.clone(), Some(numeric_budget(5))), + ®istries, ), - CoverageDecision::Authorized + Ok(()) ); assert_eq!( - evaluate_action_coverage_view( - authority_state_view(&authority), - action, - ProfileBudgetExpression::Expressible - ), - CoverageDecision::Denied(DenialReason::BudgetCeilingExceeded) + authority.authorizes(&action, ®istries), + Err(DenialReason::BudgetCeilingExceeded) + ); + + let budget_free = accepted_registries(&selected, true); + assert_eq!( + authority.authorizes(&action, &budget_free), + Ok(()), + "only a registry declaration bound to the exact action profile may reclassify absence" + ); + } + + #[test] + fn authorizes_resolves_budget_expression_for_the_exact_action_profile() { + let anchor = anchor_with_budget(Some(numeric_budget(10))); + let authority = EffectiveAuthority::from_anchor(&anchor); + let budget_free = profile("profile-a"); + let budget_capable = profile("profile-b"); + let registries = accepted_registries_for( + vec![budget_free.clone(), budget_capable.clone()], + vec![budget_free.clone()], + ); + + assert_eq!( + authority.authorizes(&action_envelope(&anchor, budget_free, None), ®istries), + Ok(()), + "the exact profile declared budget-free may omit a request" + ); + assert_eq!( + authority.authorizes(&action_envelope(&anchor, budget_capable, None), ®istries), + Err(DenialReason::BudgetCeilingExceeded), + "a different accepted profile must not inherit budget-free status" ); } diff --git a/core/crates/auths-model/src/lib.rs b/core/crates/auths-model/src/lib.rs index d13e82ec..830e7e66 100644 --- a/core/crates/auths-model/src/lib.rs +++ b/core/crates/auths-model/src/lib.rs @@ -4884,6 +4884,43 @@ mod tests { )); } + #[test] + fn critical_extension_constructor_establishes_the_refinement_invariant() { + let extension = |id: &str, byte: u8| { + CriticalExtension::new(ExtensionId::parse(id).expect("extension id"), vec![byte]) + .expect("bounded extension") + }; + let canonical = CriticalExtensions::new(vec![ + extension("z-extension-v1", 2), + extension("a-extension-v1", 1), + ]) + .expect("canonical extensions"); + assert_eq!(canonical.as_slice()[0].id().as_str(), "a-extension-v1"); + assert_eq!(canonical.as_slice()[1].id().as_str(), "z-extension-v1"); + + assert_eq!( + CriticalExtensions::new(vec![ + extension("duplicate-v1", 1), + extension("duplicate-v1", 2), + ]), + Err(ModelError::DuplicateExtension) + ); + let too_many = (0..=HARD_MAX_EXTENSIONS) + .map(|index| extension(&format!("extension-{index:02}"), 0)) + .collect(); + assert_eq!( + CriticalExtensions::new(too_many), + Err(ModelError::InvalidExtension) + ); + assert_eq!( + CriticalExtension::new( + ExtensionId::parse("oversized-v1").expect("extension id"), + vec![0; HARD_MAX_EXTENSION_BYTES + 1], + ), + Err(ModelError::InvalidExtension) + ); + } + #[test] fn action_constraint_is_monotonic() { let any = ActionConstraint::AnyBody; diff --git a/core/crates/auths-verifier/src/lib.rs b/core/crates/auths-verifier/src/lib.rs index a736686f..66e1f524 100644 --- a/core/crates/auths-verifier/src/lib.rs +++ b/core/crates/auths-verifier/src/lib.rs @@ -2248,12 +2248,7 @@ fn verify_branch_from_anchor( evaluate_extensions(grant.statement().extensions(), context, registries, meter)?; } authority - .authorizes( - action.envelope(), - context - .accepted_registries() - .profile_budget_expression(action.envelope().profile()), - ) + .authorizes(action.envelope(), context.accepted_registries()) .map_err(VerificationFailure::Denied)?; let action_control = control_for(controlled, StatementRef::Action(action_id))?; reports.push(participant_report( @@ -2972,10 +2967,7 @@ mod tests { assert_eq!( authority.authorizes( fixture.action.envelope(), - fixture - .context - .accepted_registries() - .profile_budget_expression(fixture.action.envelope().profile()) + fixture.context.accepted_registries() ), Err(DenialReason::BudgetCeilingExceeded), "the kernel alone must deny; the Wave 1 guard is defense in depth" @@ -2996,10 +2988,7 @@ mod tests { assert_eq!( EffectiveAuthority::from_anchor(inside_anchor).authorizes( inside.action.envelope(), - inside - .context - .accepted_registries() - .profile_budget_expression(inside.action.envelope().profile()) + inside.context.accepted_registries() ), Ok(()) ); @@ -3036,10 +3025,7 @@ mod tests { assert_eq!( EffectiveAuthority::from_anchor(anchor).authorizes( fixture.action.envelope(), - fixture - .context - .accepted_registries() - .profile_budget_expression(fixture.action.envelope().profile()) + fixture.context.accepted_registries() ), Ok(()) ); @@ -3137,12 +3123,8 @@ mod tests { // rest on which of the two runs first. let anchor = declared.trust_anchors().first().expect("fixture anchor"); assert_eq!( - EffectiveAuthority::from_anchor(anchor).authorizes( - fixture.action.envelope(), - declared - .accepted_registries() - .profile_budget_expression(fixture.action.envelope().profile()) - ), + EffectiveAuthority::from_anchor(anchor) + .authorizes(fixture.action.envelope(), declared.accepted_registries()), Ok(()) ); } diff --git a/formal/Auths/AssuranceAudit.lean b/formal/Auths/AssuranceAudit.lean index 34fd26de..a6fdbcf5 100644 --- a/formal/Auths/AssuranceAudit.lean +++ b/formal/Auths/AssuranceAudit.lean @@ -24,10 +24,15 @@ private def auditDeclaration throwError "assurance inventory names missing declaration '{declarationName}'" | some info => + let moduleName := + match environment.getModuleIdxFor? declarationName with + | some index => environment.header.moduleNames[index.toNat]! + | none => environment.header.mainModule (liftTermElabM <| Meta.ppExpr info.type) >>= fun statementFormat => collectAxioms declarationName >>= fun axioms => pure <| declarations.push <| Json.mkObj [ ("name", declarationName.toString), + ("module", moduleName.toString), ("kind", declarationKind info), ("statement", statementFormat.pretty 120), ("axioms", Json.arr <| axioms.qsort Name.lt |>.map (toJson ·.toString)) diff --git a/formal/Auths/Attenuation.lean b/formal/Auths/Attenuation.lean index 4d50a3f9..7362bec8 100644 --- a/formal/Auths/Attenuation.lean +++ b/formal/Auths/Attenuation.lean @@ -27,8 +27,7 @@ content exactly because `rootPreserved` is decided from `parent.root`, theorem attenuation_requires_trust_root {v : Vocabulary} (parent : ChainState v) (grant : Grant v) (accepted : - Auths.Generated.attenuationAccepts - (delegationProjection parent grant) = true) : + certifiedAccepts (delegationProjection parent grant) = true) : rootPreserved parent grant := ((rich_projection_accepts_iff_root_and_scope_depth_checks parent grant).1 accepted).1 @@ -40,8 +39,7 @@ whole projection no matter what the other ten dimensions report. theorem attenuation_denied_when_root_broken {v : Vocabulary} (parent : ChainState v) (grant : Grant v) (broken : ¬ rootPreserved parent grant) : - Auths.Generated.attenuationAccepts - (delegationProjection parent grant) = false := + certifiedAccepts (delegationProjection parent grant) = false := broken_root_denies_every_projection parent grant broken /-- @@ -52,13 +50,13 @@ returns a literal `true` can satisfy this theorem. theorem attenuation_root_dimension_is_not_a_literal {v : Vocabulary} (parent : ChainState v) (grant : Grant v) (foreign : grant.issuer ≠ parent.subject) : - (delegationProjection parent grant).rootPreserved = false := + (delegationProjection parent grant).value.rootPreserved = false := root_dimension_false_of_foreign_issuer parent grant foreign /-- The dimension reports the semantic predicate exactly, in both directions. -/ theorem attenuation_root_dimension_is_exact {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : - (delegationProjection parent grant).rootPreserved = true ↔ + (delegationProjection parent grant).value.rootPreserved = true ↔ rootPreserved parent grant := root_dimension_is_exact parent grant @@ -79,8 +77,7 @@ critical-extension set survived the edge exactly. theorem attenuation_requires_critical_extensions {v : Vocabulary} (parent : ChainState v) (grant : Grant v) (accepted : - Auths.Generated.attenuationAccepts - (delegationProjection parent grant) = true) : + certifiedAccepts (delegationProjection parent grant) = true) : extensionsLe (some grant.extensions) parent.scope.extensions := -- Was `.2.2.2.2.2.2.2.2.2.2.2`: eleven projections whose meaning depended on -- counting. The eleventh dimension is now reached by name. @@ -95,8 +92,7 @@ report. theorem attenuation_denied_when_extensions_altered {v : Vocabulary} (parent : ChainState v) (grant : Grant v) (broken : ¬ extensionsLe (some grant.extensions) parent.scope.extensions) : - Auths.Generated.attenuationAccepts - (delegationProjection parent grant) = false := + certifiedAccepts (delegationProjection parent grant) = false := altered_extensions_deny_every_projection parent grant broken /-- @@ -109,13 +105,13 @@ theorem attenuation_extension_dimension_is_not_a_literal {v : Vocabulary} (pinned : CriticalExtensions v) (pinnedBy : parent.scope.extensions = some pinned) (altered : grant.extensions ≠ pinned) : - (delegationProjection parent grant).extensionsAttenuate = false := + (delegationProjection parent grant).value.extensionsAttenuate = false := extensions_dimension_false_of_altered_set parent grant pinned pinnedBy altered /-- The dimension reports the semantic relation exactly, in both directions. -/ theorem attenuation_extension_dimension_is_exact {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : - (delegationProjection parent grant).extensionsAttenuate = true ↔ + (delegationProjection parent grant).value.extensionsAttenuate = true ↔ extensionsLe (some grant.extensions) parent.scope.extensions := extensions_dimension_is_exact parent grant diff --git a/formal/Auths/Authority.lean b/formal/Auths/Authority.lean index 54296136..1a4db69d 100644 --- a/formal/Auths/Authority.lean +++ b/formal/Auths/Authority.lean @@ -46,17 +46,18 @@ theorem first_delegation_comes_from_the_root {v : Vocabulary} first_edge_is_issued_by_the_root fresh edge /-- -Every state reachable by any number of delegations descends from the single -root the chain started at. Delegation can narrow authority; it can never -re-anchor it. +Every state in a history beginning at a fresh root selected by the caller's +trusted context descends from that same root. Delegation can narrow authority; +it can never re-anchor it. Both the context membership and freshness premises +are essential: a present `lastGrant` marker alone is not ancestry evidence. -/ theorem chain_descends_from_one_root {v : Vocabulary} + {trusted : FiniteSet (Principal v)} {start : ChainState v} {rest : List (ChainState v)} - (chain : DelegationChain start rest) : - ∀ state ∈ rest, state.root = start.root ∧ rooted state := - fun state member => - ⟨chain_preserves_root chain state member, - chain_preserves_rootedness chain state member⟩ + (chain : AnchoredChain trusted start rest) : + ∀ state, state = start ∨ state ∈ rest → + state.root = start.root ∧ rooted state ∧ state.root ∈ trusted := + anchored_chain_preserves_provenance chain /-- An authority that descends from no root delegates nothing. -/ theorem unrooted_authority_delegates_nothing {v : Vocabulary} @@ -68,8 +69,9 @@ theorem unrooted_authority_delegates_nothing {v : Vocabulary} /-- An authority that descends from no root authorizes nothing. -/ theorem unrooted_authority_authorizes_nothing {v : Vocabulary} (authority : ChainState v) (action : Action v) + (expression : BudgetExpression) (unrooted : ¬ rooted authority) : - evaluateCoverage authority action = .denied .brokenGrantChain := - unrooted_authority_covers_nothing authority action unrooted + evaluateCoverage authority action expression = .denied .brokenGrantChain := + unrooted_authority_covers_nothing authority action expression unrooted end Auths.Authority diff --git a/formal/Auths/Refinement/Production.lean b/formal/Auths/Refinement/Production.lean index c45b6235..3f70320b 100644 --- a/formal/Auths/Refinement/Production.lean +++ b/formal/Auths/Refinement/Production.lean @@ -1173,6 +1173,8 @@ abbrev productionVocabulary : Auths.Rich.Vocabulary where grantIdDecidableEq := inferInstance extensionIdDecidableEq := inferInstance extensionBodyDecidableEq := inferInstance + extensionIdLinearOrder := inferInstance + extensionBodyLinearOrder := inferInstance extensionBodySize := List.length abbrev ProductionVocabulary := productionVocabulary @@ -1200,6 +1202,11 @@ def richCriticalExtension (extension : auths_model.CriticalExtension) : Auths.Rich.CriticalExtension ProductionVocabulary := richCriticalExtensionOfKey (criticalExtensionKey extension) +/-- Exact lexicographic order of Rust's derived `Ord` on a critical extension. -/ +def CriticalExtensionKeyLt + (left right : List Std.U8 × List Std.U8) : Prop := + left.1 < right.1 ∨ (left.1 = right.1 ∧ left.2 < right.2) + /-- The representation invariants `CriticalExtensions::new` establishes. @@ -1212,6 +1219,8 @@ carried here — the same pattern as `ValidityWindowValid` and -/ structure CriticalExtensionsCanonical (extensions : auths_model.CriticalExtensions) : Prop where + sorted : + (criticalExtensionsKey extensions).Pairwise CriticalExtensionKeyLt distinctIds : (extensions.val.map fun extension => stringBytes extension.id).Nodup size : extensions.val.length ≤ Auths.Rich.hardMaxExtensions @@ -1237,6 +1246,12 @@ def richCriticalExtensions (canonical : CriticalExtensionsCanonical extensions) : Auths.Rich.CriticalExtensions ProductionVocabulary where entries := extensions.val.map richCriticalExtension + sorted := by + rw [richCriticalExtension_entries, List.pairwise_map] + exact canonical.sorted.imp (by + intro left right ordered + simpa [Auths.Rich.criticalExtensionLt, CriticalExtensionKeyLt, + richCriticalExtensionOfKey] using ordered) bodiesBounded := by intro entry membership obtain ⟨extension, source, rfl⟩ := List.mem_map.mp membership diff --git a/formal/Auths/Rich/Mutations.lean b/formal/Auths/Rich/Mutations.lean index b2cada41..9bf115ad 100644 --- a/formal/Auths/Rich/Mutations.lean +++ b/formal/Auths/Rich/Mutations.lean @@ -1,4 +1,5 @@ import Auths.Rich.Semantics +import qualification.aeneas.generated.model.Funs import Mathlib.Tactic /-! @@ -12,13 +13,17 @@ The assurance audit checked that the FILE EXISTED. Nothing checked the claims. That is the same defect this branch exists to remove -- a claim recorded rather than enforced -- sitting inside the evidence for the claims. -Each theorem below is a COMPILED counterexample: a concrete input on which the -shipping semantics DENY, and which the described mutation would accept. A -mutation that were harmless would have no such witness, and the theorem would -not compile. +Each theorem below is a COMPILED boundary example: it calls the actual ordered +decision function (`evaluateGrant`, `evaluateAuthorScope`, or +`evaluateCoverage`) on a concrete input. The expected outcome changes under +the named rich-semantics mutation, so the named theorem fails to compile. A +shipping-Rust mutation is caught separately by the Aeneas refinement theorem +that binds the translated evaluator to this unchanged rich boundary. -Every witness is decidable and closes by `decide`, so these are computations, -not appeals to a tactic that might be silently weakened. +Every witness is decidable and closes by kernel-checked `decide`, so these are +computations, not appeals to an assumption or to native-code evaluation. +Primitive relation facts are deliberately insufficient here: they would keep +compiling if the decision function stopped using the relation. -/ namespace Auths.Rich.Mutations @@ -49,6 +54,8 @@ open Auths.Rich grantIdDecidableEq := inferInstance extensionIdDecidableEq := inferInstance extensionBodyDecidableEq := inferInstance + extensionIdLinearOrder := inferInstance + extensionBodyLinearOrder := inferInstance extensionBodySize := fun _ => 0 abbrev V := natVocabulary @@ -57,82 +64,264 @@ private def window (start finish : Nat) (h : start ≤ finish := by decide) : InclusiveWindow := ⟨start, finish, h⟩ +private def baseProfileScope : ProfileScope V where + rootAllowed := {⟨0⟩} + selected := some ⟨0⟩ + selectedAllowed := by simp + +private def baseScope (assurance : Nat := 0) : AuthorityScope V where + profileScope := baseProfileScope + permissions := {⟨0⟩} + validity := window 0 10 + audiences := {⟨0⟩} + actionConstraint := .anyBody + budget := none + status := .expiryOnly + assurance := ⟨assurance⟩ + extensions := none + +private def baseState + (root subject depth : Nat) (last : Option Nat) : ChainState V where + root := ⟨root⟩ + subject := ⟨subject⟩ + scope := baseScope + remainingDepth := depth + lastGrant := last.map (fun id => ⟨id⟩) + +private def baseGrant + (issuer : Nat := 0) (remainingDepth : Nat := 0) + (parent : Option Nat := none) (assurance : Nat := 0) : Grant V where + issuer := ⟨issuer⟩ + subject := ⟨1⟩ + profile := ⟨0⟩ + permissions := {⟨0⟩} + validity := window 0 10 + audiences := {⟨0⟩} + actionConstraint := .anyBody + budget := none + remainingDepth := remainingDepth + parent := parent.map (fun id => ⟨id⟩) + status := .expiryOnly + assurance := ⟨assurance⟩ + extensions := CriticalExtensions.empty V + +private def baseAction + (actor : Nat := 0) (terminalGrant : Option Nat := none) + (profile : Nat := 0) (permission : Nat := 0) + (audience : Nat := 0) (digest : Nat := 0) + (requestedBudget : Option (BudgetCeiling V) := none) : Action V where + actor := ⟨actor⟩ + terminalGrant := terminalGrant.map (fun id => ⟨id⟩) + profile := ⟨profile⟩ + permission := ⟨permission⟩ + validity := window 0 10 + audience := ⟨audience⟩ + bodyDigest := ⟨digest⟩ + requestedBudget := requestedBudget + +private def extension (id body : Nat) : CriticalExtension V := + ⟨⟨id⟩, ⟨body⟩⟩ + +private def singletonExtension (id body : Nat) : CriticalExtensions V := + CriticalExtensions.singleton (extension id body) (by + simp [hardMaxExtensionBytes]) + +private def delegationDeniedWith + (expected : DelegationDiagnostic) : DelegationDecision V → Bool + | .accepted _ => false + | .denied actual => decide (actual = expected) + /-! ## Validity window -/ /-- `validity-start-direction`: reversing the lower bound accepts a child that begins BEFORE its parent, which is authority the parent never held. -/ theorem validity_start_direction : - ¬ windowContained (window 3 10) (window 5 10) := by decide + evaluateAuthorScope + { baseScope with validity := window 5 10 } + { baseScope with validity := window 3 10 } + 2 1 = .denied .validity := by decide /-- `validity-end-direction`: reversing the upper bound accepts a child that outlives its parent. -/ theorem validity_end_direction : - ¬ windowContained (window 5 12) (window 5 10) := by decide + evaluateAuthorScope + { baseScope with validity := window 5 10 } + { baseScope with validity := window 5 12 } + 2 1 = .denied .validity := by decide + +/-! ## Finite-set direction and membership -/ + +theorem permission_subset_direction : + evaluateAuthorScope + { baseScope with permissions := {⟨0⟩, ⟨1⟩} } + { baseScope with permissions := {⟨0⟩} } + 2 1 = .accepted := by decide + +theorem audience_subset_direction : + evaluateAuthorScope + { baseScope with audiences := {⟨0⟩, ⟨1⟩} } + { baseScope with audiences := {⟨0⟩} } + 2 1 = .accepted := by decide + +theorem permission_membership_decision : + evaluateCoverage (baseState 0 0 2 none) baseAction + .expressible = .authorized := by decide + +theorem audience_membership_decision : + evaluateCoverage (baseState 0 0 2 none) baseAction + .expressible = .authorized := by decide + +theorem body_digest_membership_decision : + evaluateCoverage + { baseState 0 0 2 none with + scope := { baseScope with + actionConstraint := .allowedBodyDigests {⟨0⟩} } } + baseAction .expressible = .authorized := by decide + +theorem body_digest_subset_direction : + evaluateAuthorScope + { baseScope with actionConstraint := .allowedBodyDigests {⟨0⟩, ⟨1⟩} } + { baseScope with actionConstraint := .allowedBodyDigests {⟨0⟩} } + 2 1 = .accepted := by decide + +/-! ## Action constraints -/ + +theorem action_exact_equality : + evaluateCoverage + { baseState 0 0 2 none with + scope := { baseScope with actionConstraint := .exactBodyDigest ⟨0⟩ } } + (baseAction (digest := 1)) .expressible = + .denied .actionConstraintMismatch := by decide + +theorem action_constructor_fallback : + evaluateAuthorScope + { baseScope with actionConstraint := .exactBodyDigest ⟨0⟩ } + { baseScope with actionConstraint := .allowedBodyDigests {⟨0⟩, ⟨1⟩} } + 2 1 = .denied .actionConstraint := by + have different : + ({⟨0⟩, ⟨1⟩} : FiniteSet (Digest V)) ≠ {⟨0⟩} := by decide + simp [evaluateAuthorScope, baseScope, baseProfileScope, profileLe, + windowContained, actionConstraintLe, different] + +theorem action_singleton_exact_rejection : + evaluateAuthorScope + { baseScope with actionConstraint := .exactBodyDigest ⟨0⟩ } + { baseScope with actionConstraint := .allowedBodyDigests {⟨0⟩} } + 2 1 = .accepted := by + simp [evaluateAuthorScope, baseScope, baseProfileScope, profileLe, + windowContained, actionConstraintLe, budgetLe, statusLe, extensionsLe] /-! ## Budget -/ /-- `budget-value-direction`: reversing the numeric comparison accepts a child ceiling ABOVE its parent. -/ theorem budget_value_direction : - ¬ budgetLe (v := V) (some ⟨⟨0⟩, 5⟩) (some ⟨⟨0⟩, 3⟩) := by decide + evaluateAuthorScope + { baseScope with budget := some ⟨⟨0⟩, 3⟩ } + { baseScope with budget := some ⟨⟨0⟩, 5⟩ } + 2 1 = .denied .budget := by decide /-- `budget-algebra-equality`: ignoring the algebra identifier compares numbers denominated in different units. -/ theorem budget_algebra_equality : - ¬ budgetLe (v := V) (some ⟨⟨1⟩, 1⟩) (some ⟨⟨0⟩, 5⟩) := by decide + evaluateAuthorScope + { baseScope with budget := some ⟨⟨0⟩, 5⟩ } + { baseScope with budget := some ⟨⟨1⟩, 1⟩ } + 2 1 = .denied .budget := by decide /-- `optional-budget-bounded-parent`: an unbounded child beneath a bounded parent is an unbounded grant. -/ theorem optional_budget_bounded_parent : - ¬ budgetLe (v := V) none (some ⟨⟨0⟩, 5⟩) := by decide + evaluateAuthorScope + { baseScope with budget := some ⟨⟨0⟩, 5⟩ } + { baseScope with budget := none } + 2 1 = .denied .budget := by decide /-- `optional-budget-no-request`: treating a missing request as vacuously covered is exactly the fail-open this branch removed from the shipping Rust. An action that declares no bound states no bound at all. -/ theorem optional_budget_no_request : - ¬ budgetCovers (v := V) (some ⟨⟨0⟩, 5⟩) none := by decide + evaluateCoverage + { baseState 0 0 2 none with + scope := { baseScope with budget := some ⟨⟨0⟩, 5⟩ } } + baseAction .expressible = .denied .budgetCeilingExceeded := by + decide /-! ## Delegation depth -/ /-- `delegation-depth-strictness`: accepting equal depth lets a chain delegate forever without ever exhausting its budget of hops. -/ theorem delegation_depth_strictness : - ¬ (3 < 3) := by decide + delegationDeniedWith .delegationExpanded + (evaluateGrant (baseState 0 0 3 none) ⟨0⟩ + (baseGrant (remainingDepth := 3))) = true := by decide /-! ## Status -/ /-- `status-age-direction`: reversing the age comparison accepts a child that tolerates STALER observations than its parent. -/ theorem status_age_direction : - ¬ statusLe (v := V) (.snapshotRequired ⟨0⟩ ⟨10, by decide⟩) - (.snapshotRequired ⟨0⟩ ⟨5, by decide⟩) := by decide + evaluateAuthorScope + { baseScope with status := .snapshotRequired ⟨0⟩ ⟨5, by decide⟩ } + { baseScope with status := .snapshotRequired ⟨0⟩ ⟨10, by decide⟩ } + 2 1 = .denied .status := by decide /-- `status-method-equality`: ignoring the method identifier accepts a snapshot produced by a different status system than the parent required. -/ theorem status_method_equality : - ¬ statusLe (v := V) (.snapshotRequired ⟨1⟩ ⟨5, by decide⟩) - (.snapshotRequired ⟨0⟩ ⟨5, by decide⟩) := by decide + evaluateAuthorScope + { baseScope with status := .snapshotRequired ⟨0⟩ ⟨5, by decide⟩ } + { baseScope with status := .snapshotRequired ⟨1⟩ ⟨5, by decide⟩ } + 2 1 = .denied .status := by decide /-! ## Identity and linkage -/ /-- `assurance-equality`: treating distinct assurance policies as equal accepts a grant issued under weaker evidence rules. -/ -theorem assurance_equality : ¬ ((1 : Nat) = 0) := by decide +theorem assurance_equality : + evaluateAuthorScope (baseScope 0) (baseScope 1) 2 1 = + .denied .assurance := by decide + +/-- `profile-version-equality`: the version is part of profile identity. -/-- `profile-version-equality`: distinct profiles are distinct. The rich layer -carries the profile as one opaque identity, so a version change is a different -profile here; ignoring it accepts a grant for semantics the parent never -authorised. -/ +Stated against the TRANSLATED `profile_ref_equal` rather than the rich carrier. +The rich layer models a profile as one opaque identity, so a version change is +indistinguishable there from any other change of profile -- which would prove +something weaker than the mutation describes. The translated function compares +`version` before `id`, so a version-only difference is decided without reaching +the opaque `as_bytes`, and this witness kills a mutant that ignores it. -/ theorem profile_version_equality : - ¬ ((⟨1⟩ : Profile V) = ⟨0⟩) := by decide + auths_model.profile_ref_equal + { id := "auths.opentofu.saved-plan-apply", version := 2#u16 } + { id := "auths.opentofu.saved-plan-apply", version := 1#u16 } = + Aeneas.Std.Result.ok false := by + rfl /-- `principal-linkage-equality`: two principals sharing a method are still two principals; conflating them breaks the chain. -/ -theorem principal_linkage_equality : ¬ ((1 : Nat) = 0) := by decide +theorem principal_linkage_equality : + delegationDeniedWith .brokenGrantChain + (evaluateGrant (baseState 0 0 1 none) ⟨0⟩ + (baseGrant (issuer := 1))) = true := by decide /-- `grant-linkage-equality`: treating any two present grant identifiers as equal lets a grant claim a parent it never descended from. -/ theorem grant_linkage_equality : - ¬ ((some 1 : Option Nat) = some 0) := by decide + delegationDeniedWith .brokenGrantChain + (evaluateGrant (baseState 0 0 1 (some 0)) ⟨2⟩ + (baseGrant (parent := some 1))) = true := by decide + +/-! ## Critical extensions -/ + +theorem critical_extension_equality : + evaluateAuthorScope + { baseScope with extensions := some (singletonExtension 0 0) } + { baseScope with extensions := some (singletonExtension 0 1) } + 2 1 = .denied .extensions := by decide + +/-- A reversed identifier pair is not a Rust-canonical extension sequence. -/ +theorem critical_extensions_reversed_not_sorted : + ¬ ([extension 1 0, extension 0 0].Pairwise criticalExtensionLt) := by + decide end Auths.Rich.Mutations diff --git a/formal/Auths/Rich/Semantics.lean b/formal/Auths/Rich/Semantics.lean index 809b8d4d..36fabb7d 100644 --- a/formal/Auths/Rich/Semantics.lean +++ b/formal/Auths/Rich/Semantics.lean @@ -49,18 +49,6 @@ instance {v : Vocabulary} cases ceiling <;> cases requested <;> simp [budgetCovers] <;> infer_instance -/-- -Whether a profile's canonical actions can state a budget at all. - -TRUSTED REGISTRY CONTEXT, not an action-controlled field. An action cannot -declare itself inexpressible to escape a ceiling: the profile registry decides -this, and the action only supplies the requested budget. --/ -inductive BudgetExpression where - | expressible - | inexpressible - deriving DecidableEq, Repr - /-- Terminal budget coverage including profile expressibility. @@ -257,13 +245,14 @@ instance {v : Vocabulary} (child parent : AuthorityScope v) : infer_instance def actionCovers {v : Vocabulary} - (scope : AuthorityScope v) (action : Action v) : Prop := + (scope : AuthorityScope v) (action : Action v) + (expression : BudgetExpression) : Prop := profileAllows scope.profileScope action.profile ∧ action.permission ∈ scope.permissions ∧ windowContained action.validity scope.validity ∧ action.audience ∈ scope.audiences ∧ actionConstraintAllows scope.actionConstraint action.bodyDigest ∧ - budgetCovers scope.budget action.requestedBudget + budgetCoversAction scope.budget action.requestedBudget expression def statusSatisfied {v : Vocabulary} (policy : StatusPolicy v) (facts : EvidenceFacts v) : Prop := @@ -282,7 +271,7 @@ def evidenceRequirementsSatisfied {v : Vocabulary} def admits {v : Vocabulary} (scope : AuthorityScope v) (facts : AuthorizationFacts v) : Prop := - actionCovers scope facts.action ∧ + actionCovers scope facts.action facts.budgetExpression ∧ evidenceRequirementsSatisfied scope facts.evidence /-- Extensional semantic containment of complete authorization facts. -/ @@ -291,12 +280,14 @@ def semanticAttenuates {v : Vocabulary} ∀ facts, admits child facts → admits parent facts /-- -A chain state genuinely descends from the root it names. - -Either an accepted edge has already been applied — and `acceptedNextState` -copies the root forward, so the root was carried by that edge — or no edge has -been applied yet and the state must still *be* the root. A state with no -applied grant whose subject differs from its root descends from nothing. +The representation-level root marker consumed by the translated raw kernel. + +`lastGrant.isSome` is not historical proof by itself. Genuine ancestry is the +`AnchoredChain trusted` relation below: it starts from a root explicitly +selected by the caller's trusted context, with `root = subject` and no prior +grant, then contains only accepted edges. Shipping Rust makes raw state views +crate-private and constructs them only from `EffectiveAuthority`, while this +predicate keeps the translated evaluator exact over its representation. -/ def rooted {v : Vocabulary} (state : ChainState v) : Prop := state.lastGrant.isSome = true ∨ state.root = state.subject @@ -450,6 +441,63 @@ def acceptedNextState {v : Vocabulary} remainingDepth := grant.remainingDepth lastGrant := some grantId +/-- +Genuine ancestry: this state was reached from its own root by real delegations. + +`rooted` is a LOCAL test. It accepts `lastGrant.isSome`, which says a grant was +recorded, not that the chain descends from `root`. A state with a mismatched +root and subject and any present marker satisfies it, which is why the raw views +are sealed in `auths-authority` -- sealing makes such a state unreachable from +outside the crate without making `rooted` true. + +This predicate is what `rooted` approximates. It is inductive, so a state has it +only by construction: either it is an origin, where root and subject coincide +and nothing has been delegated yet, or it extends a state that already had it +by an edge the scope and depth dimensions accept. + +Nothing decides it -- ancestry is history, not a property of the current record +-- so it appears as a hypothesis rather than a check. +-/ +inductive ReachableFromRoot {v : Vocabulary} : ChainState v → Prop where + | origin (state : ChainState v) + (anchored : state.root = state.subject) + (undelegated : state.lastGrant = none) : + ReachableFromRoot state + | delegated {parent : ChainState v} (grantId : GrantId v) (grant : Grant v) + (reachable : ReachableFromRoot parent) + (issued : grant.issuer = parent.subject) + (checks : scopeDepthChecks parent grant) : + ReachableFromRoot (acceptedNextState parent grantId grant checks) + +/-- An origin state is rooted, so the approximation holds where it starts. -/ +theorem rooted_of_origin {v : Vocabulary} (state : ChainState v) + (anchored : state.root = state.subject) (undelegated : state.lastGrant = none) : + rooted state := by + exact Or.inr anchored + +/-- Accepting a delegation preserves reachability, by construction. -/ +theorem reachable_accepted {v : Vocabulary} + {parent : ChainState v} (grantId : GrantId v) (grant : Grant v) + (reachable : ReachableFromRoot parent) + (issued : grant.issuer = parent.subject) + (checks : scopeDepthChecks parent grant) : + ReachableFromRoot (acceptedNextState parent grantId grant checks) := + ReachableFromRoot.delegated grantId grant reachable issued checks + +/-- +Reachability implies `rooted`, but NOT the converse. + +This is the exact statement of what the local test buys: every genuinely rooted +chain passes it, so the kernel never rejects a real chain, while a state that +merely carries a present marker can pass it without being reachable. That gap +is closed by construction -- sealing the raw views -- not by the predicate. +-/ +theorem rooted_of_reachable {v : Vocabulary} {state : ChainState v} + (reachable : ReachableFromRoot state) : rooted state := by + induction reachable with + | origin state anchored _ => exact Or.inr anchored + | delegated _ _ _ _ _ _ => exact Or.inl rfl + def delegates {v : Vocabulary} (parent : ChainState v) (grantId : GrantId v) (grant : Grant v) (child : ChainState v) : Prop := @@ -469,6 +517,15 @@ inductive DelegationChain {v : Vocabulary} : (tail : DelegationChain child rest) : DelegationChain parent (child :: rest) +/-- A delegation history rooted in an identity selected by explicit context. -/ +structure AnchoredChain {v : Vocabulary} + (trusted : FiniteSet (Principal v)) + (start : ChainState v) (rest : List (ChainState v)) : Prop where + rootTrusted : start.root ∈ trusted + rootIsSubject : start.root = start.subject + noPriorGrant : start.lastGrant = none + chain : DelegationChain start rest + inductive DelegationDiagnostic where | brokenGrantChain | delegationExpanded @@ -558,7 +615,7 @@ absent request; see `budgetCoversAction`. -/ def evaluateCoverage {v : Vocabulary} (authority : ChainState v) (action : Action v) - (expression : BudgetExpression := BudgetExpression.expressible) : + (expression : BudgetExpression) : CoverageDecision := if rooted authority ∧ action.actor = authority.subject ∧ @@ -581,11 +638,26 @@ def evaluateCoverage {v : Vocabulary} .denied .brokenGrantChain def terminalCovers {v : Vocabulary} - (authority : ChainState v) (action : Action v) : Prop := + (authority : ChainState v) (action : Action v) + (expression : BudgetExpression) : Prop := rooted authority ∧ action.actor = authority.subject ∧ action.terminalGrant = authority.lastGrant ∧ - actionCovers authority.scope action + actionCovers authority.scope action expression + +/-- Trusted, effect-free resolution of budget expressibility by exact profile. -/ +abbrev BudgetExpressionRegistry (v : Vocabulary) := + Profile v → BudgetExpression + +def evaluateCoverageWithRegistry {v : Vocabulary} + (authority : ChainState v) (action : Action v) + (registry : BudgetExpressionRegistry v) : CoverageDecision := + evaluateCoverage authority action (registry action.profile) + +def terminalCoversWithRegistry {v : Vocabulary} + (authority : ChainState v) (action : Action v) + (registry : BudgetExpressionRegistry v) : Prop := + terminalCovers authority action (registry action.profile) /-- A projection carrying a proof that every field IS its semantic decision. @@ -595,11 +667,12 @@ A projection carrying a proof that every field IS its semantic decision. eleventh dimension shipped as a literal `true` and the exactness theorems were what eventually caught it. They catch a bad projection AFTER it exists. -This type makes it unconstructible. Each field below pins one dimension to the -`decide` of its rich relation, so a literal cannot be supplied without a proof -that the literal equals the semantic answer -- and no such proof exists for a -wrong literal. The reviewer's phrasing: a projection that must carry its own -certificate. +This type makes a forged projection unconstructible at the rich semantic +boundary. The generated raw carrier remains public for generated code and +vector transport, but it cannot be passed to `certifiedAccepts`. Each field +below pins one dimension to the `decide` of its rich relation, so a literal +cannot be supplied without a proof that the literal equals the semantic answer +-- and no such proof exists for a wrong literal. Adding a twelfth dimension adds a twelfth obligation here, which no existing constructor satisfies, so the compiler demands it be addressed. @@ -641,7 +714,7 @@ structure CertifiedProjection {v : Vocabulary} value.extensionsAttenuate = decide (extensionsLe (some grant.extensions) parent.scope.extensions) -def delegationProjection {v : Vocabulary} +private def rawDelegationProjection {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : Auths.Generated.AttenuationProjection where rootPreserved := decide (rootPreserved parent grant) @@ -668,11 +741,16 @@ def delegationProjection {v : Vocabulary} extensionsAttenuate := decide (extensionsLe (some grant.extensions) parent.scope.extensions) -/-- `delegationProjection` is certified: every field is its decision, by rfl. -/ -def certifiedDelegationProjection {v : Vocabulary} +/-- +The only projection derived from a semantic parent/grant is proof-carrying. A +field mutation fails here at the certificate constructor before it can reach +any rich acceptance theorem. Raw generated/vector APIs remain explicitly +outside this semantic boundary. +-/ +def delegationProjection {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : CertifiedProjection parent grant where - value := delegationProjection parent grant + value := rawDelegationProjection parent grant rootExact := rfl depthExact := rfl profileExact := rfl @@ -685,6 +763,11 @@ def certifiedDelegationProjection {v : Vocabulary} assuranceExact := rfl extensionsExact := rfl +/-- Rich acceptance consumes only a projection certified for these inputs. -/ +def certifiedAccepts {v : Vocabulary} {parent : ChainState v} {grant : Grant v} + (projection : CertifiedProjection parent grant) : Bool := + Auths.Generated.attenuationAccepts projection.value + /-- No certified projection can report a dimension the semantics deny. @@ -710,4 +793,3 @@ theorem CertifiedProjection.root_not_forgeable {v : Vocabulary} exact decide_eq_false denied end Auths.Rich - diff --git a/formal/Auths/Rich/Theorems.lean b/formal/Auths/Rich/Theorems.lean index a9d74867..58d77941 100644 --- a/formal/Auths/Rich/Theorems.lean +++ b/formal/Auths/Rich/Theorems.lean @@ -153,6 +153,21 @@ theorem budget_coverage_monotone {v : Vocabulary} simp only [budgetCovers] at covered ⊢ exact ⟨covered.1.trans order.1, covered.2.trans order.2⟩ +theorem budget_action_coverage_monotone {v : Vocabulary} + {child parent requested : Option (BudgetCeiling v)} + {expression : BudgetExpression} + (order : budgetLe child parent) + (covered : budgetCoversAction child requested expression) : + budgetCoversAction parent requested expression := by + cases requested <;> cases expression + · simpa [budgetCoversAction] using + budget_coverage_monotone order covered + · simp [budgetCoversAction] + · simpa [budgetCoversAction] using + budget_coverage_monotone order covered + · simpa [budgetCoversAction] using + budget_coverage_monotone order covered + theorem extensions_refl {v : Vocabulary} (extensions : Option (CriticalExtensions v)) : extensionsLe extensions extensions := by @@ -382,9 +397,10 @@ theorem scope_le_canonical_antisymmetry {v : Vocabulary} theorem action_coverage_downward_closed {v : Vocabulary} {child parent : AuthorityScope v} {action : Action v} + {expression : BudgetExpression} (order : structuralScopeLe child parent) - (covered : actionCovers child action) : - actionCovers parent action := by + (covered : actionCovers child action expression) : + actionCovers parent action expression := by rcases order with ⟨profileOrder, permissionOrder, validityOrder, audienceOrder, actionOrder, budgetOrder, statusOrder, assuranceOrder, extensionsOrder⟩ @@ -396,7 +412,7 @@ theorem action_coverage_downward_closed {v : Vocabulary} window_coverage_monotone validityOrder validityCovered, audienceOrder audienceCovered, action_constraint_allows_monotone actionOrder actionCovered, - budget_coverage_monotone budgetOrder budgetCovered + budget_action_coverage_monotone budgetOrder budgetCovered ⟩ theorem evidence_requirements_downward_closed {v : Vocabulary} @@ -567,6 +583,33 @@ theorem chain_preserves_rootedness {v : Vocabulary} exact delegate_preserves_rootedness edge · exact inductionHypothesis state inTail +/-- Every state in a history preserves the root selected by explicit context, +not merely an arbitrary present `lastGrant` marker. -/ +theorem anchored_chain_preserves_provenance {v : Vocabulary} + {trusted : FiniteSet (Principal v)} + {start : ChainState v} {rest : List (ChainState v)} + (anchored : AnchoredChain trusted start rest) : + ∀ state, state = start ∨ state ∈ rest → + state.root = start.root ∧ rooted state ∧ state.root ∈ trusted := by + intro state member + rcases member with rfl | inRest + · exact ⟨rfl, Or.inr anchored.rootIsSubject, anchored.rootTrusted⟩ + · have sameRoot := chain_preserves_root anchored.chain state inRest + exact ⟨sameRoot, chain_preserves_rootedness anchored.chain state inRest, + sameRoot ▸ anchored.rootTrusted⟩ + +/-- Structural freshness does not manufacture trust: any fresh self-rooted +start can form an empty chain only after a context explicitly selects it. -/ +theorem singleton_context_selects_fresh_self_rooted_start {v : Vocabulary} + (start : ChainState v) + (selfRooted : start.root = start.subject) + (fresh : start.lastGrant = none) : + AnchoredChain {start.root} start [] where + rootTrusted := by simp + rootIsSubject := selfRooted + noPriorGrant := fresh + chain := .nil start + /-- A parent that descends from no root delegates nothing, for every grant. -/ theorem unrooted_parent_delegates_nothing {v : Vocabulary} (parent : ChainState v) (grantId : GrantId v) (grant : Grant v) @@ -577,8 +620,9 @@ theorem unrooted_parent_delegates_nothing {v : Vocabulary} /-- A parent that descends from no root authorizes no action either. -/ theorem unrooted_authority_covers_nothing {v : Vocabulary} (authority : ChainState v) (action : Action v) + (expression : BudgetExpression) (unrooted : ¬ rooted authority) : - evaluateCoverage authority action = .denied .brokenGrantChain := by + evaluateCoverage authority action expression = .denied .brokenGrantChain := by simp [evaluateCoverage, unrooted] /-- @@ -588,16 +632,19 @@ inputs, so a literal `true` would refute it. -/ theorem root_dimension_is_exact {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : - (delegationProjection parent grant).rootPreserved = true ↔ + (delegationProjection parent grant).value.rootPreserved = true ↔ rootPreserved parent grant := by - simp [delegationProjection] + rw [(delegationProjection parent grant).rootExact] + simp /-- Witness that the dimension is falsifiable, stated over all inputs. -/ theorem root_dimension_false_of_foreign_issuer {v : Vocabulary} (parent : ChainState v) (grant : Grant v) (foreign : grant.issuer ≠ parent.subject) : - (delegationProjection parent grant).rootPreserved = false := by - simp [delegationProjection, rootPreserved, foreign] + (delegationProjection parent grant).value.rootPreserved = false := by + apply CertifiedProjection.root_not_forgeable + (delegationProjection parent grant) + exact fun preserved => foreign preserved.2 /-- No other attenuation dimension can rescue a broken root: acceptance is the @@ -606,9 +653,10 @@ conjunction, so the whole projection is rejected. theorem broken_root_denies_every_projection {v : Vocabulary} (parent : ChainState v) (grant : Grant v) (broken : ¬ rootPreserved parent grant) : - Auths.Generated.attenuationAccepts - (delegationProjection parent grant) = false := by - simp [Auths.Generated.attenuationAccepts, delegationProjection, broken] + certifiedAccepts (delegationProjection parent grant) = false := by + have refused := CertifiedProjection.root_not_forgeable + (delegationProjection parent grant) broken + simp [certifiedAccepts, Auths.Generated.attenuationAccepts, refused] /-! ### Critical-extension preservation @@ -624,9 +672,10 @@ cannot satisfy them. /-- The generated extension dimension reports exactly the semantic relation. -/ theorem extensions_dimension_is_exact {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : - (delegationProjection parent grant).extensionsAttenuate = true ↔ + (delegationProjection parent grant).value.extensionsAttenuate = true ↔ extensionsLe (some grant.extensions) parent.scope.extensions := by - simp [delegationProjection] + rw [(delegationProjection parent grant).extensionsExact] + simp /-- Witness that the dimension is falsifiable, stated over all inputs: a grant that @@ -637,8 +686,10 @@ theorem extensions_dimension_false_of_altered_set {v : Vocabulary} (pinned : CriticalExtensions v) (pinnedBy : parent.scope.extensions = some pinned) (altered : grant.extensions ≠ pinned) : - (delegationProjection parent grant).extensionsAttenuate = false := by - simp [delegationProjection, extensionsLe, pinnedBy, altered] + (delegationProjection parent grant).value.extensionsAttenuate = false := by + apply CertifiedProjection.extensions_not_forgeable + (delegationProjection parent grant) + simpa [extensionsLe, pinnedBy] using altered /-- No other attenuation dimension can rescue a stripped critical extension: @@ -647,9 +698,10 @@ acceptance is the conjunction, so the whole projection is rejected. theorem altered_extensions_deny_every_projection {v : Vocabulary} (parent : ChainState v) (grant : Grant v) (broken : ¬ extensionsLe (some grant.extensions) parent.scope.extensions) : - Auths.Generated.attenuationAccepts - (delegationProjection parent grant) = false := by - simp [Auths.Generated.attenuationAccepts, delegationProjection, broken] + certifiedAccepts (delegationProjection parent grant) = false := by + have refused := CertifiedProjection.extensions_not_forgeable + (delegationProjection parent grant) broken + simp [certifiedAccepts, Auths.Generated.attenuationAccepts, refused] /-- Every accepted edge preserves a pinned critical-extension set exactly. -/ theorem delegate_preserves_pinned_extensions {v : Vocabulary} @@ -736,10 +788,10 @@ theorem chain_transitive_attenuation {v : Vocabulary} theorem authorized_action_covered {v : Vocabulary} {parent child : ChainState v} {grantId : GrantId v} {grant : Grant v} - {action : Action v} + {action : Action v} {expression : BudgetExpression} (accepted : delegates parent grantId grant child) - (authorized : actionCovers child.scope action) : - actionCovers parent.scope action := + (authorized : actionCovers child.scope action expression) : + actionCovers parent.scope action expression := action_coverage_downward_closed (delegate_implies_scope_le accepted) authorized /-- @@ -758,10 +810,21 @@ and invisible here. -/ theorem rich_projection_accepts_iff_root_and_scope_depth_checks {v : Vocabulary} (parent : ChainState v) (grant : Grant v) : - Auths.Generated.attenuationAccepts - (delegationProjection parent grant) = true ↔ + certifiedAccepts (delegationProjection parent grant) = true ↔ rootPreserved parent grant ∧ scopeDepthChecks parent grant := by - simp [Auths.Generated.attenuationAccepts, delegationProjection, + simp only [certifiedAccepts, Auths.Generated.attenuationAccepts] + rw [(delegationProjection parent grant).rootExact, + (delegationProjection parent grant).depthExact, + (delegationProjection parent grant).profileExact, + (delegationProjection parent grant).permissionsExact, + (delegationProjection parent grant).validityExact, + (delegationProjection parent grant).audiencesExact, + (delegationProjection parent grant).actionConstraintExact, + (delegationProjection parent grant).budgetExact, + (delegationProjection parent grant).statusExact, + (delegationProjection parent grant).assuranceExact, + (delegationProjection parent grant).extensionsExact] + simp [ scopeDepthChecks, grantScopeChecks, GrantScopeChecks.iff_conjunction] tauto @@ -770,8 +833,7 @@ theorem apply_grant_success_iff_linked_and_projection {v : Vocabulary} (child : ChainState v) : evaluateGrant parent grantId grant = .accepted child ↔ linked parent grant ∧ - Auths.Generated.attenuationAccepts - (delegationProjection parent grant) = true ∧ + certifiedAccepts (delegationProjection parent grant) = true ∧ ∃ checks : scopeDepthChecks parent grant, child = acceptedNextState parent grantId grant checks := by rw [rich_projection_accepts_iff_root_and_scope_depth_checks] @@ -835,50 +897,62 @@ theorem author_planning_diagnostic_sound_complete {v : Vocabulary} split_ifs <;> simp_all [structuralScopeLe] theorem coverage_decision_ok_iff_covers {v : Vocabulary} - (authority : ChainState v) (action : Action v) : - evaluateCoverage authority action = .authorized ↔ - terminalCovers authority action := by + (authority : ChainState v) (action : Action v) + (expression : BudgetExpression) : + evaluateCoverage authority action expression = .authorized ↔ + terminalCovers authority action expression := by simp only [evaluateCoverage] split_ifs <;> simp_all [terminalCovers, actionCovers] theorem coverage_diagnostic_sound_complete {v : Vocabulary} - (authority : ChainState v) (action : Action v) : - (evaluateCoverage authority action = .authorized ↔ - terminalCovers authority action) ∧ - (∀ reason, evaluateCoverage authority action = .denied reason → - ¬ terminalCovers authority action) := by + (authority : ChainState v) (action : Action v) + (expression : BudgetExpression) : + (evaluateCoverage authority action expression = .authorized ↔ + terminalCovers authority action expression) ∧ + (∀ reason, evaluateCoverage authority action expression = .denied reason → + ¬ terminalCovers authority action expression) := by constructor - · exact coverage_decision_ok_iff_covers authority action + · exact coverage_decision_ok_iff_covers authority action expression · intro reason denied covered - have authorized : evaluateCoverage authority action = .authorized := - (coverage_decision_ok_iff_covers authority action).2 covered + have authorized : evaluateCoverage authority action expression = .authorized := + (coverage_decision_ok_iff_covers authority action expression).2 covered rw [authorized] at denied contradiction +/-- Registry composition binds the trusted expression to the exact action +profile; no action field or caller-supplied naked expression intervenes. -/ +theorem registry_coverage_decision_ok_iff_covers {v : Vocabulary} + (authority : ChainState v) (action : Action v) + (registry : BudgetExpressionRegistry v) : + evaluateCoverageWithRegistry authority action registry = .authorized ↔ + terminalCoversWithRegistry authority action registry := by + exact coverage_decision_ok_iff_covers + authority action (registry action.profile) + theorem translated_rich_spec_target {v : Vocabulary} (evaluateRustGrant : ChainState v → GrantId v → Grant v → DelegationDecision v) (evaluateRustCoverage : - ChainState v → Action v → CoverageDecision) + ChainState v → Action v → BudgetExpression → CoverageDecision) (grantIdentity : ∀ parent grantId grant, evaluateRustGrant parent grantId grant = evaluateGrant parent grantId grant) (coverageIdentity : - ∀ authority action, - evaluateRustCoverage authority action = - evaluateCoverage authority action) : + ∀ authority action expression, + evaluateRustCoverage authority action expression = + evaluateCoverage authority action expression) : (∀ parent grantId grant child, evaluateRustGrant parent grantId grant = .accepted child ↔ delegates parent grantId grant child) ∧ - (∀ authority action, - evaluateRustCoverage authority action = .authorized ↔ - terminalCovers authority action) := by + (∀ authority action expression, + evaluateRustCoverage authority action expression = .authorized ↔ + terminalCovers authority action expression) := by constructor · intro parent grantId grant child rw [grantIdentity, apply_grant_success_iff_delegates] - · intro authority action + · intro authority action expression rw [coverageIdentity, coverage_decision_ok_iff_covers] end Auths.Rich diff --git a/formal/Auths/Rich/Types.lean b/formal/Auths/Rich/Types.lean index e30e4a9b..a0eebc02 100644 --- a/formal/Auths/Rich/Types.lean +++ b/formal/Auths/Rich/Types.lean @@ -35,6 +35,10 @@ structure Vocabulary where grantIdDecidableEq : DecidableEq GrantIdCarrier extensionIdDecidableEq : DecidableEq ExtensionIdCarrier extensionBodyDecidableEq : DecidableEq ExtensionBodyCarrier + /-- Exact order used by Rust's derived `Ord` for extension identifiers. -/ + extensionIdLinearOrder : LinearOrder ExtensionIdCarrier + /-- Exact order used by Rust's derived `Ord` for extension payload bytes. -/ + extensionBodyLinearOrder : LinearOrder ExtensionBodyCarrier /-- Size of an extension payload, in the units Rust bounds. `CriticalExtension::new` rejects a payload longer than @@ -185,6 +189,21 @@ instance (v : Vocabulary) : DecidableEq (CriticalExtension v) := | _, isFalse different => isFalse (by intro equality; exact different (by cases equality; rfl)) +/-- Lexicographic order of Rust's derived `(ExtensionId, Vec)` order. -/ +def criticalExtensionLt {v : Vocabulary} + (left right : CriticalExtension v) : Prop := by + letI : LinearOrder v.ExtensionIdCarrier := v.extensionIdLinearOrder + letI : LinearOrder v.ExtensionBodyCarrier := v.extensionBodyLinearOrder + exact left.id.value < right.id.value ∨ + (left.id.value = right.id.value ∧ left.body.value < right.body.value) + +instance {v : Vocabulary} (left right : CriticalExtension v) : + Decidable (criticalExtensionLt left right) := by + unfold criticalExtensionLt + letI : LinearOrder v.ExtensionIdCarrier := v.extensionIdLinearOrder + letI : LinearOrder v.ExtensionBodyCarrier := v.extensionBodyLinearOrder + infer_instance + /-- Mirrors Rust `auths_model::HARD_MAX_EXTENSIONS`. -/ def hardMaxExtensions : Nat := 32 @@ -204,13 +223,14 @@ The entries are an ordered sequence rather than a `FiniteSet` deliberately. `critical_extensions_equal` compares the two canonical vectors **positionally**; a set-valued model would identify `[a, b]` with `[b, a]` and therefore report attenuation on a pair the shipping kernel denies, which is the model being -weaker than the code. Duplicate-freedom by identifier is what makes the -sequence a faithful map from identifier to payload; the total order Rust sorts -by is a representation-level fact that the opaque carriers cannot state, and -none of the decisions below depend on it. +weaker than the code. Duplicate-freedom by identifier makes the sequence a +faithful map from identifier to payload. `sorted` additionally records the +exact order Rust establishes, so this type excludes non-constructor-reachable +permutations rather than merely assuming canonicality in prose. -/ structure CriticalExtensions (v : Vocabulary) where entries : List (CriticalExtension v) + sorted : entries.Pairwise criticalExtensionLt distinctIds : entries.Pairwise fun left right => left.id ≠ right.id bounded : entries.length ≤ hardMaxExtensions /-- Every payload is within `HARD_MAX_EXTENSION_BYTES`, as @@ -237,6 +257,7 @@ instance (v : Vocabulary) : DecidableEq (CriticalExtensions v) := /-- The empty set, the value `CriticalExtensions::empty` constructs. -/ def CriticalExtensions.empty (v : Vocabulary) : CriticalExtensions v where entries := [] + sorted := List.Pairwise.nil distinctIds := List.Pairwise.nil bounded := by simp [hardMaxExtensions] bodiesBounded := by simp @@ -248,6 +269,7 @@ def CriticalExtensions.singleton {v : Vocabulary} v.extensionBodySize extension.body.value ≤ hardMaxExtensionBytes) : CriticalExtensions v where entries := [extension] + sorted := by simp distinctIds := by simp bounded := by simp [hardMaxExtensions] bodiesBounded := by simpa using bodyBounded @@ -353,8 +375,15 @@ structure EvidenceFacts (v : Vocabulary) where statusAge : Nat assurance : AssurancePolicy v +/-- Trusted profile-registry classification, never an action-controlled bit. -/ +inductive BudgetExpression where + | expressible + | inexpressible + deriving DecidableEq, Repr + structure AuthorizationFacts (v : Vocabulary) where action : Action v + budgetExpression : BudgetExpression evidence : EvidenceFacts v end Auths.Rich diff --git a/formal/Auths/Theorems.lean b/formal/Auths/Theorems.lean index 6324d691..6ea39727 100644 --- a/formal/Auths/Theorems.lean +++ b/formal/Auths/Theorems.lean @@ -1,4 +1,5 @@ import Auths.Attenuation +import Auths.Authority import Auths.Rich.Mutations import Auths.Composition import Auths.Diversity @@ -29,6 +30,7 @@ def theoremInventory : List Lean.Name := `Auths.Rich.budget_trans, `Auths.Rich.budget_antisymm, `Auths.Rich.budget_coverage_monotone, + `Auths.Rich.budget_action_coverage_monotone, `Auths.Rich.extensions_refl, `Auths.Rich.extensions_trans, `Auths.Rich.extensions_antisymm, @@ -58,6 +60,9 @@ def theoremInventory : List Lean.Name := `Auths.Rich.delegate_implies_scope_le, `Auths.Rich.delegate_preserves_root, `Auths.Rich.delegate_updates_subject_and_parent, + `Auths.Rich.anchored_chain_preserves_provenance, + `Auths.Rich.singleton_context_selects_fresh_self_rooted_start, + `Auths.Authority.chain_descends_from_one_root, `Auths.Rich.delegate_strict_depth, `Auths.Rich.delegate_never_widens, `Auths.Rich.remaining_depth_well_founded, @@ -77,10 +82,23 @@ def theoremInventory : List Lean.Name := `Auths.Rich.authority_delegate_first_failure, `Auths.Rich.author_planning_diagnostic_sound_complete, `Auths.Rich.coverage_decision_ok_iff_covers, + `Auths.Rich.rooted_of_origin, + `Auths.Rich.reachable_accepted, + `Auths.Rich.rooted_of_reachable, + `Auths.Rich.registry_coverage_decision_ok_iff_covers, `Auths.Rich.CertifiedProjection.extensions_not_forgeable, `Auths.Rich.CertifiedProjection.root_not_forgeable, `Auths.Rich.Mutations.validity_start_direction, `Auths.Rich.Mutations.validity_end_direction, + `Auths.Rich.Mutations.permission_subset_direction, + `Auths.Rich.Mutations.audience_subset_direction, + `Auths.Rich.Mutations.permission_membership_decision, + `Auths.Rich.Mutations.audience_membership_decision, + `Auths.Rich.Mutations.body_digest_membership_decision, + `Auths.Rich.Mutations.body_digest_subset_direction, + `Auths.Rich.Mutations.action_exact_equality, + `Auths.Rich.Mutations.action_constructor_fallback, + `Auths.Rich.Mutations.action_singleton_exact_rejection, `Auths.Rich.Mutations.budget_value_direction, `Auths.Rich.Mutations.budget_algebra_equality, `Auths.Rich.Mutations.optional_budget_bounded_parent, @@ -89,6 +107,8 @@ def theoremInventory : List Lean.Name := `Auths.Rich.Mutations.status_age_direction, `Auths.Rich.Mutations.status_method_equality, `Auths.Rich.Mutations.assurance_equality, + `Auths.Rich.Mutations.critical_extension_equality, + `Auths.Rich.Mutations.critical_extensions_reversed_not_sorted, `Auths.Rich.Mutations.profile_version_equality, `Auths.Rich.Mutations.principal_linkage_equality, `Auths.Rich.Mutations.grant_linkage_equality, diff --git a/formal/Auths/VectorExport.lean b/formal/Auths/VectorExport.lean index 60f53617..29723692 100644 --- a/formal/Auths/VectorExport.lean +++ b/formal/Auths/VectorExport.lean @@ -75,6 +75,8 @@ def natVocabulary : Rich.Vocabulary where grantIdDecidableEq := inferInstance extensionIdDecidableEq := inferInstance extensionBodyDecidableEq := inferInstance + extensionIdLinearOrder := inferInstance + extensionBodyLinearOrder := inferInstance extensionBodySize := fun _ => 0 def natArrayCode (values : List Nat) : String := diff --git a/formal/assurance-manifest-v1.toml b/formal/assurance-manifest-v1.toml index d1a61249..14998c0a 100644 --- a/formal/assurance-manifest-v1.toml +++ b/formal/assurance-manifest-v1.toml @@ -1,6 +1,8 @@ schema = "auths-proof-formal-assurance/v1" lean_toolchain = "leanprover/lean4:v4.31.0" toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +statement_review = "root-provenance-and-profile-version-2026-08-16-r3" +statement_inventory_sha256 = "ba4ab5f67807cb6dc8ec0f487fd8ae4aa39ec1cfcf32e8c8a9e6815fa47fd2f0" allowed_axioms = [ "Classical.choice", "Quot.sound", @@ -13,40 +15,51 @@ claim_text = "Lean proves the rich authority property: finiteSet subset refl." claim_status = "proved" lean_declaration = "Auths.Rich.finiteSet_subset_refl" lean_statement_sha256 = "cac9d4b80390ce990484b56aee1f0d20fcb8d1b2b2b8f07ef1fa819caca6982c" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -58,7 +71,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-002" @@ -66,40 +79,51 @@ claim_text = "Lean proves the rich authority property: finiteSet subset trans." claim_status = "proved" lean_declaration = "Auths.Rich.finiteSet_subset_trans" lean_statement_sha256 = "620ac85f39efb4108727cc41c2b3b7663eea53ffde7ef5d782ee2e5897e161da" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -111,7 +135,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-003" @@ -119,40 +143,51 @@ claim_text = "Lean proves the rich authority property: finiteSet subset antisymm claim_status = "proved" lean_declaration = "Auths.Rich.finiteSet_subset_antisymm" lean_statement_sha256 = "04c8f87dfb0552e7abf656cf19ca4e29a5a81369e1178eb7d9aa634966a115be" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -164,7 +199,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-004" @@ -172,40 +207,51 @@ claim_text = "Lean proves the rich authority property: finiteSet membership mono claim_status = "proved" lean_declaration = "Auths.Rich.finiteSet_membership_monotone" lean_statement_sha256 = "526bdf55ccea3aa1a07aef06197ef303b18a5c083994c97056422713106028a2" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -217,7 +263,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-005" @@ -225,40 +271,51 @@ claim_text = "Lean proves the rich authority property: window contained refl." claim_status = "proved" lean_declaration = "Auths.Rich.window_contained_refl" lean_statement_sha256 = "52b61c239b76a3ec75ebc375c19ba060e170f9c88ab845652188da7e1f32c715" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -267,7 +324,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-006" @@ -275,40 +332,51 @@ claim_text = "Lean proves the rich authority property: window contained trans." claim_status = "proved" lean_declaration = "Auths.Rich.window_contained_trans" lean_statement_sha256 = "ed63f2b8060617d8e042f212bea8d9df13b58494cd94ef01081d7c849e19de7b" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -321,7 +389,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-007" @@ -329,40 +397,51 @@ claim_text = "Lean proves the rich authority property: window contained antisymm claim_status = "proved" lean_declaration = "Auths.Rich.window_contained_antisymm" lean_statement_sha256 = "a263d89be575fd98940d6a3324ec862360cfe26b2024dd38a13b828b763aa74a" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -375,7 +454,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-008" @@ -383,40 +462,51 @@ claim_text = "Lean proves the rich authority property: window coverage monotone. claim_status = "proved" lean_declaration = "Auths.Rich.window_coverage_monotone" lean_statement_sha256 = "e8d8a9503bde767273a9b55883a18365323b88ae798ebd4de744ffd822ee9dd7" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -429,7 +519,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-009" @@ -437,40 +527,51 @@ claim_text = "Lean proves the rich authority property: action constraint refl." claim_status = "proved" lean_declaration = "Auths.Rich.action_constraint_refl" lean_statement_sha256 = "6b41a97add7b56d69dde91e7008ca41e34f568ca4081d8b81ca5b728342e5c1d" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -482,7 +583,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-010" @@ -490,40 +591,51 @@ claim_text = "Lean proves the rich authority property: action constraint trans." claim_status = "proved" lean_declaration = "Auths.Rich.action_constraint_trans" lean_statement_sha256 = "1510dabf7d9cf6d4c1bdb99de295ced7f32f3c44678519aef38aa3fb58dcdee0" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -536,7 +648,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-011" @@ -544,40 +656,51 @@ claim_text = "Lean proves the rich authority property: action constraint antisym claim_status = "proved" lean_declaration = "Auths.Rich.action_constraint_antisymm" lean_statement_sha256 = "59a7fb2812568c3a9eabd67248452de770baf4d2e1daf9ebf2dbea0baa42c81d" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -590,7 +713,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-092" @@ -598,40 +721,51 @@ claim_text = "Lean proves the rich authority property: action constraint canonic claim_status = "proved" lean_declaration = "Auths.Rich.action_constraint_canonical_antisymm" lean_statement_sha256 = "47d8802981c338d34dedf567e68555c3cca9f298dd13c681ba4b3baa927a6813" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -644,7 +778,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-012" @@ -652,40 +786,51 @@ claim_text = "Lean proves the rich authority property: action constraint allows claim_status = "proved" lean_declaration = "Auths.Rich.action_constraint_allows_monotone" lean_statement_sha256 = "49927a15dec0e627c64244b981bd81ca171589f5ada904616bcec9811746efe2" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -698,7 +843,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-013" @@ -706,40 +851,51 @@ claim_text = "Lean proves the rich authority property: budget refl." claim_status = "proved" lean_declaration = "Auths.Rich.budget_refl" lean_statement_sha256 = "54ffb1f592e48a7292cb925afd2f0e1c4c2412a46b7b5d8aeec7113fe58fc09a" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -748,7 +904,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-014" @@ -756,40 +912,51 @@ claim_text = "Lean proves the rich authority property: budget trans." claim_status = "proved" lean_declaration = "Auths.Rich.budget_trans" lean_statement_sha256 = "313fe44a88fa3efd9bc1800bf0e6169039c5bcec051b847ea86c71d27318e0ae" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -798,7 +965,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-015" @@ -806,40 +973,51 @@ claim_text = "Lean proves the rich authority property: budget antisymm." claim_status = "proved" lean_declaration = "Auths.Rich.budget_antisymm" lean_statement_sha256 = "569b03bd90e823b38193e8a9681faf27341786455644132e4861c779c8c3395b" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -848,7 +1026,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-016" @@ -856,40 +1034,51 @@ claim_text = "Lean proves the rich authority property: budget coverage monotone. claim_status = "proved" lean_declaration = "Auths.Rich.budget_coverage_monotone" lean_statement_sha256 = "594cf3b8740d3b68643756cf665f0be72b2b5b010227f5efc0407ce78fc91520" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -898,48 +1087,120 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-093" -claim_text = "Lean proves the rich authority property: extensions refl." +claim_id = "AP-FORMAL-RICH-124" +claim_text = "Lean proves the rich authority property: budget action coverage monotone." claim_status = "proved" -lean_declaration = "Auths.Rich.extensions_refl" -lean_statement_sha256 = "3800be2f50fce51d7c628d2483de6694ad58566d2b041f0529f5cd84e70cbf86" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.budget_action_coverage_monotone" +lean_statement_sha256 = "986470888869bfa94623fd5d5f4a7a72820170912004d0710b39127565ab0379" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = ["propext"] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Theorems.lean" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" + +[[claims]] +claim_id = "AP-FORMAL-RICH-093" +claim_text = "Lean proves the rich authority property: extensions refl." +claim_status = "proved" +lean_declaration = "Auths.Rich.extensions_refl" +lean_statement_sha256 = "3800be2f50fce51d7c628d2483de6694ad58566d2b041f0529f5cd84e70cbf86" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", "formal/Auths/Composition.lean", "formal/Auths/Diversity.lean", "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -948,7 +1209,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-094" @@ -956,49 +1217,60 @@ claim_text = "Lean proves the rich authority property: extensions trans." claim_status = "proved" lean_declaration = "Auths.Rich.extensions_trans" lean_statement_sha256 = "e05d4e12a80d7c0003933711f75a212208a827b787ad87787a5f5a7d5ccaab1b" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = [] +axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-095" @@ -1006,49 +1278,60 @@ claim_text = "Lean proves the rich authority property: extensions antisymm." claim_status = "proved" lean_declaration = "Auths.Rich.extensions_antisymm" lean_statement_sha256 = "812db27beb5a86265909c1950c0f45882e0c47f196776cdd03a325537310396d" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = [] +axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-096" @@ -1056,49 +1339,60 @@ claim_text = "Lean proves the rich authority property: extensions le pinned iff. claim_status = "proved" lean_declaration = "Auths.Rich.extensions_le_pinned_iff" lean_statement_sha256 = "418ef27b7822eed76553ff7210f04e96ac27312e0b8bfb6f6eee955af3cdf3b0" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = [] +axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-097" @@ -1106,40 +1400,51 @@ claim_text = "Lean proves the rich authority property: extensions le false of dr claim_status = "proved" lean_declaration = "Auths.Rich.extensions_le_false_of_dropped" lean_statement_sha256 = "50fa5e1a3b26acec511d4f6459c5de3d8e7212f221f72267169f94be8db5d4b5" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1148,7 +1453,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-098" @@ -1156,49 +1461,60 @@ claim_text = "Lean proves the rich authority property: extensions le false of al claim_status = "proved" lean_declaration = "Auths.Rich.extensions_le_false_of_altered" lean_statement_sha256 = "081d0a8e30177f84ec4917d36954711736c6ffa22b2558174d22910f3d24e76f" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = [] +axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-105" @@ -1206,40 +1522,51 @@ claim_text = "Lean proves the rich authority property: extensions le refuses a d claim_status = "proved" lean_declaration = "Auths.Rich.extensions_le_refuses_a_dropped_singleton" lean_statement_sha256 = "28264e05fa21a73a3003dcdebb08ce275b76d77dcc5e36ecba83c1386676fa58" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1251,7 +1578,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-017" @@ -1259,40 +1586,51 @@ claim_text = "Lean proves the rich authority property: status refl." claim_status = "proved" lean_declaration = "Auths.Rich.status_refl" lean_statement_sha256 = "7d5310e68d00575620e99f894080ddc254f48770537a07e1a7e4acdae0090d63" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1301,7 +1639,7 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-018" @@ -1309,40 +1647,51 @@ claim_text = "Lean proves the rich authority property: status trans." claim_status = "proved" lean_declaration = "Auths.Rich.status_trans" lean_statement_sha256 = "7e9d6580025807c2b2c984e8b167029b84ac610f9dcd280179e4c176872a04a1" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1351,7 +1700,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-019" @@ -1359,40 +1708,51 @@ claim_text = "Lean proves the rich authority property: status antisymm." claim_status = "proved" lean_declaration = "Auths.Rich.status_antisymm" lean_statement_sha256 = "a3badc32c1fcd22da760494c3c6eb256e637312f34fa7691591e7283f504867e" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1404,7 +1764,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-020" @@ -1412,40 +1772,51 @@ claim_text = "Lean proves the rich authority property: status satisfaction monot claim_status = "proved" lean_declaration = "Auths.Rich.status_satisfaction_monotone" lean_statement_sha256 = "7f5f3cfc2e90f4a2bdf90499b6b596a0711d27f3668d3d99bcf8c5a0a146e022" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1454,7 +1825,7 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-021" @@ -1462,40 +1833,51 @@ claim_text = "Lean proves the rich authority property: profile refl." claim_status = "proved" lean_declaration = "Auths.Rich.profile_refl" lean_statement_sha256 = "c256b640094e0458a6cb336c78c358b8f35906d7838c86142f649c4d7c3cf9ea" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1507,7 +1889,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-022" @@ -1515,40 +1897,51 @@ claim_text = "Lean proves the rich authority property: profile trans." claim_status = "proved" lean_declaration = "Auths.Rich.profile_trans" lean_statement_sha256 = "bc702fae63c302ea1b30323b66eaa633f2c489dd14a84bda0802852888da4e5d" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1560,7 +1953,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-023" @@ -1568,40 +1961,51 @@ claim_text = "Lean proves the rich authority property: profile antisymm." claim_status = "proved" lean_declaration = "Auths.Rich.profile_antisymm" lean_statement_sha256 = "306251093aa948294a625b5fd0e2707c52d9d391f172185ca36949ee0bd022ba" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1613,7 +2017,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-024" @@ -1621,40 +2025,51 @@ claim_text = "Lean proves the rich authority property: profile coverage monotone claim_status = "proved" lean_declaration = "Auths.Rich.profile_coverage_monotone" lean_statement_sha256 = "fa6e1ec9008031df18e845bb6457030c5169bff159a30e5a30e4be9d7b445c16" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1666,7 +2081,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-025" @@ -1674,40 +2089,51 @@ claim_text = "Lean proves the rich authority property: structural scope le refl. claim_status = "proved" lean_declaration = "Auths.Rich.structural_scope_le_refl" lean_statement_sha256 = "e61ced1d2049cd8c84935cdde0858c82d8feb3b040f90d03ced37a27f740d874" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", - "formal/Auths/Product/Arithmetic.lean", - "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", - "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", - "formal/Auths/Refinement/Production.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", "formal/Auths/Composition.lean", "formal/Auths/Diversity.lean", "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1719,7 +2145,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-026" @@ -1727,40 +2153,51 @@ claim_text = "Lean proves the rich authority property: structural scope le trans claim_status = "proved" lean_declaration = "Auths.Rich.structural_scope_le_trans" lean_statement_sha256 = "35da0ff881c83e4a3077b07e6f8adb8930d92a72b1327b9781d6a0054b63886e" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1773,7 +2210,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-027" @@ -1781,40 +2218,51 @@ claim_text = "Lean proves the rich authority property: scope le canonical antisy claim_status = "proved" lean_declaration = "Auths.Rich.scope_le_canonical_antisymmetry" lean_statement_sha256 = "3da1337f88245aa5d26623cede90981ab56d0a541a02d164bf5f685781ea9911" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1827,48 +2275,59 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-028" claim_text = "Lean proves the rich authority property: action coverage downward closed." claim_status = "proved" lean_declaration = "Auths.Rich.action_coverage_downward_closed" -lean_statement_sha256 = "966cd95b7984d06ef0db93a5319c757c2510dc98965e6fe5234774804877a941" -formal_review = "rich-authority-refinement-2026-07-29" +lean_statement_sha256 = "331951b52f7852137f1462190a57bb951656b6ad4dadc13625e080ab4acda349" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1881,7 +2340,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-029" @@ -1889,40 +2348,51 @@ claim_text = "Lean proves the rich authority property: evidence requirements dow claim_status = "proved" lean_declaration = "Auths.Rich.evidence_requirements_downward_closed" lean_statement_sha256 = "2beb990df92aeb12bc99e21b5d4547630d571c3467658116dcba648bae91589b" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1934,7 +2404,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-030" @@ -1942,40 +2412,51 @@ claim_text = "Lean proves the rich authority property: complete admission downwa claim_status = "proved" lean_declaration = "Auths.Rich.complete_admission_downward_closed" lean_statement_sha256 = "6a8eb24b2e620903dc1682cb63c6dd3922b425e560441ca3db16127a8dab285b" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -1988,7 +2469,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-031" @@ -1996,40 +2477,51 @@ claim_text = "Lean proves the rich authority property: semantic attenuation preo claim_status = "proved" lean_declaration = "Auths.Rich.semantic_attenuation_preorder_refl" lean_statement_sha256 = "8c15f5f719bec44ca71e44f53ccc6c900cf26c67f9f8b9e3f8ef7cb8e52618f6" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2041,7 +2533,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-032" @@ -2049,40 +2541,51 @@ claim_text = "Lean proves the rich authority property: semantic attenuation preo claim_status = "proved" lean_declaration = "Auths.Rich.semantic_attenuation_preorder_trans" lean_statement_sha256 = "eadb4979eaede05d4d8262f1d47d02f0d63c5a1e76bd296e22dceb46e789d79d" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2094,7 +2597,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-033" @@ -2102,40 +2605,51 @@ claim_text = "Lean proves the rich authority property: structural scope le impli claim_status = "proved" lean_declaration = "Auths.Rich.structural_scope_le_implies_semantic_attenuation" lean_statement_sha256 = "763897035b5b8b85931035453db38a19135cd78f9e8071be632e99774849650c" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2148,7 +2662,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-034" @@ -2156,40 +2670,51 @@ claim_text = "Lean proves the rich authority property: scope semantic equivalenc claim_status = "proved" lean_declaration = "Auths.Rich.scope_semantic_equivalence" lean_statement_sha256 = "f1ffad1790150c98ed431798970479dc669fe52189b171385ea33ab1650f872e" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2201,7 +2726,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-035" @@ -2209,40 +2734,51 @@ claim_text = "Lean proves the rich authority property: structural scope le decid claim_status = "proved" lean_declaration = "Auths.Rich.structural_scope_le_decides_declared_v1_relation" lean_statement_sha256 = "b0370bdf959189b5146c84c956e48eba5b9ab855985c8b3724ec82bb1c684f3e" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2255,7 +2791,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-036" @@ -2263,40 +2799,51 @@ claim_text = "Lean proves the rich authority property: delegate implies scope le claim_status = "proved" lean_declaration = "Auths.Rich.delegate_implies_scope_le" lean_statement_sha256 = "de367444bfa8ef53140e157da7429ec957d0a7f6ccc17f80255ef3a93e3fe104" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2308,7 +2855,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-037" @@ -2316,40 +2863,51 @@ claim_text = "Lean proves the rich authority property: delegate preserves root." claim_status = "proved" lean_declaration = "Auths.Rich.delegate_preserves_root" lean_statement_sha256 = "60648d6a9993223e4261470f7d55b13fb2cc0407d28277d7b74abfcf8c468c06" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2361,7 +2919,7 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-038" @@ -2369,40 +2927,51 @@ claim_text = "Lean proves the rich authority property: delegate updates subject claim_status = "proved" lean_declaration = "Auths.Rich.delegate_updates_subject_and_parent" lean_statement_sha256 = "0db5c5d208556a219fb461f7bc0909873bad9277dad0de06413ca8b01aec8e16" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2414,48 +2983,59 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-039" -claim_text = "Lean proves the rich authority property: delegate strict depth." +claim_id = "AP-FORMAL-RICH-125" +claim_text = "Lean proves the rich authority property: anchored chain preserves provenance." claim_status = "proved" -lean_declaration = "Auths.Rich.delegate_strict_depth" -lean_statement_sha256 = "ded87036b3807b9f99d31cd220f2fa61a0b84d74d76bea27f7b2419ea6ba777a" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.anchored_chain_preserves_provenance" +lean_statement_sha256 = "be6040737a1a7e5bb6d875a0327fb9c7cab9ac357ca43573df52013e7a7eb14f" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2467,48 +3047,59 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-040" -claim_text = "Lean proves the rich authority property: delegate never widens." +claim_id = "AP-FORMAL-RICH-139" +claim_text = "Lean proves the rich authority property: singleton context selects fresh self rooted start." claim_status = "proved" -lean_declaration = "Auths.Rich.delegate_never_widens" -lean_statement_sha256 = "de367444bfa8ef53140e157da7429ec957d0a7f6ccc17f80255ef3a93e3fe104" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.singleton_context_selects_fresh_self_rooted_start" +lean_statement_sha256 = "001c165947b6631b0df1502520ca69228e8518b1858de597948cd9eb86b7e041" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2520,48 +3111,59 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-041" -claim_text = "Lean proves the rich authority property: remaining depth well founded." +claim_id = "AP-FORMAL-RICH-126" +claim_text = "Lean proves the rich authority property: chain descends from one root." claim_status = "proved" -lean_declaration = "Auths.Rich.remaining_depth_well_founded" -lean_statement_sha256 = "dcf9ed58cbeb7f0b6d9c7fa332d1b3f696f0114cee5ab770d2ad31b27d8698eb" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Authority.chain_descends_from_one_root" +lean_statement_sha256 = "be6040737a1a7e5bb6d875a0327fb9c7cab9ac357ca43573df52013e7a7eb14f" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2572,49 +3174,60 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +artifact = "formal/Auths/Authority.lean" +sha256 = "ca34648949f80c081c33c59f6066ef5393e032dacc137680a243cf7d643d250c" [[claims]] -claim_id = "AP-FORMAL-RICH-042" -claim_text = "Lean proves the rich authority property: finite delegation chain." +claim_id = "AP-FORMAL-RICH-039" +claim_text = "Lean proves the rich authority property: delegate strict depth." claim_status = "proved" -lean_declaration = "Auths.Rich.finite_delegation_chain" -lean_statement_sha256 = "be71ae8cda95eacce7a2b8249bc41432bd1927b5739bcc7e44cd69227fe2bd5f" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.delegate_strict_depth" +lean_statement_sha256 = "ded87036b3807b9f99d31cd220f2fa61a0b84d74d76bea27f7b2419ea6ba777a" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2626,210 +3239,251 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-043" -claim_text = "Lean proves the rich authority property: chain transitive attenuation." +claim_id = "AP-FORMAL-RICH-040" +claim_text = "Lean proves the rich authority property: delegate never widens." claim_status = "proved" -lean_declaration = "Auths.Rich.chain_transitive_attenuation" -lean_statement_sha256 = "4b27f3c343e686e05192449049a211a9c1b680ebdc8192d50672e90fc79a0c00" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.delegate_never_widens" +lean_statement_sha256 = "de367444bfa8ef53140e157da7429ec957d0a7f6ccc17f80255ef3a93e3fe104" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", - "Classical.choice", "Quot.sound", ] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-044" -claim_text = "Lean proves the rich authority property: authorized action covered." +claim_id = "AP-FORMAL-RICH-041" +claim_text = "Lean proves the rich authority property: remaining depth well founded." claim_status = "proved" -lean_declaration = "Auths.Rich.authorized_action_covered" -lean_statement_sha256 = "c39a96f75c9dc2d3d536b9749bbd7b27a5f54acda84f831b8eb05ea0187fa641" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.remaining_depth_well_founded" +lean_statement_sha256 = "dcf9ed58cbeb7f0b6d9c7fa332d1b3f696f0114cee5ab770d2ad31b27d8698eb" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", - "Classical.choice", "Quot.sound", ] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-099" -claim_text = "Lean proves the rich authority property: extensions dimension is exact." +claim_id = "AP-FORMAL-RICH-042" +claim_text = "Lean proves the rich authority property: finite delegation chain." claim_status = "proved" -lean_declaration = "Auths.Rich.extensions_dimension_is_exact" -lean_statement_sha256 = "8954ec95cd557c36066dbab34caf5c84638f875323780f657196567ae9135b6c" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.finite_delegation_chain" +lean_statement_sha256 = "be71ae8cda95eacce7a2b8249bc41432bd1927b5739bcc7e44cd69227fe2bd5f" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", - "Classical.choice", "Quot.sound", ] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-100" -claim_text = "Lean proves the rich authority property: extensions dimension false of altered set." +claim_id = "AP-FORMAL-RICH-043" +claim_text = "Lean proves the rich authority property: chain transitive attenuation." claim_status = "proved" -lean_declaration = "Auths.Rich.extensions_dimension_false_of_altered_set" -lean_statement_sha256 = "96f843f9c457e944659f96f87994aa6c1d2067d8d180d724a5e2e15f81f446f8" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.chain_transitive_attenuation" +lean_statement_sha256 = "4b27f3c343e686e05192449049a211a9c1b680ebdc8192d50672e90fc79a0c00" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2842,48 +3496,59 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-101" -claim_text = "Lean proves the rich authority property: altered extensions deny every projection." +claim_id = "AP-FORMAL-RICH-044" +claim_text = "Lean proves the rich authority property: authorized action covered." claim_status = "proved" -lean_declaration = "Auths.Rich.altered_extensions_deny_every_projection" -lean_statement_sha256 = "ee960b0abe0e40585a1c00b7bbccc19f72d2b6d996243e60a87ce927c596cc36" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.authorized_action_covered" +lean_statement_sha256 = "1e7f55dd8b9323aafe760603e16645ca67e8bdfc6ac08a621ef2ebd2b3049e51" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -2896,154 +3561,189 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-102" -claim_text = "Lean proves the rich authority property: delegate preserves pinned extensions." +claim_id = "AP-FORMAL-RICH-099" +claim_text = "Lean proves the rich authority property: extensions dimension is exact." claim_status = "proved" -lean_declaration = "Auths.Rich.delegate_preserves_pinned_extensions" -lean_statement_sha256 = "8da584848a272ae46a9e3e6e262e9d2eaead2b7b57f02b27c8963f0f6760e8d4" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.extensions_dimension_is_exact" +lean_statement_sha256 = "b8043a7f9d8e364111fc2ac33ba0a8cd8d0e03e0790f94640385290e96fa7ab3" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", - "formal/Auths/Product/Arithmetic.lean", - "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", - "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", - "formal/Auths/Refinement/Production.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", "formal/Auths/Composition.lean", "formal/Auths/Diversity.lean", "formal/Auths/Generated/Algebra.lean", - "formal/Auths/Theorems.lean", - "formal/qualification/aeneas/generated/authority/Funs.lean", - "formal/qualification/aeneas/generated/authority/Types.lean", - "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", + "Classical.choice", "Quot.sound", ] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-103" -claim_text = "Lean proves the rich authority property: chain preserves pinned extensions." +claim_id = "AP-FORMAL-RICH-100" +claim_text = "Lean proves the rich authority property: extensions dimension false of altered set." claim_status = "proved" -lean_declaration = "Auths.Rich.chain_preserves_pinned_extensions" -lean_statement_sha256 = "bff321e42864cf75665958de0721ddf808ae78207f1e090c86d71138540375fe" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.extensions_dimension_false_of_altered_set" +lean_statement_sha256 = "630491f9b735fdb3bc4b39089369c06896a4d14e7f9a0852502132df8d5a8f11" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", + "Classical.choice", "Quot.sound", ] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-045" -claim_text = "Lean proves the rich authority property: rich projection accepts iff root preservation and scope depth checks." +claim_id = "AP-FORMAL-RICH-101" +claim_text = "Lean proves the rich authority property: altered extensions deny every projection." claim_status = "proved" -lean_declaration = "Auths.Rich.rich_projection_accepts_iff_root_and_scope_depth_checks" -lean_statement_sha256 = "a1e6551c5196120585c745f72b806cb4c977454495e1901ecaba1e5908613930" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.altered_extensions_deny_every_projection" +lean_statement_sha256 = "ab6c08d4192e7f474881a629866bf04321b658e6e962716ab5c9f157f8cdff1d" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3056,156 +3756,187 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-046" -claim_text = "Lean proves the rich authority property: apply grant success iff linked and projection." +claim_id = "AP-FORMAL-RICH-102" +claim_text = "Lean proves the rich authority property: delegate preserves pinned extensions." claim_status = "proved" -lean_declaration = "Auths.Rich.apply_grant_success_iff_linked_and_projection" -lean_statement_sha256 = "9c17c930b5cfce57ccda484c98a7a54dd1c8ab4124dc46eba1ba315566538e2f" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.delegate_preserves_pinned_extensions" +lean_statement_sha256 = "8da584848a272ae46a9e3e6e262e9d2eaead2b7b57f02b27c8963f0f6760e8d4" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", - "Classical.choice", "Quot.sound", ] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-047" -claim_text = "Lean proves the rich authority property: apply grant success iff delegates." +claim_id = "AP-FORMAL-RICH-103" +claim_text = "Lean proves the rich authority property: chain preserves pinned extensions." claim_status = "proved" -lean_declaration = "Auths.Rich.apply_grant_success_iff_delegates" -lean_statement_sha256 = "776ca2044cca309f3dd815c4ada85d6ebbfd3aed142e71e76de00d4b1a344bb8" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.chain_preserves_pinned_extensions" +lean_statement_sha256 = "bff321e42864cf75665958de0721ddf808ae78207f1e090c86d71138540375fe" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", - "Classical.choice", "Quot.sound", ] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-048" -claim_text = "Lean proves the rich authority property: apply grant success unique." +claim_id = "AP-FORMAL-RICH-045" +claim_text = "Lean proves the rich authority property: rich projection accepts iff root preservation and scope depth checks." claim_status = "proved" -lean_declaration = "Auths.Rich.apply_grant_success_unique" -lean_statement_sha256 = "d93df7b633eb7224af71b294f781d7f9bd5e21b6321f88325bf2d116cbcc97f8" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.rich_projection_accepts_iff_root_and_scope_depth_checks" +lean_statement_sha256 = "34c1ebc88d5449d5ef3ee3205bc5e0e209fcd4a1d170df5b86b5704c6022614e" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3218,48 +3949,59 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-049" -claim_text = "Lean proves the rich authority property: authority delegate diagnostic sound complete." +claim_id = "AP-FORMAL-RICH-046" +claim_text = "Lean proves the rich authority property: apply grant success iff linked and projection." claim_status = "proved" -lean_declaration = "Auths.Rich.authority_delegate_diagnostic_sound_complete" -lean_statement_sha256 = "1ddc7be4ce1e4b31ebafbb6184a273dde09cb366054712f003d43ab6f3b0c1a6" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.apply_grant_success_iff_linked_and_projection" +lean_statement_sha256 = "cba0ce17a7febb85f9e39f1eab432dda476bf8afd27b9292f40ff9aaeccf64b4" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3272,48 +4014,59 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-050" -claim_text = "Lean proves the rich authority property: authority delegate first failure." +claim_id = "AP-FORMAL-RICH-047" +claim_text = "Lean proves the rich authority property: apply grant success iff delegates." claim_status = "proved" -lean_declaration = "Auths.Rich.authority_delegate_first_failure" -lean_statement_sha256 = "1ff9079797c906e667ee6e2a83e62c2f6890b454fbd01a300e91368781b59794" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.apply_grant_success_iff_delegates" +lean_statement_sha256 = "776ca2044cca309f3dd815c4ada85d6ebbfd3aed142e71e76de00d4b1a344bb8" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3326,48 +4079,59 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-051" -claim_text = "Lean proves the rich authority property: author planning diagnostic sound complete." +claim_id = "AP-FORMAL-RICH-048" +claim_text = "Lean proves the rich authority property: apply grant success unique." claim_status = "proved" -lean_declaration = "Auths.Rich.author_planning_diagnostic_sound_complete" -lean_statement_sha256 = "818f74284046e1a4e389b3388f8626218630bd2ab9271bcdacf1454c24562b28" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.apply_grant_success_unique" +lean_statement_sha256 = "d93df7b633eb7224af71b294f781d7f9bd5e21b6321f88325bf2d116cbcc97f8" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3380,101 +4144,124 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-052" -claim_text = "Lean proves the rich authority property: coverage decision ok iff covers." +claim_id = "AP-FORMAL-RICH-049" +claim_text = "Lean proves the rich authority property: authority delegate diagnostic sound complete." claim_status = "proved" -lean_declaration = "Auths.Rich.coverage_decision_ok_iff_covers" -lean_statement_sha256 = "8ccb1400406fde542fab87855806759b0e8ee0eb0382e85630a1295f460836b8" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.authority_delegate_diagnostic_sound_complete" +lean_statement_sha256 = "1ddc7be4ce1e4b31ebafbb6184a273dde09cb366054712f003d43ab6f3b0c1a6" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", + "Classical.choice", "Quot.sound", ] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-122" -claim_text = "Lean proves the rich authority property: extensions not forgeable." +claim_id = "AP-FORMAL-RICH-050" +claim_text = "Lean proves the rich authority property: authority delegate first failure." claim_status = "proved" -lean_declaration = "Auths.Rich.CertifiedProjection.extensions_not_forgeable" -lean_statement_sha256 = "b6037af4490fc1d541c162817c0b3751561b1d2fd08f5250903aaef200714073" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.authority_delegate_first_failure" +lean_statement_sha256 = "1ff9079797c906e667ee6e2a83e62c2f6890b454fbd01a300e91368781b59794" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3487,48 +4274,59 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-123" -claim_text = "Lean proves the rich authority property: root not forgeable." +claim_id = "AP-FORMAL-RICH-051" +claim_text = "Lean proves the rich authority property: author planning diagnostic sound complete." claim_status = "proved" -lean_declaration = "Auths.Rich.CertifiedProjection.root_not_forgeable" -lean_statement_sha256 = "efe4847e1d4fef7f69aa59aaaf160fb5cf08b27df5fe70b5751d6f4e25f8cd56" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.author_planning_diagnostic_sound_complete" +lean_statement_sha256 = "818f74284046e1a4e389b3388f8626218630bd2ab9271bcdacf1454c24562b28" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -3541,698 +4339,2060 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-109" -claim_text = "Lean proves the rich authority property: validity start direction." +claim_id = "AP-FORMAL-RICH-052" +claim_text = "Lean proves the rich authority property: coverage decision ok iff covers." claim_status = "proved" -lean_declaration = "Auths.Rich.Mutations.validity_start_direction" -lean_statement_sha256 = "5957401f73afdd064a7e9417bd53a2b656062668ae958dce6480219474359d3d" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.coverage_decision_ok_iff_covers" +lean_statement_sha256 = "e3f46b6062a781136d4260459cfef453a581441e5ad2209deb6d636109eb4f3b" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = [] +axioms = [ + "propext", + "Quot.sound", +] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-110" -claim_text = "Lean proves the rich authority property: validity end direction." +claim_id = "AP-FORMAL-RICH-140" +claim_text = "Lean proves the rich authority property: rooted of origin." claim_status = "proved" -lean_declaration = "Auths.Rich.Mutations.validity_end_direction" -lean_statement_sha256 = "0d2b1b547a469aada5a0922ed7b76d45e4bb9ec981f78a0ff964aacb3d2e8848" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.rooted_of_origin" +lean_statement_sha256 = "cc3365c691362e7ebb9230d6a09d9213eed4cd9e1630253ce9ecb85809d54340" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = [] - -[[claims.evidence]] +axioms = [ + "propext", + "Quot.sound", +] + +[[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +artifact = "formal/Auths/Rich/Semantics.lean" +sha256 = "ca5b30e65ecbfd2f964da3c64e02130fa680b64799ca739755819b92d9832c88" [[claims]] -claim_id = "AP-FORMAL-RICH-111" -claim_text = "Lean proves the rich authority property: budget value direction." +claim_id = "AP-FORMAL-RICH-141" +claim_text = "Lean proves the rich authority property: reachable accepted." claim_status = "proved" -lean_declaration = "Auths.Rich.Mutations.budget_value_direction" -lean_statement_sha256 = "606d22e22f9cfe1a1ffd073e58fda5275e08d8a3caf01816a740774bc06484fa" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.reachable_accepted" +lean_statement_sha256 = "fc66f95a6006a8c01d0835516389e3b76a35e2043d7fb2ac4f781c917515de0a" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = ["propext"] +axioms = [ + "propext", + "Quot.sound", +] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +artifact = "formal/Auths/Rich/Semantics.lean" +sha256 = "ca5b30e65ecbfd2f964da3c64e02130fa680b64799ca739755819b92d9832c88" [[claims]] -claim_id = "AP-FORMAL-RICH-112" -claim_text = "Lean proves the rich authority property: budget algebra equality." +claim_id = "AP-FORMAL-RICH-142" +claim_text = "Lean proves the rich authority property: rooted of reachable." claim_status = "proved" -lean_declaration = "Auths.Rich.Mutations.budget_algebra_equality" -lean_statement_sha256 = "474d87063541e58e75537a77553c64e312e3cfba3db14be37078f00f8ccdc6eb" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.rooted_of_reachable" +lean_statement_sha256 = "0236fa4396c932d0f43b9f88714271cbb627a70ae0d7b0a4ff28410a24c18103" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = ["propext"] +axioms = [ + "propext", + "Quot.sound", +] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +artifact = "formal/Auths/Rich/Semantics.lean" +sha256 = "ca5b30e65ecbfd2f964da3c64e02130fa680b64799ca739755819b92d9832c88" [[claims]] -claim_id = "AP-FORMAL-RICH-113" -claim_text = "Lean proves the rich authority property: optional budget bounded parent." +claim_id = "AP-FORMAL-RICH-127" +claim_text = "Lean proves the rich authority property: registry coverage decision ok iff covers." claim_status = "proved" -lean_declaration = "Auths.Rich.Mutations.optional_budget_bounded_parent" -lean_statement_sha256 = "b9a921cf80834acb58618e56f485ff23f9399945c2369898fb144dce8f78f2f1" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.registry_coverage_decision_ok_iff_covers" +lean_statement_sha256 = "6114eb401569131b3c3c772dffbd987ebe50c8ac1a06c6da85cae0d32b83776b" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = ["propext"] +axioms = [ + "propext", + "Quot.sound", +] [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] -claim_id = "AP-FORMAL-RICH-114" -claim_text = "Lean proves the rich authority property: optional budget no request." +claim_id = "AP-FORMAL-RICH-122" +claim_text = "Lean proves the rich authority property: extensions not forgeable." claim_status = "proved" -lean_declaration = "Auths.Rich.Mutations.optional_budget_no_request" -lean_statement_sha256 = "bb303fb19de165443326afa1f210414339f6ae63faafec2dcb5bc6aa40c15a25" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.CertifiedProjection.extensions_not_forgeable" +lean_statement_sha256 = "b6037af4490fc1d541c162817c0b3751561b1d2fd08f5250903aaef200714073" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = ["propext"] +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +artifact = "formal/Auths/Rich/Semantics.lean" +sha256 = "ca5b30e65ecbfd2f964da3c64e02130fa680b64799ca739755819b92d9832c88" [[claims]] -claim_id = "AP-FORMAL-RICH-115" -claim_text = "Lean proves the rich authority property: delegation depth strictness." +claim_id = "AP-FORMAL-RICH-123" +claim_text = "Lean proves the rich authority property: root not forgeable." claim_status = "proved" -lean_declaration = "Auths.Rich.Mutations.delegation_depth_strictness" -lean_statement_sha256 = "d5b8849156e28dd8593cc4f628f787b7c989ced64c103b742f492ef3ad3b0912" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.CertifiedProjection.root_not_forgeable" +lean_statement_sha256 = "efe4847e1d4fef7f69aa59aaaf160fb5cf08b27df5fe70b5751d6f4e25f8cd56" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = [] +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +artifact = "formal/Auths/Rich/Semantics.lean" +sha256 = "ca5b30e65ecbfd2f964da3c64e02130fa680b64799ca739755819b92d9832c88" [[claims]] -claim_id = "AP-FORMAL-RICH-116" -claim_text = "Lean proves the rich authority property: status age direction." +claim_id = "AP-FORMAL-RICH-109" +claim_text = "Lean proves the rich authority property: validity start direction." claim_status = "proved" -lean_declaration = "Auths.Rich.Mutations.status_age_direction" -lean_statement_sha256 = "d0f4c5d955c539cd08423f74f5c5b41656e9ed6ef7f963a442b82c0927a05073" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.Mutations.validity_start_direction" +lean_statement_sha256 = "b131c8323adfe2d1fdfb276e8b57d8ecbeeadc7ba76c42e142e9e60a1c7c8bd0" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = ["propext"] +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" [[claims]] -claim_id = "AP-FORMAL-RICH-117" -claim_text = "Lean proves the rich authority property: status method equality." +claim_id = "AP-FORMAL-RICH-110" +claim_text = "Lean proves the rich authority property: validity end direction." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.validity_end_direction" +lean_statement_sha256 = "64a8422660d220c34ccf2286b3d2b656caee30d180efb50390b98109f012cb9b" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-128" +claim_text = "Lean proves the rich authority property: permission subset direction." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.permission_subset_direction" +lean_statement_sha256 = "460b9c2c8b40e2c47974aa6f40439a0921401af0a741e636dca7e8ce1e1128cb" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-129" +claim_text = "Lean proves the rich authority property: audience subset direction." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.audience_subset_direction" +lean_statement_sha256 = "1efdeced0d7cc63e210e3eed78bf72af7188d0b4b89988645067c065df38d4ca" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-130" +claim_text = "Lean proves the rich authority property: permission membership decision." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.permission_membership_decision" +lean_statement_sha256 = "5562c113f4b1600e9fa2e351daefdf7ad676c95399913cfaf5366a4d02693859" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-131" +claim_text = "Lean proves the rich authority property: audience membership decision." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.audience_membership_decision" +lean_statement_sha256 = "5562c113f4b1600e9fa2e351daefdf7ad676c95399913cfaf5366a4d02693859" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-132" +claim_text = "Lean proves the rich authority property: body digest membership decision." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.body_digest_membership_decision" +lean_statement_sha256 = "40e8f579841509c93f52d9b1e8ceeb0068d9deee142398402b148d5dcab74f52" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-133" +claim_text = "Lean proves the rich authority property: body digest subset direction." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.body_digest_subset_direction" +lean_statement_sha256 = "6f6d71aafc31d6e2f5f5d6b41450fab50ccbcc47ea24ff1a4d10a06f547e7a3b" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-134" +claim_text = "Lean proves the rich authority property: action exact equality." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.action_exact_equality" +lean_statement_sha256 = "12d3ee8161fd73a2b9a4c289c074a70dbe838e01889bae915eb1a28a5e0b5cbf" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-135" +claim_text = "Lean proves the rich authority property: action constructor fallback." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.action_constructor_fallback" +lean_statement_sha256 = "52a8700eb9047be465715d24eb7cce83a544f68beb675fc0ca746b20adcef29d" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-136" +claim_text = "Lean proves the rich authority property: action singleton exact rejection." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.action_singleton_exact_rejection" +lean_statement_sha256 = "f748a54bcd87a1105b376fd789d539dcba915c55b382c4d84d0764697544e403" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-111" +claim_text = "Lean proves the rich authority property: budget value direction." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.budget_value_direction" +lean_statement_sha256 = "e5c3c785f61156754ca58b613f917897b7204e66cf2904be240c6ae56dc234d5" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-112" +claim_text = "Lean proves the rich authority property: budget algebra equality." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.budget_algebra_equality" +lean_statement_sha256 = "856f719d1d4fe6a2b19dab9c3bcfd40873e74a55abaf10f945308fbb05b95652" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-113" +claim_text = "Lean proves the rich authority property: optional budget bounded parent." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.optional_budget_bounded_parent" +lean_statement_sha256 = "35b7c28a001e2386a652f5e174baa301a265a3eadd74a8c68c3b43d6d0cde025" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-114" +claim_text = "Lean proves the rich authority property: optional budget no request." claim_status = "proved" -lean_declaration = "Auths.Rich.Mutations.status_method_equality" -lean_statement_sha256 = "3c8532fcf048f8f03103bd109895ba8fe6b950d01b9cb68a2ef8e48e07035013" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.Mutations.optional_budget_no_request" +lean_statement_sha256 = "b661893af19ee64775f156871b07d71fd53478d22876735146530e3d0d624299" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-115" +claim_text = "Lean proves the rich authority property: delegation depth strictness." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.delegation_depth_strictness" +lean_statement_sha256 = "7c203a21398399f9e247a106c21958e147051c105775efb5696c6103d3fcef53" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-116" +claim_text = "Lean proves the rich authority property: status age direction." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.status_age_direction" +lean_statement_sha256 = "bcd2dc8a61a687aa05525983f0a82d8eb2c30f82bd8714e98f4538219ffa4018" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-117" +claim_text = "Lean proves the rich authority property: status method equality." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.status_method_equality" +lean_statement_sha256 = "65eb51069ff53dc4a198806e1fd34df0d06c718b805fe98af2c9ce6b81818c8f" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", "formal/Auths/Rich/Semantics.lean", "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-118" +claim_text = "Lean proves the rich authority property: assurance equality." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.assurance_equality" +lean_statement_sha256 = "682a5d15cb1ed38aafddcb2a5e0e987609428320b263e5bbae4cbea456d9d8ca" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", + "formal/Auths/Product/Arithmetic.lean", "formal/Auths/Product/Commitment.lean", + "formal/Auths/Product/Eligibility.lean", + "formal/Auths/Product/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", + "formal/Auths/Refinement/Production.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", + "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/Types.lean", +] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." +residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] +toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] + +[[claims.evidence]] +kind = "lean-proof" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" + +[[claims]] +claim_id = "AP-FORMAL-RICH-137" +claim_text = "Lean proves the rich authority property: critical extension equality." +claim_status = "proved" +lean_declaration = "Auths.Rich.Mutations.critical_extension_equality" +lean_statement_sha256 = "23f565b7d27ba7241114c8362d3604ec74ed08a4e05b82d5a97dce2500c87970" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" +rust_symbols = [] +semantic_source_closure = [ + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", + "formal/Auths/Attenuation.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = ["propext"] +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" [[claims]] -claim_id = "AP-FORMAL-RICH-118" -claim_text = "Lean proves the rich authority property: assurance equality." +claim_id = "AP-FORMAL-RICH-138" +claim_text = "Lean proves the rich authority property: critical extensions reversed not sorted." claim_status = "proved" -lean_declaration = "Auths.Rich.Mutations.assurance_equality" -lean_statement_sha256 = "c51ce57777cf6e3cd3ce5c680c795ebd250e1f31a0c3d58e271f48bc1cb36f7d" -formal_review = "rich-authority-refinement-2026-07-29" +lean_declaration = "Auths.Rich.Mutations.critical_extensions_reversed_not_sorted" +lean_statement_sha256 = "1ac76c3db510279b30c76d84b5538b47ad6c5f46fd2e3ae71a45976fea3131dc" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = [] +axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" [[claims]] claim_id = "AP-FORMAL-RICH-119" claim_text = "Lean proves the rich authority property: profile version equality." claim_status = "proved" lean_declaration = "Auths.Rich.Mutations.profile_version_equality" -lean_statement_sha256 = "2450d8484295f8a6898d9aa2cd7466d24d6ea68bbffa0c68af0a2dfb6c381b0d" -formal_review = "rich-authority-refinement-2026-07-29" +lean_statement_sha256 = "d061c3229df6d475f2f0dfc7a492dc1dce9c34555a20bd03b530e579a1b34d72" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = ["propext"] +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" [[claims]] claim_id = "AP-FORMAL-RICH-120" claim_text = "Lean proves the rich authority property: principal linkage equality." claim_status = "proved" lean_declaration = "Auths.Rich.Mutations.principal_linkage_equality" -lean_statement_sha256 = "c51ce57777cf6e3cd3ce5c680c795ebd250e1f31a0c3d58e271f48bc1cb36f7d" -formal_review = "rich-authority-refinement-2026-07-29" +lean_statement_sha256 = "399766c14541a0cf9533a13cbb97e40432b86f182ea9b99bfc835aa86556998d" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = [] +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" [[claims]] claim_id = "AP-FORMAL-RICH-121" claim_text = "Lean proves the rich authority property: grant linkage equality." claim_status = "proved" lean_declaration = "Auths.Rich.Mutations.grant_linkage_equality" -lean_statement_sha256 = "922e60d926e1ec17ffb65cc2d6ec254e61e967f7f5106a31f60c55283797f6c1" -formal_review = "rich-authority-refinement-2026-07-29" +lean_statement_sha256 = "78a60f2b43f1db4ae1ea88249aa8edab996999d314cc0d09a55151ae5170fb0c" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" -axioms = [] +axioms = [ + "propext", + "Classical.choice", + "Quot.sound", +] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +artifact = "formal/Auths/Rich/Mutations.lean" +sha256 = "9b6846b0bc9d6f755526674c3eba6303353cb496164b770db445fe5ea64c86b6" [[claims]] claim_id = "AP-FORMAL-RICH-053" claim_text = "Lean proves the rich authority property: coverage diagnostic sound complete." claim_status = "proved" lean_declaration = "Auths.Rich.coverage_diagnostic_sound_complete" -lean_statement_sha256 = "9687e4cb8fa0171e91273652857969ee041c87d3f36e1092ea4a5c31da50849a" -formal_review = "rich-authority-refinement-2026-07-29" +lean_statement_sha256 = "49fbc96121a2a7e8161c504e4bd01ce777808c13731b3c3bb30b448084347415" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4244,15 +6404,15 @@ axioms = [ [[claims.evidence]] kind = "lean-proof" artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +sha256 = "9cdb62c3559cf9612195017984e5cb6d81b516d22c4f18e0dbf6928e0f2782bb" [[claims]] claim_id = "AP-FORMAL-RICH-054" claim_text = "The mechanically translated production author-scope evaluator returns exactly the rich target-V1 decision and first failing authority dimension." -claim_status = "proved" +claim_status = "qualified" lean_declaration = "Auths.Refinement.translated_rust_refines_rich_spec" lean_statement_sha256 = "02391404f631207388637d5162c0f4a9bf17050a7e7a5ae18cb658079f7e5192" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [ "auths_authority::evaluate_author_scope_view", "auths_model::profile_ref_equal", @@ -4263,43 +6423,55 @@ rust_symbols = [ "auths_model::optional_budget_attenuates", "auths_model::status_policy_attenuates", "auths_model::assurance_policy_id_equal", + "auths_model::critical_extensions_equal", ] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" -scope = "The exact Aeneas translation of the production pre-signing scope evaluator over validated, bounded Rust model views, including ordered diagnostics." +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "The exact translated pre-signing scope evaluator over validated bounded views, including all ten scope/depth dimensions and ordered diagnostics." residual_assumptions = [ - "Lean's kernel, the pinned Rust/Lean/Aeneas/Charon toolchain, the reviewed Aeneas external models, the listed foundational axioms, and the theorem's representation-validity premises are trusted.", - "Canonical byte decoding into validated model values is a separate tested trust boundary and is not claimed by this theorem.", + "Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the reviewed transparent external bridges, the listed foundational axioms, and the theorem's explicit representation-validity premises are trusted.", + "Canonical decoding and cryptographic authenticity precede this pure evaluator and are outside this theorem.", ] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ @@ -4309,19 +6481,9 @@ axioms = [ ] [[claims.evidence]] -kind = "lean-proof" +kind = "lean-refinement" artifact = "formal/Auths/Refinement/Production.lean" -sha256 = "accc5207c5203893673fa3aa6a7d341c797bfd9a487eb346faa725d7d4683163" - -[[claims.evidence]] -kind = "mechanical-translation" -artifact = "formal/qualification/aeneas/generated/authority/Funs.lean" -sha256 = "8b285906cc66125575211a1fa7860459bb91a7472ccc6ab99f81f178902ff773" - -[[claims.evidence]] -kind = "source-closure" -artifact = "formal/qualification/aeneas/source-closure.json" -sha256 = "c7db6aa29674dac02b7b9b1eafaf42a57b3ffde9d173732b320641c882d89519" +sha256 = "c7f74bd904eb94e594b2a71cdfcdc7fc6f279c8bfaf2debbf2787b0cb7253fa1" [[claims.evidence]] kind = "lean-generated-vectors" @@ -4331,60 +6493,89 @@ sha256 = "8f560ad3afa1ae5f0ef7dc5a8b77779e5da79c59d94d39bf2d2a005edac25a92" [[claims.evidence]] kind = "mutation-matrix" artifact = "formal/refinement-mutations-v1.json" -sha256 = "8da37d014eecc1aab4a2c73bcc2076956970b61f64041964daa04b48b8b9d15a" +sha256 = "8943c1db6423069752c9afa6567c9ef0dcee185b67940ae83f0a81ecf0e00ab8" + +[[claims.evidence]] +kind = "mechanical-translation" +artifact = "formal/qualification/aeneas/generated/authority/Funs.lean" +sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" + +[[claims.evidence]] +kind = "source-closure" +artifact = "formal/qualification/aeneas/source-closure.json" +sha256 = "55cef4b7d320663b4203974d79bdc089a4af169c3ca21c6a5649993dc2e733b3" [[claims]] claim_id = "AP-FORMAL-RICH-055" -claim_text = "The mechanically translated production terminal-coverage evaluator returns exactly the ordered rich target-V1 decision, including linkage, set membership, interval containment, action constraints, and requested-budget coverage." -claim_status = "proved" +claim_text = "The mechanically translated terminal-coverage evaluator returns exactly the rich ordered decision for the supplied trusted budget-expression context." +claim_status = "qualified" lean_declaration = "Auths.Refinement.translated_coverage_refines_rich_spec" lean_statement_sha256 = "e8deba0035a09886221c46411be908f1ac8c30e0e3755ca956a62884a416a0fa" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [ "auths_authority::evaluate_action_coverage_view", + "auths_authority::root_linkage", "auths_authority::selected_profile_attenuates", + "auths_algebra_kernel::root_preserved", "auths_model::principal_id_equal", "auths_model::optional_grant_id_equal", + "auths_model::profile_ref_equal", + "auths_model::profile_slice_contains", "auths_model::permission_set_contains", "auths_model::validity_window_contains", "auths_model::audience_set_contains", "auths_model::action_constraint_allows", + "auths_model::budget_ceiling_covers_action", "auths_model::optional_budget_covers", ] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" -scope = "Validated target-V1 authority and action views through the production terminal-coverage decision, including its first-failure diagnostic order." -residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget result is stateless per action, assurance uses exact identifier equality, and deterministic diagnostic precedence is not a side-channel noninterference claim. Codec validation and cryptography remain outside this claim."] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "The crate-private raw translated evaluator over validated authority/action views. The public EffectiveAuthority::authorizes boundary resolves budget expressibility from AcceptedRegistries and the exact action profile before calling it." +residual_assumptions = [ + "Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the reviewed transparent external bridges, the listed foundational axioms, and the theorem's explicit representation-validity premises are trusted.", + "The AcceptedRegistries lookup-to-kernel composition is enforced by the Rust public API and adversarial tests; the registry collection's provenance and canonical decoding are outside this translated raw-view theorem.", +] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", @@ -4393,14 +6584,9 @@ axioms = [ ] [[claims.evidence]] -kind = "lean-proof" +kind = "lean-refinement" artifact = "formal/Auths/Refinement/Production.lean" -sha256 = "accc5207c5203893673fa3aa6a7d341c797bfd9a487eb346faa725d7d4683163" - -[[claims.evidence]] -kind = "translated-rust" -artifact = "formal/qualification/aeneas/generated/authority/Funs.lean" -sha256 = "8b285906cc66125575211a1fa7860459bb91a7472ccc6ab99f81f178902ff773" +sha256 = "c7f74bd904eb94e594b2a71cdfcdc7fc6f279c8bfaf2debbf2787b0cb7253fa1" [[claims.evidence]] kind = "lean-generated-vectors" @@ -4410,20 +6596,37 @@ sha256 = "8f560ad3afa1ae5f0ef7dc5a8b77779e5da79c59d94d39bf2d2a005edac25a92" [[claims.evidence]] kind = "mutation-matrix" artifact = "formal/refinement-mutations-v1.json" -sha256 = "8da37d014eecc1aab4a2c73bcc2076956970b61f64041964daa04b48b8b9d15a" +sha256 = "8943c1db6423069752c9afa6567c9ef0dcee185b67940ae83f0a81ecf0e00ab8" + +[[claims.evidence]] +kind = "translated-rust" +artifact = "formal/qualification/aeneas/generated/authority/Funs.lean" +sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" + +[[claims.evidence]] +kind = "source-closure" +artifact = "formal/qualification/aeneas/source-closure.json" +sha256 = "55cef4b7d320663b4203974d79bdc089a4af169c3ca21c6a5649993dc2e733b3" [[claims]] claim_id = "AP-FORMAL-RICH-056" -claim_text = "The mechanically translated production delegation evaluator returns exactly the rich target-V1 linkage and attenuation decision, including the unique accepted transition fields." -claim_status = "proved" +claim_text = "The mechanically translated delegation evaluator returns exactly the rich linkage, eleven-dimension attenuation decision, diagnostic, and accepted transition." +claim_status = "qualified" lean_declaration = "Auths.Refinement.translated_delegation_refines_rich_spec" lean_statement_sha256 = "713bd7e21397824f362ff65803d2085bc74915fbeb34cdfdc0c9a9e88a57909b" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [ "auths_authority::evaluate_grant_view", + "auths_authority::root_linkage", + "auths_authority::depth_decreases", "auths_authority::selected_profile_attenuates", + "auths_authority::extensions_attenuate", + "auths_algebra_kernel::root_preserved", + "auths_algebra_kernel::generated::attenuation_checks_accept", "auths_model::principal_id_equal", "auths_model::optional_grant_id_equal", + "auths_model::profile_ref_equal", + "auths_model::profile_slice_contains", "auths_model::permission_set_is_subset", "auths_model::validity_window_contains", "auths_model::audience_set_is_subset", @@ -4431,42 +6634,56 @@ rust_symbols = [ "auths_model::optional_budget_attenuates", "auths_model::status_policy_attenuates", "auths_model::assurance_policy_id_equal", - "auths_algebra_kernel::generated::attenuation_checks_accept", + "auths_model::critical_extensions_equal", ] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" -scope = "Validated target-V1 parent-authority and grant views through the production delegation outcome and accepted transition." -residual_assumptions = ["The theorem assumes validated Rust representation invariants: string-backed identifiers are canonical bounded byte sequences; mathematical counters and timestamps lie within their Rust u16/u64 ranges; windows are well formed; sets are canonical, bounded, and non-empty where required; freshness limits are non-zero; and selected profiles are members. The budget relation is stateless per action, assurance retention is exact identifier equality rather than a lattice, and deterministic diagnostic precedence is not a side-channel noninterference claim. Root preservation and assurance retention occur in the caller around the proved returned transition; codec validation and cryptography remain outside this claim."] +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" +scope = "The crate-private raw evaluator over validated parent/grant views, including root linkage, strict depth, every attenuation dimension, and the unique accepted transition." +residual_assumptions = [ + "Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the reviewed transparent external bridges, the listed foundational axioms, and the theorem's explicit representation-validity premises are trusted.", + "A present last-grant marker is representation data, not ancestry proof. Shipping Rust seals raw views; historical provenance is stated separately by Lean's AnchoredChain theorem from a fresh anchor through accepted transitions.", +] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [ "propext", @@ -4475,14 +6692,9 @@ axioms = [ ] [[claims.evidence]] -kind = "lean-proof" +kind = "lean-refinement" artifact = "formal/Auths/Refinement/Production.lean" -sha256 = "accc5207c5203893673fa3aa6a7d341c797bfd9a487eb346faa725d7d4683163" - -[[claims.evidence]] -kind = "translated-rust" -artifact = "formal/qualification/aeneas/generated/authority/Funs.lean" -sha256 = "8b285906cc66125575211a1fa7860459bb91a7472ccc6ab99f81f178902ff773" +sha256 = "c7f74bd904eb94e594b2a71cdfcdc7fc6f279c8bfaf2debbf2787b0cb7253fa1" [[claims.evidence]] kind = "lean-generated-vectors" @@ -4492,7 +6704,17 @@ sha256 = "8f560ad3afa1ae5f0ef7dc5a8b77779e5da79c59d94d39bf2d2a005edac25a92" [[claims.evidence]] kind = "mutation-matrix" artifact = "formal/refinement-mutations-v1.json" -sha256 = "8da37d014eecc1aab4a2c73bcc2076956970b61f64041964daa04b48b8b9d15a" +sha256 = "8943c1db6423069752c9afa6567c9ef0dcee185b67940ae83f0a81ecf0e00ab8" + +[[claims.evidence]] +kind = "translated-rust" +artifact = "formal/qualification/aeneas/generated/authority/Funs.lean" +sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" + +[[claims.evidence]] +kind = "source-closure" +artifact = "formal/qualification/aeneas/source-closure.json" +sha256 = "55cef4b7d320663b4203974d79bdc089a4af169c3ca21c6a5649993dc2e733b3" [[claims]] claim_id = "AP-FORMAL-RICH-104" @@ -4500,40 +6722,51 @@ claim_text = "The regenerated Aeneas translation of optional_budget_covers answe claim_status = "proved" lean_declaration = "Auths.Refinement.translated_bounded_ceiling_denies_absent_request" lean_statement_sha256 = "9c4ea11fa46f39a993ab4038263180284eca394df782a70259346c8167d7e4fc" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4544,9 +6777,9 @@ axioms = [ ] [[claims.evidence]] -kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +kind = "lean-refinement" +artifact = "formal/Auths/Refinement/Production.lean" +sha256 = "c7f74bd904eb94e594b2a71cdfcdc7fc6f279c8bfaf2debbf2787b0cb7253fa1" [[claims]] claim_id = "AP-FORMAL-RICH-106" @@ -4554,40 +6787,51 @@ claim_text = "Lean proves the rich authority property: translated expressible ab claim_status = "proved" lean_declaration = "Auths.Refinement.translated_expressible_absent_request_is_denied" lean_statement_sha256 = "90aa025faf3b73862f51432d5b07d0ef69c1bd67826d739083b6e42d65f00e4a" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4598,9 +6842,9 @@ axioms = [ ] [[claims.evidence]] -kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +kind = "lean-refinement" +artifact = "formal/Auths/Refinement/Production.lean" +sha256 = "c7f74bd904eb94e594b2a71cdfcdc7fc6f279c8bfaf2debbf2787b0cb7253fa1" [[claims]] claim_id = "AP-FORMAL-RICH-107" @@ -4608,40 +6852,51 @@ claim_text = "Lean proves the rich authority property: translated inexpressible claim_status = "proved" lean_declaration = "Auths.Refinement.translated_inexpressible_absent_request_is_covered" lean_statement_sha256 = "294ac2be47b25a2f72d528f66b974cb3acc2dfd8a330a8c6696b8dad12fb8d97" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4652,9 +6907,9 @@ axioms = [ ] [[claims.evidence]] -kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +kind = "lean-refinement" +artifact = "formal/Auths/Refinement/Production.lean" +sha256 = "c7f74bd904eb94e594b2a71cdfcdc7fc6f279c8bfaf2debbf2787b0cb7253fa1" [[claims]] claim_id = "AP-FORMAL-RICH-108" @@ -4662,40 +6917,51 @@ claim_text = "Lean proves the rich authority property: translated absent ceiling claim_status = "proved" lean_declaration = "Auths.Refinement.translated_absent_ceiling_covers_anything" lean_statement_sha256 = "90df3548fb3d56bed30812c94acb18b2d6b0028f77aff11cd44aade8e2889961" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Rich target-V1 authority semantics over opaque identity carriers and extensional finite sets." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4706,9 +6972,9 @@ axioms = [ ] [[claims.evidence]] -kind = "lean-proof" -artifact = "formal/Auths/Rich/Theorems.lean" -sha256 = "7dcf0fbc0904c1ccc2a3ae762bb761460ddf83fed99acb512cfee13f8253a49b" +kind = "lean-refinement" +artifact = "formal/Auths/Refinement/Production.lean" +sha256 = "c7f74bd904eb94e594b2a71cdfcdc7fc6f279c8bfaf2debbf2787b0cb7253fa1" [[claims]] claim_id = "AP-FORMAL-RICH-057" @@ -4716,40 +6982,51 @@ claim_text = "Lean proves the bounded product-policy property: configuration mat claim_status = "proved" lean_declaration = "Auths.Product.configuration_match_refl" lean_statement_sha256 = "1d3b3a5ed6156f4e5616d877b35e9942f824f876e6db3aa3cd39729305a8ed4e" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4757,8 +7034,8 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Product/Theorems.lean" -sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" +artifact = "formal/Auths/Product/Commitment.lean" +sha256 = "b94e57002d8b8c31906dab9e0f84c3ee38b01450f607f4fbb15ffffcb6a18221" [[claims]] claim_id = "AP-FORMAL-RICH-058" @@ -4766,40 +7043,51 @@ claim_text = "Lean proves the bounded product-policy property: configuration mat claim_status = "proved" lean_declaration = "Auths.Product.configuration_match_deterministic" lean_statement_sha256 = "0b97aea7619fab2ffebd442c8d6376a02779d0e4c9673837e48f45c99488c8e0" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4807,8 +7095,8 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Product/Theorems.lean" -sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" +artifact = "formal/Auths/Product/Commitment.lean" +sha256 = "b94e57002d8b8c31906dab9e0f84c3ee38b01450f607f4fbb15ffffcb6a18221" [[claims]] claim_id = "AP-FORMAL-RICH-059" @@ -4816,40 +7104,51 @@ claim_text = "Lean proves the bounded product-policy property: checked add never claim_status = "proved" lean_declaration = "Auths.Product.checked_add_never_wraps" lean_statement_sha256 = "f2e86c112c2021bab2444682e85973ad5b3f3de473bf5e093a47c65ebd74cf06" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4857,8 +7156,8 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Product/Theorems.lean" -sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" +artifact = "formal/Auths/Product/Arithmetic.lean" +sha256 = "6e08fc3a8b059f5e86807f25972a5f4f97cce4c6729344f75752b7e30d913c47" [[claims]] claim_id = "AP-FORMAL-RICH-060" @@ -4866,40 +7165,51 @@ claim_text = "Lean proves the bounded product-policy property: checked sub never claim_status = "proved" lean_declaration = "Auths.Product.checked_sub_never_underflows" lean_statement_sha256 = "fc97e83a1f988edafe17d5d775cafb333138ff36e1ee3ac65609f48844129fa5" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4907,8 +7217,8 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Product/Theorems.lean" -sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" +artifact = "formal/Auths/Product/Arithmetic.lean" +sha256 = "6e08fc3a8b059f5e86807f25972a5f4f97cce4c6729344f75752b7e30d913c47" [[claims]] claim_id = "AP-FORMAL-RICH-061" @@ -4916,40 +7226,51 @@ claim_text = "Lean proves the bounded product-policy property: checked div rejec claim_status = "proved" lean_declaration = "Auths.Product.checked_div_rejects_zero" lean_statement_sha256 = "004c9c8a21f3a6416181d4d561ae3dd38aebca10d640f288eb8af0e4bba34ba4" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -4957,8 +7278,8 @@ axioms = ["propext"] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Product/Theorems.lean" -sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" +artifact = "formal/Auths/Product/Arithmetic.lean" +sha256 = "6e08fc3a8b059f5e86807f25972a5f4f97cce4c6729344f75752b7e30d913c47" [[claims]] claim_id = "AP-FORMAL-RICH-062" @@ -4966,40 +7287,51 @@ claim_text = "Lean proves the bounded product-policy property: three way partiti claim_status = "proved" lean_declaration = "Auths.Product.three_way_partition" lean_statement_sha256 = "fa4bfacdd689bfe94265f7c2627598a8d758ae6ac3667bc6270f581219151e3e" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5007,8 +7339,8 @@ axioms = [] [[claims.evidence]] kind = "lean-proof" -artifact = "formal/Auths/Product/Theorems.lean" -sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" +artifact = "formal/Auths/Product/Eligibility.lean" +sha256 = "7dd0c6d987bc8684430f0bc2a7244c4965990a5278cc0d0dce0d63795e86d4f1" [[claims]] claim_id = "AP-FORMAL-RICH-063" @@ -5016,40 +7348,51 @@ claim_text = "Lean proves the bounded product-policy property: product contract claim_status = "proved" lean_declaration = "Auths.Product.product_contract_configuration_safety" lean_statement_sha256 = "18957d267951e86ccae5c49b3b704e2e45d87c427d023f4e0a3684b895e461d5" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5066,40 +7409,51 @@ claim_text = "Lean proves the bounded product-policy property: product contract claim_status = "proved" lean_declaration = "Auths.Product.product_contract_output_completeness" lean_statement_sha256 = "b4fa5d97dd43b72133cc583f8834921ed1c968fde6ac3eb4f7c4b5ecc49f1c08" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5119,49 +7473,60 @@ claim_text = "Lean proves the bounded product-policy property: translated config claim_status = "proved" lean_declaration = "Auths.Product.Refinement.translated_configuration_refines_projection" lean_statement_sha256 = "a988d91b2f65fea6451fdbe5ebc0b75ea7324b27c0b4031b1b49144eb15ef297" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" axioms = [] [[claims.evidence]] -kind = "lean-proof" -artifact = "formal/Auths/Product/Theorems.lean" -sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" +kind = "lean-refinement" +artifact = "formal/Auths/Product/Refinement.lean" +sha256 = "7d32bf972b26a18e6ac03614731325fda8e6bec360bab7f11e6212c2438cec4c" [[claims]] claim_id = "AP-FORMAL-RICH-066" @@ -5169,40 +7534,51 @@ claim_text = "Lean proves the bounded product-policy property: translated checke claim_status = "proved" lean_declaration = "Auths.Product.Refinement.translated_checked_add_refines_nat" lean_statement_sha256 = "eed21c6bdc5e0ba56c0c8dd9249ee1d51053fa42b8df3958a762399ba0bf7ab5" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5213,9 +7589,9 @@ axioms = [ ] [[claims.evidence]] -kind = "lean-proof" -artifact = "formal/Auths/Product/Theorems.lean" -sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" +kind = "lean-refinement" +artifact = "formal/Auths/Product/Refinement.lean" +sha256 = "7d32bf972b26a18e6ac03614731325fda8e6bec360bab7f11e6212c2438cec4c" [[claims]] claim_id = "AP-FORMAL-RICH-067" @@ -5223,40 +7599,51 @@ claim_text = "Lean proves the bounded product-policy property: translated checke claim_status = "proved" lean_declaration = "Auths.Product.Refinement.translated_checked_sub_refines_nat" lean_statement_sha256 = "fed19d1701dac7a6f928957a78799fcf63eaf80d24ea0cbed66d77e490ed879c" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5267,9 +7654,9 @@ axioms = [ ] [[claims.evidence]] -kind = "lean-proof" -artifact = "formal/Auths/Product/Theorems.lean" -sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" +kind = "lean-refinement" +artifact = "formal/Auths/Product/Refinement.lean" +sha256 = "7d32bf972b26a18e6ac03614731325fda8e6bec360bab7f11e6212c2438cec4c" [[claims]] claim_id = "AP-FORMAL-RICH-068" @@ -5277,40 +7664,51 @@ claim_text = "Lean proves the bounded product-policy property: translated checke claim_status = "proved" lean_declaration = "Auths.Product.Refinement.translated_checked_mul_refines_nat" lean_statement_sha256 = "f2e5d31dcc8e7f7651c0223bfdabccadc850be49cb8431aebf966aee9f7acc35" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5321,9 +7719,9 @@ axioms = [ ] [[claims.evidence]] -kind = "lean-proof" -artifact = "formal/Auths/Product/Theorems.lean" -sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" +kind = "lean-refinement" +artifact = "formal/Auths/Product/Refinement.lean" +sha256 = "7d32bf972b26a18e6ac03614731325fda8e6bec360bab7f11e6212c2438cec4c" [[claims]] claim_id = "AP-FORMAL-RICH-069" @@ -5331,40 +7729,51 @@ claim_text = "Lean proves the bounded product-policy property: translated checke claim_status = "proved" lean_declaration = "Auths.Product.Refinement.translated_checked_div_rejects_zero" lean_statement_sha256 = "cb30a894abfc0b326ba704c9d0979adb434cb7fc94f0134e14442c313e8670fb" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 bounded product-policy commitments, checked arithmetic, configuration gating, and eligibility." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5375,9 +7784,9 @@ axioms = [ ] [[claims.evidence]] -kind = "lean-proof" -artifact = "formal/Auths/Product/Theorems.lean" -sha256 = "7c05061876df3a8d71c86e7ea07cf9db89f9e1c1eb32ecf45899008c8fade154" +kind = "lean-refinement" +artifact = "formal/Auths/Product/Refinement.lean" +sha256 = "7d32bf972b26a18e6ac03614731325fda8e6bec360bab7f11e6212c2438cec4c" [[claims]] claim_id = "AP-FORMAL-RICH-070" @@ -5385,40 +7794,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: additive claim_status = "proved" lean_declaration = "Auths.Lifecycle.additive_capacity_success_positive" lean_statement_sha256 = "dcd8c19f436246f345583eb79fa8f56ca0d79801619ec832bb1965cfa4ef7505" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5439,40 +7859,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: additive claim_status = "proved" lean_declaration = "Auths.Lifecycle.additive_capacity_success_conserves" lean_statement_sha256 = "4c9f497e6dde81872f4262a4d1ef41176e7fed5cbbfdf5ddef76cd6d2f8720a3" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5492,40 +7923,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: additive claim_status = "proved" lean_declaration = "Auths.Lifecycle.additive_capacity_success_never_overflows_u64" lean_statement_sha256 = "1505323a6e0d5ea769a5f3ce908b2e63df64a61ee675bf46ad235e4934158e55" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5545,40 +7987,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: reserve claim_status = "proved" lean_declaration = "Auths.Lifecycle.reserve_preserves_capacity" lean_statement_sha256 = "0215021eae68f54b9d07f77d465d2d5d0f574d82c956050d6a42d65f76360f1e" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5598,40 +8051,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: commit p claim_status = "proved" lean_declaration = "Auths.Lifecycle.commit_preserves_capacity" lean_statement_sha256 = "6e340919635140c32e62bbb5eaae23126fd0f53586c46407c7e16b970f4fd4ca" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5652,40 +8116,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: release claim_status = "proved" lean_declaration = "Auths.Lifecycle.release_preserves_capacity" lean_statement_sha256 = "a554833f3a765873264be9c667d0293f8b5a7cf6f3a79d9b1fa03131b6414b94" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5706,40 +8181,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: exact re claim_status = "proved" lean_declaration = "Auths.Lifecycle.exact_replay_is_stable" lean_statement_sha256 = "ddbb29e971f6f0c416d4ae2bbac689cc221c305745d7a2d90b0b36224f63320c" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5756,40 +8242,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: conflict claim_status = "proved" lean_declaration = "Auths.Lifecycle.conflicting_replay_is_not_exact" lean_statement_sha256 = "16040fad2ac968a316520f6e522063d1e6aeeb5000305573e650296cec80290f" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5806,40 +8303,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: absent r claim_status = "proved" lean_declaration = "Auths.Lifecycle.absent_replay_never_claims_effect" lean_statement_sha256 = "52a298bd35ffa236d8370ab74d78a674e9e9243a686c52e611f92be3c5cfcf64" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5856,40 +8364,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: start at claim_status = "proved" lean_declaration = "Auths.Lifecycle.start_attempt_requires_credential" lean_statement_sha256 = "4dcfb31b2e2662aef2c336d0c4f55f922b3c6ac226776bed4daacca4424d1904" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5906,40 +8425,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: provider claim_status = "proved" lean_declaration = "Auths.Lifecycle.provider_call_requires_attempt" lean_statement_sha256 = "215e750dce32f2f49fa22b96b5c44699bb15604760223b9f26ace3becbac3bdb" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -5956,40 +8486,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: commit r claim_status = "proved" lean_declaration = "Auths.Lifecycle.commit_requires_provider_entry_and_effect" lean_statement_sha256 = "de9baa696f2dbc23d1dfe965a72bc95f211cda0525f3a212af4197864aab36f2" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6006,40 +8547,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: outcome claim_status = "proved" lean_declaration = "Auths.Lifecycle.outcome_unknown_cannot_release" lean_statement_sha256 = "3d3102111fb51a188aeffe93d7dc6ad13b25a4db54b871f8d4a4b27f98e88c9b" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6056,40 +8608,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: outcome claim_status = "proved" lean_declaration = "Auths.Lifecycle.outcome_unknown_only_reconciliation_can_terminate" lean_statement_sha256 = "98af24c5f5fa0f00c4cbc0ef76b168f8fbc0811d3a3069e587d844a9406c5def" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6106,40 +8669,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: configur claim_status = "proved" lean_declaration = "Auths.Lifecycle.configuration_mismatch_stops_reservation" lean_statement_sha256 = "9eb20bb520a94f7bfd23c41793df96630035b109d3bd77ae5520e384d80f87a7" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6156,40 +8730,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: configur claim_status = "proved" lean_declaration = "Auths.Lifecycle.configuration_mismatch_stops_credential" lean_statement_sha256 = "6f4e8f2d72115f41f5d9881ece6c3d8bff1fcca74bf34415a14475b316085abb" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6206,40 +8791,51 @@ claim_text = "Lean proves the reservation/execution lifecycle property: terminal claim_status = "proved" lean_declaration = "Auths.Lifecycle.terminal_states_never_transition" lean_statement_sha256 = "ac59e2c5beb8dcaf74cafc08c46f48d6d2345ab773dcf2dae55a337bd1c30acc" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation." residual_assumptions = ["Lean's kernel, the pinned toolchain, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6256,40 +8852,51 @@ claim_text = "Lean proves the mechanically translated shipping Rust lifecycle ke claim_status = "proved" lean_declaration = "Auths.Lifecycle.Refinement.translated_terminal_refines_rich" lean_statement_sha256 = "8963dd672ee5d30584b6a8a131ca2e28a824c51ce5126805eea83ebbe55d71c9" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = ["auths_lifecycle::model::LifecycleState::is_terminal"] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::model::LifecycleState::is_terminal` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6306,40 +8913,51 @@ claim_text = "Lean proves the mechanically translated shipping Rust lifecycle ke claim_status = "proved" lean_declaration = "Auths.Lifecycle.Refinement.translated_transition_refines_rich" lean_statement_sha256 = "ed38f14b22ae254a97a391b4a7ea4064665d323a26e4ec556769f1451731c6cf" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = ["auths_lifecycle::kernel::transition_code"] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::transition_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6359,40 +8977,51 @@ claim_text = "Lean proves the mechanically translated shipping Rust lifecycle ke claim_status = "proved" lean_declaration = "Auths.Lifecycle.Refinement.translated_exclusive_capacity_refines_rich" lean_statement_sha256 = "76c468b3a6a2c9a0231675e987fabf48df92073dc24aadce05f6552165c9e8cf" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = ["auths_lifecycle::kernel::exclusive_capacity_available"] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::exclusive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6409,40 +9038,51 @@ claim_text = "Lean proves the mechanically translated shipping Rust lifecycle ke claim_status = "proved" lean_declaration = "Auths.Lifecycle.Refinement.translated_additive_capacity_refines_rich" lean_statement_sha256 = "530d89045350d0aa6be9ad09121e16e2a91d8ed3652b8e9508e5ef2a655e9ada" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = ["auths_lifecycle::kernel::additive_capacity_available"] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::additive_capacity_available` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6463,40 +9103,51 @@ claim_text = "Lean proves the mechanically translated shipping Rust lifecycle ke claim_status = "proved" lean_declaration = "Auths.Lifecycle.Refinement.translated_replay_refines_rich" lean_statement_sha256 = "ede2a75a3447bb934a2454580f45e9610dcf521b669bca3fbe697f567764d7f4" -formal_review = "rich-authority-refinement-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = ["auths_lifecycle::kernel::replay_code"] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The pinned Charon/Aeneas translation of `auths_lifecycle::kernel::replay_code` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." residual_assumptions = ["Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6513,40 +9164,51 @@ claim_text = "Binary all composition is commutative." claim_status = "proved" lean_declaration = "Auths.all_commutative" lean_statement_sha256 = "fd3226a3df5fb4730eeaeb50b3190d9f0ae29aeb961d395825787945fdcdd445" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6563,40 +9225,51 @@ claim_text = "Binary all composition is associative." claim_status = "proved" lean_declaration = "Auths.all_associative" lean_statement_sha256 = "5749607a3f2752bdd73ae4559af264763d8a36ec3d1258ed4f60b11467d7e12d" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6613,40 +9286,51 @@ claim_text = "Binary all composition is idempotent." claim_status = "proved" lean_declaration = "Auths.all_idempotent" lean_statement_sha256 = "99993635cb0eadeacab1d64ee7f674245481d4160a770deaed82022c0851a3f6" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The closed three-valued binary all operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6663,40 +9347,51 @@ claim_text = "Binary any composition is commutative." claim_status = "proved" lean_declaration = "Auths.any_commutative" lean_statement_sha256 = "a2680b963a19fb4f377fefaad4102a4469895954bbea2b7541894902e979115d" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6713,40 +9408,51 @@ claim_text = "Binary any composition is associative." claim_status = "proved" lean_declaration = "Auths.any_associative" lean_statement_sha256 = "3ceaf22f6776423ee96cbdf5fa9bc5c13fca94bdbb3355b016d6b8e4c275afa8" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6763,40 +9469,51 @@ claim_text = "Binary any composition is idempotent." claim_status = "proved" lean_declaration = "Auths.any_idempotent" lean_statement_sha256 = "8f1cb9d3d130a67d026a96f278678b9070badc44f40f7c538ee68a3f9af90d82" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The closed three-valued binary any operator." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6813,40 +9530,51 @@ claim_text = "A threshold of one over two truth values equals binary any." claim_status = "proved" lean_declaration = "Auths.threshold_one_eq_any" lean_statement_sha256 = "639a98d2dcab0db13ec9b221b75605fa48ec169a263f76feb2034f912d7a69c7" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = ["auths_algebra_kernel::threshold_counts"] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6868,40 +9596,51 @@ claim_text = "A threshold of two over two truth values equals binary all." claim_status = "proved" lean_declaration = "Auths.threshold_n_eq_all" lean_statement_sha256 = "7bfb0c0d44d1f31949d7039be3c2052cab9112fcb12d2abef005e6c52979b815" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = ["auths_algebra_kernel::threshold_counts"] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The generated two-input threshold abstraction." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6923,40 +9662,51 @@ claim_text = "Raising the two-input threshold from one to two cannot raise its a claim_status = "proved" lean_declaration = "Auths.threshold_monotone_k" lean_statement_sha256 = "34c77c69b14f1b08db8fba8da86a0a9eb65b95147b85d8d3f48811b47cabcb79" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = ["auths_algebra_kernel::threshold_counts"] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "Exactly the two-input threshold helper and the declared three-valued truth order." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -6981,40 +9731,51 @@ claim_text = "Swapping the two inputs preserves binary all and binary any result claim_status = "proved" lean_declaration = "Auths.binary_composition_swap_invariant" lean_statement_sha256 = "5a2f18a8b889fca7e2d4f239a97a12490193b19c6d3e8703164e04cc556986c7" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "A two-value swap only; arbitrary authorization-plan permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -7031,40 +9792,51 @@ claim_text = "The two-value minimum diagnostic code is invariant under swapping claim_status = "proved" lean_declaration = "Auths.canonical_diagnostic_permutation_invariant" lean_statement_sha256 = "c0eae1c5c0004d836da0c28c5d5395ecb7e534d05e9816a573d27b7c0f912806" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The two-value canonicalCode helper; arbitrary plan diagnostic permutations are not claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -7081,40 +9853,51 @@ claim_text = "The current plan visit helper is definitionally the plan leaf enum claim_status = "proved" lean_declaration = "Auths.plan_visit_is_leaf_enumeration" lean_statement_sha256 = "a9d2d3fd6140d7535c9cf3567cf92a5891e8e77fda5924a34a032bb1d9cab0cb" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "A definitional model equality; it does not prove that the shipping evaluator visits leaves once." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -7134,40 +9917,51 @@ claim_text = "The current plan cost helper is definitionally the recursive node claim_status = "proved" lean_declaration = "Auths.plan_cost_is_node_count" lean_statement_sha256 = "d0e9f71500f5da6108662057f11150fa61188b6b5059562b6db0079bd4e5820e" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = [] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "A definitional model equality; no asymptotic or shipping evaluator cost bound is claimed." residual_assumptions = ["Lean's kernel, the pinned toolchain, and the listed foundational axioms are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -7187,40 +9981,51 @@ claim_text = "An authorized generated threshold result implies the authorized co claim_status = "proved" lean_declaration = "Auths.authorized_implies_threshold_met" lean_statement_sha256 = "367522026ebf77cc4e8ee694920ff72384ab58cb573bb7cefb8406117955d2d1" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = ["auths_algebra_kernel::threshold_counts"] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -7242,40 +10047,51 @@ claim_text = "A denied generated threshold result implies authorized plus indete claim_status = "proved" lean_declaration = "Auths.denied_implies_threshold_impossible" lean_statement_sha256 = "50005cbb684ef7566e1b6f71fc766e9960de178f0a86f03e37f4eb63cf7e46d5" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = ["auths_algebra_kernel::threshold_counts"] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" @@ -7300,40 +10116,51 @@ claim_text = "An indeterminate generated threshold result means authorization is claim_status = "proved" lean_declaration = "Auths.indeterminate_implies_threshold_reachable" lean_statement_sha256 = "dd970bbab7dd61f7b1274f9e829358d9627b3c822d4119628191018722f31039" -formal_review = "formal-security-baseline-2026-07-29" +formal_review = "root-provenance-and-profile-version-2026-08-16-r3" rust_symbols = ["auths_algebra_kernel::threshold_counts"] semantic_source_closure = [ - "formal/Auths/Base.lean", - "formal/Auths/Authority.lean", + "formal/Auths.lean", + "formal/Auths/AssuranceAudit.lean", "formal/Auths/Attenuation.lean", - "formal/Auths/Rich/Types.lean", - "formal/Auths/Rich/Semantics.lean", - "formal/Auths/Rich/Theorems.lean", - "formal/Auths/Product/Commitment.lean", + "formal/Auths/Authority.lean", + "formal/Auths/Base.lean", + "formal/Auths/Composition.lean", + "formal/Auths/Diversity.lean", + "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Lifecycle/Semantics.lean", + "formal/Auths/Lifecycle/Theorems.lean", "formal/Auths/Product/Arithmetic.lean", + "formal/Auths/Product/Commitment.lean", "formal/Auths/Product/Eligibility.lean", - "formal/Auths/Product/Tightening.lean", - "formal/Auths/Product/Theorems.lean", "formal/Auths/Product/Refinement.lean", - "formal/Auths/Lifecycle/Semantics.lean", - "formal/Auths/Lifecycle/Theorems.lean", - "formal/Auths/Lifecycle/Refinement.lean", + "formal/Auths/Product/Theorems.lean", + "formal/Auths/Product/Tightening.lean", "formal/Auths/Refinement/Production.lean", - "formal/Auths/Composition.lean", - "formal/Auths/Diversity.lean", - "formal/Auths/Generated/Algebra.lean", + "formal/Auths/Rich/Mutations.lean", + "formal/Auths/Rich/Semantics.lean", + "formal/Auths/Rich/Theorems.lean", + "formal/Auths/Rich/Types.lean", "formal/Auths/Theorems.lean", + "formal/algebra-contract-v1.toml", + "formal/lake-manifest.json", + "formal/lakefile.toml", + "formal/lean-toolchain", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", "formal/qualification/aeneas/generated/bounded_policy/Types.lean", "formal/qualification/aeneas/generated/lifecycle/Funs.lean", "formal/qualification/aeneas/generated/lifecycle/Types.lean", "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", "formal/qualification/aeneas/generated/model/Types.lean", - "formal/algebra-contract-v1.toml", ] -semantic_source_closure_sha256 = "b6c51ac3d59ca544b17a01ef52cfd1980e1206f76c06d7b6960940a2474b6ac5" +semantic_source_closure_sha256 = "5a5de92545024ffe79e39081faa5d3e1c49e1dc56e4f26c0a040cb66cee367f8" scope = "The generated count classifier and its exhaustive target-V1 bounded Rust vectors." residual_assumptions = ["Lean's kernel, the pinned toolchain, listed foundational axioms, generator correctness, and the Rust vector harness are trusted."] toolchain_lock_sha256 = "0c45a8a08ac06e313d775e669749097bd3a0abfb8e3395c69416ba0238770d53" diff --git a/formal/qualification/aeneas/generated/authority/Funs.lean b/formal/qualification/aeneas/generated/authority/Funs.lean index 9c512375..cd35f1c0 100644 --- a/formal/qualification/aeneas/generated/authority/Funs.lean +++ b/formal/qualification/aeneas/generated/authority/Funs.lean @@ -20,14 +20,14 @@ noncomputable section namespace auths_authority /-- [auths_authority::{impl core::cmp::PartialEq> for auths_authority::CanonicalPrincipal<'_0>}::eq]: - Source: 'core/crates/auths-authority/src/lib.rs', lines 140:4-142:5 + Source: 'core/crates/auths-authority/src/lib.rs', lines 146:4-148:5 Visibility: public -/ def CanonicalPrincipal.Insts.CoreCmpPartialEqCanonicalPrincipal.eq (self : CanonicalPrincipal) (other : CanonicalPrincipal) : Result Bool := do auths_model.principal_id_equal self other /-- Trait implementation: [auths_authority::{impl core::cmp::PartialEq> for auths_authority::CanonicalPrincipal<'_0>}] - Source: 'core/crates/auths-authority/src/lib.rs', lines 139:0-143:1 -/ + Source: 'core/crates/auths-authority/src/lib.rs', lines 145:0-149:1 -/ @[reducible] def CanonicalPrincipal.Insts.CoreCmpPartialEqCanonicalPrincipal : core.cmp.PartialEq CanonicalPrincipal CanonicalPrincipal := { @@ -35,7 +35,7 @@ def CanonicalPrincipal.Insts.CoreCmpPartialEqCanonicalPrincipal : } /-- [auths_authority::extensions_attenuate]: - Source: 'core/crates/auths-authority/src/lib.rs', lines 154:0-162:1 -/ + Source: 'core/crates/auths-authority/src/lib.rs', lines 160:0-168:1 -/ def extensions_attenuate (parent_extensions : Option auths_model.CriticalExtensions) (grant_extensions : auths_model.CriticalExtensions) : @@ -47,7 +47,7 @@ def extensions_attenuate auths_model.critical_extensions_equal grant_extensions parent /-- [auths_authority::depth_decreases]: - Source: 'core/crates/auths-authority/src/lib.rs', lines 174:0-179:1 -/ + Source: 'core/crates/auths-authority/src/lib.rs', lines 180:0-185:1 -/ def depth_decreases (parent_remaining : Std.U16) (grant_remaining : Std.U16) : Result Bool := do if parent_remaining = 0#u16 @@ -55,7 +55,7 @@ def depth_decreases else ok (grant_remaining < parent_remaining) /-- [auths_authority::root_linkage]: - Source: 'core/crates/auths-authority/src/lib.rs', lines 182:0-192:1 -/ + Source: 'core/crates/auths-authority/src/lib.rs', lines 188:0-198:1 -/ def root_linkage (parent : AuthorityStateView) (issuer : auths_model.PrincipalId) : Result (auths_algebra_kernel.RootLinkage CanonicalPrincipal) @@ -70,7 +70,7 @@ def root_linkage } /-- [auths_authority::selected_profile_attenuates]: - Source: 'core/crates/auths-authority/src/lib.rs', lines 194:0-203:1 -/ + Source: 'core/crates/auths-authority/src/lib.rs', lines 200:0-209:1 -/ def selected_profile_attenuates (selected : Option auths_model.ProfileRef) (allowed_profiles : Slice auths_model.ProfileRef) @@ -82,7 +82,7 @@ def selected_profile_attenuates | some parent => auths_model.profile_ref_equal parent child /-- [auths_authority::evaluate_author_scope_view]: - Source: 'core/crates/auths-authority/src/lib.rs', lines 209:0-244:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 215:0-250:1 Visibility: public -/ def evaluate_author_scope_view (parent : auths_model.ScopeAuthorityView) @@ -156,8 +156,7 @@ def evaluate_author_scope_view else ok (AuthorScopeDecision.Denied AuthorityDimension.Profile) /-- [auths_authority::evaluate_grant_view]: - Source: 'core/crates/auths-authority/src/lib.rs', lines 265:0-329:1 - Visibility: public -/ + Source: 'core/crates/auths-authority/src/lib.rs', lines 271:0-335:1 -/ def evaluate_grant_view (parent : AuthorityStateView) (grant_id : auths_model.GrantId) (grant : auths_model.GrantAuthorityView) : @@ -309,8 +308,7 @@ def evaluate_grant_view } /-- [auths_authority::evaluate_action_coverage_view]: - Source: 'core/crates/auths-authority/src/lib.rs', lines 350:0-391:1 - Visibility: public -/ + Source: 'core/crates/auths-authority/src/lib.rs', lines 356:0-397:1 -/ def evaluate_action_coverage_view (authority : AuthorityStateView) (action : auths_model.ActionAuthorityView) (expression : auths_model.ProfileBudgetExpression) : diff --git a/formal/qualification/aeneas/generated/authority/Types.lean b/formal/qualification/aeneas/generated/authority/Types.lean index 430709ca..dfb395f3 100644 --- a/formal/qualification/aeneas/generated/authority/Types.lean +++ b/formal/qualification/aeneas/generated/authority/Types.lean @@ -146,7 +146,7 @@ inductive auths_model.DenialReason where | LocalPolicyDenied : auths_model.DenialReason /-- [auths_authority::AcceptedTransition] - Source: 'core/crates/auths-authority/src/lib.rs', lines 46:0-58:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 52:0-64:1 Visibility: public -/ structure AcceptedTransition where subject : auths_model.PrincipalId @@ -162,7 +162,7 @@ structure AcceptedTransition where extensions : auths_model.CriticalExtensions /-- [auths_authority::DelegationOutcome] - Source: 'core/crates/auths-authority/src/lib.rs', lines 63:0-66:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 69:0-72:1 Visibility: public -/ @[discriminant isize] inductive DelegationOutcome where @@ -170,14 +170,14 @@ inductive DelegationOutcome where | Denied : auths_model.DenialReason → DelegationOutcome /-- [auths_authority::DelegationEvaluation] - Source: 'core/crates/auths-authority/src/lib.rs', lines 71:0-74:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 77:0-80:1 Visibility: public -/ structure DelegationEvaluation where checks : auths_algebra_kernel.generated.AttenuationChecks outcome : DelegationOutcome /-- [auths_authority::CoverageDecision] - Source: 'core/crates/auths-authority/src/lib.rs', lines 79:0-82:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 85:0-88:1 Visibility: public -/ @[discriminant isize] inductive CoverageDecision where @@ -185,7 +185,7 @@ inductive CoverageDecision where | Denied : auths_model.DenialReason → CoverageDecision /-- [auths_authority::AuthorityDimension] - Source: 'core/crates/auths-authority/src/lib.rs', lines 86:0-97:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 92:0-103:1 Visibility: public -/ @[discriminant isize] inductive AuthorityDimension where @@ -201,7 +201,7 @@ inductive AuthorityDimension where | Extensions : AuthorityDimension /-- [auths_authority::AuthorScopeDecision] - Source: 'core/crates/auths-authority/src/lib.rs', lines 102:0-105:1 + Source: 'core/crates/auths-authority/src/lib.rs', lines 108:0-111:1 Visibility: public -/ @[discriminant isize] inductive AuthorScopeDecision where @@ -209,8 +209,7 @@ inductive AuthorScopeDecision where | Denied : AuthorityDimension → AuthorScopeDecision /-- [auths_authority::AuthorityStateView] - Source: 'core/crates/auths-authority/src/lib.rs', lines 110:0-128:1 - Visibility: public -/ + Source: 'core/crates/auths-authority/src/lib.rs', lines 116:0-134:1 -/ structure AuthorityStateView where root : auths_model.PrincipalId subject : auths_model.PrincipalId @@ -228,7 +227,7 @@ structure AuthorityStateView where extensions : Option auths_model.CriticalExtensions /-- [auths_authority::CanonicalPrincipal] - Source: 'core/crates/auths-authority/src/lib.rs', lines 137:0-137:47 -/ + Source: 'core/crates/auths-authority/src/lib.rs', lines 143:0-143:47 -/ @[reducible] def CanonicalPrincipal := auths_model.PrincipalId diff --git a/formal/qualification/aeneas/generated/authority/translation.json b/formal/qualification/aeneas/generated/authority/translation.json index 46b1df24..3051f3d3 100644 --- a/formal/qualification/aeneas/generated/authority/translation.json +++ b/formal/qualification/aeneas/generated/authority/translation.json @@ -317,8 +317,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 140, - "end_line": 142 + "begin_line": 146, + "end_line": 148 }, "is_opaque": false, "can_fail": true, @@ -334,8 +334,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 154, - "end_line": 162 + "begin_line": 160, + "end_line": 168 }, "is_opaque": false, "can_fail": true, @@ -351,8 +351,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 174, - "end_line": 179 + "begin_line": 180, + "end_line": 185 }, "is_opaque": false, "can_fail": true, @@ -368,8 +368,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 182, - "end_line": 192 + "begin_line": 188, + "end_line": 198 }, "is_opaque": false, "can_fail": true, @@ -385,8 +385,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 194, - "end_line": 203 + "begin_line": 200, + "end_line": 209 }, "is_opaque": false, "can_fail": true, @@ -402,8 +402,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 209, - "end_line": 244 + "begin_line": 215, + "end_line": 250 }, "is_opaque": false, "can_fail": true, @@ -419,8 +419,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 265, - "end_line": 329 + "begin_line": 271, + "end_line": 335 }, "is_opaque": false, "can_fail": true, @@ -436,8 +436,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 350, - "end_line": 391 + "begin_line": 356, + "end_line": 397 }, "is_opaque": false, "can_fail": true, @@ -707,8 +707,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 46, - "end_line": 58 + "begin_line": 52, + "end_line": 64 } }, { @@ -719,8 +719,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 63, - "end_line": 66 + "begin_line": 69, + "end_line": 72 } }, { @@ -731,8 +731,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 71, - "end_line": 74 + "begin_line": 77, + "end_line": 80 } }, { @@ -743,8 +743,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 79, - "end_line": 82 + "begin_line": 85, + "end_line": 88 } }, { @@ -755,8 +755,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 86, - "end_line": 97 + "begin_line": 92, + "end_line": 103 } }, { @@ -767,8 +767,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 102, - "end_line": 105 + "begin_line": 108, + "end_line": 111 } }, { @@ -779,8 +779,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 110, - "end_line": 128 + "begin_line": 116, + "end_line": 134 } }, { @@ -791,8 +791,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 137, - "end_line": 137 + "begin_line": 143, + "end_line": 143 } } ], @@ -807,8 +807,8 @@ "is_local": true, "source": { "file": "core/crates/auths-authority/src/lib.rs", - "begin_line": 139, - "end_line": 143 + "begin_line": 145, + "end_line": 149 }, "impl_trait_def_id": 0, "impl_trait_rust_name": "core::cmp::PartialEq", diff --git a/formal/qualification/aeneas/qualification.toml b/formal/qualification/aeneas/qualification.toml index 3390a8bf..6fb05b0f 100644 --- a/formal/qualification/aeneas/qualification.toml +++ b/formal/qualification/aeneas/qualification.toml @@ -11,6 +11,7 @@ translation_warnings = [] required_compiled_external_axioms = 0 source_files = [ + ".cargo/config.toml", "Cargo.toml", "Cargo.lock", "core/crates/auths-model/Cargo.toml", @@ -30,6 +31,11 @@ source_files = [ "formal/algebra-contract-v1.toml", "formal/qualification/aeneas/qualification.toml", "formal/translation-toolchain.lock", + "formal/qualification/aeneas/cases/Model.lean", + "formal/qualification/aeneas/cases/Algebra.lean", + "formal/qualification/aeneas/cases/Authority.lean", + "formal/qualification/aeneas/cases/BoundedPolicy.lean", + "formal/qualification/aeneas/cases/Lifecycle.lean", "xtask/ci-plan/Cargo.toml", "xtask/ci-plan/src/lib.rs", "xtask/src/formal.rs", @@ -163,6 +169,18 @@ artifact = "formal/.lake/packages/aeneas/backends/lean/Aeneas/Std/StringIter.lea upstream_lines = [12, 18] classification = "Pinned upstream iterator proof-support sorry; the qualified source uses String::as_bytes, not StringIter." +[[warning_inventory]] +id = "aeneas-tactic-step-star" +artifact = "formal/.lake/packages/aeneas/backends/lean/Aeneas/Tactic/Step/StepStar.lean" +upstream_lines = [521, 610, 650, 778] +classification = "Pinned upstream tactic proof holes; documented guard-message examples are excluded as comments, and audited Auths declarations are separately required to have zero transitive sorryAx dependencies." + +[[warning_inventory]] +id = "aeneas-meta-utils" +artifact = "formal/.lake/packages/aeneas/backends/lean/AeneasMeta/Utils.lean" +upstream_lines = [1344] +classification = "Pinned upstream meta proof hole; comments and diagnostic strings are excluded, and audited Auths declarations are separately required to have zero transitive sorryAx dependencies." + [[translations]] crate_name = "auths_model" translation_json = "formal/qualification/aeneas/generated/model/translation.json" diff --git a/formal/qualification/aeneas/source-closure.json b/formal/qualification/aeneas/source-closure.json index 70c424c6..53856d7d 100644 --- a/formal/qualification/aeneas/source-closure.json +++ b/formal/qualification/aeneas/source-closure.json @@ -1,16 +1,19 @@ { "schema": "auths-proof-translation-source-closure/v2", - "digest": "7b03bb763da7c07498c718fb69f8a3bcb25c51fe4feab310685be818cc9013d1", + "digest": "19156ae078d747c7c09b7d2bf9dc1637a8b65458a0f7e5bb5a125764e6f081a5", "files": [ + { + "path": ".cargo/config.toml", + "sha256": "783fdc447abb5b8f8b5652157aa9ede64da640813fa89679c29c5251c8e0cbe6" + }, { "path": "Cargo.lock", - "sha256": "5e15049f4f52dfecccc1b99bc62f9049e0d28846a2a7c695909fd740b31a2c1d", + "sha256": "20ab48b1b099f118bd9056a8ea5acc84b005027ebfb5c55b4387ba45625f0044", "normalization": "translated-cargo-closure-v1" }, { "path": "Cargo.toml", - "sha256": "9b81b88782ac8b8ec24ad72ec2889f3b66a9c69938960109cea7772f026dcc19", - "normalization": "translated-cargo-closure-v1" + "sha256": "d6ac26afb8ddf6c1c3e7a9c1aaaeafa1ffd113cbadb124025b22a068a20618ee" }, { "path": "core/crates/auths-algebra-kernel/Cargo.toml", @@ -30,7 +33,7 @@ }, { "path": "core/crates/auths-authority/src/lib.rs", - "sha256": "8c261b346969cb58e9708f091dadaea0a7f3b4b0844613b404b21e2a863212cc" + "sha256": "61462507cb6b79090ce1528e55bb3d818321df9d990b37a5ecd7043769ae4857" }, { "path": "core/crates/auths-model/Cargo.toml", @@ -38,15 +41,35 @@ }, { "path": "core/crates/auths-model/src/lib.rs", - "sha256": "3aef299e43b58c12a0b11356668cf6674fc4d72f37bfb0ace1f384c56c1362ad" + "sha256": "95fcccddeacd3fb3c250d820e7b4bd3f81ad0377659798168b3ed2fc8315deba" }, { "path": "formal/algebra-contract-v1.toml", "sha256": "2fd14b11c08381a0c6e7cf6b8104b17ff4c243fd4ef170601f2fa8fb3765fbb6" }, + { + "path": "formal/qualification/aeneas/cases/Algebra.lean", + "sha256": "c5f47639299529bb377afda9a59533f404c0190157179ebfe8636181cbd29778" + }, + { + "path": "formal/qualification/aeneas/cases/Authority.lean", + "sha256": "7527aa0a59714213c1c3c7e6a0e3033dc75d3f65988add45bf61f68e0d92e9c2" + }, + { + "path": "formal/qualification/aeneas/cases/BoundedPolicy.lean", + "sha256": "588b5f70fb2ddab50a931b644c7789baa0aa1398f37d831862de0079b5ff1c54" + }, + { + "path": "formal/qualification/aeneas/cases/Lifecycle.lean", + "sha256": "decdbc3d3788662842e9941f4bbb6e4f9439ce33823c79cf54815790c489b062" + }, + { + "path": "formal/qualification/aeneas/cases/Model.lean", + "sha256": "61654570f7bce38f6a702d1287524afec5c817c0920047eaee00978940631296" + }, { "path": "formal/qualification/aeneas/qualification.toml", - "sha256": "ab95c80acfc15fe972d880fb0290b1e3d1d91df9d4ce0426b36c6398b6632c81" + "sha256": "2bd827b534c14ebeb0a72afe483a2fb9298a9c4c806a36fe4ecebdad584773fd" }, { "path": "formal/translation-toolchain.lock", @@ -56,6 +79,38 @@ "path": "product/policy/auths-bounded-policy/Cargo.toml", "sha256": "babd5d200923137ead71d8c2528f847cea323ce98da942a1320b054436612c47" }, + { + "path": "product/policy/auths-bounded-policy/fuzz/Cargo.toml", + "sha256": "f5a813638b1a20607e51d81ec0fba91991e3e7e7a0c2e53bbd5471b34c97ca89" + }, + { + "path": "product/policy/auths-bounded-policy/fuzz/corpus/target_bounded_policy/17-byte-boundary-seed", + "sha256": "4eaeca677d57a71a930297c981f31591ae842442e286df1e6394cc00a67e3ed5" + }, + { + "path": "product/policy/auths-bounded-policy/fuzz/fuzz_targets/target_bounded_policy.rs", + "sha256": "ce909fa573176a086b9cf2c3a6aaf7a931357b85b40e42efa7a8fe7078520b4b" + }, + { + "path": "product/policy/auths-bounded-policy/fuzz/tests/corpus.rs", + "sha256": "da1ba768eb9be04e999c0a12fec89bbe66cb553593e674c859271a68df3b706b" + }, + { + "path": "product/policy/auths-bounded-policy/src/arithmetic.rs", + "sha256": "75e43c5d4eab8d1f71c1daf53839f5f9fe45a400afc1f63a30c6f55ce3632627" + }, + { + "path": "product/policy/auths-bounded-policy/src/commitment.rs", + "sha256": "c5706f2d583454e1a7f570f7ada138d4d864bef0205dfae7c1296774f396bb3f" + }, + { + "path": "product/policy/auths-bounded-policy/src/eligibility.rs", + "sha256": "c9acdfb793074e687fc36efad506be27dbd99ffbf40387bc66c8de39ef238432" + }, + { + "path": "product/policy/auths-bounded-policy/src/identifier.rs", + "sha256": "0006a32bae9eb16c4178cf797df418d2b0a781f2bc6474b741dc1a908141831c" + }, { "path": "product/policy/auths-bounded-policy/src/kernel.rs", "sha256": "472bd4b23121644ca3a51dc272df72176c3787e9c8eae7f34b41bceb15b52bb3" @@ -64,10 +119,30 @@ "path": "product/policy/auths-bounded-policy/src/lib.rs", "sha256": "76a18638b810e62e594d6e951504679305ce21841388e0c282ad51cd7841b91c" }, + { + "path": "product/policy/auths-bounded-policy/src/receipt.rs", + "sha256": "3b9eae82f349956d3e7f29b57b254bff5ce67cc703e6cf43445ce2827676d1ad" + }, + { + "path": "product/policy/auths-bounded-policy/src/registry.rs", + "sha256": "4e251ea5833559841f8ee0bd9b66c0c01c39fa5e3a21720a19c4e8adbfcb99d0" + }, { "path": "product/runtime/auths-lifecycle/Cargo.toml", "sha256": "7084097893fca25f8a4e959faf86841c4295944ed70591490517c38170a9f01a" }, + { + "path": "product/runtime/auths-lifecycle/src/codec.rs", + "sha256": "51c4b746749150e8bfcd8ff972b3f6c1ef40bec15583448ec1b471d9ad737dc0" + }, + { + "path": "product/runtime/auths-lifecycle/src/digest.rs", + "sha256": "55282cd7d8aba37713c2dcb5357c9877819edb72a30eadefb860d28d7d3a3eb7" + }, + { + "path": "product/runtime/auths-lifecycle/src/identifier.rs", + "sha256": "31620e8c16de68d3d2d3b84d6353c1a5b9d4151d8247f91fa389b906a8e660f1" + }, { "path": "product/runtime/auths-lifecycle/src/kernel.rs", "sha256": "2abb8c094b36d35b364e74d78091d14c5d6f3d1eb6552cca3b5538dbd017ce23" @@ -80,21 +155,165 @@ "path": "product/runtime/auths-lifecycle/src/model.rs", "sha256": "1af12bbeeeaa65ec02ddcebd7d2c8c92ec1e339fa40ae85b45ab9b01a722a4fa" }, + { + "path": "product/runtime/auths-lifecycle/src/registry.rs", + "sha256": "53495fe6f7a77422593a41623e28716f1c140dcc1e00f87f6b0ffbfb93a5ef90" + }, + { + "path": "product/runtime/auths-lifecycle/src/sealed.rs", + "sha256": "2e0c03b5813c926c159598bb7890d1523825edbe57cd687d42a5dfb3374a0c19" + }, + { + "path": "product/runtime/auths-lifecycle/src/test_support.rs", + "sha256": "221d9a5c5ef8e1f9f8fd66927484eaf813571d0568470278ecd8fa08a5eec8cd" + }, + { + "path": "product/runtime/auths-lifecycle/src/transition.rs", + "sha256": "011805a28efdd541f44ccea54dd0e34a1e58f4004447ee58013703b4fb6b2822" + }, + { + "path": "product/runtime/auths-lifecycle/tests/lifecycle.rs", + "sha256": "735466062bd8f032f9a9d410384d62107e32bcff5457520426d7deca5aecd686" + }, + { + "path": "xtask/Cargo.toml", + "sha256": "5958f5ae42c7830a8dd0cde470402cdf8b9f8357b9bfd953fbba96955d016566" + }, { "path": "xtask/ci-plan/Cargo.toml", "sha256": "7e69e5c636c2f584113a7f8e96736b1a3feb78a1d4921fe1af8ee832212e4d35" }, + { + "path": "xtask/ci-plan/src/formal_update.rs", + "sha256": "668e3b714a0cd07b8c8c32ed658cf7bdcc07439a36f9e0edebcdc65529f54ab4" + }, { "path": "xtask/ci-plan/src/lib.rs", - "sha256": "443790c550b81d7921337362a17513ce9752ede661633221c03a414d04c517b3" + "sha256": "42ca248e1c169063e3f782c0250a16f03b750a1baf3425f80d36deab411dc2db" + }, + { + "path": "xtask/ci-plan/src/main.rs", + "sha256": "adcb0b3e4db7979aa9d58050f09102d23bc32adc65017d9b42639625e3ff55a8" + }, + { + "path": "xtask/src/architecture.rs", + "sha256": "3344b796e095c16fd679416f5610544006fd356df31e914a6d0463fdeee84fc2" + }, + { + "path": "xtask/src/assurance.rs", + "sha256": "88811480332341265de50418e887968d35f3454daf63d0f600a22adbc3940b0a" + }, + { + "path": "xtask/src/benchmark.rs", + "sha256": "76698fa709e7eb6bef8f6b42bbf3a705e4e8100ec175843446a11b4877b78da4" + }, + { + "path": "xtask/src/binding_semantics.rs", + "sha256": "d30b0bd074aa61a62b87146d27c8c5ec7d42a30ca3aa45b139f4df2268af189e" + }, + { + "path": "xtask/src/bounded_benchmark.rs", + "sha256": "838f9153ee521c4bbb38de34caae3c1c01d7d688e17c2b961d2d8b35e22224e6" + }, + { + "path": "xtask/src/bounded_domains.rs", + "sha256": "3703534c0bdcbb05244672657e735e08d330c954f285fe13fbca58db463818e5" + }, + { + "path": "xtask/src/checks.rs", + "sha256": "867dedc085734b1a713124b9a22200ed231b1c7de07284802839224ea9482e80" + }, + { + "path": "xtask/src/compliance.rs", + "sha256": "46d85cd9d166910031d3eb2996a7ed8704739a020f4bd75a2f3b10dde1610929" + }, + { + "path": "xtask/src/conformance.rs", + "sha256": "788f2b838e9553bbfb66fc6eb7382c394a73534c638fdce17c3633b3b807622d" + }, + { + "path": "xtask/src/error_registry.rs", + "sha256": "9c9c734100f7a1db41a5a441af948d1ed7f405790a710fab546c5eaec4f6efcf" + }, + { + "path": "xtask/src/evolution_policy.rs", + "sha256": "4eb23d63527ffe9ce8d1f40c29538a6ee47cea91a24fdca9312ec8f0c815be9b" + }, + { + "path": "xtask/src/fixtures.rs", + "sha256": "a9e2799459b12d6939b98edd0c293453c2caa168e7d75fbf0ba6756b5660602e" }, { "path": "xtask/src/formal.rs", - "sha256": "d6b8a4e826590386179e9cf7e2f502229eceffa721ed44572b5ac469a167f040" + "sha256": "d113c3f263b4685000e355369eb883f4e068bf4d6b3b5983db699ef266a5e2ae" }, { "path": "xtask/src/formal_qualification.rs", - "sha256": "7c465ac38d869415deb551eed721bb01b4bd22874bd47054269088a645507de3" + "sha256": "7fa6e6bab2ad01ff071a12940064726a743767b4a0861377789434e1bedbfcac" + }, + { + "path": "xtask/src/fuzz.rs", + "sha256": "0ea7bfdede11543d14f19ecb05b64a67ec4ba9138727186f7dbce5b4b7020f99" + }, + { + "path": "xtask/src/live_demo.rs", + "sha256": "ecebd3a67276960d18dbc0dbf481d1972c5f3202aaa08e660f6fe2ba205905e3" + }, + { + "path": "xtask/src/main.rs", + "sha256": "3950c5eace93c58ef80b14ddb8b40bd91bc0acc7bc67e680d9a1e4acea9b9d91" + }, + { + "path": "xtask/src/mcp_session_contract.rs", + "sha256": "d47c9cf1f23faf3cd01c61381fa23947d59350f2845c340c029604ddf0780c30" + }, + { + "path": "xtask/src/mechanism_conformance.rs", + "sha256": "80d62edd545ca2eccfa4175a7fb14f6e1190f927acd0a1fd8e8dbb530c52a3e3" + }, + { + "path": "xtask/src/prelude.rs", + "sha256": "1fe26fb3edbe41baeb171f87c99cd24147e765c484cc2804b4a0f2def0dad52a" + }, + { + "path": "xtask/src/process.rs", + "sha256": "c05c8c7260a513fa8a43cb4cbc34bde300cb7fdf3b3f86f5d0d30888f36a41a7" + }, + { + "path": "xtask/src/product_waist.rs", + "sha256": "323b1410f96809b9b462eda2c8865f6ade74cef8c17322ba1045576b184013b9" + }, + { + "path": "xtask/src/production_contract.rs", + "sha256": "9fb7537773f758bb6012e943bc505a94aea650bb2bdb720a70ffdae261c66342" + }, + { + "path": "xtask/src/public_naming.rs", + "sha256": "f33fcc9b2b79c7fb567a6beb400a94bdd2c7b161a71c02cec334a44fa94051e2" + }, + { + "path": "xtask/src/release.rs", + "sha256": "8717b630f84e20e1c09480734d67114a0c4ec2f75ff21e983c9905ba42f7b65d" + }, + { + "path": "xtask/src/release_control.rs", + "sha256": "8dd95fb137d704c78ddf15bfa0c78d0813e67e4ba1e4f376af6ad56b0d7579cd" + }, + { + "path": "xtask/src/sdk_experience.rs", + "sha256": "0c3d9329d5e2c17b00c77cf1f5edbb870e053111e24da3816d332732cf202c0f" + }, + { + "path": "xtask/src/sdk_vocabulary.rs", + "sha256": "6e656f6429f3b3ec31b3aff5a7c12764f8f00e09b98555350090ce211ecce3d1" + }, + { + "path": "xtask/src/semantic_freeze.rs", + "sha256": "da7a7a5be21a7227f958faf054c1b2f8d37cff87cf72917d039679749392672f" + }, + { + "path": "xtask/src/stripe.rs", + "sha256": "69d296cfb809053bd57b40604bbbad2072719053c5c91a7f594a3fa8b1875c33" } ] } diff --git a/formal/refinement-mutations-v1.json b/formal/refinement-mutations-v1.json index e266db95..353af472 100644 --- a/formal/refinement-mutations-v1.json +++ b/formal/refinement-mutations-v1.json @@ -4,64 +4,73 @@ { "id": "validity-start-direction", "operator": "Reverse the inclusive lower-bound comparison.", - "witness": "A child beginning after its parent must remain contained.", + "witness": "A child beginning before its parent must be rejected.", "lean_declaration": "Auths.Rich.Mutations.validity_start_direction" }, { "id": "validity-end-direction", "operator": "Reverse the inclusive upper-bound comparison.", - "witness": "A child ending before its parent must remain contained.", + "witness": "A child ending after its parent must be rejected.", "lean_declaration": "Auths.Rich.Mutations.validity_end_direction" }, { "id": "permission-subset-direction", "operator": "Reverse child and parent in permission subset evaluation.", - "witness": "A singleton child is permitted by a strict two-element parent." + "witness": "A singleton child is permitted by a strict two-element parent.", + "lean_declaration": "Auths.Rich.Mutations.permission_subset_direction" }, { "id": "audience-subset-direction", "operator": "Reverse child and parent in audience subset evaluation.", - "witness": "A singleton child is permitted by a strict two-element parent." + "witness": "A singleton child is permitted by a strict two-element parent.", + "lean_declaration": "Auths.Rich.Mutations.audience_subset_direction" }, { "id": "permission-membership-decision", "operator": "Negate permission membership.", - "witness": "A permission present in the authority set must be found." + "witness": "A permission present in the authority set must be found.", + "lean_declaration": "Auths.Rich.Mutations.permission_membership_decision" }, { "id": "audience-membership-decision", "operator": "Negate audience membership.", - "witness": "An audience present in the authority set must be found." + "witness": "An audience present in the authority set must be found.", + "lean_declaration": "Auths.Rich.Mutations.audience_membership_decision" }, { "id": "body-digest-membership-decision", "operator": "Negate body-digest membership.", - "witness": "A digest present in an allowed-body set must be found." + "witness": "A digest present in an allowed-body set must be found.", + "lean_declaration": "Auths.Rich.Mutations.body_digest_membership_decision" }, { "id": "body-digest-subset-direction", "operator": "Reverse child and parent in body-digest subset evaluation.", - "witness": "A singleton digest set attenuates a strict two-element parent." + "witness": "A singleton digest set attenuates a strict two-element parent.", + "lean_declaration": "Auths.Rich.Mutations.body_digest_subset_direction" }, { "id": "action-exact-equality", "operator": "Accept a different digest for an exact-body constraint.", - "witness": "Distinct 32-byte body digests must not compare equal." + "witness": "Distinct 32-byte body digests must not compare equal.", + "lean_declaration": "Auths.Rich.Mutations.action_exact_equality" }, { "id": "action-constructor-fallback", "operator": "Accept a multi-digest allowed-body set beneath an exact-body parent.", - "witness": "A two-digest set admits more actions than an exact constraint." + "witness": "A two-digest set admits more actions than an exact constraint.", + "lean_declaration": "Auths.Rich.Mutations.action_constructor_fallback" }, { "id": "action-singleton-exact-rejection", "operator": "Reject a singleton allowed-body set beneath its equivalent exact-body parent.", - "witness": "Allowed({d}) and Exact(d) admit the same canonical body digest." + "witness": "Allowed({d}) and Exact(d) admit the same canonical body digest.", + "lean_declaration": "Auths.Rich.Mutations.action_singleton_exact_rejection" }, { "id": "budget-value-direction", "operator": "Reverse the numeric budget ceiling comparison.", - "witness": "A lower child ceiling attenuates a higher parent ceiling.", + "witness": "A child ceiling above its parent must be rejected.", "lean_declaration": "Auths.Rich.Mutations.budget_value_direction" }, { @@ -85,7 +94,7 @@ { "id": "status-age-direction", "operator": "Reverse the maximum status age comparison.", - "witness": "A fresher child requirement attenuates a less-fresh parent.", + "witness": "A child allowing older status evidence than its parent must be rejected.", "lean_declaration": "Auths.Rich.Mutations.status_age_direction" }, { @@ -109,7 +118,8 @@ { "id": "critical-extension-equality", "operator": "Ignore changes to a critical extension payload.", - "witness": "Target V1 requires the complete canonical critical-extension set to remain exactly equal." + "witness": "Target V1 requires the complete canonical critical-extension set to remain exactly equal.", + "lean_declaration": "Auths.Rich.Mutations.critical_extension_equality" }, { "id": "delegation-depth-strictness", diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index ab3fffbc..a4117111 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 124, + "freezeVersion": 125, "publicSurface": { "rustRoots": [ "auths", @@ -70,7 +70,7 @@ "entries": [ { "id": "auths.core.protocol", - "version": 16, + "version": 17, "classification": "frozen-meaning", "categories": [ "protocol-versions", @@ -87,7 +87,7 @@ "core/crates/auths-verifier/src", "core/spec/v1" ], - "sha256": "e5320ad6ebb314a3644c92d53a8c7bed76309eff012d83c18a4f3743407be1cf" + "sha256": "ae55274703779bb8ea5d2d625e6a25668335a776f099f7310864688803464227" }, { "id": "auths.frozen-bytes/architecture/dependency-graph.json", @@ -199,7 +199,7 @@ }, { "id": "auths.frozen-bytes/formal/assurance-manifest-v1.toml", - "version": 11, + "version": 12, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -207,11 +207,11 @@ "owners": [ "formal/assurance-manifest-v1.toml" ], - "sha256": "9a6c3454ad149b8702ebb1e33d5630342b0b5187c981d4d06832e02bc3da94b2" + "sha256": "a91cabdddc257c47ad4a2fbf6ad1c061ea003acd29b18b356cf32a8d62b4ba9f" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/generated", - "version": 5, + "version": 6, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -219,11 +219,11 @@ "owners": [ "formal/qualification/aeneas/generated" ], - "sha256": "7f0afdea68851dfb2e65b885dcdb7e82fd6d5c2f9aa0a1c8a400d7853ebfed26" + "sha256": "a9a332751864bbd5267afa8c2fbd3e454868d9c048b4ac7d4530875b40537810" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/qualification.toml", - "version": 4, + "version": 5, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -231,11 +231,11 @@ "owners": [ "formal/qualification/aeneas/qualification.toml" ], - "sha256": "d8ea3165a51a80e7a75a559aa7118f976b7817c4d7569970e6733ed199a74bf6" + "sha256": "0af12e3cd98e531abaeffd66fb41a22c5a5504c2494e42ce173eb503fe3f454d" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/source-closure.json", - "version": 18, + "version": 19, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -243,7 +243,7 @@ "owners": [ "formal/qualification/aeneas/source-closure.json" ], - "sha256": "185abec95406f2f224ec490c6f07e6ff42d60db1156f643779cba99954e63918" + "sha256": "28f7bced1a3be1355aa45f4b42cc067fde322efc5303b23a15b31df4b8d8ccd8" }, { "id": "auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json", @@ -620,7 +620,7 @@ }, { "id": "auths.portable-abi-bindings", - "version": 52, + "version": 53, "classification": "frozen-meaning", "categories": [ "portable-abi", @@ -637,7 +637,7 @@ "core/crates/auths-model/src/lib.rs", "core/spec/v1/auths-proof.cddl" ], - "sha256": "a3962aa28a0e79beed7366b0c266bc9cd02955693835569b00fc7da411bd81c2" + "sha256": "141beaea9021042cd5d18bc0f0d9fa6d169c5e7b1ea77d674c49d7ed6216d6a5" }, { "id": "auths.product.bounded-domains", @@ -1013,7 +1013,7 @@ }, { "id": "auths.release.evolution-contract", - "version": 15, + "version": 16, "classification": "frozen-meaning", "categories": [ "version-axes", @@ -1034,11 +1034,11 @@ "release/fixtures/evolution", "xtask/src/evolution_policy.rs" ], - "sha256": "4a04dd12e619f95e98881b2bc259f36d4a203778c227d8d8b3e394dd03fd174d" + "sha256": "71ebc9c8a7a5bfa14b582aa5444cce4cf66af0e8493086de30076e60c973d3d5" }, { "id": "auths.release.public-surface", - "version": 123, + "version": 124, "classification": "release-metadata", "categories": [ "package-names", @@ -1132,7 +1132,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "ae35b7e2e2a06a04af3042bf00e8f834874bdfd58a14122d87ef16946253b704" + "sha256": "960661072f7be01364a738a1062ad833e16682d7bf58978a5016c4a6c90ff78e" } ] } diff --git a/xtask/ci-plan/src/lib.rs b/xtask/ci-plan/src/lib.rs index e189ed2e..0d718af8 100644 --- a/xtask/ci-plan/src/lib.rs +++ b/xtask/ci-plan/src/lib.rs @@ -1492,21 +1492,24 @@ pub fn formal_source_closure_json( translation_roots: &[String], ) -> Result { let semantic_cargo = semantic_formal_cargo_inputs(root, translation_roots)?; + for relative in paths { + validated_formal_source_path(root, relative)?; + } let mut ordered = paths.to_vec(); + let authored_count = ordered.len(); ordered.sort(); ordered.dedup(); - if ordered.len() != paths.len() { + if ordered.len() != authored_count { return Err("production translation source paths must be unique".to_owned()); } + ordered.extend(local_formal_rust_source_paths(root, translation_roots)?); + ordered.sort(); + ordered.dedup(); let mut aggregate = Sha256::new(); let mut entries = Vec::with_capacity(ordered.len()); for relative in ordered { let (bytes, normalization) = match relative.as_str() { - "Cargo.toml" => ( - semantic_cargo.workspace_manifest.as_slice(), - Some("translated-cargo-closure-v1"), - ), "Cargo.lock" => ( semantic_cargo.resolved_dependencies.as_slice(), Some("translated-cargo-closure-v1"), @@ -1548,8 +1551,226 @@ pub fn formal_source_closure_json( })) } +fn validate_formal_source_path(relative: &str) -> Result<(), String> { + let path = Path::new(relative); + if relative.is_empty() + || path.is_absolute() + || path + .components() + .any(|component| !matches!(component, std::path::Component::Normal(_))) + { + return Err(format!( + "production translation source must be a normalized workspace-relative path: {relative}" + )); + } + Ok(()) +} + +fn validated_formal_source_path(root: &Path, relative: &str) -> Result { + validate_formal_source_path(relative)?; + let unresolved = root.join(relative); + let metadata = fs::symlink_metadata(&unresolved).map_err(|error| { + format!( + "could not stat production translation source {}: {error}", + unresolved.display() + ) + })?; + if !metadata.file_type().is_file() || metadata.file_type().is_symlink() { + return Err(format!( + "production translation source is not a regular non-symlink file: {relative}" + )); + } + let workspace = fs::canonicalize(root) + .map_err(|error| format!("could not resolve workspace root: {error}"))?; + let resolved = fs::canonicalize(&unresolved).map_err(|error| { + format!( + "could not resolve production translation source {}: {error}", + unresolved.display() + ) + })?; + if !resolved.starts_with(workspace) { + return Err(format!( + "production translation source escapes workspace: {relative}" + )); + } + Ok(resolved) +} + +/// Conservatively closes the local Rust source side of the formal boundary. +/// +/// Charon follows Rust modules through the compiler, whereas the original +/// evidence list was hand-maintained. A newly imported `mod policy;` could +/// therefore influence the generated Lean without appearing in the recorded +/// source closure. Include every Rust source in every local package reachable +/// from a translation root, plus the complete xtask/control-plane packages. +/// This is intentionally broader than module parsing: every regular package +/// file is bound, including `include_str!`/`include_bytes!` inputs and +/// build-script data. Unused files may cause a harmless digest change, but a +/// compiler-visible local input cannot be missed. +fn local_formal_rust_source_paths( + root: &Path, + translation_roots: &[String], +) -> Result, String> { + let output = ProcessCommand::new("cargo") + .args(["metadata", "--locked", "--format-version", "1"]) + .current_dir(root) + .output() + .map_err(|error| format!("could not run cargo metadata: {error}"))?; + if !output.status.success() { + return Err(format!( + "cargo metadata failed: {}", + String::from_utf8_lossy(&output.stderr) + )); + } + let metadata: Value = serde_json::from_slice(&output.stdout) + .map_err(|error| format!("invalid cargo metadata: {error}"))?; + local_formal_rust_source_paths_from_values(root, &metadata, translation_roots) +} + +fn local_formal_rust_source_paths_from_values( + root: &Path, + metadata: &Value, + translation_roots: &[String], +) -> Result, String> { + let packages = metadata["packages"] + .as_array() + .ok_or("cargo metadata omits packages")?; + let mut ids_by_name: BTreeMap> = BTreeMap::new(); + let mut package_by_id = BTreeMap::new(); + for package in packages { + let id = required_json_string(package, "id")?.to_owned(); + let name = required_json_string(package, "name")?.to_owned(); + ids_by_name.entry(name).or_default().insert(id.clone()); + package_by_id.insert(id, package); + } + let nodes = metadata["resolve"]["nodes"] + .as_array() + .ok_or("cargo metadata omits resolve.nodes")?; + let mut dependencies = BTreeMap::new(); + for node in nodes { + let id = required_json_string(node, "id")?.to_owned(); + let deps = node["dependencies"] + .as_array() + .ok_or("cargo metadata node omits dependencies")? + .iter() + .map(|dependency| { + dependency + .as_str() + .ok_or_else(|| "cargo dependency ID is not a string".to_owned()) + .map(str::to_owned) + }) + .collect::, _>>()?; + dependencies.insert(id, deps); + } + + let mut root_names = translation_roots.to_vec(); + root_names.sort(); + root_names.dedup(); + let mut root_ids = BTreeSet::new(); + for name in root_names { + let package_name = name.replace('_', "-"); + let ids = ids_by_name + .get(&package_name) + .ok_or_else(|| format!("formal source root package is absent: {name}"))?; + if ids.len() != 1 { + return Err(format!( + "formal source root package {name} resolves to {} package IDs", + ids.len() + )); + } + root_ids.extend(ids.iter().cloned()); + } + + let mut closure = graph_closure(&root_ids, &dependencies); + // The control plane is itself assurance-relevant, but its many unrelated + // workspace dependencies are not translation inputs. Bind both complete + // package trees without pulling every adapter/test fixture in the + // workspace into the production semantic closure. + for name in ["auths-ci-plan", "xtask"] { + let ids = ids_by_name + .get(name) + .ok_or_else(|| format!("formal control package is absent: {name}"))?; + if ids.len() != 1 { + return Err(format!( + "formal control package {name} resolves to {} package IDs", + ids.len() + )); + } + closure.extend(ids.iter().cloned()); + } + let mut sources = BTreeSet::new(); + for id in closure { + let package = package_by_id + .get(&id) + .ok_or_else(|| format!("resolved dependency package is absent: {id}"))?; + let manifest = PathBuf::from(required_json_string(package, "manifest_path")?); + let Some(relative_manifest) = local_manifest_relative_path(root, package, &manifest)? + else { + continue; + }; + sources.insert(relative_manifest.to_string_lossy().replace('\\', "/")); + let package_root = manifest + .parent() + .ok_or_else(|| format!("package manifest has no parent: {}", manifest.display()))?; + collect_package_sources(root, package_root, &mut sources)?; + } + Ok(sources.into_iter().collect()) +} + +fn local_manifest_relative_path<'a>( + root: &'a Path, + package: &Value, + manifest: &'a Path, +) -> Result, String> { + match manifest.strip_prefix(root) { + Ok(relative) => Ok(Some(relative)), + Err(_) if package["source"].is_null() => Err(format!( + "local path dependency escapes the formal workspace source closure: {}", + manifest.display() + )), + Err(_) => Ok(None), + } +} + +fn collect_package_sources( + workspace_root: &Path, + directory: &Path, + sources: &mut BTreeSet, +) -> Result<(), String> { + for entry in fs::read_dir(directory) + .map_err(|error| format!("could not read {}: {error}", directory.display()))? + { + let entry = entry + .map_err(|error| format!("could not read entry in {}: {error}", directory.display()))?; + let path = entry.path(); + let file_type = entry + .file_type() + .map_err(|error| format!("could not stat {}: {error}", path.display()))?; + if file_type.is_symlink() { + return Err(format!( + "local formal package source contains a symlink: {}", + path.display() + )); + } + if file_type.is_dir() { + let name = entry.file_name(); + if name != "target" && name != ".git" { + collect_package_sources(workspace_root, &path, sources)?; + } + } else if file_type.is_file() { + let relative = path.strip_prefix(workspace_root).map_err(|_| { + format!( + "local formal Rust source escapes workspace: {}", + path.display() + ) + })?; + sources.insert(relative.to_string_lossy().replace('\\', "/")); + } + } + Ok(()) +} + struct SemanticCargoInputs { - workspace_manifest: Vec, resolved_dependencies: Vec, } @@ -1572,8 +1793,14 @@ fn semantic_formal_cargo_inputs( .map_err(|error| format!("invalid cargo metadata: {error}"))?; let mut semantic_roots = translation_roots.to_vec(); semantic_roots.push("auths-ci-plan".to_owned()); + semantic_roots.push("xtask".to_owned()); semantic_roots.sort(); semantic_roots.dedup(); + let cargo_lock = parse_toml( + &fs::read(root.join("Cargo.lock")) + .map_err(|error| format!("could not read Cargo.lock: {error}"))?, + "Cargo.lock", + )?; semantic_formal_cargo_inputs_from_values( root, &metadata, @@ -1582,6 +1809,7 @@ fn semantic_formal_cargo_inputs( .map_err(|error| format!("could not read Cargo.toml: {error}"))?, "Cargo.toml", )?, + &cargo_lock, &semantic_roots, ) } @@ -1589,7 +1817,8 @@ fn semantic_formal_cargo_inputs( fn semantic_formal_cargo_inputs_from_values( root: &Path, metadata: &Value, - workspace_manifest: &toml::Value, + _workspace_manifest: &toml::Value, + cargo_lock: &toml::Value, translation_roots: &[String], ) -> Result { let packages = metadata["packages"] @@ -1639,13 +1868,6 @@ fn semantic_formal_cargo_inputs_from_values( roots.extend(ids.iter().cloned()); } let closure = graph_closure(&roots, &dependencies); - let closure_names: BTreeSet<_> = closure - .iter() - .filter_map(|id| package_by_id.get(id)) - .filter_map(|package| package["name"].as_str()) - .map(str::to_owned) - .collect(); - let mut normalized_packages = Vec::new(); for id in &closure { let package = package_by_id @@ -1671,46 +1893,33 @@ fn semantic_formal_cargo_inputs_from_values( .ok_or("cargo metadata node omits enabled features")? .clone(); enabled_features.sort_by_key(canonical_json); + let mut resolved_dependencies = dependencies + .get(id) + .ok_or_else(|| format!("resolved dependency edges are absent: {id}"))? + .iter() + .map(|dependency_id| { + let dependency = package_by_id.get(dependency_id).ok_or_else(|| { + format!("resolved dependency package is absent: {dependency_id}") + })?; + normalized_package_identity(root, dependency) + }) + .collect::, String>>()?; + resolved_dependencies.sort_by_key(canonical_json); + let locked = normalized_lock_identity(cargo_lock, package)?; normalized_packages.push(serde_json::json!({ "name": package["name"], "version": package["version"], "source": package["source"], "manifest": normalized_manifest, + "locked": locked, "dependencies": package_dependencies, + "resolved_dependencies": resolved_dependencies, "enabled_features": enabled_features, })); } normalized_packages.sort_by_key(canonical_json); - let workspace = workspace_manifest - .get("workspace") - .and_then(toml::Value::as_table) - .ok_or("Cargo.toml omits [workspace]")?; - let workspace_package = workspace - .get("package") - .cloned() - .unwrap_or_else(|| toml::Value::Table(toml::map::Map::new())); - let mut relevant_workspace_dependencies = toml::map::Map::new(); - if let Some(dependencies) = workspace - .get("dependencies") - .and_then(toml::Value::as_table) - { - for (key, value) in dependencies { - let package_name = value - .get("package") - .and_then(toml::Value::as_str) - .unwrap_or(key); - if closure_names.contains(package_name) { - relevant_workspace_dependencies.insert(key.clone(), value.clone()); - } - } - } - let normalized_workspace = serde_json::json!({ - "workspace_package": workspace_package, - "workspace_dependencies": relevant_workspace_dependencies, - }); Ok(SemanticCargoInputs { - workspace_manifest: canonical_json(&normalized_workspace).into_bytes(), resolved_dependencies: canonical_json(&serde_json::json!({ "translation_roots": translation_roots, "packages": normalized_packages, @@ -1719,6 +1928,50 @@ fn semantic_formal_cargo_inputs_from_values( }) } +fn normalized_package_identity(root: &Path, package: &Value) -> Result { + let manifest_path = PathBuf::from(required_json_string(package, "manifest_path")?); + let manifest = manifest_path + .strip_prefix(root) + .map(|path| path.to_string_lossy().replace('\\', "/")) + .unwrap_or_else(|_| "".to_owned()); + Ok(serde_json::json!({ + "name": required_json_string(package, "name")?, + "version": required_json_string(package, "version")?, + "source": package["source"], + "manifest": manifest, + })) +} + +fn normalized_lock_identity(cargo_lock: &toml::Value, package: &Value) -> Result { + let name = required_json_string(package, "name")?; + let version = required_json_string(package, "version")?; + let source = package["source"].as_str(); + let entries = cargo_lock + .get("package") + .and_then(toml::Value::as_array) + .ok_or("Cargo.lock omits [[package]] entries")?; + let matches = entries + .iter() + .filter(|entry| { + entry.get("name").and_then(toml::Value::as_str) == Some(name) + && entry.get("version").and_then(toml::Value::as_str) == Some(version) + && entry.get("source").and_then(toml::Value::as_str) == source + }) + .collect::>(); + if matches.len() != 1 { + return Err(format!( + "Cargo.lock has {} exact entries for resolved package {name} {version} {:?}", + matches.len(), + source + )); + } + let entry = matches[0]; + Ok(serde_json::json!({ + "source": entry.get("source").and_then(toml::Value::as_str), + "checksum": entry.get("checksum").and_then(toml::Value::as_str), + })) +} + fn normalize_dependency(value: &Value) -> Result { Ok(serde_json::json!({ "name": required_json_string(value, "name")?, @@ -1972,12 +2225,20 @@ unrelated = "9" .expect("valid manifest"); let first = formal_metadata("1.0.0", "9.0.0"); let second = formal_metadata("1.0.0", "10.0.0"); + let first_lock = formal_lock("1.0.0", "serde-a", "9.0.0", "unrelated-a"); + let second_lock = formal_lock("1.0.0", "serde-a", "10.0.0", "unrelated-b"); let roots = ["auths-model".to_owned()]; - let first = semantic_formal_cargo_inputs_from_values(root, &first, &manifest, &roots) - .expect("first closure"); - let second = semantic_formal_cargo_inputs_from_values(root, &second, &manifest, &roots) - .expect("second closure"); - assert_eq!(first.workspace_manifest, second.workspace_manifest); + let first = + semantic_formal_cargo_inputs_from_values(root, &first, &manifest, &first_lock, &roots) + .expect("first closure"); + let second = semantic_formal_cargo_inputs_from_values( + root, + &second, + &manifest, + &second_lock, + &roots, + ) + .expect("second closure"); assert_eq!(first.resolved_dependencies, second.resolved_dependencies); } @@ -1996,6 +2257,7 @@ serde = "1" root, &formal_metadata("1.0.0", "9.0.0"), &manifest, + &formal_lock("1.0.0", "serde-a", "9.0.0", "unrelated-a"), &roots, ) .expect("first closure"); @@ -2003,12 +2265,213 @@ serde = "1" root, &formal_metadata("2.0.0", "9.0.0"), &manifest, + &formal_lock("2.0.0", "serde-b", "9.0.0", "unrelated-a"), &roots, ) .expect("second closure"); assert_ne!(first.resolved_dependencies, second.resolved_dependencies); } + #[test] + fn translated_lock_checksum_change_drifts_but_unrelated_checksum_does_not() { + let root = Path::new("/repo"); + let manifest: toml::Value = + toml::from_str("[workspace]\n[workspace.dependencies]\nserde = '1'\n") + .expect("valid manifest"); + let metadata = formal_metadata("1.0.0", "9.0.0"); + let roots = ["auths-model".to_owned()]; + let baseline = semantic_formal_cargo_inputs_from_values( + root, + &metadata, + &manifest, + &formal_lock("1.0.0", "serde-a", "9.0.0", "unrelated-a"), + &roots, + ) + .expect("baseline closure"); + let unrelated = semantic_formal_cargo_inputs_from_values( + root, + &metadata, + &manifest, + &formal_lock("1.0.0", "serde-a", "9.0.0", "unrelated-b"), + &roots, + ) + .expect("unrelated closure"); + let relevant = semantic_formal_cargo_inputs_from_values( + root, + &metadata, + &manifest, + &formal_lock("1.0.0", "serde-b", "9.0.0", "unrelated-a"), + &roots, + ) + .expect("relevant closure"); + assert_eq!( + baseline.resolved_dependencies, + unrelated.resolved_dependencies + ); + assert_ne!( + baseline.resolved_dependencies, + relevant.resolved_dependencies + ); + } + + #[test] + fn resolved_dependency_edge_change_drifts_even_when_package_set_is_unchanged() { + let root = Path::new("/repo"); + let manifest: toml::Value = + toml::from_str("[workspace]\n[workspace.dependencies]\nserde = '1'\n") + .expect("valid manifest"); + let mut baseline_metadata = formal_metadata("1.0.0", "9.0.0"); + baseline_metadata["resolve"]["nodes"][0]["dependencies"] = + serde_json::json!(["registry+serde#1.0.0", "registry+unrelated#9.0.0"]); + let mut changed_metadata = baseline_metadata.clone(); + changed_metadata["resolve"]["nodes"][1]["dependencies"] = + serde_json::json!(["registry+unrelated#9.0.0"]); + let lock = formal_lock("1.0.0", "serde-a", "9.0.0", "unrelated-a"); + let roots = ["auths-model".to_owned()]; + let baseline = semantic_formal_cargo_inputs_from_values( + root, + &baseline_metadata, + &manifest, + &lock, + &roots, + ) + .expect("baseline graph"); + let changed = semantic_formal_cargo_inputs_from_values( + root, + &changed_metadata, + &manifest, + &lock, + &roots, + ) + .expect("changed graph"); + assert_ne!( + baseline.resolved_dependencies, + changed.resolved_dependencies + ); + } + + #[test] + fn local_rust_source_closure_finds_new_modules_and_control_plane_sources() { + let unique = format!( + "auths-proof-source-closure-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("clock after epoch") + .as_nanos() + ); + let root = env::temp_dir().join(unique); + let packages = [ + ("auths-model", "core/auths-model"), + ("auths-ci-plan", "xtask/ci-plan"), + ("xtask", "xtask"), + ]; + for (_, relative) in packages { + let package = root.join(relative); + fs::create_dir_all(package.join("src")).expect("package source directory"); + fs::write(package.join("Cargo.toml"), "[package]\nname='fixture'\n") + .expect("fixture manifest"); + fs::write(package.join("src/lib.rs"), "pub fn present() {}\n").expect("fixture source"); + } + fs::write( + root.join("core/auths-model/src/policy.rs"), + "pub const POLICY: &[u8] = include_bytes!(\"policy.dat\");\n", + ) + .expect("new imported module"); + fs::write(root.join("core/auths-model/src/policy.dat"), b"deny\n") + .expect("included policy data"); + fs::write(root.join("xtask/src/main.rs"), "fn main() {}\n").expect("xtask dispatcher"); + + let package_json = |name: &str, relative: &str| { + let id = format!("path+file://fixture/{name}#1.0.0"); + serde_json::json!({ + "id": id, + "name": name, + "manifest_path": root.join(relative).join("Cargo.toml"), + }) + }; + let packages_json = packages + .iter() + .map(|(name, relative)| package_json(name, relative)) + .collect::>(); + let nodes = packages + .iter() + .map(|(name, _)| { + serde_json::json!({ + "id": format!("path+file://fixture/{name}#1.0.0"), + "dependencies": [], + }) + }) + .collect::>(); + let metadata = serde_json::json!({ + "packages": packages_json, + "resolve": { "nodes": nodes }, + }); + let paths = local_formal_rust_source_paths_from_values( + &root, + &metadata, + &["auths_model".to_owned()], + ) + .expect("derived local source closure"); + assert!(paths.contains(&"core/auths-model/src/policy.rs".to_owned())); + assert!(paths.contains(&"core/auths-model/src/policy.dat".to_owned())); + assert!(paths.contains(&"xtask/src/main.rs".to_owned())); + assert!(paths.contains(&"xtask/ci-plan/src/lib.rs".to_owned())); + + fs::remove_dir_all(&root).expect("remove owned fixture directory"); + } + + #[test] + fn formal_source_paths_and_external_local_dependencies_fail_closed() { + for unsafe_path in ["", ".", "../outside", "/etc/passwd"] { + assert!(validate_formal_source_path(unsafe_path).is_err()); + } + validate_formal_source_path("core/crates/auths-model/src/lib.rs") + .expect("normalized workspace path"); + + let root = Path::new("/workspace"); + let external = Path::new("/external/auths-local/Cargo.toml"); + let local_package = serde_json::json!({ + "name": "auths-local", + "source": null, + "manifest_path": external, + }); + assert!(local_manifest_relative_path(root, &local_package, external).is_err()); + + let registry_package = serde_json::json!({ + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "manifest_path": "/registry/serde/Cargo.toml", + }); + assert_eq!( + local_manifest_relative_path( + root, + ®istry_package, + Path::new("/registry/serde/Cargo.toml") + ) + .expect("registry dependency is lock-bound"), + None + ); + } + + #[cfg(unix)] + #[test] + fn authored_formal_source_symlink_is_rejected() { + let unique = format!("auths-source-symlink-{}", std::process::id()); + let root = env::temp_dir().join(unique); + fs::create_dir_all(&root).expect("owned fixture root"); + let outside = env::temp_dir().join(format!( + "auths-source-symlink-target-{}", + std::process::id() + )); + fs::write(&outside, "boundary\n").expect("outside fixture"); + std::os::unix::fs::symlink(&outside, root.join("source.rs")).expect("source symlink"); + assert!(validated_formal_source_path(&root, "source.rs").is_err()); + fs::remove_file(root.join("source.rs")).expect("remove fixture symlink"); + fs::remove_file(outside).expect("remove fixture target"); + fs::remove_dir(root).expect("remove fixture root"); + } + #[test] fn records_only_path_does_not_schedule_other_domains_or_formal_translation() { let root = Path::new(env!("CARGO_MANIFEST_DIR")) @@ -2131,4 +2594,33 @@ serde = "1" } }) } + + fn formal_lock( + serde_version: &str, + serde_checksum: &str, + unrelated_version: &str, + unrelated_checksum: &str, + ) -> toml::Value { + toml::from_str(&format!( + r#"version = 4 + +[[package]] +name = "auths-model" +version = "0.1.0" + +[[package]] +name = "serde" +version = "{serde_version}" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "{serde_checksum}" + +[[package]] +name = "unrelated" +version = "{unrelated_version}" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "{unrelated_checksum}" +"# + )) + .expect("valid fixture Cargo.lock") + } } diff --git a/xtask/src/formal.rs b/xtask/src/formal.rs index 58a6a871..5a30aafc 100644 --- a/xtask/src/formal.rs +++ b/xtask/src/formal.rs @@ -324,15 +324,24 @@ pub(crate) fn formal(skip_kani: bool, update: bool) -> Result<(), String> { pub(crate) fn ci_formal_translation() -> Result<(), String> { formal_qualification::validate_source_closure(&root())?; - let (formal_root, attenuation_dimensions) = prepare_formal(true, false)?; - // The build in `prepare_formal` ran against pre-regeneration Lean, so the - // gate is re-run after qualification synchronizes its outputs. + let (formal_root, attenuation_dimensions) = prepare_formal_translation(true, false)?; formal_qualification::qualify(&root(), &attenuation_dimensions, false, &|| { build_and_audit_formal(&formal_root, false) })?; run_formal_semantic_checks(&formal_root, false, false) } +/// Runs the expensive semantic/Kani checks after a PR's update qualification. +/// The immediately preceding `formal qualify aeneas --update` already did two +/// reproductions plus the Lean build/audit, so repeating qualification here +/// would provide no independent evidence and roughly double CI time. +pub(crate) fn ci_formal_post_qualification() -> Result<(), String> { + formal_qualification::validate_source_closure(&root())?; + let (formal_root, attenuation_dimensions) = prepare_formal_translation(true, false)?; + formal_qualification::validate(&root(), &attenuation_dimensions)?; + run_formal_semantic_checks(&formal_root, false, false) +} + /// One workspace package whose `#[kani::proof]` harnesses the formal gate runs. struct KaniHarnessPackage { /// Cargo package name passed to `cargo kani -p`. @@ -487,48 +496,124 @@ fn collect_rust_sources( /// the upstream crate can never produce the symbol. Translation must come /// first; the complete build and audit still gate success, from /// [`build_and_audit_formal`] after synchronization. -/// Requires every mutation case naming a witness to name a COMPILED one. -/// -/// `formal/refinement-mutations-v1.json` used to be existence-checked only: the -/// audit confirmed the file was present and read nothing inside it. Each case -/// carried an `operator` describing how a dimension could be weakened and a -/// `witness` sentence asserting the weakening would be caught -- prose, in an -/// evidence file, standing in for a check. -/// -/// A case that names `lean_declaration` must now name a declaration the -/// assurance audit actually compiled. Deleting the theorem, renaming it, or -/// pointing a case at something that does not exist fails here. -/// -/// Cases with no `lean_declaration` are still prose and are reported as such by -/// `mutation_witness_coverage`; they are not silently treated as proved. +const MUTATION_CASE_IDS: [&str; 23] = [ + "validity-start-direction", + "validity-end-direction", + "permission-subset-direction", + "audience-subset-direction", + "permission-membership-decision", + "audience-membership-decision", + "body-digest-membership-decision", + "body-digest-subset-direction", + "action-exact-equality", + "action-constructor-fallback", + "action-singleton-exact-rejection", + "budget-value-direction", + "budget-algebra-equality", + "optional-budget-bounded-parent", + "optional-budget-no-request", + "status-age-direction", + "status-method-equality", + "profile-version-equality", + "assurance-equality", + "critical-extension-equality", + "delegation-depth-strictness", + "principal-linkage-equality", + "grant-linkage-equality", +]; + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct MutationMatrix { + schema: String, + cases: Vec, +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct MutationCase { + id: String, + operator: String, + witness: String, + lean_declaration: String, +} + +fn expected_mutation_declaration(identifier: &str) -> String { + format!("Auths.Rich.Mutations.{}", identifier.replace('-', "_")) +} + +/// Requires the mutation matrix to have exactly the reviewed schema and all 23 +/// compiled witnesses. Malformed JSON, omitted declarations, duplicate or +/// unknown identifiers, and redirection to an unrelated theorem all fail +/// closed: this artifact is executable assurance evidence, not prose. fn validate_mutation_witnesses( artifact: &Path, compiled: &BTreeMap, ) -> Result<(), String> { - if artifact.extension().and_then(|value| value.to_str()) != Some("json") { + if artifact.file_name().and_then(|value| value.to_str()) != Some("refinement-mutations-v1.json") + { return Ok(()); } - let Ok(source) = fs::read_to_string(artifact) else { - return Ok(()); - }; - let Ok(document) = serde_json::from_str::(&source) else { - return Ok(()); - }; - let Some(cases) = document.get("cases").and_then(Value::as_array) else { - return Ok(()); - }; - for case in cases { - let Some(declaration) = case.get("lean_declaration").and_then(Value::as_str) else { - continue; - }; - let identifier = case - .get("id") - .and_then(Value::as_str) - .unwrap_or(""); - if !compiled.contains_key(declaration) { + let source = fs::read_to_string(artifact).map_err(|error| { + format!( + "could not read mutation matrix {}: {error}", + artifact.display() + ) + })?; + validate_mutation_matrix_source(&source, compiled) +} + +fn validate_mutation_matrix_source( + source: &str, + compiled: &BTreeMap, +) -> Result<(), String> { + let matrix: MutationMatrix = serde_json::from_str(&source) + .map_err(|error| format!("invalid mutation matrix: {error}"))?; + if matrix.schema != "auths-proof-semantic-mutations/v1" { + return Err(format!( + "unsupported mutation matrix schema {}", + matrix.schema + )); + } + if matrix.cases.len() != MUTATION_CASE_IDS.len() { + return Err(format!( + "mutation matrix must contain exactly {} cases, found {}", + MUTATION_CASE_IDS.len(), + matrix.cases.len() + )); + } + let mut seen = BTreeSet::new(); + for (index, case) in matrix.cases.iter().enumerate() { + let expected_id = MUTATION_CASE_IDS[index]; + if case.id != expected_id { + return Err(format!( + "mutation case {index} must be {expected_id}, found {}", + case.id + )); + } + if !seen.insert(case.id.as_str()) { + return Err(format!("mutation matrix repeats case {}", case.id)); + } + if case.operator.trim().is_empty() || case.witness.trim().is_empty() { + return Err(format!("mutation case {} is incomplete", case.id)); + } + let expected = expected_mutation_declaration(&case.id); + if case.lean_declaration != expected { return Err(format!( - "mutation case {identifier} names witness {declaration}, which the \ - assurance audit did not compile" + "mutation case {} must name deterministic witness {expected}, found {}", + case.id, case.lean_declaration + )); + } + let declaration = compiled.get(&case.lean_declaration).ok_or_else(|| { + format!( + "mutation case {} names witness {}, which the assurance audit did not compile", + case.id, case.lean_declaration + ) + })?; + if declaration.kind != "theorem" { + return Err(format!( + "mutation case {} witness {} compiled as {}, not theorem", + case.id, case.lean_declaration, declaration.kind )); } } @@ -656,6 +741,11 @@ pub(crate) struct FormalAssuranceManifest { schema: String, lean_toolchain: String, toolchain_lock_sha256: String, + /// Human-reviewed batch identifier for the exact compiled declaration set. + statement_review: String, + /// Digest of names, modules, kinds, statements, and transitive axioms. + /// Qualification never refreshes this field automatically. + statement_inventory_sha256: String, allowed_axioms: Vec, claims: Vec, } @@ -682,7 +772,7 @@ pub(crate) struct FormalAssuranceClaim { #[derive(Deserialize, Serialize)] #[serde(deny_unknown_fields)] pub(crate) struct FormalEvidence { - kind: String, + kind: FormalEvidenceKind, artifact: String, /// SHA-256 of the artifact's bytes. /// @@ -690,11 +780,85 @@ pub(crate) struct FormalEvidence { /// was present at the path and read nothing. An artifact could be emptied, /// rewritten, or replaced wholesale and every claim citing it still passed. /// - /// Optional so entries can be introduced before their digest is recorded, - /// but once present it is verified, and `cargo xtask formal --update` - /// fills it in for every entry. - #[serde(default)] - sha256: Option, + /// Mandatory. Update mode computes it; check mode verifies it. An evidence + /// entry is never permitted to exist in an unhashed intermediate state. + sha256: String, +} + +#[derive(Clone, Copy, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "kebab-case")] +enum FormalEvidenceKind { + LeanProof, + LeanRefinement, + MechanicalTranslation, + TranslatedRust, + SourceClosure, + LeanGeneratedVectors, + MutationMatrix, + TestCorpus, +} + +impl FormalEvidenceKind { + fn validates_path(self, path: &str) -> bool { + match self { + Self::LeanProof | Self::LeanRefinement => path.ends_with(".lean"), + Self::MechanicalTranslation | Self::TranslatedRust => { + path.starts_with("formal/qualification/aeneas/generated/") + && path.ends_with(".lean") + } + Self::SourceClosure => path == "formal/qualification/aeneas/source-closure.json", + Self::LeanGeneratedVectors => { + path.starts_with("core/formal-vectors/v1/") && path.ends_with(".json") + } + Self::MutationMatrix => path == "formal/refinement-mutations-v1.json", + Self::TestCorpus => !path.trim().is_empty(), + } + } +} + +fn validated_evidence_path(relative: &str) -> Result { + let path = Path::new(relative); + if path.is_absolute() + || path + .components() + .any(|component| !matches!(component, std::path::Component::Normal(_))) + { + return Err(format!( + "evidence path must be a normalized workspace-relative path: {relative}" + )); + } + let unresolved = root().join(path); + let metadata = fs::symlink_metadata(&unresolved) + .map_err(|error| format!("could not stat evidence {}: {error}", unresolved.display()))?; + if !metadata.file_type().is_file() || metadata.file_type().is_symlink() { + return Err(format!( + "evidence path is not a regular non-symlink file: {relative}" + )); + } + let workspace = fs::canonicalize(root()) + .map_err(|error| format!("could not resolve workspace root: {error}"))?; + let resolved = fs::canonicalize(&unresolved).map_err(|error| { + format!( + "could not resolve evidence {}: {error}", + unresolved.display() + ) + })?; + if !resolved.starts_with(&workspace) { + return Err(format!("evidence path escapes workspace: {relative}")); + } + Ok(resolved) +} + +fn validate_sha256(value: &str) -> Result<(), String> { + if value.len() == 64 + && value + .bytes() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte)) + { + Ok(()) + } else { + Err(format!("invalid lowercase SHA-256 {value}")) + } } #[derive(Deserialize)] @@ -708,11 +872,37 @@ pub(crate) struct LeanAssuranceAudit { #[serde(deny_unknown_fields)] pub(crate) struct LeanAssuranceDeclaration { name: String, + module: String, kind: String, statement: String, axioms: Vec, } +fn lean_statement_inventory_sha256(audit: &LeanAssuranceAudit) -> String { + let mut declarations = audit.declarations.iter().collect::>(); + declarations.sort_by(|left, right| left.name.cmp(&right.name)); + let mut digest = Sha256::new(); + for declaration in declarations { + let mut axioms = declaration.axioms.clone(); + axioms.sort(); + for value in [ + declaration.name.as_str(), + declaration.module.as_str(), + declaration.kind.as_str(), + declaration.statement.as_str(), + ] { + digest.update(value.as_bytes()); + digest.update([0]); + } + for axiom in axioms { + digest.update(axiom.as_bytes()); + digest.update([0]); + } + digest.update([0xff]); + } + hex::encode(digest.finalize()) +} + pub(crate) fn formal_assurance_audit(formal_root: &Path, update: bool) -> Result<(), String> { let manifest_path = formal_root.join("assurance-manifest-v1.toml"); let mut manifest: FormalAssuranceManifest = toml::from_str( @@ -762,6 +952,16 @@ pub(crate) fn formal_assurance_audit(formal_root: &Path, update: bool) -> Result audit.schema )); } + let statement_inventory_sha256 = lean_statement_inventory_sha256(&audit); + if manifest.statement_review.trim().is_empty() { + return Err("formal assurance statement review batch is empty".to_owned()); + } + if manifest.statement_inventory_sha256 != statement_inventory_sha256 { + return Err(format!( + "compiled Lean statement inventory is not explicitly reviewed: manifest binds {}, compiled audit is {statement_inventory_sha256}; review the complete audit, change statement_review, and set statement_inventory_sha256 explicitly before update", + manifest.statement_inventory_sha256 + )); + } if update { synchronize_formal_assurance_manifest( @@ -787,6 +987,7 @@ pub(crate) fn formal_assurance_audit(formal_root: &Path, update: bool) -> Result )); } } + let expected_source_closure = semantic_source_closure_paths(formal_root)?; let mut reviewed = BTreeSet::new(); let mut claim_ids = BTreeSet::new(); for claim in &manifest.claims { @@ -815,6 +1016,12 @@ pub(crate) fn formal_assurance_audit(formal_root: &Path, update: bool) -> Result )); } } + if claim.formal_review != manifest.statement_review { + return Err(format!( + "formal claim {} review tag does not match reviewed statement batch {}", + claim.lean_declaration, manifest.statement_review + )); + } if !matches!( claim.claim_status.as_str(), "proved" | "qualified" | "assumed" @@ -824,6 +1031,34 @@ pub(crate) fn formal_assurance_audit(formal_root: &Path, update: bool) -> Result claim.lean_declaration, claim.claim_status )); } + if let Some(metadata) = production_refinement_metadata(&claim.lean_declaration) { + let expected_symbols = metadata + .rust_symbols + .iter() + .map(|symbol| (*symbol).to_owned()) + .collect::>(); + let expected_residual = metadata + .residual_assumptions + .iter() + .map(|assumption| (*assumption).to_owned()) + .collect::>(); + if claim.claim_status != "qualified" + || claim.claim_text != metadata.claim_text + || claim.rust_symbols != expected_symbols + || claim.scope != metadata.scope + || claim.residual_assumptions != expected_residual + { + return Err(format!( + "formal claim {} production refinement metadata drifted from the executable boundary table", + claim.lean_declaration + )); + } + validate_production_evidence( + &claim.lean_declaration, + &claim.evidence, + metadata.translation_evidence_kind, + )?; + } if claim.toolchain_lock_sha256 != toolchain_lock_digest { return Err(format!( "formal claim {} has stale toolchain lock digest", @@ -839,6 +1074,12 @@ pub(crate) fn formal_assurance_audit(formal_root: &Path, update: bool) -> Result claim.lean_declaration )); } + if claim.semantic_source_closure != expected_source_closure { + return Err(format!( + "formal claim {} semantic source closure is not the Lean-resolved transitive assurance closure", + claim.lean_declaration + )); + } let closure_digest = semantic_source_closure_digest(&claim.semantic_source_closure)?; if closure_digest != claim.semantic_source_closure_sha256 { return Err(format!( @@ -854,35 +1095,55 @@ pub(crate) fn formal_assurance_audit(formal_root: &Path, update: bool) -> Result )); } } + let expected_proof = + declaration_source_path(compiled.get(&claim.lean_declaration).ok_or_else(|| { + format!( + "reviewed Lean declaration {} is absent from the compiled environment", + claim.lean_declaration + ) + })?)?; + let primary = claim + .evidence + .iter() + .filter(|item| { + matches!( + item.kind, + FormalEvidenceKind::LeanProof | FormalEvidenceKind::LeanRefinement + ) + }) + .collect::>(); + if primary.len() != 1 || primary[0].artifact != expected_proof { + return Err(format!( + "formal claim {} must cite exactly its defining Lean module {expected_proof} as primary proof evidence", + claim.lean_declaration + )); + } for item in &claim.evidence { - if item.kind.trim().is_empty() || item.artifact.trim().is_empty() { + if item.artifact.trim().is_empty() || !item.kind.validates_path(&item.artifact) { return Err(format!( - "formal claim {} contains incomplete evidence", - claim.lean_declaration + "formal claim {} contains incomplete or kind/path-mismatched evidence {}", + claim.lean_declaration, item.artifact )); } - let artifact = root().join(&item.artifact); - if !artifact.exists() { + let artifact = validated_evidence_path(&item.artifact)?; + validate_mutation_witnesses(&artifact, &compiled)?; + validate_sha256(&item.sha256).map_err(|error| { + format!( + "formal claim {} evidence {} has {error}", + claim.lean_declaration, item.artifact + ) + })?; + let bytes = fs::read(&artifact).map_err(|error| { + format!("could not read evidence {}: {error}", artifact.display()) + })?; + let actual = hex::encode(Sha256::digest(&bytes)); + if actual != item.sha256 { return Err(format!( - "formal claim {} evidence artifact does not exist: {}", - claim.lean_declaration, - artifact.display() + "formal claim {} cites evidence {} with digest {}, \ + but the artifact hashes to {actual}", + claim.lean_declaration, item.artifact, item.sha256 )); } - validate_mutation_witnesses(&artifact, &compiled)?; - if let Some(expected) = item.sha256.as_deref() { - let bytes = fs::read(&artifact).map_err(|error| { - format!("could not read evidence {}: {error}", artifact.display()) - })?; - let actual = hex::encode(Sha256::digest(&bytes)); - if actual != expected { - return Err(format!( - "formal claim {} cites evidence {} with digest {expected}, \ - but the artifact hashes to {actual}", - claim.lean_declaration, item.artifact - )); - } - } } let declaration = compiled.get(&claim.lean_declaration).ok_or_else(|| { @@ -958,37 +1219,7 @@ pub(crate) fn synchronize_formal_assurance_manifest( audit: &LeanAssuranceAudit, toolchain_lock_digest: &str, ) -> Result<(), String> { - let source_closure = vec![ - "formal/Auths/Base.lean".to_owned(), - "formal/Auths/Authority.lean".to_owned(), - "formal/Auths/Attenuation.lean".to_owned(), - "formal/Auths/Rich/Types.lean".to_owned(), - "formal/Auths/Rich/Semantics.lean".to_owned(), - "formal/Auths/Rich/Theorems.lean".to_owned(), - "formal/Auths/Product/Commitment.lean".to_owned(), - "formal/Auths/Product/Arithmetic.lean".to_owned(), - "formal/Auths/Product/Eligibility.lean".to_owned(), - "formal/Auths/Product/Tightening.lean".to_owned(), - "formal/Auths/Product/Theorems.lean".to_owned(), - "formal/Auths/Product/Refinement.lean".to_owned(), - "formal/Auths/Lifecycle/Semantics.lean".to_owned(), - "formal/Auths/Lifecycle/Theorems.lean".to_owned(), - "formal/Auths/Lifecycle/Refinement.lean".to_owned(), - "formal/Auths/Refinement/Production.lean".to_owned(), - "formal/Auths/Composition.lean".to_owned(), - "formal/Auths/Diversity.lean".to_owned(), - "formal/Auths/Generated/Algebra.lean".to_owned(), - "formal/Auths/Theorems.lean".to_owned(), - "formal/qualification/aeneas/generated/authority/Funs.lean".to_owned(), - "formal/qualification/aeneas/generated/authority/Types.lean".to_owned(), - "formal/qualification/aeneas/generated/bounded_policy/Funs.lean".to_owned(), - "formal/qualification/aeneas/generated/bounded_policy/Types.lean".to_owned(), - "formal/qualification/aeneas/generated/lifecycle/Funs.lean".to_owned(), - "formal/qualification/aeneas/generated/lifecycle/Types.lean".to_owned(), - "formal/qualification/aeneas/generated/model/Funs.lean".to_owned(), - "formal/qualification/aeneas/generated/model/Types.lean".to_owned(), - "formal/algebra-contract-v1.toml".to_owned(), - ]; + let source_closure = semantic_source_closure_paths(formal_root)?; let source_closure_digest = semantic_source_closure_digest(&source_closure)?; let mut rich_index = manifest .claims @@ -1033,15 +1264,9 @@ pub(crate) fn synchronize_formal_assurance_manifest( semantic_source_closure: Vec::new(), semantic_source_closure_sha256: String::new(), evidence: vec![FormalEvidence { - kind: "lean-proof".to_owned(), - artifact: if is_lifecycle { - "formal/Auths/Lifecycle/Theorems.lean".to_owned() - } else if is_product { - "formal/Auths/Product/Theorems.lean".to_owned() - } else { - "formal/Auths/Rich/Theorems.lean".to_owned() - }, - sha256: None, + kind: FormalEvidenceKind::LeanProof, + artifact: declaration_source_path(declaration)?, + sha256: String::new(), }], scope: if is_lifecycle { "Pure V1 lifecycle transitions, capacity conservation, replay, configuration gates, credential ordering, provider entry, and reconciliation.".to_owned() @@ -1058,6 +1283,26 @@ pub(crate) fn synchronize_formal_assurance_manifest( } }; claim.lean_statement_sha256 = statement_digest; + claim.formal_review.clone_from(&manifest.statement_review); + let proof_kind = if declaration.module.contains(".Refinement") { + FormalEvidenceKind::LeanRefinement + } else { + FormalEvidenceKind::LeanProof + }; + claim.evidence.retain(|item| { + !matches!( + item.kind, + FormalEvidenceKind::LeanProof | FormalEvidenceKind::LeanRefinement + ) + }); + claim.evidence.insert( + 0, + FormalEvidence { + kind: proof_kind, + artifact: declaration_source_path(declaration)?, + sha256: String::new(), + }, + ); claim.semantic_source_closure.clone_from(&source_closure); claim .semantic_source_closure_sha256 @@ -1070,9 +1315,9 @@ pub(crate) fn synchronize_formal_assurance_manifest( ); claim.rust_symbols = vec![rust_symbol.to_owned()]; claim.evidence = vec![FormalEvidence { - kind: "lean-refinement".to_owned(), + kind: FormalEvidenceKind::LeanRefinement, artifact: "formal/Auths/Lifecycle/Refinement.lean".to_owned(), - sha256: None, + sha256: String::new(), }]; claim.scope = format!( "The pinned Charon/Aeneas translation of `{rust_symbol}` is extensionally equivalent to the corresponding rich Lean V1 lifecycle semantics." @@ -1081,13 +1326,46 @@ pub(crate) fn synchronize_formal_assurance_manifest( "Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the qualified translation boundary, the listed foundational axioms, and the theorem premises are trusted.".to_owned(), ]; } + if let Some(metadata) = production_refinement_metadata(&declaration.name) { + claim.claim_text = metadata.claim_text.to_owned(); + claim.claim_status = "qualified".to_owned(); + claim.rust_symbols = metadata + .rust_symbols + .iter() + .map(|symbol| (*symbol).to_owned()) + .collect(); + claim.scope = metadata.scope.to_owned(); + claim.residual_assumptions = metadata + .residual_assumptions + .iter() + .map(|assumption| (*assumption).to_owned()) + .collect(); + claim.evidence.retain(|item| { + !matches!( + item.kind, + FormalEvidenceKind::SourceClosure + | FormalEvidenceKind::MechanicalTranslation + | FormalEvidenceKind::TranslatedRust + ) + }); + claim.evidence.push(FormalEvidence { + kind: metadata.translation_evidence_kind, + artifact: "formal/qualification/aeneas/generated/authority/Funs.lean".to_owned(), + sha256: String::new(), + }); + claim.evidence.push(FormalEvidence { + kind: FormalEvidenceKind::SourceClosure, + artifact: "formal/qualification/aeneas/source-closure.json".to_owned(), + sha256: String::new(), + }); + } claims.push(claim); } if !existing.is_empty() { - println!( - "Formal assurance update: removed {} declarations absent from compiled inventory", - existing.len() - ); + return Err(format!( + "formal assurance update refuses to auto-retire reviewed declarations absent from the compiled inventory: {:?}; remove each claim explicitly in the reviewed change", + existing.keys().collect::>() + )); } manifest.claims = claims; // Refresh every evidence digest before rendering. @@ -1099,10 +1377,11 @@ pub(crate) fn synchronize_formal_assurance_manifest( // non-update path still verifies and refuses a mismatch. for claim in &mut manifest.claims { for item in &mut claim.evidence { - let artifact = root().join(&item.artifact); - if let Ok(bytes) = fs::read(&artifact) { - item.sha256 = Some(hex::encode(Sha256::digest(&bytes))); - } + let artifact = validated_evidence_path(&item.artifact)?; + let bytes = fs::read(&artifact).map_err(|error| { + format!("could not read evidence {}: {error}", artifact.display()) + })?; + item.sha256 = hex::encode(Sha256::digest(&bytes)); } } let rendered = toml::to_string_pretty(manifest) @@ -1113,10 +1392,154 @@ pub(crate) fn synchronize_formal_assurance_manifest( "Formal assurance update: {} compiled claims synchronized", manifest.claims.len() ); - let _ = formal_root; Ok(()) } +fn declaration_source_path(declaration: &LeanAssuranceDeclaration) -> Result { + if declaration.module.trim().is_empty() + || !declaration.module.split('.').all(|part| { + !part.is_empty() + && part + .bytes() + .all(|byte| byte.is_ascii_alphanumeric() || byte == b'_') + }) + { + return Err(format!( + "compiled Lean declaration {} has invalid defining module {}", + declaration.name, declaration.module + )); + } + let path = format!("formal/{}.lean", declaration.module.replace('.', "/")); + if !root().join(&path).is_file() { + return Err(format!( + "compiled Lean declaration {} defining module is not a repository file: {path}", + declaration.name + )); + } + Ok(path) +} + +/// Computes the repository-local transitive Lean import closure from the exact +/// module compiled by the assurance audit. This replaces a hand-maintained list +/// which had omitted mutation proofs and reviewed/generated external bridges. +fn semantic_source_closure_paths(formal_root: &Path) -> Result, String> { + validate_lake_dependency_worktrees(formal_root)?; + let workspace_root = root(); + let mut pending = vec![formal_root.join("Auths/AssuranceAudit.lean")]; + let mut visited = BTreeSet::new(); + while let Some(path) = pending.pop() { + let path = fs::canonicalize(&path).map_err(|error| { + format!("could not resolve Lean source {}: {error}", path.display()) + })?; + let relative = path + .strip_prefix(&workspace_root) + .map_err(|error| format!("Lean source escaped workspace: {error}"))? + .to_string_lossy() + .replace('\\', "/"); + if !visited.insert(relative) { + continue; + } + let formal_relative = path + .strip_prefix(formal_root) + .map_err(|error| format!("Lean source escaped formal root: {error}"))? + .to_string_lossy() + .into_owned(); + let dependencies = command_output_in( + "lake", + &["env", "lean", "--src-deps", formal_relative.as_str()], + formal_root, + None, + )?; + for dependency in dependencies.lines().filter(|line| !line.trim().is_empty()) { + let candidate = PathBuf::from(dependency.trim()); + let candidate = if candidate.is_absolute() { + candidate + } else { + formal_root.join(candidate) + }; + let Ok(candidate) = fs::canonicalize(candidate) else { + continue; + }; + if is_repository_lean_dependency(formal_root, &candidate) { + pending.push(candidate); + } + } + } + for relative in [ + "formal/algebra-contract-v1.toml", + "formal/lakefile.toml", + "formal/lake-manifest.json", + "formal/lean-toolchain", + ] { + let path = workspace_root.join(relative); + let metadata = fs::symlink_metadata(&path) + .map_err(|error| format!("could not stat Lean closure input {relative}: {error}"))?; + if !metadata.file_type().is_file() || metadata.file_type().is_symlink() { + return Err(format!( + "Lean closure input is not a regular non-symlink file: {relative}" + )); + } + visited.insert(relative.to_owned()); + } + Ok(visited.into_iter().collect()) +} + +fn validate_lake_dependency_worktrees(formal_root: &Path) -> Result<(), String> { + let manifest_path = formal_root.join("lake-manifest.json"); + let manifest: Value = serde_json::from_slice( + &fs::read(&manifest_path) + .map_err(|error| format!("could not read {}: {error}", manifest_path.display()))?, + ) + .map_err(|error| format!("invalid {}: {error}", manifest_path.display()))?; + let packages_dir = manifest["packagesDir"] + .as_str() + .ok_or("Lake manifest omits packagesDir")?; + let packages = manifest["packages"] + .as_array() + .ok_or("Lake manifest omits packages")?; + for package in packages { + if package["type"].as_str() != Some("git") { + continue; + } + let name = package["name"] + .as_str() + .ok_or("Lake git package omits name")?; + let expected_rev = package["rev"] + .as_str() + .ok_or_else(|| format!("Lake git package {name} omits rev"))?; + let directory = formal_root.join(packages_dir).join(name); + let head = command_output_in("git", &["rev-parse", "HEAD"], &directory, None)?; + let status = command_output_in("git", &["status", "--porcelain=v1"], &directory, None)?; + validate_lake_package_state(name, expected_rev, head.trim(), status.trim())?; + } + Ok(()) +} + +fn validate_lake_package_state( + name: &str, + expected_rev: &str, + head: &str, + status: &str, +) -> Result<(), String> { + if head != expected_rev { + return Err(format!( + "Lake dependency {name} is at {head}, expected manifest rev {expected_rev}" + )); + } + if !status.is_empty() { + return Err(format!( + "Lake dependency {name} has local modifications; assurance refuses a dirty proof dependency" + )); + } + Ok(()) +} + +fn is_repository_lean_dependency(formal_root: &Path, candidate: &Path) -> bool { + candidate.starts_with(formal_root) + && !candidate.starts_with(formal_root.join(".lake")) + && candidate.extension().is_some_and(|ext| ext == "lean") +} + fn lifecycle_refinement_metadata(declaration: &str) -> Option<(&'static str, &'static str)> { match declaration { "Auths.Lifecycle.Refinement.translated_terminal_refines_rich" => Some(( @@ -1143,6 +1566,134 @@ fn lifecycle_refinement_metadata(declaration: &str) -> Option<(&'static str, &'s } } +struct ProductionRefinementMetadata { + claim_text: &'static str, + rust_symbols: &'static [&'static str], + scope: &'static str, + residual_assumptions: &'static [&'static str], + translation_evidence_kind: FormalEvidenceKind, +} + +fn validate_production_evidence( + declaration: &str, + evidence: &[FormalEvidence], + translation_kind: FormalEvidenceKind, +) -> Result<(), String> { + let source_closure = evidence + .iter() + .filter(|item| item.kind == FormalEvidenceKind::SourceClosure) + .collect::>(); + let translation = evidence + .iter() + .filter(|item| { + matches!( + item.kind, + FormalEvidenceKind::MechanicalTranslation | FormalEvidenceKind::TranslatedRust + ) + }) + .collect::>(); + if source_closure.len() != 1 + || source_closure[0].artifact != "formal/qualification/aeneas/source-closure.json" + || translation.len() != 1 + || translation[0].kind != translation_kind + || translation[0].artifact != "formal/qualification/aeneas/generated/authority/Funs.lean" + { + return Err(format!( + "formal claim {declaration} must bind exactly one production source closure and one generated authority translation" + )); + } + Ok(()) +} + +fn production_refinement_metadata(declaration: &str) -> Option { + const COMMON: &str = "Lean's kernel, the pinned Rust/Charon/Aeneas/Lean toolchain, the reviewed transparent external bridges, the listed foundational axioms, and the theorem's explicit representation-validity premises are trusted."; + match declaration { + "Auths.Refinement.translated_rust_refines_rich_spec" => { + Some(ProductionRefinementMetadata { + claim_text: "The mechanically translated production author-scope evaluator returns exactly the rich target-V1 decision and first failing authority dimension.", + rust_symbols: &[ + "auths_authority::evaluate_author_scope_view", + "auths_model::profile_ref_equal", + "auths_model::permission_set_is_subset", + "auths_model::validity_window_contains", + "auths_model::audience_set_is_subset", + "auths_model::action_constraint_attenuates", + "auths_model::optional_budget_attenuates", + "auths_model::status_policy_attenuates", + "auths_model::assurance_policy_id_equal", + "auths_model::critical_extensions_equal", + ], + scope: "The exact translated pre-signing scope evaluator over validated bounded views, including all ten scope/depth dimensions and ordered diagnostics.", + residual_assumptions: &[ + COMMON, + "Canonical decoding and cryptographic authenticity precede this pure evaluator and are outside this theorem.", + ], + translation_evidence_kind: FormalEvidenceKind::MechanicalTranslation, + }) + } + "Auths.Refinement.translated_coverage_refines_rich_spec" => { + Some(ProductionRefinementMetadata { + claim_text: "The mechanically translated terminal-coverage evaluator returns exactly the rich ordered decision for the supplied trusted budget-expression context.", + rust_symbols: &[ + "auths_authority::evaluate_action_coverage_view", + "auths_authority::root_linkage", + "auths_authority::selected_profile_attenuates", + "auths_algebra_kernel::root_preserved", + "auths_model::principal_id_equal", + "auths_model::optional_grant_id_equal", + "auths_model::profile_ref_equal", + "auths_model::profile_slice_contains", + "auths_model::permission_set_contains", + "auths_model::validity_window_contains", + "auths_model::audience_set_contains", + "auths_model::action_constraint_allows", + "auths_model::budget_ceiling_covers_action", + "auths_model::optional_budget_covers", + ], + scope: "The crate-private raw translated evaluator over validated authority/action views. The public EffectiveAuthority::authorizes boundary resolves budget expressibility from AcceptedRegistries and the exact action profile before calling it.", + residual_assumptions: &[ + COMMON, + "The AcceptedRegistries lookup-to-kernel composition is enforced by the Rust public API and adversarial tests; the registry collection's provenance and canonical decoding are outside this translated raw-view theorem.", + ], + translation_evidence_kind: FormalEvidenceKind::TranslatedRust, + }) + } + "Auths.Refinement.translated_delegation_refines_rich_spec" => { + Some(ProductionRefinementMetadata { + claim_text: "The mechanically translated delegation evaluator returns exactly the rich linkage, eleven-dimension attenuation decision, diagnostic, and accepted transition.", + rust_symbols: &[ + "auths_authority::evaluate_grant_view", + "auths_authority::root_linkage", + "auths_authority::depth_decreases", + "auths_authority::selected_profile_attenuates", + "auths_authority::extensions_attenuate", + "auths_algebra_kernel::root_preserved", + "auths_algebra_kernel::generated::attenuation_checks_accept", + "auths_model::principal_id_equal", + "auths_model::optional_grant_id_equal", + "auths_model::profile_ref_equal", + "auths_model::profile_slice_contains", + "auths_model::permission_set_is_subset", + "auths_model::validity_window_contains", + "auths_model::audience_set_is_subset", + "auths_model::action_constraint_attenuates", + "auths_model::optional_budget_attenuates", + "auths_model::status_policy_attenuates", + "auths_model::assurance_policy_id_equal", + "auths_model::critical_extensions_equal", + ], + scope: "The crate-private raw evaluator over validated parent/grant views, including root linkage, strict depth, every attenuation dimension, and the unique accepted transition.", + residual_assumptions: &[ + COMMON, + "A present last-grant marker is representation data, not ancestry proof. Shipping Rust seals raw views; historical provenance is stated separately by Lean's AnchoredChain theorem from a fresh anchor through accepted transitions.", + ], + translation_evidence_kind: FormalEvidenceKind::TranslatedRust, + }) + } + _ => None, + } +} + pub(crate) fn semantic_source_closure_digest(paths: &[String]) -> Result { let mut ordered = paths.to_vec(); ordered.sort(); @@ -1182,6 +1733,39 @@ pub(crate) fn formal_qualify_aeneas(update: bool) -> Result<(), String> { #[cfg(test)] mod phase_ordering { + use super::*; + + fn compiled_mutations() -> BTreeMap { + MUTATION_CASE_IDS + .iter() + .map(|identifier| { + let name = expected_mutation_declaration(identifier); + ( + name.clone(), + LeanAssuranceDeclaration { + name, + module: "Auths.Rich.Mutations".to_owned(), + kind: "theorem".to_owned(), + statement: "True".to_owned(), + axioms: Vec::new(), + }, + ) + }) + .collect() + } + + fn mutation_matrix_json() -> Value { + serde_json::json!({ + "schema": "auths-proof-semantic-mutations/v1", + "cases": MUTATION_CASE_IDS.iter().map(|identifier| serde_json::json!({ + "id": identifier, + "operator": "mutate", + "witness": "compiled counterexample", + "lean_declaration": expected_mutation_declaration(identifier), + })).collect::>() + }) + } + /// `formal qualify aeneas` must never compile Lean before it reproduces the /// translations. /// @@ -1246,4 +1830,200 @@ mod phase_ordering { "the compiled gate must run BEFORE qualification evidence is written" ); } + + #[test] + fn production_refinement_metadata_cannot_retain_stale_boundaries() { + let coverage = production_refinement_metadata( + "Auths.Refinement.translated_coverage_refines_rich_spec", + ) + .expect("coverage metadata"); + for required in [ + "auths_authority::root_linkage", + "auths_algebra_kernel::root_preserved", + "auths_model::budget_ceiling_covers_action", + ] { + assert!(coverage.rust_symbols.contains(&required)); + } + let delegation = production_refinement_metadata( + "Auths.Refinement.translated_delegation_refines_rich_spec", + ) + .expect("delegation metadata"); + for required in [ + "auths_authority::depth_decreases", + "auths_authority::extensions_attenuate", + "auths_model::critical_extensions_equal", + ] { + assert!(delegation.rust_symbols.contains(&required)); + } + assert!( + !delegation + .residual_assumptions + .join(" ") + .contains("occur in the caller") + ); + + let translated = FormalEvidence { + kind: FormalEvidenceKind::TranslatedRust, + artifact: "formal/qualification/aeneas/generated/authority/Funs.lean".to_owned(), + sha256: "a".repeat(64), + }; + for declaration in [ + "Auths.Refinement.translated_coverage_refines_rich_spec", + "Auths.Refinement.translated_delegation_refines_rich_spec", + ] { + assert!( + validate_production_evidence( + declaration, + std::slice::from_ref(&translated), + FormalEvidenceKind::TranslatedRust, + ) + .is_err(), + "qualified production claim without source closure must fail" + ); + } + let complete = vec![ + translated, + FormalEvidence { + kind: FormalEvidenceKind::SourceClosure, + artifact: "formal/qualification/aeneas/source-closure.json".to_owned(), + sha256: "b".repeat(64), + }, + ]; + validate_production_evidence( + "Auths.Refinement.translated_coverage_refines_rich_spec", + &complete, + FormalEvidenceKind::TranslatedRust, + ) + .expect("complete production evidence"); + } + + #[test] + fn post_qualification_gate_never_reproduces_or_rebuilds_lean() { + let source = include_str!("formal.rs"); + let body = source + .split_once("pub(crate) fn ci_formal_post_qualification(") + .expect("post qualification function") + .1 + .split_once("\n}\n") + .expect("function body") + .0; + assert!(!body.contains("qualify(")); + assert!(!body.contains("build_and_audit_formal(")); + assert!(!body.contains("prepare_formal(")); + assert!(body.contains("run_formal_semantic_checks(")); + } + + #[test] + fn mutation_matrix_is_strict_and_deterministically_bound() { + let compiled = compiled_mutations(); + let valid = mutation_matrix_json(); + validate_mutation_matrix_source(&valid.to_string(), &compiled).expect("complete matrix"); + assert!(validate_mutation_matrix_source("not json", &compiled).is_err()); + + let mut short = valid.clone(); + short["cases"].as_array_mut().expect("cases").pop(); + assert!(validate_mutation_matrix_source(&short.to_string(), &compiled).is_err()); + + let mut reordered = valid.clone(); + reordered["cases"].as_array_mut().expect("cases").swap(0, 1); + assert!(validate_mutation_matrix_source(&reordered.to_string(), &compiled).is_err()); + + let mut redirected = valid.clone(); + redirected["cases"][0]["lean_declaration"] = + Value::String("Auths.Rich.window_contained_refl".to_owned()); + assert!(validate_mutation_matrix_source(&redirected.to_string(), &compiled).is_err()); + + let mut missing = valid; + missing["cases"][0] + .as_object_mut() + .expect("case") + .remove("lean_declaration"); + assert!(validate_mutation_matrix_source(&missing.to_string(), &compiled).is_err()); + } + + #[test] + fn evidence_digest_and_path_validation_fail_closed() { + let missing_digest = "kind = \"lean-proof\"\nartifact = \"formal/Auths.lean\"\n"; + assert!(toml::from_str::(missing_digest).is_err()); + assert!(validate_sha256(&"a".repeat(64)).is_ok()); + assert!(validate_sha256(&"A".repeat(64)).is_err()); + assert!(validate_sha256("abcd").is_err()); + assert!(validated_evidence_path("../outside").is_err()); + assert!(validated_evidence_path("formal").is_err()); + assert!(validated_evidence_path("formal/does-not-exist").is_err()); + } + + #[test] + fn statement_review_digest_changes_on_semantic_weakening() { + let audit = |statement: &str| LeanAssuranceAudit { + schema: "auths-proof-lean-assurance-audit/v1".to_owned(), + declarations: vec![LeanAssuranceDeclaration { + name: "Fixture.boundary".to_owned(), + module: "Fixture".to_owned(), + kind: "theorem".to_owned(), + statement: statement.to_owned(), + axioms: Vec::new(), + }], + }; + let strong = lean_statement_inventory_sha256(&audit("StrongBoundary input")); + let weakened = lean_statement_inventory_sha256(&audit("True")); + assert_ne!(strong, weakened); + + let source = include_str!("formal.rs"); + let synchronizer = source + .split_once("pub(crate) fn synchronize_formal_assurance_manifest(") + .expect("manifest synchronizer") + .1 + .split_once("\n}\n") + .expect("synchronizer body") + .0; + assert!( + !synchronizer.contains("statement_inventory_sha256 ="), + "ordinary update must never auto-accept a new statement inventory" + ); + } + + #[test] + fn semantic_closure_includes_repository_imports_but_excludes_lake_packages() { + let formal_root = root().join("formal"); + assert!(is_repository_lean_dependency( + &formal_root, + &formal_root.join("Auths/Backdoor.lean") + )); + assert!(is_repository_lean_dependency( + &formal_root, + &formal_root.join("qualification/aeneas/generated/authority/Funs.lean") + )); + assert!(!is_repository_lean_dependency( + &formal_root, + &formal_root.join(".lake/packages/aeneas/Aeneas.lean") + )); + } + + #[test] + fn lake_dependency_state_rejects_wrong_revision_and_dirty_worktree() { + validate_lake_package_state("mathlib", "abc", "abc", "").expect("exact clean dependency"); + assert!(validate_lake_package_state("mathlib", "abc", "def", "").is_err()); + assert!(validate_lake_package_state("mathlib", "abc", "abc", " M Mathlib.lean").is_err()); + } + + #[cfg(unix)] + #[test] + fn evidence_symlinks_are_refused() { + let link = root().join(format!( + "target/evidence-symlink-test-{}", + std::process::id() + )); + if let Some(parent) = link.parent() { + fs::create_dir_all(parent).expect("target directory"); + } + let _ = fs::remove_file(&link); + std::os::unix::fs::symlink(root().join("formal/Auths.lean"), &link).expect("test symlink"); + let relative = link + .strip_prefix(root()) + .expect("relative") + .to_string_lossy(); + assert!(validated_evidence_path(&relative).is_err()); + fs::remove_file(&link).expect("remove test symlink"); + } } diff --git a/xtask/src/formal_qualification.rs b/xtask/src/formal_qualification.rs index 6b895a4b..11561ed5 100644 --- a/xtask/src/formal_qualification.rs +++ b/xtask/src/formal_qualification.rs @@ -2,6 +2,7 @@ use serde::Deserialize; use serde_json::{Value, json}; +use sha2::{Digest, Sha256}; use std::{ collections::{BTreeMap, BTreeSet}, env, fs, @@ -11,6 +12,167 @@ use std::{ const QUALIFICATION_PATH: &str = "formal/qualification/aeneas/qualification.toml"; const QUALIFICATION_SCHEMA: &str = "auths-proof-aeneas-qualification/v1"; +const QUALIFICATION_BOUNDARY_CONTRACT_SHA256: &str = + "dd329a08f632fafdcd536ff4bba53ef67d8a9574240baeca54365097a47a9ee0"; + +const AENEAS_OUTPUT_MAPPINGS: &[(&str, &str)] = &[ + ( + "model-run/qualification/aeneas/generated/model/Types.lean", + "formal/qualification/aeneas/generated/model/Types.lean", + ), + ( + "model-run/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + ), + ( + "model-run/qualification/aeneas/generated/model/FunsExternal_Template.lean", + "formal/qualification/aeneas/generated/model/FunsExternal_Template.lean", + ), + ( + "model-run/translation.json", + "formal/qualification/aeneas/generated/model/translation.json", + ), + ( + "algebra-run/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/algebra/Types.lean", + ), + ( + "algebra-run/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + ), + ( + "algebra-run/translation.json", + "formal/qualification/aeneas/generated/algebra/translation.json", + ), + ( + "authority-run/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/Types.lean", + ), + ( + "authority-run/qualification/aeneas/generated/authority/TypesExternal_Template.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal_Template.lean", + ), + ( + "authority-run/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + ), + ( + "authority-run/qualification/aeneas/generated/authority/FunsExternal_Template.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal_Template.lean", + ), + ( + "authority-run/translation.json", + "formal/qualification/aeneas/generated/authority/translation.json", + ), + ( + "bounded_policy-run/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + ), + ( + "bounded_policy-run/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + ), + ( + "bounded_policy-run/translation.json", + "formal/qualification/aeneas/generated/bounded_policy/translation.json", + ), + ( + "lifecycle-run/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + ), + ( + "lifecycle-run/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + ), + ( + "lifecycle-run/translation.json", + "formal/qualification/aeneas/generated/lifecycle/translation.json", + ), +]; + +const REVIEWED_BRIDGE_ARTIFACTS: &[&str] = &[ + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", +]; + +/// Exact artifact set whose bytes a successful clean reproduction certifies. +/// Keep this ordered list aligned with the checked-in qualification manifest; +/// synchronization and reviewed bridge destinations are checked against it. +const GENERATED_ARTIFACTS: &[&str] = &[ + "formal/qualification/aeneas/generated/model/Types.lean", + "formal/qualification/aeneas/generated/model/Funs.lean", + "formal/qualification/aeneas/generated/model/FunsExternal_Template.lean", + "formal/qualification/aeneas/generated/model/FunsExternal.lean", + "formal/qualification/aeneas/generated/model/translation.json", + "formal/qualification/aeneas/generated/algebra/Types.lean", + "formal/qualification/aeneas/generated/algebra/Funs.lean", + "formal/qualification/aeneas/generated/algebra/translation.json", + "formal/qualification/aeneas/generated/authority/Types.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal_Template.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal.lean", + "formal/qualification/aeneas/generated/authority/Funs.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal_Template.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal.lean", + "formal/qualification/aeneas/generated/authority/translation.json", + "formal/qualification/aeneas/generated/bounded_policy/Types.lean", + "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", + "formal/qualification/aeneas/generated/bounded_policy/translation.json", + "formal/qualification/aeneas/generated/lifecycle/Types.lean", + "formal/qualification/aeneas/generated/lifecycle/Funs.lean", + "formal/qualification/aeneas/generated/lifecycle/translation.json", +]; + +const EXPECTED_CASE_MODULES: &[&str] = &[ + "qualification.aeneas.cases.Model", + "qualification.aeneas.cases.Algebra", + "qualification.aeneas.cases.Authority", + "qualification.aeneas.cases.BoundedPolicy", + "qualification.aeneas.cases.Lifecycle", +]; + +const REQUIRED_AUTHORED_SOURCE_INPUTS: &[&str] = &[ + ".cargo/config.toml", + "Cargo.toml", + "Cargo.lock", + "formal/algebra-contract-v1.toml", + "formal/qualification/aeneas/qualification.toml", + "formal/translation-toolchain.lock", + "formal/qualification/aeneas/cases/Model.lean", + "formal/qualification/aeneas/cases/Algebra.lean", + "formal/qualification/aeneas/cases/Authority.lean", + "formal/qualification/aeneas/cases/BoundedPolicy.lean", + "formal/qualification/aeneas/cases/Lifecycle.lean", +]; + +const EXPECTED_TRANSLATIONS: &[(&str, &str)] = &[ + ( + "auths_model", + "formal/qualification/aeneas/generated/model/translation.json", + ), + ( + "auths_algebra_kernel", + "formal/qualification/aeneas/generated/algebra/translation.json", + ), + ( + "auths_lifecycle", + "formal/qualification/aeneas/generated/lifecycle/translation.json", + ), + ( + "auths_authority", + "formal/qualification/aeneas/generated/authority/translation.json", + ), + ( + "auths_bounded_policy", + "formal/qualification/aeneas/generated/bounded_policy/translation.json", + ), +]; + +const EXPECTED_TEMPLATE_AXIOMS: &[&str] = &[ + "formal/qualification/aeneas/generated/model/FunsExternal_Template.lean", + "formal/qualification/aeneas/generated/authority/TypesExternal_Template.lean", + "formal/qualification/aeneas/generated/authority/FunsExternal_Template.lean", +]; #[derive(Deserialize)] #[serde(deny_unknown_fields)] @@ -109,6 +271,7 @@ struct TranslationFunction { pub(crate) fn validate(root: &Path, attenuation_dimensions: &[String]) -> Result { let qualification = load_qualification(root)?; validate_manifest(root, &qualification)?; + ensure_clean_extraction_environment(root, &qualification)?; let closure_digest = synchronize_source_closure(root, &qualification, false)?; synchronize_reviewed_bridges(root, attenuation_dimensions, false)?; validate_generated_inventory(root, &qualification)?; @@ -152,7 +315,7 @@ pub(crate) fn qualify( if !update { synchronize_source_closure(root, &qualification, false)?; } - ensure_clean_extraction_environment(&qualification)?; + ensure_clean_extraction_environment(root, &qualification)?; let charon = required_tool("AUTHS_CHARON_BIN", "charon")?; let aeneas = required_tool("AUTHS_AENEAS_BIN", "aeneas")?; validate_translation_tool_versions(&charon, &aeneas, &qualification)?; @@ -237,6 +400,11 @@ fn validate_manifest(root: &Path, qualification: &Qualification) -> Result<(), S qualification.decision )); } + if qualification.adr != "docs/adr/0011-rich-authority-rust-lean-link.md" + || qualification.source_closure != "formal/qualification/aeneas/source-closure.json" + { + return Err("Aeneas qualification ADR or source-closure path drifted".to_owned()); + } if qualification.production_features != ["default"] || !qualification.semantic_cfg.is_empty() || !qualification.extraction_cfg.is_empty() @@ -253,7 +421,7 @@ fn validate_manifest(root: &Path, qualification: &Qualification) -> Result<(), S if qualification.case_modules.len() != 5 || qualification.translations.len() != 5 || qualification.external_models.len() != 4 - || qualification.warning_inventory.len() != 2 + || qualification.warning_inventory.len() != 4 || qualification.template_axioms.len() != 3 { return Err("Aeneas qualification inventory cardinality drifted".to_owned()); @@ -268,8 +436,50 @@ fn validate_manifest(root: &Path, qualification: &Qualification) -> Result<(), S } } require_unique_nonempty("source files", &qualification.source_files)?; - require_unique_nonempty("generated files", &qualification.generated_files)?; - require_unique_nonempty("case modules", &qualification.case_modules)?; + for required in REQUIRED_AUTHORED_SOURCE_INPUTS { + if !qualification + .source_files + .iter() + .any(|path| path == required) + { + return Err(format!( + "Aeneas authored source inventory omits required qualification input {required}" + )); + } + } + validate_generated_artifact_inventory(&qualification.generated_files)?; + validate_exact_inventory( + "case modules", + &qualification.case_modules, + EXPECTED_CASE_MODULES, + )?; + let actual_translations = qualification + .translations + .iter() + .map(|translation| { + ( + translation.crate_name.as_str(), + translation.translation_json.as_str(), + ) + }) + .collect::>(); + if actual_translations != EXPECTED_TRANSLATIONS { + return Err("Aeneas translation crate/report inventory drifted".to_owned()); + } + let actual_templates = qualification + .template_axioms + .iter() + .map(|inventory| inventory.artifact.as_str()) + .collect::>(); + if actual_templates != EXPECTED_TEMPLATE_AXIOMS { + return Err("Aeneas template axiom inventory drifted".to_owned()); + } + let boundary_digest = qualification_boundary_contract_sha256(qualification); + if boundary_digest != QUALIFICATION_BOUNDARY_CONTRACT_SHA256 { + return Err(format!( + "Aeneas reviewed translation/external boundary contract drifted: expected {QUALIFICATION_BOUNDARY_CONTRACT_SHA256}, found {boundary_digest}" + )); + } let mut external_ids = BTreeSet::new(); for model in &qualification.external_models { @@ -311,62 +521,257 @@ fn validate_manifest(root: &Path, qualification: &Qualification) -> Result<(), S validate_locked_tools(root, &qualification.tools) } +fn qualification_boundary_contract_sha256(qualification: &Qualification) -> String { + let external_models = qualification + .external_models + .iter() + .map(|model| { + json!({ + "id": model.id, + "kind": model.kind, + "artifact": model.artifact, + "rust_symbols": model.rust_symbols, + "authority_semantics": model.authority_semantics, + "reviewed": model.reviewed, + "axiom": model.axiom, + "scope": model.scope, + }) + }) + .collect::>(); + let translations = qualification + .translations + .iter() + .map(|translation| { + json!({ + "crate_name": translation.crate_name, + "translation_json": translation.translation_json, + "local_functions": translation.local_functions, + "external_functions": translation.external_functions, + "opaque_local_functions": translation.opaque_local_functions, + "required_symbols": translation.required_symbols, + "allowed_external_symbols": translation.allowed_external_symbols, + }) + }) + .collect::>(); + let warnings = qualification + .warning_inventory + .iter() + .map(|warning| { + json!({ + "id": warning.id, + "artifact": warning.artifact, + "upstream_lines": warning.upstream_lines, + "classification": warning.classification, + }) + }) + .collect::>(); + let templates = qualification + .template_axioms + .iter() + .map(|template| { + json!({ + "artifact": template.artifact, + "count": template.count, + "compiled": template.compiled, + }) + }) + .collect::>(); + let bytes = serde_json::to_vec(&json!({ + "external_models": external_models, + "translations": translations, + "warning_inventory": warnings, + "template_axioms": templates, + })) + .expect("in-memory qualification contract serializes"); + hex::encode(Sha256::digest(bytes)) +} + fn validate_warning_inventory(root: &Path, qualification: &Qualification) -> Result<(), String> { + let package = root.join("formal/.lake/packages/aeneas"); + let actual_commit = run_output("git", &["rev-parse", "HEAD"], &package, &[])?; + if actual_commit.trim() != qualification.tools.aeneas_commit { + return Err(format!( + "pinned Aeneas package commit drifted: expected {}, found {}", + qualification.tools.aeneas_commit, + actual_commit.trim() + )); + } + let status = run_output("git", &["status", "--porcelain=v1"], &package, &[])?; + if !status.trim().is_empty() { + return Err(format!( + "pinned Aeneas package has local modifications; qualification refuses a dirty dependency:\n{}", + status.trim_end() + )); + } + + let mut present = BTreeMap::new(); + collect_lean_sorries(root, &package.join("backends/lean"), &mut present)?; + let mut inventoried = BTreeMap::new(); for warning in &qualification.warning_inventory { - let path = root.join(&warning.artifact); - let source = fs::read_to_string(&path).map_err(|error| { - format!( - "could not read pinned Aeneas warning source {}: {error}", - path.display() - ) - })?; - let lines: Vec<_> = source.lines().collect(); - for line in &warning.upstream_lines { - let source_line = line - .checked_sub(1) - .and_then(|index| lines.get(index)) - .ok_or_else(|| { - format!( - "warning {} points outside {} at line {line}", - warning.id, warning.artifact - ) - })?; - if !source_line.contains("sorry") { - return Err(format!( - "warning {} no longer identifies an upstream `sorry` at {}:{line}; review and update the inventory", - warning.id, warning.artifact - )); - } - } - // The inventory confirmed each RECORDED line still holds a `sorry`. It - // did not confirm there are no OTHERS: a new upstream `sorry`, arriving - // with a toolchain bump, would have been accepted silently while the - // inventory still read as a complete account of them. - // - // The audited theorems provably depend on none of these -- all 139 - // compiled declarations reduce to `Classical.choice`, `Quot.sound` and - // `propext`, with zero `sorryAx` -- so this is not about soundness. It - // is about the inventory meaning what it says. - let present = lines - .iter() - .enumerate() - .filter(|(_, text)| text.contains("sorry")) - .map(|(index, _)| index + 1) - .collect::>(); - let inventoried: BTreeSet<_> = warning.upstream_lines.iter().copied().collect(); - let unaccounted = present - .iter() - .copied() - .filter(|line| !inventoried.contains(line)) - .collect::>(); - if !unaccounted.is_empty() { + if inventoried + .insert(warning.artifact.clone(), warning.upstream_lines.clone()) + .is_some() + { return Err(format!( - "warning {} accounts for {:?} in {} but the file also carries \ - `sorry` at {unaccounted:?}; review and update the inventory", - warning.id, warning.upstream_lines, warning.artifact + "Aeneas warning inventory repeats artifact {}", + warning.artifact )); } } + if present != inventoried { + return Err(format!( + "pinned Aeneas package `sorry` inventory drifted: inventoried={inventoried:?}, present={present:?}" + )); + } + Ok(()) +} + +/// Finds code-level `sorry`/`admit` identifiers while ignoring comments and +/// literal contents. Syntax quotations remain code and are inventoried because +/// a tactic capable of manufacturing `sorry` is part of the package surface. +fn lean_sorry_lines(source: &str) -> Vec { + let bytes = source.as_bytes(); + let mut lines = BTreeSet::new(); + let mut index = 0; + let mut line = 1; + let mut block_depth = 0usize; + let mut in_string = false; + let mut escaped = false; + while index < bytes.len() { + let byte = bytes[index]; + let next = bytes.get(index + 1).copied(); + if block_depth > 0 { + if byte == b'\n' { + line += 1; + } + if byte == b'/' && next == Some(b'-') { + block_depth += 1; + index += 2; + continue; + } + if byte == b'-' && next == Some(b'/') { + block_depth -= 1; + index += 2; + continue; + } + index += 1; + continue; + } + if in_string { + if byte == b'\n' { + line += 1; + } + if escaped { + escaped = false; + } else if byte == b'\\' { + escaped = true; + } else if byte == b'"' { + in_string = false; + } + index += 1; + continue; + } + if byte == b'-' && next == Some(b'-') { + while index < bytes.len() && bytes[index] != b'\n' { + index += 1; + } + continue; + } + if byte == b'/' && next == Some(b'-') { + block_depth = 1; + index += 2; + continue; + } + if byte == b'"' { + in_string = true; + index += 1; + continue; + } + if byte == b'\'' { + // Lean also uses `'(` for syntax quotation and apostrophes in + // identifiers. Only skip a lexically complete character literal; + // treating every apostrophe as one can hide arbitrary later code. + index = lean_char_literal_end(source, index).unwrap_or(index + 1); + continue; + } + if byte == b'\n' { + line += 1; + index += 1; + continue; + } + if byte.is_ascii_alphabetic() || byte == b'_' { + let start = index; + index += 1; + while index < bytes.len() + && (bytes[index].is_ascii_alphanumeric() + || bytes[index] == b'_' + || bytes[index] == b'\'') + { + index += 1; + } + let token = &source[start..index]; + if token == "sorry" || token == "admit" { + lines.insert(line); + } + continue; + } + index += 1; + } + lines.into_iter().collect() +} + +fn lean_char_literal_end(source: &str, quote: usize) -> Option { + let tail = source.get(quote + 1..)?; + let mut chars = tail.char_indices(); + let (_, first) = chars.next()?; + if first == '\n' || first == '\r' || first == '\'' { + return None; + } + if first != '\\' { + let closing = quote + 1 + first.len_utf8(); + return (source.as_bytes().get(closing) == Some(&b'\'')).then_some(closing + 1); + } + + let escaped = tail.as_bytes().get(1).copied()?; + let closing = match escaped { + b'x' => quote + 5, + b'u' if tail.as_bytes().get(2) == Some(&b'{') => { + let brace = tail.find('}')?; + quote + 1 + brace + 1 + } + _ => quote + 3, + }; + (source.as_bytes().get(closing) == Some(&b'\'')).then_some(closing + 1) +} + +fn collect_lean_sorries( + workspace_root: &Path, + directory: &Path, + output: &mut BTreeMap>, +) -> Result<(), String> { + let mut entries = fs::read_dir(directory) + .map_err(|error| format!("could not read {}: {error}", directory.display()))? + .collect::, _>>() + .map_err(|error| format!("could not enumerate {}: {error}", directory.display()))?; + entries.sort_by_key(std::fs::DirEntry::file_name); + for entry in entries { + let path = entry.path(); + if path.is_dir() { + collect_lean_sorries(workspace_root, &path, output)?; + } else if path.extension().and_then(|value| value.to_str()) == Some("lean") { + let source = fs::read_to_string(&path) + .map_err(|error| format!("could not read {}: {error}", path.display()))?; + let lines = lean_sorry_lines(&source); + if !lines.is_empty() { + let relative = path + .strip_prefix(workspace_root) + .map_err(|error| format!("Aeneas source escaped workspace: {error}"))? + .to_string_lossy() + .replace('\\', "/"); + output.insert(relative, lines); + } + } + } Ok(()) } @@ -834,7 +1239,32 @@ fn validate_ci_workflow_gates(ci: &str) -> Result<(), String> { "cargo xtask ci compliance", "target/formal/", ], - ) + )?; + if ci + .matches("cargo xtask formal qualify aeneas --update") + .count() + != 1 + || ci + .matches("cargo xtask ci formal-post-qualification") + .count() + != 1 + { + return Err( + "hosted PR CI must reproduce/update exactly once and then run exactly one non-reproducing post-qualification gate".to_owned(), + ); + } + let formal_job = ci + .split_once("\n formal-translation-run:") + .and_then(|(_, tail)| tail.split_once("\n compliance-run:")) + .map(|(job, _)| job) + .ok_or("hosted CI omits the formal-translation-run job boundary")?; + if !formal_job.contains("compiler-cache: \"false\"") { + return Err( + "hosted formal translation must disable the compiler cache and its semantic Rust environment overrides" + .to_owned(), + ); + } + Ok(()) } fn validate_release_workflow_gates(orchestration: &str, builder: &str) -> Result<(), String> { @@ -943,25 +1373,113 @@ fn validate_translation_tool_versions( Ok(()) } -fn ensure_clean_extraction_environment(qualification: &Qualification) -> Result<(), String> { - for variable in [ - "RUSTFLAGS", - "CARGO_ENCODED_RUSTFLAGS", - "RUSTDOCFLAGS", - "CARGO_BUILD_TARGET", - ] { - if env::var_os(variable).is_some() { +fn ensure_clean_extraction_environment( + root: &Path, + qualification: &Qualification, +) -> Result<(), String> { + for (variable, _) in env::vars_os() { + let variable = variable.to_string_lossy(); + if is_semantic_build_variable(&variable) { return Err(format!( "Aeneas qualification refuses ambient semantic build variable {variable}" )); } } + validate_ambient_cargo_configuration(root)?; if !qualification.semantic_cfg.is_empty() || !qualification.extraction_cfg.is_empty() { return Err("qualification requires empty shipping and extraction semantic cfg".to_owned()); } Ok(()) } +fn is_semantic_build_variable(variable: &str) -> bool { + if variable == "CARGO_TARGET_DIR" { + return false; + } + matches!( + variable, + "RUSTFLAGS" + | "CARGO_ENCODED_RUSTFLAGS" + | "RUSTDOCFLAGS" + | "RUSTC" + | "RUSTC_WRAPPER" + | "RUSTC_WORKSPACE_WRAPPER" + | "RUSTC_BOOTSTRAP" + | "CARGO_BUILD_TARGET" + ) || variable.starts_with("CARGO_PROFILE_") + || variable.starts_with("CARGO_TARGET_") + || variable.starts_with("CARGO_BUILD_") +} + +fn validate_ambient_cargo_configuration(root: &Path) -> Result<(), String> { + let rustc = run_output("rustc", &["-vV"], Path::new("."), &[])?; + let host = rustc + .lines() + .find_map(|line| line.strip_prefix("host: ")) + .ok_or("rustc -vV omitted host triple")?; + let cargo_home = env::var_os("CARGO_HOME") + .map(PathBuf::from) + .or_else(|| env::var_os("HOME").map(|home| PathBuf::from(home).join(".cargo"))) + .ok_or("cannot resolve Cargo home for ambient configuration validation")?; + let mut candidates = BTreeSet::new(); + for ancestor in root.ancestors() { + for name in ["config.toml", "config"] { + candidates.insert(ancestor.join(".cargo").join(name)); + } + } + for name in ["config.toml", "config"] { + candidates.insert(cargo_home.join(name)); + } + for path in candidates { + if !path.exists() { + continue; + } + let source = fs::read_to_string(&path).map_err(|error| { + format!( + "could not read ambient Cargo config {}: {error}", + path.display() + ) + })?; + validate_ambient_cargo_config_source(&source, host).map_err(|error| { + format!( + "ambient Cargo config {} is semantic: {error}", + path.display() + ) + })?; + } + Ok(()) +} + +fn validate_ambient_cargo_config_source(source: &str, host: &str) -> Result<(), String> { + let config: toml::Value = + toml::from_str(source).map_err(|error| format!("invalid Cargo config: {error}"))?; + let table = config + .as_table() + .ok_or("Cargo config root is not a table")?; + for (key, value) in table { + match key.as_str() { + // Aliases affect command spelling, not Cargo's build semantics. + "alias" => {} + // A concrete non-host target cannot affect this qualification, + // which deliberately compiles for workspace-host. `cfg(...)` + // selectors are rejected because they may match the host. + "target" => { + let targets = value + .as_table() + .ok_or("Cargo config [target] is not a table")?; + if targets + .keys() + .any(|target| target == host || target.starts_with("cfg(")) + { + return Err("host-applicable [target] configuration is present".to_owned()); + } + } + _ => return Err(format!("unsupported ambient Cargo config section [{key}]")), + } + } + Ok(()) +} + fn recreate_directory(path: &Path) -> Result<(), String> { if path.exists() { fs::remove_dir_all(path) @@ -1177,81 +1695,7 @@ fn charon_arguments( } fn synchronize_aeneas_output(root: &Path, reproduced: &Path, update: bool) -> Result<(), String> { - let mappings = [ - ( - "model-run/qualification/aeneas/generated/model/Types.lean", - "formal/qualification/aeneas/generated/model/Types.lean", - ), - ( - "model-run/qualification/aeneas/generated/model/Funs.lean", - "formal/qualification/aeneas/generated/model/Funs.lean", - ), - ( - "model-run/qualification/aeneas/generated/model/FunsExternal_Template.lean", - "formal/qualification/aeneas/generated/model/FunsExternal_Template.lean", - ), - ( - "model-run/translation.json", - "formal/qualification/aeneas/generated/model/translation.json", - ), - ( - "algebra-run/qualification/aeneas/generated/algebra/Types.lean", - "formal/qualification/aeneas/generated/algebra/Types.lean", - ), - ( - "algebra-run/qualification/aeneas/generated/algebra/Funs.lean", - "formal/qualification/aeneas/generated/algebra/Funs.lean", - ), - ( - "algebra-run/translation.json", - "formal/qualification/aeneas/generated/algebra/translation.json", - ), - ( - "authority-run/qualification/aeneas/generated/authority/Types.lean", - "formal/qualification/aeneas/generated/authority/Types.lean", - ), - ( - "authority-run/qualification/aeneas/generated/authority/TypesExternal_Template.lean", - "formal/qualification/aeneas/generated/authority/TypesExternal_Template.lean", - ), - ( - "authority-run/qualification/aeneas/generated/authority/Funs.lean", - "formal/qualification/aeneas/generated/authority/Funs.lean", - ), - ( - "authority-run/qualification/aeneas/generated/authority/FunsExternal_Template.lean", - "formal/qualification/aeneas/generated/authority/FunsExternal_Template.lean", - ), - ( - "authority-run/translation.json", - "formal/qualification/aeneas/generated/authority/translation.json", - ), - ( - "bounded_policy-run/qualification/aeneas/generated/bounded_policy/Types.lean", - "formal/qualification/aeneas/generated/bounded_policy/Types.lean", - ), - ( - "bounded_policy-run/qualification/aeneas/generated/bounded_policy/Funs.lean", - "formal/qualification/aeneas/generated/bounded_policy/Funs.lean", - ), - ( - "bounded_policy-run/translation.json", - "formal/qualification/aeneas/generated/bounded_policy/translation.json", - ), - ( - "lifecycle-run/qualification/aeneas/generated/lifecycle/Types.lean", - "formal/qualification/aeneas/generated/lifecycle/Types.lean", - ), - ( - "lifecycle-run/qualification/aeneas/generated/lifecycle/Funs.lean", - "formal/qualification/aeneas/generated/lifecycle/Funs.lean", - ), - ( - "lifecycle-run/translation.json", - "formal/qualification/aeneas/generated/lifecycle/translation.json", - ), - ]; - for (source, destination) in mappings { + for &(source, destination) in AENEAS_OUTPUT_MAPPINGS { let bytes = fs::read(reproduced.join(source)).map_err(|error| { format!("could not read reproduced Aeneas output {source}: {error}") })?; @@ -1363,22 +1807,107 @@ fn write_evidence( reproduced: bool, ) -> Result<(), String> { let path = root.join("target/formal/aeneas-qualification.json"); - write_pretty_json( - &path, - &json!({ - "schema": "auths-proof-aeneas-qualification-evidence/v1", - "decision": qualification.decision, - "source_closure_sha256": closure_digest, - "production_features": qualification.production_features, - "semantic_cfg": qualification.semantic_cfg, - "extraction_cfg": qualification.extraction_cfg, - "compiled_external_axioms": 0, - "reviewed_external_models": qualification.external_models.len(), - "case_modules": qualification.case_modules, - "clean_reproduction": if reproduced { "byte-identical" } else { "not-run-committed-artifacts-validated" }, - "adr": qualification.adr, - }), - ) + let generated_artifacts_digest = generated_artifact_digest(root)?; + let evidence = json!({ + "schema": "auths-proof-aeneas-qualification-evidence/v1", + "decision": qualification.decision, + "source_closure_sha256": closure_digest, + "generated_artifacts_sha256": generated_artifacts_digest, + "production_features": qualification.production_features, + "semantic_cfg": qualification.semantic_cfg, + "extraction_cfg": qualification.extraction_cfg, + "compiled_external_axioms": 0, + "reviewed_external_models": qualification.external_models.len(), + "case_modules": qualification.case_modules, + "clean_reproduction": if reproduced { "byte-identical" } else { "not-run-committed-artifacts-validated" }, + "adr": qualification.adr, + }); + write_evidence_monotonic(&path, &evidence, reproduced) +} + +fn validate_generated_artifact_inventory(paths: &[String]) -> Result<(), String> { + let expected = GENERATED_ARTIFACTS + .iter() + .map(|path| (*path).to_owned()) + .collect::>(); + if paths != expected { + return Err(format!( + "Aeneas generated artifact inventory drifted; expected the canonical {}-artifact synchronization set", + expected.len() + )); + } + + let synchronized = AENEAS_OUTPUT_MAPPINGS + .iter() + .map(|(_, destination)| *destination) + .chain(REVIEWED_BRIDGE_ARTIFACTS.iter().copied()) + .collect::>(); + let canonical = GENERATED_ARTIFACTS.iter().copied().collect::>(); + if synchronized != canonical { + return Err( + "internal Aeneas synchronization destinations do not equal the canonical generated artifact inventory" + .to_owned(), + ); + } + Ok(()) +} + +fn validate_exact_inventory( + label: &str, + actual: &[String], + expected: &[&str], +) -> Result<(), String> { + if actual.iter().map(String::as_str).collect::>() != expected { + return Err(format!("Aeneas {label} inventory drifted")); + } + Ok(()) +} + +fn generated_artifact_digest(root: &Path) -> Result { + let mut aggregate = Sha256::new(); + for relative in GENERATED_ARTIFACTS { + let path = root.join(relative); + let metadata = fs::symlink_metadata(&path) + .map_err(|error| format!("could not stat generated artifact {relative}: {error}"))?; + if !metadata.file_type().is_file() || metadata.file_type().is_symlink() { + return Err(format!( + "generated artifact is not a regular non-symlink file: {relative}" + )); + } + let bytes = fs::read(&path) + .map_err(|error| format!("could not read generated artifact {relative}: {error}"))?; + aggregate.update(relative.as_bytes()); + aggregate.update([0]); + aggregate.update(&bytes); + aggregate.update([0xff]); + } + Ok(hex::encode(aggregate.finalize())) +} + +/// A cheaper committed-artifact validation must never erase stronger evidence +/// from an immediately preceding byte-identical reproduction. Preservation is +/// allowed only when every other field (especially source digest and config) +/// is identical; stale strong evidence is downgraded rather than carried over. +fn write_evidence_monotonic( + path: &Path, + candidate: &Value, + reproduced: bool, +) -> Result<(), String> { + if !reproduced && path.is_file() { + let existing: Value = serde_json::from_slice( + &fs::read(path) + .map_err(|error| format!("could not read {}: {error}", path.display()))?, + ) + .map_err(|error| format!("invalid existing evidence {}: {error}", path.display()))?; + let mut normalized = existing.clone(); + if existing["clean_reproduction"] == "byte-identical" { + normalized["clean_reproduction"] = candidate["clean_reproduction"].clone(); + if normalized == *candidate { + return Ok(()); + } + } + } + write_pretty_json(path, candidate) } fn write_pretty_json(path: &Path, value: &Value) -> Result<(), String> { @@ -1436,6 +1965,11 @@ fn command_output>( .env_remove("CARGO_ENCODED_RUSTFLAGS") .env_remove("RUSTDOCFLAGS") .env_remove("CARGO_BUILD_TARGET"); + for (variable, _) in env::vars_os() { + if is_semantic_build_variable(&variable.to_string_lossy()) { + command.env_remove(variable); + } + } for (name, value) in environment { command.env(name, value); } @@ -1462,7 +1996,7 @@ fn format_command_failure(arguments: &[String], directory: &Path, output: &Outpu mod tests { use super::*; - const CI_GATES: &str = "uses: ./.github/actions/setup-lean\nkani-verifier --version 0.67.0\ncargo xtask ci authoritative\ncargo xtask ci formal-translation\ncargo xtask ci compliance\ntarget/formal/\n"; + const CI_GATES: &str = "uses: ./.github/actions/setup-lean\nkani-verifier --version 0.67.0\ncargo xtask ci authoritative\ncargo xtask ci formal-translation\n formal-translation-run:\ncompiler-cache: \"false\"\ncargo xtask formal qualify aeneas --update\ncargo xtask ci formal-post-qualification\n compliance-run:\ncargo xtask ci compliance\ntarget/formal/\n"; const BUILDER_GATES: &str = "leanprover/lean-action@\nkani-verifier --version 0.67.0\ncargo xtask release-check\ncargo xtask formal qualify aeneas\n"; #[test] @@ -1473,7 +2007,7 @@ mod tests { #[test] fn hosted_ci_cannot_omit_pinned_lean_setup() { let error = validate_ci_workflow_gates( - "kani-verifier --version 0.67.0\ncargo xtask ci authoritative\ncargo xtask ci formal-translation\ncargo xtask ci compliance\ntarget/formal/\n", + "kani-verifier --version 0.67.0\ncargo xtask ci authoritative\ncargo xtask ci formal-translation\ncargo xtask formal qualify aeneas --update\ncargo xtask ci formal-post-qualification\ncargo xtask ci compliance\ntarget/formal/\n", ) .expect_err("missing pinned Lean setup must fail"); assert!(error.contains("hosted CI omits required formal gate")); @@ -1504,4 +2038,137 @@ mod tests { .expect_err("incomplete reusable builder must fail"); assert!(error.contains("reusable release builder omits required formal gate")); } + + #[test] + fn sorry_scanner_ignores_literals_and_comments_but_not_code_or_quotations() { + let source = r#" +def x' : True := by sorry +-- sorry +/- admit -/ +def text := "sorry" +def quoted ← `(tactic| sorry) +def oldQuoted := '(term| by sorry) +def character := 's' +"#; + assert_eq!(lean_sorry_lines(source), vec![2, 6, 7]); + } + + #[test] + fn sorry_scanner_handles_nested_comments() { + let source = "/- outer\n/- sorry -/\n-/\nexample : True := by admit\n"; + assert_eq!(lean_sorry_lines(source), vec![4]); + } + + #[test] + fn qualification_evidence_is_fail_monotonic_but_never_preserves_stale_digest() { + let path = env::temp_dir().join(format!( + "auths-qualification-evidence-{}.json", + std::process::id() + )); + let strong = json!({ + "source_closure_sha256": "current", + "generated_artifacts_sha256": "generated-current", + "clean_reproduction": "byte-identical", + }); + let weak = json!({ + "source_closure_sha256": "current", + "generated_artifacts_sha256": "generated-current", + "clean_reproduction": "not-run-committed-artifacts-validated", + }); + write_evidence_monotonic(&path, &strong, true).expect("strong evidence"); + write_evidence_monotonic(&path, &weak, false).expect("later validation"); + let retained: Value = serde_json::from_slice(&fs::read(&path).expect("retained evidence")) + .expect("valid retained evidence"); + assert_eq!(retained["clean_reproduction"], "byte-identical"); + + let stale = json!({ + "source_closure_sha256": "current", + "generated_artifacts_sha256": "generated-changed", + "clean_reproduction": "not-run-committed-artifacts-validated", + }); + write_evidence_monotonic(&path, &stale, false).expect("stale downgrade"); + let downgraded: Value = + serde_json::from_slice(&fs::read(&path).expect("downgraded evidence")) + .expect("valid downgraded evidence"); + assert_eq!( + downgraded["clean_reproduction"], + "not-run-committed-artifacts-validated" + ); + assert_eq!(downgraded["source_closure_sha256"], "current"); + assert_eq!( + downgraded["generated_artifacts_sha256"], + "generated-changed" + ); + fs::remove_file(path).expect("remove owned evidence fixture"); + } + + #[test] + fn generated_artifact_inventory_rejects_omission_and_substitution() { + let exact = GENERATED_ARTIFACTS + .iter() + .map(|path| (*path).to_owned()) + .collect::>(); + validate_generated_artifact_inventory(&exact).expect("canonical inventory"); + + let mut omitted = exact.clone(); + omitted.pop(); + assert!(validate_generated_artifact_inventory(&omitted).is_err()); + + let mut substituted = exact; + substituted[0] = "formal/qualification/aeneas/generated/forged.lean".to_owned(); + assert!(validate_generated_artifact_inventory(&substituted).is_err()); + } + + #[test] + fn semantic_rust_environment_and_host_cargo_config_fail_closed() { + for variable in [ + "RUSTC_WRAPPER", + "RUSTC_WORKSPACE_WRAPPER", + "RUSTC_BOOTSTRAP", + "CARGO_PROFILE_DEV_OPT_LEVEL", + "CARGO_TARGET_AARCH64_APPLE_DARWIN_RUSTFLAGS", + ] { + assert!(is_semantic_build_variable(variable)); + } + assert!(!is_semantic_build_variable("CARGO_TARGET_DIR")); + + validate_ambient_cargo_config_source( + "[alias]\nxtask = 'run -p xtask --'\n[target.aarch64-apple-ios]\nlinker = 'clang'\n", + "aarch64-apple-darwin", + ) + .expect("alias and foreign target are non-semantic for host extraction"); + assert!( + validate_ambient_cargo_config_source( + "[target.aarch64-apple-darwin]\nrustflags = ['--cfg', 'forged']\n", + "aarch64-apple-darwin", + ) + .is_err() + ); + assert!( + validate_ambient_cargo_config_source( + "[target.'cfg(unix)']\nrustflags = ['--cfg', 'forged']\n", + "aarch64-apple-darwin", + ) + .is_err() + ); + assert!( + validate_ambient_cargo_config_source( + "[build]\nrustflags = ['--cfg', 'forged']\n", + "aarch64-apple-darwin", + ) + .is_err() + ); + } + + #[test] + fn reviewed_translation_boundary_contract_is_exact() { + let repository = Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .expect("xtask has repository parent"); + let qualification = load_qualification(repository).expect("qualification manifest"); + assert_eq!( + qualification_boundary_contract_sha256(&qualification), + QUALIFICATION_BOUNDARY_CONTRACT_SHA256 + ); + } } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index bb704b79..eb9b7842 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -58,7 +58,7 @@ pub(crate) use sdk_vocabulary::*; pub(crate) use semantic_freeze::*; pub(crate) use stripe::*; -const USAGE: &str = "usage: cargo xtask ...|semantic-freeze [--update]|production-contract [--update]|evolution-policy [--update]|sdk-experience [--update]|sdk-vocabulary|error-registry [--update]|mcp-session-contract [--update]|mechanism-conformance [--update]|product-waist-conformance [--update]|public-naming|release-contract|release-control ...|binding-semantics|core-boundary|workspace-msrv|abi|core|exchange|product|bindings|demos|package|wire [--update]|spec-sync|conformance|exchange-conformance|product-conformance|stripe-profiles|bounded-domains|compliance|matrix|cross-language|product-fixtures [--update]|semantic-digest|wasm|live-demo|fuzz-inventory|fuzz-smoke|platform-artifact [output]|formal [--skip-kani] [--update]|formal qualify aeneas [--update]|adversarial-conformance [--surface |--adapter |--case ]|bench |ci [authoritative|formal-translation|compliance]|release-check>"; +const USAGE: &str = "usage: cargo xtask ...|semantic-freeze [--update]|production-contract [--update]|evolution-policy [--update]|sdk-experience [--update]|sdk-vocabulary|error-registry [--update]|mcp-session-contract [--update]|mechanism-conformance [--update]|product-waist-conformance [--update]|public-naming|release-contract|release-control ...|binding-semantics|core-boundary|workspace-msrv|abi|core|exchange|product|bindings|demos|package|wire [--update]|spec-sync|conformance|exchange-conformance|product-conformance|stripe-profiles|bounded-domains|compliance|matrix|cross-language|product-fixtures [--update]|semantic-digest|wasm|live-demo|fuzz-inventory|fuzz-smoke|platform-artifact [output]|formal [--skip-kani] [--update]|formal qualify aeneas [--update]|adversarial-conformance [--surface |--adapter |--case ]|bench |ci [authoritative|formal-translation|formal-post-qualification|compliance]|release-check>"; fn main() -> ExitCode { match run() { @@ -84,9 +84,10 @@ fn dispatch(arguments: impl IntoIterator) -> Result<(), String> { [] => ci(), [phase] if phase == "authoritative" => ci_authoritative(), [phase] if phase == "formal-translation" => ci_formal_translation(), + [phase] if phase == "formal-post-qualification" => ci_formal_post_qualification(), [phase] if phase == "compliance" => ci_compliance(), _ => Err(format!( - "unknown CI phase {}; expected authoritative, formal-translation, or compliance", + "unknown CI phase {}; expected authoritative, formal-translation, formal-post-qualification, or compliance", arguments.join(" ") )), } @@ -174,7 +175,7 @@ mod tests { fn help_output_is_stable() { assert_eq!( USAGE, - "usage: cargo xtask ...|semantic-freeze [--update]|production-contract [--update]|evolution-policy [--update]|sdk-experience [--update]|sdk-vocabulary|error-registry [--update]|mcp-session-contract [--update]|mechanism-conformance [--update]|product-waist-conformance [--update]|public-naming|release-contract|release-control ...|binding-semantics|core-boundary|workspace-msrv|abi|core|exchange|product|bindings|demos|package|wire [--update]|spec-sync|conformance|exchange-conformance|product-conformance|stripe-profiles|bounded-domains|compliance|matrix|cross-language|product-fixtures [--update]|semantic-digest|wasm|live-demo|fuzz-inventory|fuzz-smoke|platform-artifact [output]|formal [--skip-kani] [--update]|formal qualify aeneas [--update]|adversarial-conformance [--surface |--adapter |--case ]|bench |ci [authoritative|formal-translation|compliance]|release-check>" + "usage: cargo xtask ...|semantic-freeze [--update]|production-contract [--update]|evolution-policy [--update]|sdk-experience [--update]|sdk-vocabulary|error-registry [--update]|mcp-session-contract [--update]|mechanism-conformance [--update]|product-waist-conformance [--update]|public-naming|release-contract|release-control ...|binding-semantics|core-boundary|workspace-msrv|abi|core|exchange|product|bindings|demos|package|wire [--update]|spec-sync|conformance|exchange-conformance|product-conformance|stripe-profiles|bounded-domains|compliance|matrix|cross-language|product-fixtures [--update]|semantic-digest|wasm|live-demo|fuzz-inventory|fuzz-smoke|platform-artifact [output]|formal [--skip-kani] [--update]|formal qualify aeneas [--update]|adversarial-conformance [--surface |--adapter |--case ]|bench |ci [authoritative|formal-translation|formal-post-qualification|compliance]|release-check>" ); } @@ -184,7 +185,7 @@ mod tests { dispatch(["ci".to_owned(), "unknown".to_owned()]).expect_err("unknown phase must fail"); assert_eq!( error, - "unknown CI phase unknown; expected authoritative, formal-translation, or compliance" + "unknown CI phase unknown; expected authoritative, formal-translation, formal-post-qualification, or compliance" ); } } diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index 3a347979..5182209f 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 124; +const FREEZE_VERSION: u64 = 125; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -167,7 +167,7 @@ fn generate_inventory() -> Result { let mut entries = vec![ freeze_entry( "auths.core.protocol", - 16, + 17, FreezeClassification::FrozenMeaning, &[ "protocol-versions", @@ -246,7 +246,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.portable-abi-bindings", - 52, + 53, FreezeClassification::FrozenMeaning, &["portable-abi", "authoring-abi", "binding-contracts"], vec![ @@ -589,7 +589,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.release.evolution-contract", - 15, + 16, FreezeClassification::FrozenMeaning, &[ "version-axes", @@ -633,10 +633,10 @@ fn generate_inventory() -> Result { "bindings/wasm/auths-proof-wasm/identity-abi-v1.json" => 4, "core/fixtures/v1/manifest.json" => 4, "product/conformance/v1/simplified-product-waist.json" => 2, - "formal/assurance-manifest-v1.toml" => 11, - "formal/qualification/aeneas/qualification.toml" => 4, - "formal/qualification/aeneas/generated" => 5, - "formal/qualification/aeneas/source-closure.json" => 18, + "formal/assurance-manifest-v1.toml" => 12, + "formal/qualification/aeneas/qualification.toml" => 5, + "formal/qualification/aeneas/generated" => 6, + "formal/qualification/aeneas/source-closure.json" => 19, "product/fixtures/v1/errors/manifest.json" => 4, "product/fixtures/v1/bounded-policy/manifest.json" => 2, "product/fixtures/v1/github/manifest.json" @@ -696,7 +696,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 123, + 124, FreezeClassification::ReleaseMetadata, &[ "package-names", From ff0c73b4fb8a40f346ea9668119539b29df05cc5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 16 Aug 2026 20:40:41 +0000 Subject: [PATCH 51/61] chore(formal): regenerate qualification artifacts Source-SHA: 7d584b35dd13f8dc361fe36cf1abd7252a7e2859 Workflow-Run: 31970247848 --- formal/qualification/aeneas/source-closure.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/formal/qualification/aeneas/source-closure.json b/formal/qualification/aeneas/source-closure.json index 53856d7d..eb004273 100644 --- a/formal/qualification/aeneas/source-closure.json +++ b/formal/qualification/aeneas/source-closure.json @@ -1,6 +1,6 @@ { "schema": "auths-proof-translation-source-closure/v2", - "digest": "19156ae078d747c7c09b7d2bf9dc1637a8b65458a0f7e5bb5a125764e6f081a5", + "digest": "59a25f7d4759e3356c3dd80153d1f97a36a0ad6dd2596920b0dc34bef34d6649", "files": [ { "path": ".cargo/config.toml", @@ -309,7 +309,7 @@ }, { "path": "xtask/src/semantic_freeze.rs", - "sha256": "da7a7a5be21a7227f958faf054c1b2f8d37cff87cf72917d039679749392672f" + "sha256": "9bfa10fc40b1d1340add52ce2df3051a60dd5dc8ed1e52e1a49a0d01bacd11a7" }, { "path": "xtask/src/stripe.rs", From d3287563450d0cd308200ec0db1c33fb0838100e Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 16 Aug 2026 23:16:14 +0100 Subject: [PATCH 52/61] ci: close semantic and platform reliability gaps Break the semantic-freeze/formal-closure digest cycle and enforce the freeze in pre-commit. Make the installed reference validity tolerant of second-boundary clock skew, and make Windows checkpoint replacement retry transient destination locks without deleting durable state. --- .pre-commit-config.yaml | 8 ++ bindings/customer-journey-matrix-v1.json | 2 +- .../src/internal/development-store-node.ts | 51 +++++++- .../test/unit/development-store-node.test.js | 65 ++++++++++ formal/assurance-manifest-v1.toml | 6 +- .../qualification/aeneas/source-closure.json | 4 +- .../src/bin/auths-local-authority.rs | 26 ++-- release/semantic-freeze-versions.toml | 71 +++++++++++ release/semantic-freeze.json | 23 ++-- xtask/src/semantic_freeze.rs | 119 +++++++++++------- 10 files changed, 302 insertions(+), 73 deletions(-) create mode 100644 bindings/typescript/test/unit/development-store-node.test.js create mode 100644 release/semantic-freeze-versions.toml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b84decdb..d9f01eeb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,3 +18,11 @@ repos: files: \.rs$ pass_filenames: false require_serial: true + + - id: semantic-freeze + name: Verify reviewed semantic identities + entry: cargo xtask semantic-freeze + language: system + always_run: true + pass_filenames: false + require_serial: true diff --git a/bindings/customer-journey-matrix-v1.json b/bindings/customer-journey-matrix-v1.json index 62b4b926..093cccba 100644 --- a/bindings/customer-journey-matrix-v1.json +++ b/bindings/customer-journey-matrix-v1.json @@ -9,7 +9,7 @@ "typescriptEntryPoints": 8, "typescriptPublicSymbols": 191, "pythonModules": 8, - "pythonPublicSymbols": 168, + "pythonPublicSymbols": 169, "maintainedTypescriptRecipes": 5, "maintainedPythonRecipes": 5 }, diff --git a/bindings/typescript/src/internal/development-store-node.ts b/bindings/typescript/src/internal/development-store-node.ts index 69ba75a6..98cad3fc 100644 --- a/bindings/typescript/src/internal/development-store-node.ts +++ b/bindings/typescript/src/internal/development-store-node.ts @@ -4,6 +4,8 @@ import { platform } from "node:os"; import { dirname, join, resolve } from "node:path"; const MANIFEST = "auths-development-v2.json"; +const WINDOWS_RENAME_RETRIES = 8; +const WINDOWS_RENAME_RETRY_DELAY_MS = 25; export interface RecoverableDevelopmentResources { readonly resources: McpExecutionState & McpReceiptSink; @@ -182,7 +184,7 @@ async function atomicWrite(path: string, bytes: Uint8Array): Promise { await handle.close(); } try { - await rename(temporary, path); + await renameWithWindowsGracePeriod(temporary, path); await syncDirectory(dirname(path)); } catch (error) { await unlink(temporary).catch(() => undefined); @@ -190,6 +192,53 @@ async function atomicWrite(path: string, bytes: Uint8Array): Promise { } } +type RenameOperation = (from: string, to: string) => Promise; +type RenameRetryOptions = Readonly<{ + operation?: RenameOperation; + operatingSystem?: string; + wait?: (milliseconds: number) => Promise; +}>; + +/** + * Gives Windows scanners and indexers a bounded grace period to release a + * destination file without deleting the last durable checkpoint first. + * + * @internal Exported only so the failure policy can be fault-injection tested; + * this module is not a public package export. + */ +export async function renameWithWindowsGracePeriod( + from: string, + to: string, + options: RenameRetryOptions = {}, +): Promise { + const operation = options.operation ?? rename; + const operatingSystem = options.operatingSystem ?? platform(); + const wait = options.wait ?? delay; + for (let attempt = 0; ; attempt += 1) { + try { + await operation(from, to); + return; + } catch (error) { + if (operatingSystem !== "win32" + || attempt >= WINDOWS_RENAME_RETRIES + || !isTransientWindowsRename(error)) { + throw error; + } + await wait(WINDOWS_RENAME_RETRY_DELAY_MS * (attempt + 1)); + } + } +} + +function isTransientWindowsRename(error: unknown): boolean { + if (error === null || typeof error !== "object" || !("code" in error)) return false; + const code = (error as { code?: unknown }).code; + return code === "EPERM" || code === "EACCES" || code === "EBUSY"; +} + +function delay(milliseconds: number): Promise { + return new Promise((resolveDelay) => setTimeout(resolveDelay, milliseconds)); +} + async function syncDirectory(path: string): Promise { if (platform() === "win32") return; const handle = await open(path, "r"); diff --git a/bindings/typescript/test/unit/development-store-node.test.js b/bindings/typescript/test/unit/development-store-node.test.js new file mode 100644 index 00000000..88f3da02 --- /dev/null +++ b/bindings/typescript/test/unit/development-store-node.test.js @@ -0,0 +1,65 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { renameWithWindowsGracePeriod } from "../../dist/internal/development-store-node.js"; + +const fileError = (code) => Object.assign(new Error(code), { code }); + +test("Windows checkpoint replacement waits through transient destination locks", async () => { + let attempts = 0; + const waits = []; + await renameWithWindowsGracePeriod("checkpoint.tmp", "checkpoint.json", { + operatingSystem: "win32", + async operation() { + attempts += 1; + if (attempts < 3) throw fileError("EPERM"); + }, + async wait(milliseconds) { + waits.push(milliseconds); + }, + }); + + assert.equal(attempts, 3); + assert.deepEqual(waits, [25, 50]); +}); + +test("checkpoint replacement never retries a permanent or non-Windows error", async () => { + for (const [operatingSystem, code] of [["win32", "ENOENT"], ["linux", "EPERM"]]) { + let attempts = 0; + const error = fileError(code); + await assert.rejects( + renameWithWindowsGracePeriod("checkpoint.tmp", "checkpoint.json", { + operatingSystem, + async operation() { + attempts += 1; + throw error; + }, + async wait() { + assert.fail("a permanent rename error must not wait"); + }, + }), + (received) => received === error, + ); + assert.equal(attempts, 1); + } +}); + +test("Windows checkpoint replacement retry budget is bounded", async () => { + let attempts = 0; + let waits = 0; + const error = fileError("EBUSY"); + await assert.rejects( + renameWithWindowsGracePeriod("checkpoint.tmp", "checkpoint.json", { + operatingSystem: "win32", + async operation() { + attempts += 1; + throw error; + }, + async wait() { + waits += 1; + }, + }), + (received) => received === error, + ); + assert.equal(attempts, 9); + assert.equal(waits, 8); +}); diff --git a/formal/assurance-manifest-v1.toml b/formal/assurance-manifest-v1.toml index 14998c0a..e7426faa 100644 --- a/formal/assurance-manifest-v1.toml +++ b/formal/assurance-manifest-v1.toml @@ -6503,7 +6503,7 @@ sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" [[claims.evidence]] kind = "source-closure" artifact = "formal/qualification/aeneas/source-closure.json" -sha256 = "55cef4b7d320663b4203974d79bdc089a4af169c3ca21c6a5649993dc2e733b3" +sha256 = "27c411d24fc1050d88033015fe842c0ef0e1fb564b5a416c1563bb1f2445f74b" [[claims]] claim_id = "AP-FORMAL-RICH-055" @@ -6606,7 +6606,7 @@ sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" [[claims.evidence]] kind = "source-closure" artifact = "formal/qualification/aeneas/source-closure.json" -sha256 = "55cef4b7d320663b4203974d79bdc089a4af169c3ca21c6a5649993dc2e733b3" +sha256 = "27c411d24fc1050d88033015fe842c0ef0e1fb564b5a416c1563bb1f2445f74b" [[claims]] claim_id = "AP-FORMAL-RICH-056" @@ -6714,7 +6714,7 @@ sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" [[claims.evidence]] kind = "source-closure" artifact = "formal/qualification/aeneas/source-closure.json" -sha256 = "55cef4b7d320663b4203974d79bdc089a4af169c3ca21c6a5649993dc2e733b3" +sha256 = "27c411d24fc1050d88033015fe842c0ef0e1fb564b5a416c1563bb1f2445f74b" [[claims]] claim_id = "AP-FORMAL-RICH-104" diff --git a/formal/qualification/aeneas/source-closure.json b/formal/qualification/aeneas/source-closure.json index eb004273..f6f2e911 100644 --- a/formal/qualification/aeneas/source-closure.json +++ b/formal/qualification/aeneas/source-closure.json @@ -1,6 +1,6 @@ { "schema": "auths-proof-translation-source-closure/v2", - "digest": "59a25f7d4759e3356c3dd80153d1f97a36a0ad6dd2596920b0dc34bef34d6649", + "digest": "88f5df8b0ee0639aa9b6f01d5bd102b230e5474b1b0a7ea45d9899f1146ad497", "files": [ { "path": ".cargo/config.toml", @@ -309,7 +309,7 @@ }, { "path": "xtask/src/semantic_freeze.rs", - "sha256": "9bfa10fc40b1d1340add52ce2df3051a60dd5dc8ed1e52e1a49a0d01bacd11a7" + "sha256": "a0829ad71401a297a04a2dd4472c9c8a6fedb2a90b5b78b0b40ccd1b784de7dd" }, { "path": "xtask/src/stripe.rs", diff --git a/product/runtime/auths-node/src/bin/auths-local-authority.rs b/product/runtime/auths-node/src/bin/auths-local-authority.rs index e351604f..293ae716 100644 --- a/product/runtime/auths-node/src/bin/auths-local-authority.rs +++ b/product/runtime/auths-node/src/bin/auths-local-authority.rs @@ -70,7 +70,7 @@ fn main() -> ExitCode { return fail("the action file is unavailable"); }; - let Ok((anchorKey, anchorPrincipal)) = anchor_principal(&seed) else { + let Ok((anchor_key, anchor_principal)) = anchor_principal(&seed) else { return fail("the trust anchor could not be derived"); }; let Ok(profile_ref) = reference_profile(&profile) else { @@ -108,14 +108,14 @@ fn main() -> ExitCode { .duration_since(UNIX_EPOCH) .map_or(0, |duration| duration.as_secs()); // The grant must be CONTAINED in the anchor's window, and the anchor's was - // fixed when the context was generated -- earlier than now. A window as long - // as the context's would end after it and be refused as DelegationExpanded, - // which is the validity dimension working: a child cannot outlive its - // parent. Fifteen minutes fits comfortably inside any context lifetime the - // fixture generates. - let Ok(validity) = - ValidityWindow::new(Timestamp::new(now), Timestamp::new(now.saturating_add(900))) - else { + // fixed when the context was generated -- earlier than now. Start one minute + // in the past so a second boundary or host/container clock skew cannot make + // a freshly authored action not-yet-valid. Fifteen minutes still fits + // comfortably inside the fixture anchor's one-hour window. + let Ok(validity) = ValidityWindow::new( + Timestamp::new(now.saturating_sub(60)), + Timestamp::new(now.saturating_add(900)), + ) else { return fail("the validity window is invalid"); }; let Ok(algebra) = BudgetAlgebraId::parse("numeric-ceiling-v1") else { @@ -129,7 +129,7 @@ fn main() -> ExitCode { // Root grant: the anchor delegates to the agent, one hop, bounded. let statement = GrantStatement::new( - anchorPrincipal.clone(), + anchor_principal.clone(), agent_principal, profile_ref, permissions, @@ -145,13 +145,13 @@ fn main() -> ExitCode { ); let Ok(anchor_raw_descriptor) = RawKeyDescriptor::new( RawKeyType::Ed25519, - anchorKey.verifying_key().to_bytes().to_vec(), + anchor_key.verifying_key().to_bytes().to_vec(), ) else { return fail("the anchor key is not a valid raw-key descriptor"); }; let (Ok(method), Ok(verification), Ok(suite)) = ( PrincipalMethodId::parse(RAW_KEY_V1), - VerificationMethod::parse(anchorPrincipal.as_str()), + VerificationMethod::parse(anchor_principal.as_str()), SignatureSuiteId::parse(ED25519_V1), ) else { return fail("the anchor signature descriptor is malformed"); @@ -161,7 +161,7 @@ fn main() -> ExitCode { return fail("the root grant could not be prepared"); }; let Ok(signature) = SignatureBytes::new( - anchorKey + anchor_key .sign(request.signing_preimage()) .to_bytes() .to_vec(), diff --git a/release/semantic-freeze-versions.toml b/release/semantic-freeze-versions.toml new file mode 100644 index 00000000..04e36484 --- /dev/null +++ b/release/semantic-freeze-versions.toml @@ -0,0 +1,71 @@ +freeze_version = 126 + +# Semantic identity counters live outside the xtask source tree deliberately. +# The formal source closure binds xtask's executable code, while the semantic +# inventory binds that formal closure. Keeping these counters in the generator +# made the two generated artifacts hash each other and therefore unable to +# converge. This file is a reviewed release input, never an automatic output. +[entries] +"auths.core.protocol" = 17 +"auths.frozen-bytes/architecture/dependency-graph.json" = 29 +"auths.frozen-bytes/bindings/wasm/auths-proof-wasm/identity-abi-v1.json" = 4 +"auths.frozen-bytes/bounded-domains.toml" = 1 +"auths.frozen-bytes/core/conformance/v1/manifest.json" = 1 +"auths.frozen-bytes/core/explanations/v1/fact-inventory.json" = 1 +"auths.frozen-bytes/core/fixtures/identity/v1/vectors.json" = 1 +"auths.frozen-bytes/core/fixtures/v1/manifest.json" = 4 +"auths.frozen-bytes/core/formal-vectors/v1/manifest.json" = 1 +"auths.frozen-bytes/demos/benchmarks/profiles/release.toml" = 1 +"auths.frozen-bytes/formal/assurance-manifest-v1.toml" = 13 +"auths.frozen-bytes/formal/qualification/aeneas/generated" = 6 +"auths.frozen-bytes/formal/qualification/aeneas/qualification.toml" = 5 +"auths.frozen-bytes/formal/qualification/aeneas/source-closure.json" = 20 +"auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json" = 1 +"auths.frozen-bytes/product/conformance/v1/simplified-product-waist.json" = 2 +"auths.frozen-bytes/product/fixtures/v1/bounded-policy/manifest.json" = 2 +"auths.frozen-bytes/product/fixtures/v1/custody/manifest.json" = 1 +"auths.frozen-bytes/product/fixtures/v1/errors/manifest.json" = 4 +"auths.frozen-bytes/product/fixtures/v1/github/manifest.json" = 2 +"auths.frozen-bytes/product/fixtures/v1/kubernetes/manifest.json" = 1 +"auths.frozen-bytes/product/fixtures/v1/lifecycle/manifest.json" = 2 +"auths.frozen-bytes/product/fixtures/v1/opentofu/manifest.json" = 2 +"auths.frozen-bytes/product/fixtures/v1/postgresql/manifest.json" = 1 +"auths.frozen-bytes/product/fixtures/v1/production-client/manifest.json" = 1 +"auths.frozen-bytes/product/fixtures/v1/radicle/manifest.json" = 1 +"auths.frozen-bytes/product/fixtures/v1/records-api/manifest.json" = 1 +"auths.frozen-bytes/product/fixtures/v1/stripe/manifest.json" = 1 +"auths.frozen-bytes/product/integrations/auths-stripe/fixtures/connect-transfer/v1/manifest.sha256.json" = 1 +"auths.frozen-bytes/product/integrations/auths-stripe/fixtures/merchant-authorize/v1/manifest.sha256.json" = 1 +"auths.frozen-bytes/product/integrations/auths-stripe/fixtures/merchant-cancel/v1/manifest.sha256.json" = 1 +"auths.frozen-bytes/product/integrations/auths-stripe/fixtures/merchant-capture/v1/manifest.sha256.json" = 1 +"auths.frozen-bytes/product/integrations/auths-stripe/fixtures/merchant-collect/v1/manifest.sha256.json" = 1 +"auths.frozen-bytes/product/integrations/auths-stripe/fixtures/payment-mandate/v1/manifest.sha256.json" = 1 +"auths.frozen-bytes/product/integrations/auths-stripe/fixtures/payout/v1/manifest.sha256.json" = 1 +"auths.frozen-bytes/product/integrations/auths-stripe/fixtures/purchase-authorization/v1/manifest.sha256.json" = 1 +"auths.frozen-bytes/product/integrations/auths-stripe/fixtures/subscription-cancel/v1/manifest.sha256.json" = 1 +"auths.frozen-bytes/product/integrations/auths-stripe/fixtures/subscription-create/v1/manifest.sha256.json" = 1 +"auths.frozen-bytes/product/integrations/auths-stripe/fixtures/subscription-modify/v1/manifest.sha256.json" = 1 +"auths.frozen-bytes/product/integrations/auths-stripe/fixtures/v1/manifest.sha256.json" = 1 +"auths.identity.protocol" = 30 +"auths.modular-components" = 8 +"auths.portable-abi-bindings" = 54 +"auths.product.bounded-domains" = 7 +"auths.product.bounded-policy" = 2 +"auths.product.configuration-commitments" = 1 +"auths.product.development-composition" = 9 +"auths.product.error-recovery-contract" = 11 +"auths.product.external-custody" = 3 +"auths.product.facade" = 11 +"auths.product.lifecycle" = 9 +"auths.product.mcp-closed-execution" = 14 +"auths.product.mechanism-profile-conformance" = 5 +"auths.product.open-production-contract" = 13 +"auths.product.operations" = 5 +"auths.product.public-sdk-contract" = 43 +"auths.product.receipts" = 5 +"auths.product.release-assurance" = 4 +"auths.product.simplified-waist" = 8 +"auths.product.vocabulary" = 9 +"auths.release.benchmark-contract" = 1 +"auths.release.evolution-contract" = 16 +"auths.release.public-surface" = 125 diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index a4117111..1b165948 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 125, + "freezeVersion": 126, "publicSurface": { "rustRoots": [ "auths", @@ -199,7 +199,7 @@ }, { "id": "auths.frozen-bytes/formal/assurance-manifest-v1.toml", - "version": 12, + "version": 13, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -207,7 +207,7 @@ "owners": [ "formal/assurance-manifest-v1.toml" ], - "sha256": "a91cabdddc257c47ad4a2fbf6ad1c061ea003acd29b18b356cf32a8d62b4ba9f" + "sha256": "2b245df5c1714e7bd9fa080fb890826834d6d596813b6f731fcf122f473caccc" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/generated", @@ -235,7 +235,7 @@ }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/source-closure.json", - "version": 19, + "version": 20, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -243,7 +243,7 @@ "owners": [ "formal/qualification/aeneas/source-closure.json" ], - "sha256": "28f7bced1a3be1355aa45f4b42cc067fde322efc5303b23a15b31df4b8d8ccd8" + "sha256": "c821e9ef66973ef33026d2f00e03eb859abb6d1f24fabc0af13f41475a6dab8a" }, { "id": "auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json", @@ -620,7 +620,7 @@ }, { "id": "auths.portable-abi-bindings", - "version": 53, + "version": 54, "classification": "frozen-meaning", "categories": [ "portable-abi", @@ -637,7 +637,7 @@ "core/crates/auths-model/src/lib.rs", "core/spec/v1/auths-proof.cddl" ], - "sha256": "141beaea9021042cd5d18bc0f0d9fa6d169c5e7b1ea77d674c49d7ed6216d6a5" + "sha256": "328d997a6b77e8d0412255caa0f9915f895be9b69cb4f768d476eda80ae291f9" }, { "id": "auths.product.bounded-domains", @@ -695,7 +695,7 @@ }, { "id": "auths.product.development-composition", - "version": 8, + "version": 9, "classification": "frozen-meaning", "categories": [ "explicit-development-mode", @@ -710,7 +710,7 @@ "bindings/typescript/src/internal/development-store-node.ts", "bindings/typescript/src/internal/development.ts" ], - "sha256": "1f7128df9ee76f5985b4c569a60f3728928ed461e710481279f2e1e2486114e8" + "sha256": "fc5ff27a96b723f3f8aea1d9cac23cc3b5a583c85f905e0f293e692ac17da3b0" }, { "id": "auths.product.error-recovery-contract", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 124, + "version": 125, "classification": "release-metadata", "categories": [ "package-names", @@ -1119,6 +1119,7 @@ "release/release-manifest.contract-fixture.json", "release/release-manifest.schema.json", "release/release-subjects.toml", + "release/semantic-freeze-versions.toml", "release/slsa-build-level-3-assessment.json", "rust-toolchain.toml", "xtask/src/architecture.rs", @@ -1132,7 +1133,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "960661072f7be01364a738a1062ad833e16682d7bf58978a5016c4a6c90ff78e" + "sha256": "2e81f3c9f513828fb44c59a4e241bfaf24c3f5344c45491a68c2eda6e04937fd" } ] } diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index 5182209f..ccc92fa9 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -4,7 +4,7 @@ use crate::*; const INVENTORY_PATH: &str = "release/semantic-freeze.json"; const INVENTORY_SCHEMA: &str = "auths.semantic-freeze/1"; -const FREEZE_VERSION: u64 = 125; +const VERSIONS_PATH: &str = "release/semantic-freeze-versions.toml"; const PUBLIC_RUST_ROOTS: [&str; 10] = [ "auths", "auths-byte-channel", @@ -91,6 +91,45 @@ struct FreezeEntry { sha256: String, } +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct SemanticFreezeVersions { + freeze_version: u64, + entries: BTreeMap, +} + +impl SemanticFreezeVersions { + fn entry(&self, id: &str) -> Result { + self.entries + .get(id) + .copied() + .ok_or_else(|| format!("semantic identity has no reviewed version: {id}")) + } + + fn validate_exact_inventory(&self, entries: &[FreezeEntry]) -> Result<(), String> { + let generated = entries + .iter() + .map(|entry| entry.id.as_str()) + .collect::>(); + let configured = self + .entries + .keys() + .map(String::as_str) + .collect::>(); + if generated != configured { + return Err(set_drift( + "semantic identity version inventory", + &generated.into_iter().map(str::to_owned).collect(), + &configured.into_iter().map(str::to_owned).collect(), + )); + } + if self.freeze_version == 0 || self.entries.values().any(|version| *version == 0) { + return Err("semantic identity versions must be non-zero".to_owned()); + } + Ok(()) + } +} + #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)] #[serde(rename_all = "kebab-case")] enum FreezeClassification { @@ -161,13 +200,14 @@ pub(crate) fn semantic_freeze(update: bool) -> Result<(), String> { } fn generate_inventory() -> Result { + let versions = load_versions()?; let rust_surface = rust_surface()?; let bounded_domain_sources = bounded_domain_sources()?; let mut entries = vec![ freeze_entry( "auths.core.protocol", - 17, + versions.entry("auths.core.protocol")?, FreezeClassification::FrozenMeaning, &[ "protocol-versions", @@ -187,7 +227,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.identity.protocol", - 30, + versions.entry("auths.identity.protocol")?, FreezeClassification::FrozenMeaning, &[ "identity-protocol-versions", @@ -213,7 +253,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.modular-components", - 8, + versions.entry("auths.modular-components")?, FreezeClassification::FrozenMeaning, &[ "published-neutral-ports", @@ -246,7 +286,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.portable-abi-bindings", - 53, + versions.entry("auths.portable-abi-bindings")?, FreezeClassification::FrozenMeaning, &["portable-abi", "authoring-abi", "binding-contracts"], vec![ @@ -262,7 +302,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.public-sdk-contract", - 43, + versions.entry("auths.product.public-sdk-contract")?, FreezeClassification::FrozenMeaning, &[ "rust-sdk-contract", @@ -283,7 +323,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.mcp-closed-execution", - 14, + versions.entry("auths.product.mcp-closed-execution")?, FreezeClassification::FrozenMeaning, &[ "profile-session", @@ -306,7 +346,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.simplified-waist", - 8, + versions.entry("auths.product.simplified-waist")?, FreezeClassification::FrozenMeaning, &[ "product-waist-invariants", @@ -325,7 +365,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.facade", - 11, + versions.entry("auths.product.facade")?, FreezeClassification::FrozenMeaning, &[ "create", @@ -347,7 +387,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.development-composition", - 8, + versions.entry("auths.product.development-composition")?, FreezeClassification::FrozenMeaning, &[ "explicit-development-mode", @@ -365,7 +405,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.mechanism-profile-conformance", - 5, + versions.entry("auths.product.mechanism-profile-conformance")?, FreezeClassification::FrozenMeaning, &[ "contract-inventory", @@ -383,7 +423,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.vocabulary", - 9, + versions.entry("auths.product.vocabulary")?, FreezeClassification::FrozenMeaning, &[ "customer-vocabulary", @@ -404,7 +444,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.error-recovery-contract", - 11, + versions.entry("auths.product.error-recovery-contract")?, FreezeClassification::FrozenMeaning, &[ "error-envelope", @@ -427,7 +467,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.bounded-policy", - 2, + versions.entry("auths.product.bounded-policy")?, FreezeClassification::FrozenMeaning, &[ "policy-semantic-ids", @@ -441,7 +481,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.bounded-domains", - 7, + versions.entry("auths.product.bounded-domains")?, FreezeClassification::FrozenMeaning, &[ "bounded-domain-inventory", @@ -456,7 +496,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.lifecycle", - 9, + versions.entry("auths.product.lifecycle")?, FreezeClassification::FrozenMeaning, &[ "reservation-state", @@ -476,7 +516,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.receipts", - 5, + versions.entry("auths.product.receipts")?, FreezeClassification::FrozenMeaning, &["receipt-schemas", "receipt-commitment-meanings"], vec![ @@ -486,7 +526,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.configuration-commitments", - 1, + versions.entry("auths.product.configuration-commitments")?, FreezeClassification::FrozenMeaning, &[ "required-configuration-commitments", @@ -499,7 +539,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.open-production-contract", - 13, + versions.entry("auths.product.open-production-contract")?, FreezeClassification::FrozenMeaning, &[ "production-topology", @@ -525,7 +565,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.release-assurance", - 4, + versions.entry("auths.product.release-assurance")?, FreezeClassification::FrozenMeaning, &[ "immutable-candidate-binding", @@ -545,7 +585,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.external-custody", - 3, + versions.entry("auths.product.external-custody")?, FreezeClassification::FrozenMeaning, &[ "transaction-bound-signing", @@ -568,7 +608,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.product.operations", - 5, + versions.entry("auths.product.operations")?, FreezeClassification::FrozenMeaning, &[ "privacy-safe-events", @@ -589,7 +629,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.release.evolution-contract", - 16, + versions.entry("auths.release.evolution-contract")?, FreezeClassification::FrozenMeaning, &[ "version-axes", @@ -613,7 +653,7 @@ fn generate_inventory() -> Result { )?, freeze_entry( "auths.release.benchmark-contract", - 1, + versions.entry("auths.release.benchmark-contract")?, FreezeClassification::FrozenMeaning, &["benchmark-definition", "accepted-baseline"], vec![ @@ -628,22 +668,7 @@ fn generate_inventory() -> Result { ]; for (id, path) in frozen_byte_inventories()? { - let version = match path.as_str() { - "architecture/dependency-graph.json" => 29, - "bindings/wasm/auths-proof-wasm/identity-abi-v1.json" => 4, - "core/fixtures/v1/manifest.json" => 4, - "product/conformance/v1/simplified-product-waist.json" => 2, - "formal/assurance-manifest-v1.toml" => 12, - "formal/qualification/aeneas/qualification.toml" => 5, - "formal/qualification/aeneas/generated" => 6, - "formal/qualification/aeneas/source-closure.json" => 19, - "product/fixtures/v1/errors/manifest.json" => 4, - "product/fixtures/v1/bounded-policy/manifest.json" => 2, - "product/fixtures/v1/github/manifest.json" - | "product/fixtures/v1/opentofu/manifest.json" => 2, - "product/fixtures/v1/lifecycle/manifest.json" => 2, - _ => 1, - }; + let version = versions.entry(&id)?; entries.push(freeze_entry( &id, version, @@ -676,6 +701,7 @@ fn generate_inventory() -> Result { "release/SLSA_BUILD_LEVEL_3_ASSESSMENT.md".to_owned(), "release/slsa-build-level-3-assessment.json".to_owned(), "release/RELEASE_CANDIDATE_NOTES.md".to_owned(), + VERSIONS_PATH.to_owned(), "release/release-manifest.contract-fixture.json".to_owned(), "release/release-manifest.schema.json".to_owned(), "release/release-subjects.toml".to_owned(), @@ -696,7 +722,7 @@ fn generate_inventory() -> Result { ]); entries.push(freeze_entry( "auths.release.public-surface", - 124, + versions.entry("auths.release.public-surface")?, FreezeClassification::ReleaseMetadata, &[ "package-names", @@ -712,9 +738,10 @@ fn generate_inventory() -> Result { )?); entries.sort_by(|left, right| left.id.cmp(&right.id)); + versions.validate_exact_inventory(&entries)?; Ok(SemanticFreezeInventory { schema: INVENTORY_SCHEMA.to_owned(), - freeze_version: FREEZE_VERSION, + freeze_version: versions.freeze_version, public_surface: rust_surface.public, entries, }) @@ -1238,6 +1265,14 @@ fn load_inventory(path: &Path) -> Result { .map_err(|error| format!("invalid semantic freeze {}: {error}", path.display())) } +fn load_versions() -> Result { + let path = root().join(VERSIONS_PATH); + let bytes = fs::read_to_string(&path) + .map_err(|error| format!("could not read {}: {error}", path.display()))?; + toml::from_str(&bytes) + .map_err(|error| format!("invalid semantic versions {}: {error}", path.display())) +} + fn with_paths(base: [&str; N], additions: &[String]) -> Vec { base.into_iter() .map(str::to_owned) From 65e0a1089e76d45224619a234cd7020dc051f509 Mon Sep 17 00:00:00 2001 From: bordumb Date: Mon, 17 Aug 2026 00:12:16 +0100 Subject: [PATCH 53/61] fix: fail closed across clean-checkout CI --- core/testkit/auths-testkit/src/lib.rs | 2 +- formal/assurance-manifest-v1.toml | 6 +- .../qualification/aeneas/source-closure.json | 6 +- .../src/bin/auths-local-authority.rs | 4 + product/runtime/auths-node/src/kernel.rs | 110 +++++++++- .../runtime/auths-node/src/local_fixture.rs | 21 +- product/runtime/auths-node/src/main.rs | 12 +- .../auths-node/tests/kernel_differential.rs | 23 ++- .../tests/local_fixture_authorizes.rs | 36 ++-- release/semantic-freeze-versions.toml | 10 +- release/semantic-freeze.json | 18 +- xtask/src/formal.rs | 2 +- xtask/src/semantic_freeze.rs | 191 +++++++++++------- 13 files changed, 298 insertions(+), 143 deletions(-) diff --git a/core/testkit/auths-testkit/src/lib.rs b/core/testkit/auths-testkit/src/lib.rs index 0d1f105e..894da9c1 100644 --- a/core/testkit/auths-testkit/src/lib.rs +++ b/core/testkit/auths-testkit/src/lib.rs @@ -769,7 +769,7 @@ fn declaring_budget_free(source: &TrustedContext, budget_free: Vec) .with_budget_free_profiles(budget_free) .expect("budget-free declaration"), source.expected_audience().clone(), - source.expected_challenge().clone(), + source.expected_challenge(), source.evaluation_time(), source.assurance_policy().clone(), source.principal_status_snapshot().clone(), diff --git a/formal/assurance-manifest-v1.toml b/formal/assurance-manifest-v1.toml index e7426faa..53c1a05b 100644 --- a/formal/assurance-manifest-v1.toml +++ b/formal/assurance-manifest-v1.toml @@ -6503,7 +6503,7 @@ sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" [[claims.evidence]] kind = "source-closure" artifact = "formal/qualification/aeneas/source-closure.json" -sha256 = "27c411d24fc1050d88033015fe842c0ef0e1fb564b5a416c1563bb1f2445f74b" +sha256 = "e5eea784fbe91f4336e6d736152f30aebbb28fc567e217fee728117a2f5907cf" [[claims]] claim_id = "AP-FORMAL-RICH-055" @@ -6606,7 +6606,7 @@ sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" [[claims.evidence]] kind = "source-closure" artifact = "formal/qualification/aeneas/source-closure.json" -sha256 = "27c411d24fc1050d88033015fe842c0ef0e1fb564b5a416c1563bb1f2445f74b" +sha256 = "e5eea784fbe91f4336e6d736152f30aebbb28fc567e217fee728117a2f5907cf" [[claims]] claim_id = "AP-FORMAL-RICH-056" @@ -6714,7 +6714,7 @@ sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" [[claims.evidence]] kind = "source-closure" artifact = "formal/qualification/aeneas/source-closure.json" -sha256 = "27c411d24fc1050d88033015fe842c0ef0e1fb564b5a416c1563bb1f2445f74b" +sha256 = "e5eea784fbe91f4336e6d736152f30aebbb28fc567e217fee728117a2f5907cf" [[claims]] claim_id = "AP-FORMAL-RICH-104" diff --git a/formal/qualification/aeneas/source-closure.json b/formal/qualification/aeneas/source-closure.json index f6f2e911..4f7ba3af 100644 --- a/formal/qualification/aeneas/source-closure.json +++ b/formal/qualification/aeneas/source-closure.json @@ -1,6 +1,6 @@ { "schema": "auths-proof-translation-source-closure/v2", - "digest": "88f5df8b0ee0639aa9b6f01d5bd102b230e5474b1b0a7ea45d9899f1146ad497", + "digest": "f07cf64b7b6e4198a767335bf59773a35b9f37e40b750f6c5753a549fa3b320a", "files": [ { "path": ".cargo/config.toml", @@ -245,7 +245,7 @@ }, { "path": "xtask/src/formal.rs", - "sha256": "d113c3f263b4685000e355369eb883f4e068bf4d6b3b5983db699ef266a5e2ae" + "sha256": "4c00c10761c5e0e13699943d25b7e248cb465bd44a7612e9cb8e4702a46b6e1c" }, { "path": "xtask/src/formal_qualification.rs", @@ -309,7 +309,7 @@ }, { "path": "xtask/src/semantic_freeze.rs", - "sha256": "a0829ad71401a297a04a2dd4472c9c8a6fedb2a90b5b78b0b40ccd1b784de7dd" + "sha256": "41e62d0797b665bb7fd27d68ed7c5424d0f439fc1b73447e6854d9561f11d92b" }, { "path": "xtask/src/stripe.rs", diff --git a/product/runtime/auths-node/src/bin/auths-local-authority.rs b/product/runtime/auths-node/src/bin/auths-local-authority.rs index 293ae716..15aacb29 100644 --- a/product/runtime/auths-node/src/bin/auths-local-authority.rs +++ b/product/runtime/auths-node/src/bin/auths-local-authority.rs @@ -47,6 +47,10 @@ fn fail(message: &str) -> ExitCode { ExitCode::from(1) } +#[allow( + clippy::too_many_lines, + reason = "the local fixture is one auditable linear authoring transcript" +)] fn main() -> ExitCode { let mut arguments = env::args().skip(1); let (Some(profile), Some(action_path), Some(agent_label)) = diff --git a/product/runtime/auths-node/src/kernel.rs b/product/runtime/auths-node/src/kernel.rs index bfbc38df..e88322f3 100644 --- a/product/runtime/auths-node/src/kernel.rs +++ b/product/runtime/auths-node/src/kernel.rs @@ -18,7 +18,7 @@ use crate::{ MemorySandboxStore, PendingEffect, PostgresSandboxStore, SandboxStore, StoredReceipt, }, }; -use auths_model::{CanonicalAction, Timestamp, TrustedContext}; +use auths_model::{CanonicalAction, Timestamp, TrustedContext, VerifierConfigurationId}; use auths_operations::EffectState; use auths_ports::{PrincipalMethod, SignatureSuite}; use auths_production_client::{ @@ -87,12 +87,85 @@ pub struct NodeKernel { signature_suites: Vec>, } +struct BuiltInVerifierComponents { + principal_methods: Vec>, + signature_suites: Vec>, +} + +impl BuiltInVerifierComponents { + fn configuration_id(&self) -> Result { + executable_configuration_id(&self.principal_methods, &self.signature_suites) + } +} + +fn executable_configuration_id( + principal_methods: &[Box], + signature_suites: &[Box], +) -> Result { + let methods = principal_methods + .iter() + .map(|method| method.as_ref() as &dyn PrincipalMethod) + .collect::>(); + let suites = signature_suites + .iter() + .map(|suite| suite.as_ref() as &dyn SignatureSuite) + .collect::>(); + ImmutableRegistries::new(&methods, &suites) + .map(|registries| registries.configuration_id()) + .map_err(|_| RuntimeFailure::Malformed) +} + +fn built_in_verifier_components() -> Result { + Ok(BuiltInVerifierComponents { + principal_methods: vec![ + Box::new(auths_raw_key::RawKeyMethod::new().map_err(|_| RuntimeFailure::Unavailable)?), + Box::new(auths_did_key::DidKeyMethod::new().map_err(|_| RuntimeFailure::Unavailable)?), + Box::new( + auths_did_keri::DidKeriMethod::new().map_err(|_| RuntimeFailure::Unavailable)?, + ), + ], + signature_suites: vec![ + Box::new( + auths_signature::Ed25519Suite::new().map_err(|_| RuntimeFailure::Unavailable)?, + ), + Box::new( + auths_signature::P256Sha256Suite::new().map_err(|_| RuntimeFailure::Unavailable)?, + ), + ], + }) +} + impl NodeKernel { + /// Builds the node with the exact verifier registry shipped by the binary. + /// + /// The same component inventory computes the configuration commitment used + /// by local-fixture trusted contexts. Keeping construction and commitment + /// on one path prevents a context from authorizing under a smaller registry + /// than the deployed node actually executes. + /// + /// # Errors + /// + /// Returns unavailable if a built-in method or suite cannot initialize. + pub fn with_built_ins(context_template: TrustedContext) -> Result { + let components = built_in_verifier_components()?; + Self::new( + context_template, + components.principal_methods, + components.signature_suites, + ) + } + + pub(crate) fn built_in_configuration_id() -> Result { + built_in_verifier_components()?.configuration_id() + } + /// Builds the node's immutable verification inputs. /// /// # Errors /// - /// Returns malformed when either executable registry is empty. + /// Returns malformed when either executable registry is empty, contains a + /// duplicate exact identifier, or does not match the verifier + /// configuration committed by the trusted context. pub fn new( context_template: TrustedContext, principal_methods: Vec>, @@ -101,6 +174,11 @@ impl NodeKernel { if principal_methods.is_empty() || signature_suites.is_empty() { return Err(RuntimeFailure::Malformed); } + let executable_configuration = + executable_configuration_id(&principal_methods, &signature_suites)?; + if context_template.configuration() != executable_configuration { + return Err(RuntimeFailure::Malformed); + } Ok(Self { context_template, principal_methods, @@ -691,6 +769,34 @@ mod tests { .unwrap() } + #[test] + fn construction_rejects_registries_that_do_not_match_the_trusted_context() { + let context = crate::local_fixture::build_context(&[7; 32], 1_700_000_000, 3_600) + .expect("local trusted context"); + let result = NodeKernel::new( + context, + vec![Box::new(auths_raw_key::RawKeyMethod::new().unwrap())], + vec![Box::new(auths_signature::Ed25519Suite::new().unwrap())], + ); + assert!(matches!(result, Err(RuntimeFailure::Malformed))); + } + + #[test] + fn construction_rejects_duplicate_registry_identifiers() { + let context = crate::local_fixture::build_context(&[7; 32], 1_700_000_000, 3_600) + .expect("local trusted context"); + let mut components = built_in_verifier_components().expect("built-in registry"); + components + .principal_methods + .push(Box::new(auths_raw_key::RawKeyMethod::new().unwrap())); + let result = NodeKernel::new( + context, + components.principal_methods, + components.signature_suites, + ); + assert!(matches!(result, Err(RuntimeFailure::Malformed))); + } + fn execute(fixture: &CorpusFixture, identity: &[u8]) -> ProductionRequest { ProductionRequest::new( ProductVerb::Execute, diff --git a/product/runtime/auths-node/src/local_fixture.rs b/product/runtime/auths-node/src/local_fixture.rs index 0852acaf..a8604b27 100644 --- a/product/runtime/auths-node/src/local_fixture.rs +++ b/product/runtime/auths-node/src/local_fixture.rs @@ -20,11 +20,10 @@ use auths_model::{ ParticipantRole, Permission, PermissionSet, PrincipalId, PrincipalMethodId, PrincipalStatusSnapshot, ProfileId, ProfilePolicyId, ProfileRef, ResourceId, ResourceMatcherId, SignatureSuiteId, StatusPolicy, StatusSnapshotId, Timestamp, TrustAnchor, TrustAnchorId, - TrustedContext, ValidityWindow, VerifierConfigurationId, VerifierLimits, + TrustedContext, ValidityWindow, VerifierLimits, }; -use auths_ports::{PrincipalMethod, SignatureSuite}; -use auths_raw_key::{RAW_KEY_V1, RawKeyDescriptor, RawKeyMethod, RawKeyType}; -use auths_signature::{ED25519_V1, Ed25519Suite}; +use auths_raw_key::{RAW_KEY_V1, RawKeyDescriptor, RawKeyType}; +use auths_signature::ED25519_V1; use ed25519_dalek::SigningKey; use sha2::{Digest as _, Sha256}; @@ -111,17 +110,6 @@ pub fn anchor_principal(seed: &[u8; 32]) -> Result<(SigningKey, PrincipalId), Fi Ok((signing, principal)) } -fn verifier_configuration() -> Result { - let method = RawKeyMethod::new().map_err(|_| fail("raw-key method unavailable"))?; - let suite = Ed25519Suite::new().map_err(|_| fail("ed25519 suite unavailable"))?; - auths_registries::ImmutableRegistries::new( - &[&method as &dyn PrincipalMethod], - &[&suite as &dyn SignatureSuite], - ) - .map(|registries| registries.configuration_id()) - .map_err(|_| fail("the verifier configuration could not be computed")) -} - /// The permission, namespace, audience and assurance policy the trusted /// context accepts for one reference profile. /// @@ -327,7 +315,8 @@ pub fn build_context( ) .map_err(|_| fail("the fixture trust anchor is invalid"))?; TrustedContext::new( - verifier_configuration()?, + crate::NodeKernel::built_in_configuration_id() + .map_err(|_| fail("the verifier configuration could not be computed"))?, CompositionRequirement::new(None, 1, 1, 1) .map_err(|_| fail("the composition requirement is invalid"))?, vec![anchor], diff --git a/product/runtime/auths-node/src/main.rs b/product/runtime/auths-node/src/main.rs index b966f437..f800dc3c 100644 --- a/product/runtime/auths-node/src/main.rs +++ b/product/runtime/auths-node/src/main.rs @@ -3,7 +3,6 @@ use auths_node::{ KernelRuntime, NodeConfig, NodeKernel, NodeRuntime, PostgresSandboxStore, app, shutdown, }; -use auths_ports::{PrincipalMethod, SignatureSuite}; use base64ct::{Base64UrlUnpadded, Encoding as _}; use std::{ env, fs, @@ -40,16 +39,7 @@ fn kernel(config: &NodeConfig) -> Result> .map_err(|_| "the trusted context is unavailable")?; let context = auths_codec::decode_verifier_context(&bytes) .map_err(|_| "the trusted context is not canonical")?; - let methods: Vec> = vec![ - Box::new(auths_raw_key::RawKeyMethod::new()?), - Box::new(auths_did_key::DidKeyMethod::new()?), - Box::new(auths_did_keri::DidKeriMethod::new()?), - ]; - let suites: Vec> = vec![ - Box::new(auths_signature::Ed25519Suite::new()?), - Box::new(auths_signature::P256Sha256Suite::new()?), - ]; - Ok(NodeKernel::new(context, methods, suites)?) + Ok(NodeKernel::with_built_ins(context)?) } async fn run() -> Result<(), Box> { diff --git a/product/runtime/auths-node/tests/kernel_differential.rs b/product/runtime/auths-node/tests/kernel_differential.rs index d3fac75f..a22a9f89 100644 --- a/product/runtime/auths-node/tests/kernel_differential.rs +++ b/product/runtime/auths-node/tests/kernel_differential.rs @@ -185,11 +185,19 @@ fn reference_side_matches_the_normative_corpus() { } #[test] -fn node_and_kernel_agree_on_every_canonical_corpus_input() { +fn node_and_kernel_agree_on_every_startable_canonical_corpus_input() { let mut disagreements = Vec::new(); let mut compared = 0_usize; for fixture in auths_testkit::corpus() { let context = fixture_context(&fixture); + if context.configuration() != auths_testkit::corpus_configuration_id() { + assert_eq!( + fixture.name(), + "verifier-configuration-mismatch", + "only the deliberate startup mismatch may carry another configuration" + ); + continue; + } let action_bytes = fixture_action_bytes(&fixture); let kernel = kernel_decision(fixture.proof_bytes(), fixture.canonical_action(), &context); let node = node_decision(&context, fixture.proof_bytes(), &action_bytes); @@ -209,6 +217,19 @@ fn node_and_kernel_agree_on_every_canonical_corpus_input() { ); } +#[test] +fn a_mismatched_corpus_configuration_is_rejected_before_the_node_can_serve() { + let fixture = auths_testkit::corpus() + .into_iter() + .find(|fixture| fixture.name() == "verifier-configuration-mismatch") + .expect("configuration mismatch fixture"); + let context = fixture_context(&fixture); + assert!(matches!( + NodeKernel::new(context, methods(), suites()), + Err(RuntimeFailure::Malformed) + )); +} + #[test] fn dimension_coverage_is_complete() { let names: BTreeSet<&str> = auths_testkit::corpus() diff --git a/product/runtime/auths-node/tests/local_fixture_authorizes.rs b/product/runtime/auths-node/tests/local_fixture_authorizes.rs index bd7793eb..2cca5e29 100644 --- a/product/runtime/auths-node/tests/local_fixture_authorizes.rs +++ b/product/runtime/auths-node/tests/local_fixture_authorizes.rs @@ -11,12 +11,22 @@ use auths_node::local_fixture::build_context; use std::{ + collections::BTreeSet, process::Command, + sync::Arc, time::{SystemTime, UNIX_EPOCH}, }; const SEED_B64: &str = "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE"; +struct FixedClock(u64); + +impl auths_node::NodeClock for FixedClock { + fn now_unix_seconds(&self) -> u64 { + self.0 + } +} + fn authored(profile: &str, body: &str, agent: &str) -> (Vec, Vec) { use base64ct::{Base64UrlUnpadded, Encoding as _}; let action_path = std::env::temp_dir().join(format!("auths-fixture-{agent}.bin")); @@ -53,22 +63,20 @@ fn an_offline_authored_proof_is_authorized_by_the_generated_context() { .duration_since(UNIX_EPOCH) .map_or(0, |duration| duration.as_secs()); let context = build_context(&seed, now, 3_600).expect("trusted context"); + let kernel = auths_node::NodeKernel::with_built_ins(context) + .expect("the deployed verifier registry must initialize"); + let runtime = auths_node::KernelRuntime::with_clock( + kernel, + [0x55; 32], + BTreeSet::from([auths_production_client::QualifiedProfile::OpenTofuSavedPlanApply]), + Arc::new(FixedClock(now)), + ) + .expect("runtime"); for profile in auths_node::local_fixture::REFERENCE_PROFILES { let (proof, action_bytes) = authored(profile, "exact reference operation", "fixture-agent"); - let action = auths_codec::decode_canonical_action(&action_bytes, context.limits()) - .expect("the authored action is canonical"); - - let method = auths_raw_key::RawKeyMethod::new().expect("raw-key method"); - let suite = auths_signature::Ed25519Suite::new().expect("ed25519 suite"); - let methods: [&dyn auths_ports::PrincipalMethod; 1] = [&method]; - let suites: [&dyn auths_ports::SignatureSuite; 1] = [&suite]; - let registries = - auths_registries::ImmutableRegistries::new(&methods, &suites).expect("registries"); - - match auths_verifier::verify(&proof, &action, &context, ®istries) { - auths_verifier::VerificationOutcome::Authorized(_) => {} - other => panic!("{profile} was not authorized: {other:?}"), - } + runtime + .authorize(&proof, &action_bytes) + .unwrap_or_else(|error| panic!("{profile} was not authorized: {error:?}")); } } diff --git a/release/semantic-freeze-versions.toml b/release/semantic-freeze-versions.toml index 04e36484..81c9d1d0 100644 --- a/release/semantic-freeze-versions.toml +++ b/release/semantic-freeze-versions.toml @@ -1,4 +1,4 @@ -freeze_version = 126 +freeze_version = 129 # Semantic identity counters live outside the xtask source tree deliberately. # The formal source closure binds xtask's executable code, while the semantic @@ -16,10 +16,10 @@ freeze_version = 126 "auths.frozen-bytes/core/fixtures/v1/manifest.json" = 4 "auths.frozen-bytes/core/formal-vectors/v1/manifest.json" = 1 "auths.frozen-bytes/demos/benchmarks/profiles/release.toml" = 1 -"auths.frozen-bytes/formal/assurance-manifest-v1.toml" = 13 +"auths.frozen-bytes/formal/assurance-manifest-v1.toml" = 15 "auths.frozen-bytes/formal/qualification/aeneas/generated" = 6 "auths.frozen-bytes/formal/qualification/aeneas/qualification.toml" = 5 -"auths.frozen-bytes/formal/qualification/aeneas/source-closure.json" = 20 +"auths.frozen-bytes/formal/qualification/aeneas/source-closure.json" = 22 "auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json" = 1 "auths.frozen-bytes/product/conformance/v1/simplified-product-waist.json" = 2 "auths.frozen-bytes/product/fixtures/v1/bounded-policy/manifest.json" = 2 @@ -56,7 +56,7 @@ freeze_version = 126 "auths.product.error-recovery-contract" = 11 "auths.product.external-custody" = 3 "auths.product.facade" = 11 -"auths.product.lifecycle" = 9 +"auths.product.lifecycle" = 10 "auths.product.mcp-closed-execution" = 14 "auths.product.mechanism-profile-conformance" = 5 "auths.product.open-production-contract" = 13 @@ -68,4 +68,4 @@ freeze_version = 126 "auths.product.vocabulary" = 9 "auths.release.benchmark-contract" = 1 "auths.release.evolution-contract" = 16 -"auths.release.public-surface" = 125 +"auths.release.public-surface" = 128 diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index 1b165948..d22bb152 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 126, + "freezeVersion": 129, "publicSurface": { "rustRoots": [ "auths", @@ -199,7 +199,7 @@ }, { "id": "auths.frozen-bytes/formal/assurance-manifest-v1.toml", - "version": 13, + "version": 15, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -207,7 +207,7 @@ "owners": [ "formal/assurance-manifest-v1.toml" ], - "sha256": "2b245df5c1714e7bd9fa080fb890826834d6d596813b6f731fcf122f473caccc" + "sha256": "d0d78a01ee17953dbb173529525001b05a89c4d3c9344f512b516bb560818b9c" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/generated", @@ -235,7 +235,7 @@ }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/source-closure.json", - "version": 20, + "version": 22, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -243,7 +243,7 @@ "owners": [ "formal/qualification/aeneas/source-closure.json" ], - "sha256": "c821e9ef66973ef33026d2f00e03eb859abb6d1f24fabc0af13f41475a6dab8a" + "sha256": "3789288422a62464bdfef7c2ded3152ea7ce1e476cb60a343b88c11188e98548" }, { "id": "auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json", @@ -785,7 +785,7 @@ }, { "id": "auths.product.lifecycle", - "version": 9, + "version": 10, "classification": "frozen-meaning", "categories": [ "reservation-state", @@ -802,7 +802,7 @@ "product/stores/auths-stores/tests/postgres_lifecycle.rs", "product/stores/auths-stores/tests/postgres_tls" ], - "sha256": "3f1e0738e7007f2264dcd0eb76ba92621cc3f958cc48850305709c75c6541267" + "sha256": "2bf64aa4f1b3a0b2dc9b7d7f9fdca91f69f226bda7e50e6a8bc02875452dce36" }, { "id": "auths.product.mcp-closed-execution", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 125, + "version": 128, "classification": "release-metadata", "categories": [ "package-names", @@ -1133,7 +1133,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "2e81f3c9f513828fb44c59a4e241bfaf24c3f5344c45491a68c2eda6e04937fd" + "sha256": "9017b69c34d3c9638838cf092dfc26ca5d8d10ca0f14cd23754f6994b62c5c1d" } ] } diff --git a/xtask/src/formal.rs b/xtask/src/formal.rs index 5a30aafc..d6c59f96 100644 --- a/xtask/src/formal.rs +++ b/xtask/src/formal.rs @@ -567,7 +567,7 @@ fn validate_mutation_matrix_source( source: &str, compiled: &BTreeMap, ) -> Result<(), String> { - let matrix: MutationMatrix = serde_json::from_str(&source) + let matrix: MutationMatrix = serde_json::from_str(source) .map_err(|error| format!("invalid mutation matrix: {error}"))?; if matrix.schema != "auths-proof-semantic-mutations/v1" { return Err(format!( diff --git a/xtask/src/semantic_freeze.rs b/xtask/src/semantic_freeze.rs index ccc92fa9..570796ea 100644 --- a/xtask/src/semantic_freeze.rs +++ b/xtask/src/semantic_freeze.rs @@ -988,12 +988,11 @@ fn selected_files( )); } let mut selected = Vec::new(); - visit_files(directory, &mut |path| { - if predicate(path) { - selected.push(repository_relative(path)?); + for path in tracked_files_under(directory)? { + if predicate(&path) { + selected.push(repository_relative(&path)?); } - Ok(()) - })?; + } selected.sort(); if selected.is_empty() { return Err(format!( @@ -1047,15 +1046,14 @@ fn digest_owners(owners: &[String]) -> Result { if metadata.is_file() { files.insert(owner.clone(), read_owned_file(&path)?); } else if metadata.is_dir() { - let before = files.len(); - visit_files(&path, &mut |file| { - let relative = repository_relative(file)?; - files.insert(relative, read_owned_file(file)?); - Ok(()) - })?; - if files.len() == before { + let tracked = tracked_files_under(&path)?; + if tracked.is_empty() { return Err(format!("semantic owner directory is empty: {owner}")); } + for file in tracked { + let relative = repository_relative(&file)?; + files.insert(relative, read_owned_file(&file)?); + } } else { return Err(format!( "semantic owner is not a file or directory: {owner}" @@ -1073,68 +1071,41 @@ fn digest_owners(owners: &[String]) -> Result { Ok(hex::encode(hasher.finalize())) } -fn visit_files( - directory: &Path, - visitor: &mut impl FnMut(&Path) -> Result<(), String>, -) -> Result<(), String> { - let mut entries = fs::read_dir(directory) - .map_err(|error| format!("could not read {}: {error}", directory.display()))? - .collect::, _>>() - .map_err(|error| format!("could not enumerate {}: {error}", directory.display()))?; - entries.sort_by_key(std::fs::DirEntry::file_name); - for entry in entries { - let path = entry.path(); +fn tracked_files_under(directory: &Path) -> Result, String> { + tracked_files_under_in(&root(), directory) +} + +fn tracked_files_under_in(repository: &Path, directory: &Path) -> Result, String> { + let relative = repository_relative_to(repository, directory)?; + let output = Command::new("git") + .args(["ls-files", "--cached", "-z", "--", &relative]) + .current_dir(repository) + .output() + .map_err(|error| format!("could not inspect tracked semantic owners: {error}"))?; + if !output.status.success() { + return Err("git ls-files failed while freezing semantic owners".to_owned()); + } + let mut files = Vec::new(); + for encoded in output.stdout.split(|byte| *byte == 0) { + if encoded.is_empty() { + continue; + } + let tracked = std::str::from_utf8(encoded) + .map_err(|_| "tracked semantic owner path is not UTF-8".to_owned())?; + validate_relative_path(tracked)?; + let path = repository.join(tracked); let metadata = fs::symlink_metadata(&path) - .map_err(|error| format!("could not inspect {}: {error}", path.display()))?; - if metadata.file_type().is_symlink() { + .map_err(|error| format!("tracked semantic owner is absent {tracked}: {error}"))?; + if metadata.file_type().is_symlink() || !metadata.is_file() { return Err(format!( - "semantic owner trees must not contain symlinks: {}", - path.display() + "tracked semantic owner must be a regular non-symlink file: {tracked}" )); } - if metadata.is_dir() { - if generated_owner_directory(&path) { - continue; - } - visit_files(&path, visitor)?; - } else if metadata.is_file() { - if generated_owner_file(&path) { - continue; - } - visitor(&path)?; - } - } - Ok(()) -} - -fn generated_owner_directory(path: &Path) -> bool { - matches!( - path.file_name().and_then(|name| name.to_str()), - Some( - ".git" - | ".lake" - | ".mypy_cache" - | ".pytest_cache" - | ".ruff_cache" - | ".venv" - | "__pycache__" - | "node_modules" - | "target" - ) - ) -} - -fn generated_owner_file(path: &Path) -> bool { - if matches!( - path.file_name().and_then(|name| name.to_str()), - Some(".DS_Store" | ".coverage") - ) { - return true; + files.push(path); } - matches!( - path.extension().and_then(|extension| extension.to_str()), - Some("dll" | "dylib" | "pyc" | "pyd" | "pyo" | "so") - ) + files.sort(); + files.dedup(); + Ok(files) } fn read_owned_file(path: &Path) -> Result, String> { @@ -1147,8 +1118,12 @@ fn hash_field(hasher: &mut Sha256, bytes: &[u8]) { } fn repository_relative(path: &Path) -> Result { + repository_relative_to(&root(), path) +} + +fn repository_relative_to(repository: &Path, path: &Path) -> Result { let relative = path - .strip_prefix(root()) + .strip_prefix(repository) .map_err(|_| format!("path escapes repository: {}", path.display()))?; let text = relative.to_string_lossy().replace('\\', "/"); validate_relative_path(&text)?; @@ -1398,12 +1373,74 @@ mod tests { } #[test] - fn generated_owner_artifacts_are_excluded() { - assert!(generated_owner_directory(Path::new("auths/__pycache__"))); - assert!(generated_owner_directory(Path::new("auths/node_modules"))); - assert!(generated_owner_file(Path::new("auths/module.pyc"))); - assert!(generated_owner_file(Path::new("auths/_native.abi3.so"))); - assert!(!generated_owner_directory(Path::new("auths/profiles"))); - assert!(!generated_owner_file(Path::new("auths/verify.py"))); + fn tracked_inventory_is_exact_and_fails_closed() { + fn git(repository: &Path, arguments: &[&str]) { + let status = Command::new("git") + .args(arguments) + .current_dir(repository) + .status() + .expect("git command starts"); + assert!(status.success(), "git {arguments:?} failed"); + } + + fn tracked_digest(repository: &Path, directory: &Path) -> String { + let mut hasher = Sha256::new(); + for path in + tracked_files_under_in(repository, directory).expect("tracked semantic inventory") + { + let relative = repository_relative_to(repository, &path) + .expect("repository-relative tracked path"); + hash_field(&mut hasher, relative.as_bytes()); + hash_field( + &mut hasher, + &fs::read(path).expect("read tracked semantic owner"), + ); + } + hex::encode(hasher.finalize()) + } + + let repository = tempfile::tempdir().expect("temporary repository"); + let repository = repository.path(); + let owner = repository.join("owner"); + fs::create_dir_all(owner.join("generated")).expect("semantic owner directories"); + fs::write( + repository.join(".gitignore"), + "owner/generated/\nowner/untracked.txt\n", + ) + .expect("ignore rules"); + fs::write(owner.join("tracked.txt"), b"tracked").expect("tracked owner"); + git(repository, &["init", "--quiet"]); + git(repository, &["add", ".gitignore", "owner/tracked.txt"]); + + let baseline = tracked_digest(repository, &owner); + let tracked = tracked_files_under_in(repository, &owner).expect("tracked inventory"); + assert_eq!(tracked, vec![owner.join("tracked.txt")]); + + fs::write(owner.join("generated/ignored.txt"), b"ignored").expect("ignored owner"); + fs::write(owner.join("untracked.txt"), b"untracked").expect("untracked owner"); + assert_eq!(tracked_digest(repository, &owner), baseline); + + fs::write(owner.join("staged.txt"), b"staged").expect("staged owner"); + git(repository, &["add", "owner/staged.txt"]); + assert_ne!(tracked_digest(repository, &owner), baseline); + git( + repository, + &["rm", "--cached", "--quiet", "owner/staged.txt"], + ); + assert_eq!(tracked_digest(repository, &owner), baseline); + + fs::write(owner.join("missing.txt"), b"missing").expect("missing owner"); + git(repository, &["add", "owner/missing.txt"]); + fs::remove_file(owner.join("missing.txt")).expect("remove tracked owner"); + let error = tracked_files_under_in(repository, &owner) + .expect_err("a tracked-but-missing owner must fail closed"); + assert!(error.contains("tracked semantic owner is absent")); + + let not_a_repository = tempfile::tempdir().expect("non-repository directory"); + let untracked_owner = not_a_repository.path().join("owner"); + fs::create_dir(&untracked_owner).expect("non-repository owner"); + let error = tracked_files_under_in(not_a_repository.path(), &untracked_owner) + .expect_err("git inventory failure must fail closed"); + assert!(error.contains("git ls-files failed")); } } From fabeb557198c56ee80c72758b9dc51049dd93068 Mon Sep 17 00:00:00 2001 From: bordumb Date: Mon, 17 Aug 2026 00:29:35 +0100 Subject: [PATCH 54/61] fix: verify signed receipts across replicas --- docs/product/PRODUCTION_SDK_QUICKSTART.md | 5 +- product/runtime/auths-node/src/api.rs | 48 +++- product/runtime/auths-node/src/kernel.rs | 274 +++++++++++++++++++-- product/runtime/auths-node/src/profiles.rs | 38 ++- 4 files changed, 333 insertions(+), 32 deletions(-) diff --git a/docs/product/PRODUCTION_SDK_QUICKSTART.md b/docs/product/PRODUCTION_SDK_QUICKSTART.md index d707b1e9..af3782eb 100644 --- a/docs/product/PRODUCTION_SDK_QUICKSTART.md +++ b/docs/product/PRODUCTION_SDK_QUICKSTART.md @@ -73,8 +73,9 @@ provider errors or credential material. - `denied`: the request definitely lacks authority and must not be retried; - `indeterminate`: the runtime could not safely decide; use its retry class; - `recoverable`: use only the returned opaque reference with `resume`; -- `verified`: the supplied authority or receipt is valid under the runtime's - trusted context; and +- `verified`: the supplied authority satisfies the runtime's trusted context, + or the receipt is canonical and authentic under that runtime's receipt key; + and - `rejected`: verification definitely failed. See [production failures and recovery](recipes/06_PRODUCTION_FAILURES.md) for diff --git a/product/runtime/auths-node/src/api.rs b/product/runtime/auths-node/src/api.rs index c4314bd3..35814db2 100644 --- a/product/runtime/auths-node/src/api.rs +++ b/product/runtime/auths-node/src/api.rs @@ -256,12 +256,12 @@ async fn production_call( ) -> Response { let started = Instant::now(); if !state.accepting.load(Ordering::Acquire) { - let response = failure_response(RuntimeFailure::Unavailable); + let response = failure_response(expected_verb, RuntimeFailure::Unavailable); record_operation(state, expected_verb, &response, started); return encoded_response(&response); } if !content_type_is_exact(headers, PRODUCTION_CLIENT_CONTENT_TYPE) { - let response = failure_response(RuntimeFailure::Malformed); + let response = failure_response(expected_verb, RuntimeFailure::Malformed); record_operation(state, expected_verb, &response, started); return encoded_response(&response); } @@ -273,7 +273,7 @@ async fn production_call( request } _ => { - let response = failure_response(RuntimeFailure::Malformed); + let response = failure_response(expected_verb, RuntimeFailure::Malformed); record_operation(state, expected_verb, &response, started); return encoded_response(&response); } @@ -281,7 +281,7 @@ async fn production_call( let runtime = Arc::clone(&state.runtime); let response = call_runtime(move || runtime.handle(request)) .await - .unwrap_or_else(failure_response); + .unwrap_or_else(|error| failure_response(expected_verb, error)); record_operation(state, expected_verb, &response, started); encoded_response(&response) } @@ -586,6 +586,46 @@ sandbox_providers = true ); } + #[tokio::test] + async fn verify_route_parse_failures_are_rejected_not_denied() { + let router = app( + &config(), + Arc::new(Runtime), + Arc::new(AtomicBool::new(true)), + ); + let missing_content_type = router + .clone() + .oneshot( + axum::http::Request::builder() + .method("POST") + .uri("/v1/authority/verify") + .body(axum::body::Body::from(vec![0x80])) + .unwrap(), + ) + .await + .unwrap(); + let malformed_cbor = router + .oneshot( + axum::http::Request::builder() + .method("POST") + .uri("/v1/authority/verify") + .header(header::CONTENT_TYPE, PRODUCTION_CLIENT_CONTENT_TYPE) + .body(axum::body::Body::from(vec![0xff])) + .unwrap(), + ) + .await + .unwrap(); + for response in [missing_content_type, malformed_cbor] { + let bytes = axum::body::to_bytes(response.into_body(), 1024) + .await + .unwrap(); + assert_eq!( + decode_response(&bytes).unwrap().kind(), + ClientOutcomeKind::Rejected + ); + } + } + #[tokio::test] async fn readiness_drains_before_liveness() { let accepting = Arc::new(AtomicBool::new(false)); diff --git a/product/runtime/auths-node/src/kernel.rs b/product/runtime/auths-node/src/kernel.rs index e88322f3..d2a09b08 100644 --- a/product/runtime/auths-node/src/kernel.rs +++ b/product/runtime/auths-node/src/kernel.rs @@ -18,7 +18,9 @@ use crate::{ MemorySandboxStore, PendingEffect, PostgresSandboxStore, SandboxStore, StoredReceipt, }, }; -use auths_model::{CanonicalAction, Timestamp, TrustedContext, VerifierConfigurationId}; +use auths_model::{ + CanonicalAction, DenialReason, Timestamp, TrustedContext, VerifierConfigurationId, +}; use auths_operations::EffectState; use auths_ports::{PrincipalMethod, SignatureSuite}; use auths_production_client::{ @@ -28,8 +30,8 @@ use auths_production_client::{ use auths_registries::ImmutableRegistries; use auths_verifier::{VerificationFailure, VerificationOutcome, VerifiedAction}; use base64ct::{Base64UrlUnpadded, Encoding as _}; -use ed25519_dalek::{Signer as _, SigningKey}; -use minicbor::Encoder; +use ed25519_dalek::{Signature, Signer as _, SigningKey}; +use minicbor::{Decoder, Encoder}; use sha2::{Digest as _, Sha256}; use std::{ collections::BTreeSet, @@ -503,6 +505,94 @@ impl KernelRuntime { .map_err(runtime_failure) } + /// Verifies one canonical receipt emitted by any replica holding this + /// deployment's receipt key. + /// + /// The client contract accepts authorities and receipts at the same + /// effect-free endpoint. Receipt verification is cryptographic rather than + /// store-backed so a receipt created by one replica remains verifiable by + /// every other replica. Re-encoding both CBOR layers rejects alternate + /// encodings before the signature is trusted. + fn verify_receipt( + &self, + receipt: &[u8], + expected_profile: QualifiedProfile, + ) -> Result<(), RuntimeFailure> { + let mut envelope = Decoder::new(receipt); + if envelope.array().map_err(|_| RuntimeFailure::Malformed)? != Some(3) + || envelope.u16().map_err(|_| RuntimeFailure::Malformed)? != 1 + { + return Err(RuntimeFailure::Malformed); + } + let payload = envelope.bytes().map_err(|_| RuntimeFailure::Malformed)?; + let signature: [u8; 64] = envelope + .bytes() + .map_err(|_| RuntimeFailure::Malformed)? + .try_into() + .map_err(|_| RuntimeFailure::Malformed)?; + if envelope.position() != receipt.len() + || encode_envelope(payload, &signature)?.as_slice() != receipt + { + return Err(RuntimeFailure::Malformed); + } + + let mut decoded = Decoder::new(payload); + if decoded.array().map_err(|_| RuntimeFailure::Malformed)? != Some(6) + || decoded.u16().map_err(|_| RuntimeFailure::Malformed)? != 1 + { + return Err(RuntimeFailure::Malformed); + } + let profile = + QualifiedProfile::parse(decoded.str().map_err(|_| RuntimeFailure::Malformed)?) + .map_err(|_| RuntimeFailure::Malformed)?; + if profile != expected_profile { + return Err(RuntimeFailure::Malformed); + } + let claim: [u8; 32] = decoded + .bytes() + .map_err(|_| RuntimeFailure::Malformed)? + .try_into() + .map_err(|_| RuntimeFailure::Malformed)?; + let action: [u8; 32] = decoded + .bytes() + .map_err(|_| RuntimeFailure::Malformed)? + .try_into() + .map_err(|_| RuntimeFailure::Malformed)?; + let result: [u8; 32] = decoded + .bytes() + .map_err(|_| RuntimeFailure::Malformed)? + .try_into() + .map_err(|_| RuntimeFailure::Malformed)?; + let completed_at = decoded.u64().map_err(|_| RuntimeFailure::Malformed)?; + if decoded.position() != payload.len() + || encode_receipt_payload(profile, claim, action, result, completed_at)?.as_slice() + != payload + { + return Err(RuntimeFailure::Malformed); + } + + self.signing + .verifying_key() + .verify_strict( + &preimage(RECEIPT_DOMAIN, payload), + &Signature::from_bytes(&signature), + ) + .map_err(|_| RuntimeFailure::Malformed) + } + + fn verify_material( + &self, + material: &[u8], + expected_profile: QualifiedProfile, + ) -> Result<(), RuntimeFailure> { + match self.verify_proof(material) { + Err(RuntimeFailure::AuthorizationDenied(DenialReason::MalformedProof)) => { + self.verify_receipt(material, expected_profile) + } + result => result, + } + } + fn recovery_reference(claim: [u8; 32]) -> Result { let mut nonce = [0; 32]; getrandom::fill(&mut nonce).map_err(|_| RuntimeFailure::Unavailable)?; @@ -546,15 +636,29 @@ impl NodeRuntime for KernelRuntime { ProductVerb::Execute => self.execute(&request), ProductVerb::Resume => self.resume(&request), ProductVerb::Verify => { - self.verify_proof(request.body().ok_or(RuntimeFailure::Malformed)?)?; - ProductionResponse::new( - ClientOutcomeKind::Verified, - None, - NextCall::Never, - None, - None, - None, - ) + let material = request.body().ok_or(RuntimeFailure::Malformed)?; + let verification = self + .require_profile(request.profile()) + .and_then(|()| self.verify_material(material, request.profile())); + match verification { + Ok(()) => ProductionResponse::new( + ClientOutcomeKind::Verified, + None, + NextCall::Never, + None, + None, + None, + ), + Err(error) if error.retry() == NextCall::Never => ProductionResponse::new( + ClientOutcomeKind::Rejected, + Some(error.code().to_owned()), + NextCall::Never, + None, + None, + None, + ), + Err(error) => return Err(error), + } .map_err(|_| RuntimeFailure::Malformed) } } @@ -757,18 +861,30 @@ mod tests { .unwrap_or_else(|| panic!("corpus fixture {name}")) } - fn runtime_for(fixture: &CorpusFixture) -> KernelRuntime { + fn runtime_for_with( + fixture: &CorpusFixture, + seed: [u8; 32], + profiles: BTreeSet, + ) -> KernelRuntime { let context = auths_codec::decode_verifier_context(fixture.context_bytes()).unwrap(); let evaluation_time = context.evaluation_time().get(); KernelRuntime::with_clock( NodeKernel::new(context, corpus_methods(), corpus_suites()).unwrap(), - [7; 32], - [QualifiedProfile::GitHubIssueAddress].into_iter().collect(), + seed, + profiles, Arc::new(FrozenClock(evaluation_time)), ) .unwrap() } + fn runtime_for(fixture: &CorpusFixture) -> KernelRuntime { + runtime_for_with( + fixture, + [7; 32], + [QualifiedProfile::GitHubIssueAddress].into_iter().collect(), + ) + } + #[test] fn construction_rejects_registries_that_do_not_match_the_trusted_context() { let context = crate::local_fixture::build_context(&[7; 32], 1_700_000_000, 3_600) @@ -1009,11 +1125,133 @@ mod tests { ClientOutcomeKind::Verified ); let malformed = fixture("trailing-bytes"); + let rejected = runtime + .handle(request(malformed.proof_bytes().to_vec())) + .expect("verification rejection is a bounded response"); + assert_eq!(rejected.kind(), ClientOutcomeKind::Rejected); + assert_eq!(rejected.code(), Some(RuntimeFailure::Malformed.code())); + } + + fn issued_receipt(fixture: &CorpusFixture) -> Vec { + runtime_for(fixture) + .handle(execute(fixture, b"caller")) + .expect("authorized effect") + .receipt() + .expect("signed receipt") + .to_vec() + } + + fn verification_request(profile: QualifiedProfile, body: Vec) -> ProductionRequest { + ProductionRequest::new( + ProductVerb::Verify, + profile, + b"caller".to_vec(), + None, + Some(body), + None, + ) + .unwrap() + } + + #[test] + fn every_replica_can_verify_a_canonical_receipt_and_reject_tampering() { + let authorized = fixture("raw-key-chain"); + let receipt = issued_receipt(&authorized); + let verifying = runtime_for(&authorized); assert_eq!( - runtime.handle(request(malformed.proof_bytes().to_vec())), - Err(RuntimeFailure::AuthorizationDenied( - auths_model::DenialReason::MalformedProof + verifying + .handle(verification_request( + QualifiedProfile::GitHubIssueAddress, + receipt.clone(), + )) + .unwrap() + .kind(), + ClientOutcomeKind::Verified + ); + + let wrong_key = runtime_for_with( + &authorized, + [8; 32], + [QualifiedProfile::GitHubIssueAddress].into_iter().collect(), + ); + assert_eq!( + wrong_key + .handle(verification_request( + QualifiedProfile::GitHubIssueAddress, + receipt.clone(), + )) + .unwrap() + .kind(), + ClientOutcomeKind::Rejected, + "another deployment's receipt key was trusted" + ); + + let mut tampered = receipt; + let last = tampered.last_mut().expect("receipt byte"); + *last ^= 1; + let rejected = verifying + .handle(verification_request( + QualifiedProfile::GitHubIssueAddress, + tampered, )) + .unwrap(); + assert_eq!(rejected.kind(), ClientOutcomeKind::Rejected); + assert_eq!(rejected.retry(), NextCall::Never); + } + + #[test] + fn a_still_signed_but_noncanonical_receipt_envelope_is_rejected() { + let authorized = fixture("raw-key-chain"); + let receipt = issued_receipt(&authorized); + let mut noncanonical = Vec::with_capacity(receipt.len() + 1); + assert_eq!(&receipt[..2], &[0x83, 0x01]); + noncanonical.extend_from_slice(&[0x83, 0x18, 0x01]); + noncanonical.extend_from_slice(&receipt[2..]); + + let response = runtime_for(&authorized) + .handle(verification_request( + QualifiedProfile::GitHubIssueAddress, + noncanonical, + )) + .unwrap(); + assert_eq!(response.kind(), ClientOutcomeKind::Rejected); + } + + #[test] + fn receipt_verification_requires_the_exact_enabled_profile() { + let authorized = fixture("raw-key-chain"); + let receipt = issued_receipt(&authorized); + let two_profiles = runtime_for_with( + &authorized, + [7; 32], + [ + QualifiedProfile::GitHubIssueAddress, + QualifiedProfile::OpenTofuSavedPlanApply, + ] + .into_iter() + .collect(), ); + let cross_profile = two_profiles + .handle(verification_request( + QualifiedProfile::OpenTofuSavedPlanApply, + receipt.clone(), + )) + .unwrap(); + assert_eq!(cross_profile.kind(), ClientOutcomeKind::Rejected); + + let disabled_profile = runtime_for_with( + &authorized, + [7; 32], + [QualifiedProfile::OpenTofuSavedPlanApply] + .into_iter() + .collect(), + ); + let disabled = disabled_profile + .handle(verification_request( + QualifiedProfile::GitHubIssueAddress, + receipt, + )) + .unwrap(); + assert_eq!(disabled.kind(), ClientOutcomeKind::Rejected); } } diff --git a/product/runtime/auths-node/src/profiles.rs b/product/runtime/auths-node/src/profiles.rs index 72e2166a..1224a342 100644 --- a/product/runtime/auths-node/src/profiles.rs +++ b/product/runtime/auths-node/src/profiles.rs @@ -351,24 +351,35 @@ fn completed_authority(value: Vec) -> Result ProductionResponse { +pub fn failure_response(verb: ProductVerb, error: RuntimeFailure) -> ProductionResponse { let kind = match error { + RuntimeFailure::AuthorizationIndeterminate(_) + | RuntimeFailure::StateConflict + | RuntimeFailure::Unavailable + | RuntimeFailure::ProviderOutcomeUnknown => ClientOutcomeKind::Indeterminate, RuntimeFailure::AuthorizationDenied(_) | RuntimeFailure::UnauthenticatedPrincipal | RuntimeFailure::ReplayBudgetExhausted | RuntimeFailure::Malformed | RuntimeFailure::ProfileDisabled | RuntimeFailure::UnknownReference - | RuntimeFailure::DisclosureDenied => ClientOutcomeKind::Denied, - RuntimeFailure::AuthorizationIndeterminate(_) - | RuntimeFailure::StateConflict - | RuntimeFailure::Unavailable - | RuntimeFailure::ProviderOutcomeUnknown => ClientOutcomeKind::Indeterminate, + | RuntimeFailure::DisclosureDenied => { + if verb == ProductVerb::Verify { + ClientOutcomeKind::Rejected + } else { + ClientOutcomeKind::Denied + } + } }; debug_assert!( !(error.effect() == EffectState::Possible && error.retry().asserts_non_effect()), @@ -410,7 +421,7 @@ mod tests { assert_eq!(failure.code(), "core.outcome-unknown"); assert!(!failure.retry().asserts_non_effect()); assert_eq!(failure.retry(), NextCall::Reconcile); - let response = failure_response(failure); + let response = failure_response(ProductVerb::Execute, failure); assert_eq!(response.code(), Some("core.outcome-unknown")); assert!(!response.retry().asserts_non_effect()); } @@ -437,12 +448,23 @@ mod tests { "{failure:?} told the caller nothing happened" ); } - let response = failure_response(failure); + let response = failure_response(ProductVerb::Execute, failure); assert_eq!(response.code(), Some(failure.code())); assert_eq!(response.retry(), failure.retry()); } } + #[test] + fn verification_uses_rejected_for_terminal_failures() { + let rejected = failure_response(ProductVerb::Verify, RuntimeFailure::Malformed); + assert_eq!(rejected.kind(), ClientOutcomeKind::Rejected); + assert_eq!(rejected.retry(), NextCall::Never); + + let indeterminate = failure_response(ProductVerb::Verify, RuntimeFailure::Unavailable); + assert_eq!(indeterminate.kind(), ClientOutcomeKind::Indeterminate); + assert_eq!(indeterminate.retry(), NextCall::Backoff); + } + /// Every code this node can put on the wire must exist in the product error /// registry. Eight of the ten it previously emitted did not: /// `authority.denied`, `authority.indeterminate`, `profile.disabled`, From 299ed389373bb667a7b2ed7e66821dc6503c0dd2 Mon Sep 17 00:00:00 2001 From: bordumb Date: Mon, 17 Aug 2026 00:41:51 +0100 Subject: [PATCH 55/61] fix: align reference recovery contract --- .github/workflows/python-sdk.yml | 4 +- .github/workflows/typescript-sdk.yml | 2 +- .../tests/static-policy.sh | 12 ++++ product/runtime/auths-node/src/kernel.rs | 3 +- .../runtime/auths-node/src/local_fixture.rs | 4 ++ .../tests/local_fixture_authorizes.rs | 65 ++++++++++++++++++- 6 files changed, 85 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-sdk.yml b/.github/workflows/python-sdk.yml index ab08399a..e355301e 100644 --- a/.github/workflows/python-sdk.yml +++ b/.github/workflows/python-sdk.yml @@ -114,7 +114,7 @@ jobs: wheel-build: name: build abi3 wheel (${{ matrix.os }}) strategy: - fail-fast: false + fail-fast: true matrix: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} @@ -145,7 +145,7 @@ jobs: name: installed CPython ${{ matrix.python }} (${{ matrix.os }}) needs: [consumer-contract, wheel-build] strategy: - fail-fast: false + fail-fast: true matrix: os: [ubuntu-latest, macos-latest, windows-latest] python: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] diff --git a/.github/workflows/typescript-sdk.yml b/.github/workflows/typescript-sdk.yml index eb17a301..b25f1c2c 100644 --- a/.github/workflows/typescript-sdk.yml +++ b/.github/workflows/typescript-sdk.yml @@ -53,7 +53,7 @@ jobs: name: packed Node ${{ matrix.node }} (${{ matrix.os }}) needs: package-runtime strategy: - fail-fast: false + fail-fast: true matrix: os: [ubuntu-latest, macos-latest, windows-latest] node: [20.19.6, 22.23.1] diff --git a/demos/open-production-reference/tests/static-policy.sh b/demos/open-production-reference/tests/static-policy.sh index 24858946..edbf82c6 100755 --- a/demos/open-production-reference/tests/static-policy.sh +++ b/demos/open-production-reference/tests/static-policy.sh @@ -16,3 +16,15 @@ fi if grep -R --exclude-dir=certs -E 'AKIA[0-9A-Z]{16}|BEGIN (RSA |EC )?PRIVATE KEY|AUTHS_LOCAL_SEED[[:space:]]*=' "$root"; then exit 1 fi + +# The node and both installed SDK tests must agree on the reference-only +# trigger for exercising durable recovery. This exact cross-language drift +# previously made the live stack complete an action the clients expected to be +# recoverable. +recovery_marker='AUTHS-SANDBOX-RECOVER' +grep -Fq "REFERENCE_RECOVERABLE_BODY_MARKER: &str = \"$recovery_marker\";" \ + "$root/../../product/runtime/auths-node/src/local_fixture.rs" +grep -Fq "writeFileSync(recoverPath, \"$recovery_marker issue 104\");" \ + "$root/tests/installed-sdk-e2e.mjs" +grep -Fq "b\"$recovery_marker issue 104\"," \ + "$root/tests/test_installed_sdk.py" diff --git a/product/runtime/auths-node/src/kernel.rs b/product/runtime/auths-node/src/kernel.rs index d2a09b08..6b283144 100644 --- a/product/runtime/auths-node/src/kernel.rs +++ b/product/runtime/auths-node/src/kernel.rs @@ -47,7 +47,8 @@ const CLAIM_DOMAIN: &[u8] = b"AUTHS-NODE-EFFECT-CLAIM\x00\x01"; /// Marker prefix inside a canonical action body that makes one effect /// deliberately recoverable, used by the open-production reference to exercise /// the outcome-unknown path. -const RECOVERABLE_BODY_MARKER: &[u8] = b"AUTHS-NODE-RECOVER"; +const RECOVERABLE_BODY_MARKER: &[u8] = + crate::local_fixture::REFERENCE_RECOVERABLE_BODY_MARKER.as_bytes(); /// A stateful (proof, action) pair may produce at most this many effects. /// diff --git a/product/runtime/auths-node/src/local_fixture.rs b/product/runtime/auths-node/src/local_fixture.rs index a8604b27..10a110f4 100644 --- a/product/runtime/auths-node/src/local_fixture.rs +++ b/product/runtime/auths-node/src/local_fixture.rs @@ -53,6 +53,10 @@ pub const REFERENCE_PROFILES: [&str; 3] = [ "auths.github.issue-address/1", ]; +/// Action-body prefix used only by the open-production reference to exercise +/// the durable outcome-unknown and resume path. +pub const REFERENCE_RECOVERABLE_BODY_MARKER: &str = "AUTHS-SANDBOX-RECOVER"; + /// Namespace the fixture anchor may delegate within, one per profile. const REFERENCE_NAMESPACES: [&str; 3] = [ "opentofu://reference", diff --git a/product/runtime/auths-node/tests/local_fixture_authorizes.rs b/product/runtime/auths-node/tests/local_fixture_authorizes.rs index 2cca5e29..352fb806 100644 --- a/product/runtime/auths-node/tests/local_fixture_authorizes.rs +++ b/product/runtime/auths-node/tests/local_fixture_authorizes.rs @@ -9,7 +9,10 @@ //! //! This test closes that loop by running the real verifier. -use auths_node::local_fixture::build_context; +use auths_node::{NodeRuntime, local_fixture::build_context}; +use auths_production_client::{ + ClientOutcomeKind, ProductVerb, ProductionRequest, QualifiedProfile, +}; use std::{ collections::BTreeSet, process::Command, @@ -80,3 +83,63 @@ fn an_offline_authored_proof_is_authorized_by_the_generated_context() { .unwrap_or_else(|error| panic!("{profile} was not authorized: {error:?}")); } } + +#[test] +fn the_reference_recovery_marker_enters_and_resolves_the_resume_flow() { + use base64ct::{Base64UrlUnpadded, Encoding as _}; + let mut seed = [0_u8; 32]; + Base64UrlUnpadded::decode(SEED_B64, &mut seed).expect("seed"); + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_or(0, |duration| duration.as_secs()); + let context = build_context(&seed, now, 3_600).expect("trusted context"); + let kernel = auths_node::NodeKernel::with_built_ins(context) + .expect("the deployed verifier registry must initialize"); + let runtime = auths_node::KernelRuntime::with_clock( + kernel, + [0x55; 32], + BTreeSet::from([QualifiedProfile::GitHubIssueAddress]), + Arc::new(FixedClock(now)), + ) + .expect("runtime"); + + // Keep this literal independent from the kernel constant: drift between + // the installed-client contract and the node caused the hosted failure + // this test exists to catch. + let body = "AUTHS-SANDBOX-RECOVER issue 104"; + let (proof, action) = authored( + QualifiedProfile::GitHubIssueAddress.as_str(), + body, + "fixture-recovery-agent", + ); + let execute = ProductionRequest::new( + ProductVerb::Execute, + QualifiedProfile::GitHubIssueAddress, + b"fixture-recovery-agent".to_vec(), + Some(proof), + Some(action), + None, + ) + .expect("execute request"); + let unknown = runtime.handle(execute).expect("recoverable outcome"); + assert_eq!(unknown.kind(), ClientOutcomeKind::Recoverable); + + let resume = ProductionRequest::new( + ProductVerb::Resume, + QualifiedProfile::GitHubIssueAddress, + b"fixture-recovery-agent".to_vec(), + None, + None, + Some( + unknown + .recovery_reference() + .expect("recovery reference") + .clone(), + ), + ) + .expect("resume request"); + assert_eq!( + runtime.handle(resume).expect("completed resume").kind(), + ClientOutcomeKind::Completed + ); +} From 0636b8a2b3a57e1e2f1bbcb97114efb99a24ecd4 Mon Sep 17 00:00:00 2001 From: bordumb Date: Mon, 17 Aug 2026 01:41:27 +0100 Subject: [PATCH 56/61] fix: fail fast before expensive CI --- .github/workflows/ci.yml | 47 ++++++++-- bindings/wasm/auths-proof-wasm/src/lib.rs | 85 ++++++------------- compliance.toml | 4 +- formal/assurance-manifest-v1.toml | 6 +- .../qualification/aeneas/source-closure.json | 8 +- release/semantic-freeze-versions.toml | 18 ++-- release/semantic-freeze.json | 34 ++++---- xtask/src/checks.rs | 17 ++++ xtask/src/formal_qualification.rs | 70 ++++++++++++++- xtask/src/main.rs | 9 +- 10 files changed, 191 insertions(+), 107 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b2b36af..0ff4534b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -135,13 +135,35 @@ jobs: exit 1 fi + repository-preflight: + name: repository preflight + needs: [ci-plan, formal-update-gate] + if: >- + always() && + needs.ci-plan.result == 'success' && + needs.formal-update-gate.result == 'success' + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + fetch-depth: 0 + persist-credentials: false + - uses: ./.github/actions/setup-rust-cache + with: + toolchain: 1.97.1 + components: rustfmt, clippy + - name: Fail fast on repository-wide deterministic gates + run: cargo xtask ci preflight + authoritative-run: name: authoritative implementation - needs: [ci-plan, formal-update-gate] + needs: [ci-plan, formal-update-gate, repository-preflight] if: >- always() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && + needs.repository-preflight.result == 'success' && needs.ci-plan.outputs.authoritative_required == 'true' runs-on: ubuntu-latest timeout-minutes: 120 @@ -203,11 +225,12 @@ jobs: formal-translation-run: name: formal-translation implementation - needs: [ci-plan, formal-update-gate] + needs: [ci-plan, formal-update-gate, repository-preflight] if: >- always() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && + needs.repository-preflight.result == 'success' && needs.ci-plan.outputs.formal_translation_required == 'true' runs-on: ubuntu-latest timeout-minutes: 120 @@ -334,11 +357,12 @@ jobs: compliance-run: name: compliance implementation - needs: [ci-plan, formal-update-gate] + needs: [ci-plan, formal-update-gate, repository-preflight] if: >- always() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && + needs.repository-preflight.result == 'success' && needs.ci-plan.outputs.compliance_required == 'true' runs-on: ubuntu-latest timeout-minutes: 60 @@ -374,7 +398,7 @@ jobs: npm ci --prefix bindings/typescript - run: cargo xtask ci compliance - name: Preserve compliance evidence - if: always() + if: always() && hashFiles('target/compliance/**') != '' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: auths-product-core-compliance-${{ github.run_id }}-${{ github.run_attempt }} @@ -391,11 +415,12 @@ jobs: dependencies-run: name: dependencies implementation - needs: [ci-plan, formal-update-gate] + needs: [ci-plan, formal-update-gate, repository-preflight] if: >- always() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && + needs.repository-preflight.result == 'success' && needs.ci-plan.outputs.dependencies_required == 'true' runs-on: ubuntu-latest timeout-minutes: 20 @@ -425,11 +450,12 @@ jobs: secrets-run: name: secrets implementation - needs: [ci-plan, formal-update-gate] + needs: [ci-plan, formal-update-gate, repository-preflight] if: >- always() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && + needs.repository-preflight.result == 'success' && needs.ci-plan.outputs.secrets_required == 'true' runs-on: ubuntu-latest timeout-minutes: 15 @@ -459,11 +485,12 @@ jobs: opentofu-live-run: name: opentofu-live implementation - needs: [ci-plan, formal-update-gate] + needs: [ci-plan, formal-update-gate, repository-preflight] if: >- always() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && + needs.repository-preflight.result == 'success' && needs.ci-plan.outputs.opentofu_live_required == 'true' runs-on: ubuntu-latest timeout-minutes: 45 @@ -537,11 +564,12 @@ jobs: postgresql-live-run: name: postgresql-live implementation - needs: [ci-plan, formal-update-gate] + needs: [ci-plan, formal-update-gate, repository-preflight] if: >- always() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && + needs.repository-preflight.result == 'success' && needs.ci-plan.outputs.postgresql_live_required == 'true' runs-on: ubuntu-latest timeout-minutes: 45 @@ -650,11 +678,12 @@ jobs: records-api-live-run: name: records-api-live implementation - needs: [ci-plan, formal-update-gate] + needs: [ci-plan, formal-update-gate, repository-preflight] if: >- always() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && + needs.repository-preflight.result == 'success' && needs.ci-plan.outputs.records_api_live_required == 'true' runs-on: ubuntu-latest timeout-minutes: 30 diff --git a/bindings/wasm/auths-proof-wasm/src/lib.rs b/bindings/wasm/auths-proof-wasm/src/lib.rs index e1e3b55f..1508e3e4 100644 --- a/bindings/wasm/auths-proof-wasm/src/lib.rs +++ b/bindings/wasm/auths-proof-wasm/src/lib.rs @@ -147,7 +147,7 @@ pub fn decode_production_request_v1(input: &[u8]) -> Result { /// and next call. A failure that is not provably before transmission, on a verb /// that applies an effect, is `core.outcome-unknown` with `reconcile`, never a /// code whose registered effect is `not-applied`. A language binding that chose -/// this itself would be telling a caller that a possibly-applied PostgreSQL +/// this itself would be telling a caller that a possibly-applied `PostgreSQL` /// update is safe to blindly retry. /// /// # Errors @@ -725,7 +725,7 @@ fn assurance_policy(input: AssuranceInput) -> Result Result { +fn validate_registry_input(input: &RegistryInput) -> Result<(), EngineError> { if contains_duplicates(&input.principal_methods) || contains_duplicates(&input.signature_suites) || contains_duplicates(&input.evidence_types) @@ -757,6 +757,21 @@ fn accepted_registries(input: RegistryInput) -> Result( + values: Vec, + parse: impl Fn(&str) -> Result, +) -> Result, EngineError> { + values + .into_iter() + .map(|value| parse(&value).map_err(EngineError::from)) + .collect() +} + +fn accepted_registries(input: RegistryInput) -> Result { + validate_registry_input(&input)?; let profiles = input .profiles .into_iter() @@ -764,62 +779,18 @@ fn accepted_registries(input: RegistryInput) -> Result, _>>()?; Ok(AcceptedRegistries::new( auths_registries::TARGET_V1_REGISTRY_MANIFEST, - input - .principal_methods - .into_iter() - .map(|value| PrincipalMethodId::parse(&value)) - .collect::, _>>()?, - input - .signature_suites - .into_iter() - .map(|value| SignatureSuiteId::parse(&value)) - .collect::, _>>()?, - input - .evidence_types - .into_iter() - .map(|value| EvidenceTypeId::parse(&value)) - .collect::, _>>()?, - input - .principal_status_methods - .into_iter() - .map(|value| StatusMethodId::parse(&value)) - .collect::, _>>()?, - input - .grant_status_methods - .into_iter() - .map(|value| StatusMethodId::parse(&value)) - .collect::, _>>()?, - input - .assurance_claims - .into_iter() - .map(|value| AssuranceClaimId::parse(&value)) - .collect::, _>>()?, - input - .assurance_implications - .into_iter() - .map(|value| AssuranceImplicationId::parse(&value)) - .collect::, _>>()?, - input - .resource_matchers - .into_iter() - .map(|value| ResourceMatcherId::parse(&value)) - .collect::, _>>()?, - input - .budget_algebras - .into_iter() - .map(|value| BudgetAlgebraId::parse(&value)) - .collect::, _>>()?, - input - .critical_extensions - .into_iter() - .map(|value| ExtensionId::parse(&value)) - .collect::, _>>()?, + parse_registry_ids(input.principal_methods, PrincipalMethodId::parse)?, + parse_registry_ids(input.signature_suites, SignatureSuiteId::parse)?, + parse_registry_ids(input.evidence_types, EvidenceTypeId::parse)?, + parse_registry_ids(input.principal_status_methods, StatusMethodId::parse)?, + parse_registry_ids(input.grant_status_methods, StatusMethodId::parse)?, + parse_registry_ids(input.assurance_claims, AssuranceClaimId::parse)?, + parse_registry_ids(input.assurance_implications, AssuranceImplicationId::parse)?, + parse_registry_ids(input.resource_matchers, ResourceMatcherId::parse)?, + parse_registry_ids(input.budget_algebras, BudgetAlgebraId::parse)?, + parse_registry_ids(input.critical_extensions, ExtensionId::parse)?, profiles.clone(), - input - .profile_policies - .into_iter() - .map(|value| ProfilePolicyId::parse(&value)) - .collect::, _>>()?, + parse_registry_ids(input.profile_policies, ProfilePolicyId::parse)?, )? .with_budget_free_profiles( profiles diff --git a/compliance.toml b/compliance.toml index ed584bcd..e5cae6c0 100644 --- a/compliance.toml +++ b/compliance.toml @@ -1333,13 +1333,13 @@ security_state = ["opaque-recovery-reference", "receipt-disclosure", "shared-lif [packages.auths-node.claims] core-api-consumer = [ - "product/runtime/auths-node/tests/kernel_differential.rs#node_and_kernel_agree_on_every_canonical_corpus_input", + "product/runtime/auths-node/tests/kernel_differential.rs#node_and_kernel_agree_on_every_startable_canonical_corpus_input", "product/runtime/auths-node/src/kernel.rs#the_node_refuses_to_mint_authority_from_a_self_asserted_identity", ] runtime-enforcement-boundary = [ "product/runtime/auths-node/src/api.rs#route_and_envelope_must_agree", "product/runtime/auths-node/src/kernel.rs#exact_action_and_replay_budget_are_enforced", - "product/runtime/auths-node/tests/kernel_differential.rs#node_and_kernel_agree_on_every_canonical_corpus_input", + "product/runtime/auths-node/tests/kernel_differential.rs#node_and_kernel_agree_on_every_startable_canonical_corpus_input", ] operational-diagnostics = ["product/runtime/auths-node/src/api.rs#metrics_expose_only_the_frozen_operational_vocabulary"] stateful-replay-budget-component = [ diff --git a/formal/assurance-manifest-v1.toml b/formal/assurance-manifest-v1.toml index 53c1a05b..4bb0fad5 100644 --- a/formal/assurance-manifest-v1.toml +++ b/formal/assurance-manifest-v1.toml @@ -6503,7 +6503,7 @@ sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" [[claims.evidence]] kind = "source-closure" artifact = "formal/qualification/aeneas/source-closure.json" -sha256 = "e5eea784fbe91f4336e6d736152f30aebbb28fc567e217fee728117a2f5907cf" +sha256 = "5166f5a29bcea85f90c44e22a89df2f4505c65de117f112757bc80cd6b74f135" [[claims]] claim_id = "AP-FORMAL-RICH-055" @@ -6606,7 +6606,7 @@ sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" [[claims.evidence]] kind = "source-closure" artifact = "formal/qualification/aeneas/source-closure.json" -sha256 = "e5eea784fbe91f4336e6d736152f30aebbb28fc567e217fee728117a2f5907cf" +sha256 = "5166f5a29bcea85f90c44e22a89df2f4505c65de117f112757bc80cd6b74f135" [[claims]] claim_id = "AP-FORMAL-RICH-056" @@ -6714,7 +6714,7 @@ sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" [[claims.evidence]] kind = "source-closure" artifact = "formal/qualification/aeneas/source-closure.json" -sha256 = "e5eea784fbe91f4336e6d736152f30aebbb28fc567e217fee728117a2f5907cf" +sha256 = "5166f5a29bcea85f90c44e22a89df2f4505c65de117f112757bc80cd6b74f135" [[claims]] claim_id = "AP-FORMAL-RICH-104" diff --git a/formal/qualification/aeneas/source-closure.json b/formal/qualification/aeneas/source-closure.json index 4f7ba3af..9aa2a23d 100644 --- a/formal/qualification/aeneas/source-closure.json +++ b/formal/qualification/aeneas/source-closure.json @@ -1,6 +1,6 @@ { "schema": "auths-proof-translation-source-closure/v2", - "digest": "f07cf64b7b6e4198a767335bf59773a35b9f37e40b750f6c5753a549fa3b320a", + "digest": "0fbb1153de69314c95db7cb138b9f9bf0c2c885ab09a27164039aacd3fa0dad2", "files": [ { "path": ".cargo/config.toml", @@ -221,7 +221,7 @@ }, { "path": "xtask/src/checks.rs", - "sha256": "867dedc085734b1a713124b9a22200ed231b1c7de07284802839224ea9482e80" + "sha256": "928cabc1f6ced5ecfd01d63bba5ac64a51e4f8b67f502b3e791f74083f21eeca" }, { "path": "xtask/src/compliance.rs", @@ -249,7 +249,7 @@ }, { "path": "xtask/src/formal_qualification.rs", - "sha256": "7fa6e6bab2ad01ff071a12940064726a743767b4a0861377789434e1bedbfcac" + "sha256": "dee7d3dc4c5f8ed62e426365c98de9bb3cf62a00c493525ab3bbd08d4dcc37a5" }, { "path": "xtask/src/fuzz.rs", @@ -261,7 +261,7 @@ }, { "path": "xtask/src/main.rs", - "sha256": "3950c5eace93c58ef80b14ddb8b40bd91bc0acc7bc67e680d9a1e4acea9b9d91" + "sha256": "f8d04a848eecd0abdbacae997b15b3c686186f6b9de0c24459d164510adc3b43" }, { "path": "xtask/src/mcp_session_contract.rs", diff --git a/release/semantic-freeze-versions.toml b/release/semantic-freeze-versions.toml index 81c9d1d0..244e7b0f 100644 --- a/release/semantic-freeze-versions.toml +++ b/release/semantic-freeze-versions.toml @@ -1,4 +1,4 @@ -freeze_version = 129 +freeze_version = 131 # Semantic identity counters live outside the xtask source tree deliberately. # The formal source closure binds xtask's executable code, while the semantic @@ -16,10 +16,10 @@ freeze_version = 129 "auths.frozen-bytes/core/fixtures/v1/manifest.json" = 4 "auths.frozen-bytes/core/formal-vectors/v1/manifest.json" = 1 "auths.frozen-bytes/demos/benchmarks/profiles/release.toml" = 1 -"auths.frozen-bytes/formal/assurance-manifest-v1.toml" = 15 +"auths.frozen-bytes/formal/assurance-manifest-v1.toml" = 16 "auths.frozen-bytes/formal/qualification/aeneas/generated" = 6 "auths.frozen-bytes/formal/qualification/aeneas/qualification.toml" = 5 -"auths.frozen-bytes/formal/qualification/aeneas/source-closure.json" = 22 +"auths.frozen-bytes/formal/qualification/aeneas/source-closure.json" = 23 "auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json" = 1 "auths.frozen-bytes/product/conformance/v1/simplified-product-waist.json" = 2 "auths.frozen-bytes/product/fixtures/v1/bounded-policy/manifest.json" = 2 @@ -46,9 +46,9 @@ freeze_version = 129 "auths.frozen-bytes/product/integrations/auths-stripe/fixtures/subscription-create/v1/manifest.sha256.json" = 1 "auths.frozen-bytes/product/integrations/auths-stripe/fixtures/subscription-modify/v1/manifest.sha256.json" = 1 "auths.frozen-bytes/product/integrations/auths-stripe/fixtures/v1/manifest.sha256.json" = 1 -"auths.identity.protocol" = 30 +"auths.identity.protocol" = 31 "auths.modular-components" = 8 -"auths.portable-abi-bindings" = 54 +"auths.portable-abi-bindings" = 55 "auths.product.bounded-domains" = 7 "auths.product.bounded-policy" = 2 "auths.product.configuration-commitments" = 1 @@ -57,15 +57,15 @@ freeze_version = 129 "auths.product.external-custody" = 3 "auths.product.facade" = 11 "auths.product.lifecycle" = 10 -"auths.product.mcp-closed-execution" = 14 +"auths.product.mcp-closed-execution" = 15 "auths.product.mechanism-profile-conformance" = 5 "auths.product.open-production-contract" = 13 "auths.product.operations" = 5 -"auths.product.public-sdk-contract" = 43 +"auths.product.public-sdk-contract" = 44 "auths.product.receipts" = 5 "auths.product.release-assurance" = 4 "auths.product.simplified-waist" = 8 "auths.product.vocabulary" = 9 "auths.release.benchmark-contract" = 1 -"auths.release.evolution-contract" = 16 -"auths.release.public-surface" = 128 +"auths.release.evolution-contract" = 17 +"auths.release.public-surface" = 130 diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index d22bb152..57a08056 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 129, + "freezeVersion": 131, "publicSurface": { "rustRoots": [ "auths", @@ -199,7 +199,7 @@ }, { "id": "auths.frozen-bytes/formal/assurance-manifest-v1.toml", - "version": 15, + "version": 16, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -207,7 +207,7 @@ "owners": [ "formal/assurance-manifest-v1.toml" ], - "sha256": "d0d78a01ee17953dbb173529525001b05a89c4d3c9344f512b516bb560818b9c" + "sha256": "5ad03fd0b99091be06fd82784979aa34abdba91b0132f52d89783ba69402c81a" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/generated", @@ -235,7 +235,7 @@ }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/source-closure.json", - "version": 22, + "version": 23, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -243,7 +243,7 @@ "owners": [ "formal/qualification/aeneas/source-closure.json" ], - "sha256": "3789288422a62464bdfef7c2ded3152ea7ce1e476cb60a343b88c11188e98548" + "sha256": "919dff5a4d619f76e8d22884ed200b2c3838890d984bc16258b213f5897dca57" }, { "id": "auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json", @@ -559,7 +559,7 @@ }, { "id": "auths.identity.protocol", - "version": 30, + "version": 31, "classification": "frozen-meaning", "categories": [ "identity-protocol-versions", @@ -582,7 +582,7 @@ "core/fixtures/identity/v1/vectors.json", "core/spec/identity/v1" ], - "sha256": "e0310136be5fb0cbfd6c13768a901c5cdc9a9632766c616ba4ad47eca6c8a512" + "sha256": "66794f756f54af9beadcad1cc480ce027229b5a01162eb9b4d37dce53c1321fb" }, { "id": "auths.modular-components", @@ -620,7 +620,7 @@ }, { "id": "auths.portable-abi-bindings", - "version": 54, + "version": 55, "classification": "frozen-meaning", "categories": [ "portable-abi", @@ -637,7 +637,7 @@ "core/crates/auths-model/src/lib.rs", "core/spec/v1/auths-proof.cddl" ], - "sha256": "328d997a6b77e8d0412255caa0f9915f895be9b69cb4f768d476eda80ae291f9" + "sha256": "bee86e4b434721b6eedb065416cf6c4c44e2fbfe062a00692fc7b19e8591cafa" }, { "id": "auths.product.bounded-domains", @@ -806,7 +806,7 @@ }, { "id": "auths.product.mcp-closed-execution", - "version": 14, + "version": 15, "classification": "frozen-meaning", "categories": [ "profile-session", @@ -826,7 +826,7 @@ "product/profiles/auths-profile-mcp/src/session.rs", "xtask/src/mcp_session_contract.rs" ], - "sha256": "00ffc07c04fa73722ac697902cff1bb0fe6082adcfa080c67f5d70d7d9825cc8" + "sha256": "f5100dc2696a3fe55d9485f4a9ee2a5fe6f5211d2b9d4ac7bbaf37c715131b68" }, { "id": "auths.product.mechanism-profile-conformance", @@ -897,7 +897,7 @@ }, { "id": "auths.product.public-sdk-contract", - "version": 43, + "version": 44, "classification": "frozen-meaning", "categories": [ "rust-sdk-contract", @@ -915,7 +915,7 @@ "product/runtime/auths-runtime/src", "product/sdk/auths-sdk/src" ], - "sha256": "c3fed7eab6dffb763afaa2983f2877de63c3f81edc2fb121312d0ce42fa7ed3a" + "sha256": "0215c0ebd55b39d44b565655e0bffd6e9251141d73b148de92869db6d5458b19" }, { "id": "auths.product.receipts", @@ -1013,7 +1013,7 @@ }, { "id": "auths.release.evolution-contract", - "version": 16, + "version": 17, "classification": "frozen-meaning", "categories": [ "version-axes", @@ -1034,11 +1034,11 @@ "release/fixtures/evolution", "xtask/src/evolution_policy.rs" ], - "sha256": "71ebc9c8a7a5bfa14b582aa5444cce4cf66af0e8493086de30076e60c973d3d5" + "sha256": "e4a1ab38dd7a1f5a69b4e201815c20c4cb60044e4f77ef9f1ac0fdfec20efff1" }, { "id": "auths.release.public-surface", - "version": 128, + "version": 130, "classification": "release-metadata", "categories": [ "package-names", @@ -1133,7 +1133,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "9017b69c34d3c9638838cf092dfc26ca5d8d10ca0f14cd23754f6994b62c5c1d" + "sha256": "df962fe91cdd010d41d61d4042f0efcb2b865fbeefb8e14b1a4b7bf1021982fa" } ] } diff --git a/xtask/src/checks.rs b/xtask/src/checks.rs index 6111713b..ee418440 100644 --- a/xtask/src/checks.rs +++ b/xtask/src/checks.rs @@ -8,6 +8,23 @@ pub(crate) fn ci() -> Result<(), String> { ci_compliance() } +/// Runs inexpensive, repository-wide gates before CI fans out into the +/// long-running implementation and formal jobs. +pub(crate) fn ci_preflight() -> Result<(), String> { + cargo(&["fmt", "--all", "--check"])?; + semantic_freeze(false)?; + compliance_inventory()?; + cargo(&[ + "clippy", + "--workspace", + "--all-targets", + "--all-features", + "--", + "-D", + "warnings", + ]) +} + pub(crate) fn ci_authoritative() -> Result<(), String> { format_all()?; arch(false)?; diff --git a/xtask/src/formal_qualification.rs b/xtask/src/formal_qualification.rs index 11561ed5..b6fb25de 100644 --- a/xtask/src/formal_qualification.rs +++ b/xtask/src/formal_qualification.rs @@ -1234,6 +1234,7 @@ fn validate_ci_workflow_gates(ci: &str) -> Result<(), String> { &[ "uses: ./.github/actions/setup-lean", "kani-verifier --version 0.67.0", + "cargo xtask ci preflight", "cargo xtask ci authoritative", "cargo xtask ci formal-translation", "cargo xtask ci compliance", @@ -1264,9 +1265,51 @@ fn validate_ci_workflow_gates(ci: &str) -> Result<(), String> { .to_owned(), ); } + for job_name in [ + "authoritative-run", + "formal-translation-run", + "compliance-run", + "dependencies-run", + "secrets-run", + "opentofu-live-run", + "postgresql-live-run", + "records-api-live-run", + ] { + let job = workflow_job_source(ci, job_name)?; + if !job.contains("needs: [ci-plan, formal-update-gate, repository-preflight]") + || !job.contains("needs.repository-preflight.result == 'success'") + { + return Err(format!( + "hosted CI job `{job_name}` can start before the repository preflight succeeds" + )); + } + } + let compliance_job = workflow_job_source(ci, "compliance-run")?; + if !compliance_job.contains("if: always() && hashFiles('target/compliance/**') != ''") { + return Err( + "hosted compliance evidence upload must skip a missing evidence directory without masking the primary failure" + .to_owned(), + ); + } Ok(()) } +fn workflow_job_source<'a>(workflow: &'a str, job_name: &str) -> Result<&'a str, String> { + let marker = format!("\n {job_name}:"); + let tail = workflow + .split_once(&marker) + .map(|(_, tail)| tail) + .ok_or_else(|| format!("hosted CI omits the `{job_name}` job boundary"))?; + let next_job = tail.match_indices("\n ").find_map(|(index, _)| { + tail[index + 3..] + .chars() + .next() + .filter(|character| !character.is_whitespace()) + .map(|_| index) + }); + Ok(next_job.map_or(tail, |index| &tail[..index])) +} + fn validate_release_workflow_gates(orchestration: &str, builder: &str) -> Result<(), String> { validate_gate_source( "release orchestration", @@ -1996,7 +2039,7 @@ fn format_command_failure(arguments: &[String], directory: &Path, output: &Outpu mod tests { use super::*; - const CI_GATES: &str = "uses: ./.github/actions/setup-lean\nkani-verifier --version 0.67.0\ncargo xtask ci authoritative\ncargo xtask ci formal-translation\n formal-translation-run:\ncompiler-cache: \"false\"\ncargo xtask formal qualify aeneas --update\ncargo xtask ci formal-post-qualification\n compliance-run:\ncargo xtask ci compliance\ntarget/formal/\n"; + const CI_GATES: &str = "uses: ./.github/actions/setup-lean\nkani-verifier --version 0.67.0\ncargo xtask ci preflight\ncargo xtask ci authoritative\ncargo xtask ci formal-translation\ntarget/formal/\n authoritative-run:\nneeds: [ci-plan, formal-update-gate, repository-preflight]\nneeds.repository-preflight.result == 'success'\n formal-translation-run:\nneeds: [ci-plan, formal-update-gate, repository-preflight]\nneeds.repository-preflight.result == 'success'\ncompiler-cache: \"false\"\ncargo xtask formal qualify aeneas --update\ncargo xtask ci formal-post-qualification\n compliance-run:\nneeds: [ci-plan, formal-update-gate, repository-preflight]\nneeds.repository-preflight.result == 'success'\ncargo xtask ci compliance\nif: always() && hashFiles('target/compliance/**') != ''\n dependencies-run:\nneeds: [ci-plan, formal-update-gate, repository-preflight]\nneeds.repository-preflight.result == 'success'\n secrets-run:\nneeds: [ci-plan, formal-update-gate, repository-preflight]\nneeds.repository-preflight.result == 'success'\n opentofu-live-run:\nneeds: [ci-plan, formal-update-gate, repository-preflight]\nneeds.repository-preflight.result == 'success'\n postgresql-live-run:\nneeds: [ci-plan, formal-update-gate, repository-preflight]\nneeds.repository-preflight.result == 'success'\n records-api-live-run:\nneeds: [ci-plan, formal-update-gate, repository-preflight]\nneeds.repository-preflight.result == 'success'\n"; const BUILDER_GATES: &str = "leanprover/lean-action@\nkani-verifier --version 0.67.0\ncargo xtask release-check\ncargo xtask formal qualify aeneas\n"; #[test] @@ -2007,12 +2050,35 @@ mod tests { #[test] fn hosted_ci_cannot_omit_pinned_lean_setup() { let error = validate_ci_workflow_gates( - "kani-verifier --version 0.67.0\ncargo xtask ci authoritative\ncargo xtask ci formal-translation\ncargo xtask formal qualify aeneas --update\ncargo xtask ci formal-post-qualification\ncargo xtask ci compliance\ntarget/formal/\n", + &CI_GATES.replace("uses: ./.github/actions/setup-lean\n", ""), ) .expect_err("missing pinned Lean setup must fail"); assert!(error.contains("hosted CI omits required formal gate")); } + #[test] + fn every_expensive_ci_job_waits_for_repository_preflight() { + let bypass = CI_GATES.replacen( + "needs: [ci-plan, formal-update-gate, repository-preflight]", + "needs: [ci-plan, formal-update-gate]", + 1, + ); + let error = validate_ci_workflow_gates(&bypass) + .expect_err("an implementation job must not bypass the shared preflight"); + assert!(error.contains("can start before the repository preflight succeeds")); + } + + #[test] + fn compliance_upload_does_not_mask_the_primary_failure() { + let masking = CI_GATES.replace( + "if: always() && hashFiles('target/compliance/**') != ''", + "if: always()", + ); + let error = validate_ci_workflow_gates(&masking) + .expect_err("missing compliance evidence must not create a second failure"); + assert!(error.contains("without masking the primary failure")); + } + #[test] fn reusable_release_builder_owns_formal_gates() { validate_release_workflow_gates( diff --git a/xtask/src/main.rs b/xtask/src/main.rs index eb9b7842..e115065a 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -58,7 +58,7 @@ pub(crate) use sdk_vocabulary::*; pub(crate) use semantic_freeze::*; pub(crate) use stripe::*; -const USAGE: &str = "usage: cargo xtask ...|semantic-freeze [--update]|production-contract [--update]|evolution-policy [--update]|sdk-experience [--update]|sdk-vocabulary|error-registry [--update]|mcp-session-contract [--update]|mechanism-conformance [--update]|product-waist-conformance [--update]|public-naming|release-contract|release-control ...|binding-semantics|core-boundary|workspace-msrv|abi|core|exchange|product|bindings|demos|package|wire [--update]|spec-sync|conformance|exchange-conformance|product-conformance|stripe-profiles|bounded-domains|compliance|matrix|cross-language|product-fixtures [--update]|semantic-digest|wasm|live-demo|fuzz-inventory|fuzz-smoke|platform-artifact [output]|formal [--skip-kani] [--update]|formal qualify aeneas [--update]|adversarial-conformance [--surface |--adapter |--case ]|bench |ci [authoritative|formal-translation|formal-post-qualification|compliance]|release-check>"; +const USAGE: &str = "usage: cargo xtask ...|semantic-freeze [--update]|production-contract [--update]|evolution-policy [--update]|sdk-experience [--update]|sdk-vocabulary|error-registry [--update]|mcp-session-contract [--update]|mechanism-conformance [--update]|product-waist-conformance [--update]|public-naming|release-contract|release-control ...|binding-semantics|core-boundary|workspace-msrv|abi|core|exchange|product|bindings|demos|package|wire [--update]|spec-sync|conformance|exchange-conformance|product-conformance|stripe-profiles|bounded-domains|compliance|matrix|cross-language|product-fixtures [--update]|semantic-digest|wasm|live-demo|fuzz-inventory|fuzz-smoke|platform-artifact [output]|formal [--skip-kani] [--update]|formal qualify aeneas [--update]|adversarial-conformance [--surface |--adapter |--case ]|bench |ci [preflight|authoritative|formal-translation|formal-post-qualification|compliance]|release-check>"; fn main() -> ExitCode { match run() { @@ -82,12 +82,13 @@ fn dispatch(arguments: impl IntoIterator) -> Result<(), String> { let arguments: Vec<_> = args.collect(); match arguments.as_slice() { [] => ci(), + [phase] if phase == "preflight" => ci_preflight(), [phase] if phase == "authoritative" => ci_authoritative(), [phase] if phase == "formal-translation" => ci_formal_translation(), [phase] if phase == "formal-post-qualification" => ci_formal_post_qualification(), [phase] if phase == "compliance" => ci_compliance(), _ => Err(format!( - "unknown CI phase {}; expected authoritative, formal-translation, formal-post-qualification, or compliance", + "unknown CI phase {}; expected preflight, authoritative, formal-translation, formal-post-qualification, or compliance", arguments.join(" ") )), } @@ -175,7 +176,7 @@ mod tests { fn help_output_is_stable() { assert_eq!( USAGE, - "usage: cargo xtask ...|semantic-freeze [--update]|production-contract [--update]|evolution-policy [--update]|sdk-experience [--update]|sdk-vocabulary|error-registry [--update]|mcp-session-contract [--update]|mechanism-conformance [--update]|product-waist-conformance [--update]|public-naming|release-contract|release-control ...|binding-semantics|core-boundary|workspace-msrv|abi|core|exchange|product|bindings|demos|package|wire [--update]|spec-sync|conformance|exchange-conformance|product-conformance|stripe-profiles|bounded-domains|compliance|matrix|cross-language|product-fixtures [--update]|semantic-digest|wasm|live-demo|fuzz-inventory|fuzz-smoke|platform-artifact [output]|formal [--skip-kani] [--update]|formal qualify aeneas [--update]|adversarial-conformance [--surface |--adapter |--case ]|bench |ci [authoritative|formal-translation|formal-post-qualification|compliance]|release-check>" + "usage: cargo xtask ...|semantic-freeze [--update]|production-contract [--update]|evolution-policy [--update]|sdk-experience [--update]|sdk-vocabulary|error-registry [--update]|mcp-session-contract [--update]|mechanism-conformance [--update]|product-waist-conformance [--update]|public-naming|release-contract|release-control ...|binding-semantics|core-boundary|workspace-msrv|abi|core|exchange|product|bindings|demos|package|wire [--update]|spec-sync|conformance|exchange-conformance|product-conformance|stripe-profiles|bounded-domains|compliance|matrix|cross-language|product-fixtures [--update]|semantic-digest|wasm|live-demo|fuzz-inventory|fuzz-smoke|platform-artifact [output]|formal [--skip-kani] [--update]|formal qualify aeneas [--update]|adversarial-conformance [--surface |--adapter |--case ]|bench |ci [preflight|authoritative|formal-translation|formal-post-qualification|compliance]|release-check>" ); } @@ -185,7 +186,7 @@ mod tests { dispatch(["ci".to_owned(), "unknown".to_owned()]).expect_err("unknown phase must fail"); assert_eq!( error, - "unknown CI phase unknown; expected authoritative, formal-translation, formal-post-qualification, or compliance" + "unknown CI phase unknown; expected preflight, authoritative, formal-translation, formal-post-qualification, or compliance" ); } } From 25146022a59512c61e53e6c161b8e8256e311001 Mon Sep 17 00:00:00 2001 From: bordumb Date: Mon, 17 Aug 2026 02:15:17 +0100 Subject: [PATCH 57/61] ci: require complete current-head qualification --- .github/workflows/ci.yml | 47 +++++++++++++++++++++++++++ release/semantic-freeze-versions.toml | 6 ++-- release/semantic-freeze.json | 10 +++--- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ff4534b..6c4c28cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -892,3 +892,50 @@ jobs: echo "Decision: ${REQUIRED}; implementation: ${RUN_RESULT}; ${REASON}" >> "$GITHUB_STEP_SUMMARY" [[ "$PLANNER" == success ]] [[ "$REQUIRED" == true && "$RUN_RESULT" == success ]] || [[ "$REQUIRED" == false && "$RUN_RESULT" == skipped ]] + + ci-qualified: + name: CI qualified + if: always() + needs: + - authoritative + - formal-translation + - compliance + - dependencies + - secrets + - opentofu-live + - postgresql-live + - records-api-live + runs-on: ubuntu-latest + steps: + - name: Require every planned CI result + env: + AUTHORITATIVE: ${{ needs.authoritative.result }} + FORMAL_TRANSLATION: ${{ needs.formal-translation.result }} + COMPLIANCE: ${{ needs.compliance.result }} + DEPENDENCIES: ${{ needs.dependencies.result }} + SECRETS: ${{ needs.secrets.result }} + OPENTOFU_LIVE: ${{ needs.opentofu-live.result }} + POSTGRESQL_LIVE: ${{ needs.postgresql-live.result }} + RECORDS_API_LIVE: ${{ needs.records-api-live.result }} + run: | + set -euo pipefail + { + echo "| Gate | Result |" + echo "| --- | --- |" + echo "| authoritative | ${AUTHORITATIVE} |" + echo "| formal-translation | ${FORMAL_TRANSLATION} |" + echo "| compliance | ${COMPLIANCE} |" + echo "| dependencies | ${DEPENDENCIES} |" + echo "| secrets | ${SECRETS} |" + echo "| opentofu-live | ${OPENTOFU_LIVE} |" + echo "| postgresql-live | ${POSTGRESQL_LIVE} |" + echo "| records-api-live | ${RECORDS_API_LIVE} |" + } >> "$GITHUB_STEP_SUMMARY" + [[ "$AUTHORITATIVE" == success ]] + [[ "$FORMAL_TRANSLATION" == success ]] + [[ "$COMPLIANCE" == success ]] + [[ "$DEPENDENCIES" == success ]] + [[ "$SECRETS" == success ]] + [[ "$OPENTOFU_LIVE" == success ]] + [[ "$POSTGRESQL_LIVE" == success ]] + [[ "$RECORDS_API_LIVE" == success ]] diff --git a/release/semantic-freeze-versions.toml b/release/semantic-freeze-versions.toml index 244e7b0f..a45f7592 100644 --- a/release/semantic-freeze-versions.toml +++ b/release/semantic-freeze-versions.toml @@ -1,4 +1,4 @@ -freeze_version = 131 +freeze_version = 132 # Semantic identity counters live outside the xtask source tree deliberately. # The formal source closure binds xtask's executable code, while the semantic @@ -67,5 +67,5 @@ freeze_version = 131 "auths.product.simplified-waist" = 8 "auths.product.vocabulary" = 9 "auths.release.benchmark-contract" = 1 -"auths.release.evolution-contract" = 17 -"auths.release.public-surface" = 130 +"auths.release.evolution-contract" = 18 +"auths.release.public-surface" = 131 diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index 57a08056..8754d3a4 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 131, + "freezeVersion": 132, "publicSurface": { "rustRoots": [ "auths", @@ -1013,7 +1013,7 @@ }, { "id": "auths.release.evolution-contract", - "version": 17, + "version": 18, "classification": "frozen-meaning", "categories": [ "version-axes", @@ -1034,11 +1034,11 @@ "release/fixtures/evolution", "xtask/src/evolution_policy.rs" ], - "sha256": "e4a1ab38dd7a1f5a69b4e201815c20c4cb60044e4f77ef9f1ac0fdfec20efff1" + "sha256": "cc426b66fa161ef75e8115028d809b373ffa741d99363c88c9df9a25bc337c53" }, { "id": "auths.release.public-surface", - "version": 130, + "version": 131, "classification": "release-metadata", "categories": [ "package-names", @@ -1133,7 +1133,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "df962fe91cdd010d41d61d4042f0efcb2b865fbeefb8e14b1a4b7bf1021982fa" + "sha256": "67fb5f2b1d734ac81c55e817c0273de91ff99f1ed82841177ca55dabd1fac836" } ] } From 50d503e33c37b16cf0d89a71f1476f2c6c773fe4 Mon Sep 17 00:00:00 2001 From: bordumb Date: Mon, 17 Aug 2026 02:29:11 +0100 Subject: [PATCH 58/61] fix: clear fail-closed repository preflight --- core/crates/auths-verifier/src/lib.rs | 2 +- .../profiles/auths-profile-domains/src/lib.rs | 27 +++++++++++-------- release/semantic-freeze-versions.toml | 8 +++--- release/semantic-freeze.json | 14 +++++----- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/core/crates/auths-verifier/src/lib.rs b/core/crates/auths-verifier/src/lib.rs index 66e1f524..39b9dec6 100644 --- a/core/crates/auths-verifier/src/lib.rs +++ b/core/crates/auths-verifier/src/lib.rs @@ -3067,7 +3067,7 @@ mod tests { .with_budget_free_profiles(profiles) .expect("budget-free declaration"), context.expected_audience().clone(), - context.expected_challenge().clone(), + context.expected_challenge(), context.evaluation_time(), context.assurance_policy().clone(), context.principal_status_snapshot().clone(), diff --git a/product/profiles/auths-profile-domains/src/lib.rs b/product/profiles/auths-profile-domains/src/lib.rs index e7719be3..926cb4d9 100644 --- a/product/profiles/auths-profile-domains/src/lib.rs +++ b/product/profiles/auths-profile-domains/src/lib.rs @@ -1149,17 +1149,10 @@ mod tests { } } - /// Every domain profile's declared budget capability must match the bytes - /// its canonicalizer actually produces. - /// - /// A profile that declared `Inexpressible` while emitting a requested - /// budget would tell a verifier its actions provably spend zero when they - /// do not — the one way this mechanism could become unsound. Both sides are - /// computed here; neither is a literal. - #[test] - fn domain_budget_declaration_matches_canonical_bytes() { + fn canonical_domain_budget_cases() -> Vec<(ProfileRef, ProfileBudgetExpression, CanonicalAction)> + { let digest = "11".repeat(32); - let cases: Vec<(ProfileRef, ProfileBudgetExpression, CanonicalAction)> = vec![ + vec![ ( profile::().unwrap(), ::BUDGET_EXPRESSION, @@ -1246,7 +1239,19 @@ mod tests { ) .unwrap(), ), - ]; + ] + } + + /// Every domain profile's declared budget capability must match the bytes + /// its canonicalizer actually produces. + /// + /// A profile that declared `Inexpressible` while emitting a requested + /// budget would tell a verifier its actions provably spend zero when they + /// do not — the one way this mechanism could become unsound. Both sides are + /// computed here; neither is a literal. + #[test] + fn domain_budget_declaration_matches_canonical_bytes() { + let cases = canonical_domain_budget_cases(); // Both readings must actually occur, otherwise the comparison below // would pass for a table that is constant in one direction. assert!( diff --git a/release/semantic-freeze-versions.toml b/release/semantic-freeze-versions.toml index a45f7592..f9146c20 100644 --- a/release/semantic-freeze-versions.toml +++ b/release/semantic-freeze-versions.toml @@ -1,4 +1,4 @@ -freeze_version = 132 +freeze_version = 133 # Semantic identity counters live outside the xtask source tree deliberately. # The formal source closure binds xtask's executable code, while the semantic @@ -6,7 +6,7 @@ freeze_version = 132 # made the two generated artifacts hash each other and therefore unable to # converge. This file is a reviewed release input, never an automatic output. [entries] -"auths.core.protocol" = 17 +"auths.core.protocol" = 18 "auths.frozen-bytes/architecture/dependency-graph.json" = 29 "auths.frozen-bytes/bindings/wasm/auths-proof-wasm/identity-abi-v1.json" = 4 "auths.frozen-bytes/bounded-domains.toml" = 1 @@ -61,11 +61,11 @@ freeze_version = 132 "auths.product.mechanism-profile-conformance" = 5 "auths.product.open-production-contract" = 13 "auths.product.operations" = 5 -"auths.product.public-sdk-contract" = 44 +"auths.product.public-sdk-contract" = 45 "auths.product.receipts" = 5 "auths.product.release-assurance" = 4 "auths.product.simplified-waist" = 8 "auths.product.vocabulary" = 9 "auths.release.benchmark-contract" = 1 "auths.release.evolution-contract" = 18 -"auths.release.public-surface" = 131 +"auths.release.public-surface" = 132 diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index 8754d3a4..d468a1b5 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 132, + "freezeVersion": 133, "publicSurface": { "rustRoots": [ "auths", @@ -70,7 +70,7 @@ "entries": [ { "id": "auths.core.protocol", - "version": 17, + "version": 18, "classification": "frozen-meaning", "categories": [ "protocol-versions", @@ -87,7 +87,7 @@ "core/crates/auths-verifier/src", "core/spec/v1" ], - "sha256": "ae55274703779bb8ea5d2d625e6a25668335a776f099f7310864688803464227" + "sha256": "794c22e944bc30bbe1eba8cb94bc0ee41787524742529bd777bac9ce42777c32" }, { "id": "auths.frozen-bytes/architecture/dependency-graph.json", @@ -897,7 +897,7 @@ }, { "id": "auths.product.public-sdk-contract", - "version": 44, + "version": 45, "classification": "frozen-meaning", "categories": [ "rust-sdk-contract", @@ -915,7 +915,7 @@ "product/runtime/auths-runtime/src", "product/sdk/auths-sdk/src" ], - "sha256": "0215c0ebd55b39d44b565655e0bffd6e9251141d73b148de92869db6d5458b19" + "sha256": "cb498d849c47ea9224c932f96244a5551999ebd53fc487b41400514e84d9ac74" }, { "id": "auths.product.receipts", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 131, + "version": 132, "classification": "release-metadata", "categories": [ "package-names", @@ -1133,7 +1133,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "67fb5f2b1d734ac81c55e817c0273de91ff99f1ed82841177ca55dabd1fac836" + "sha256": "59a77d5c8089c1ffe7bfd3d2fa14c3add091ff11a38631468d834da58c13b797" } ] } From 30d9d7dbd90e7254c4fad2ef97b06ccd1b0f9703 Mon Sep 17 00:00:00 2001 From: bordumb Date: Mon, 17 Aug 2026 02:54:52 +0100 Subject: [PATCH 59/61] fix: keep node docs within public API --- product/runtime/auths-node/src/kernel.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product/runtime/auths-node/src/kernel.rs b/product/runtime/auths-node/src/kernel.rs index 6b283144..e6ed5474 100644 --- a/product/runtime/auths-node/src/kernel.rs +++ b/product/runtime/auths-node/src/kernel.rs @@ -8,8 +8,8 @@ //! inside `auths_verifier::verify`, and `tests/kernel_differential.rs` holds //! that equality against the canonical corpus. //! -//! The node issues no authority. `/v1/authority/create` and -//! `/v1/authority/delegate` refuse: see [`KernelRuntime::create`]. +//! The node issues no authority: its request dispatcher refuses both +//! `/v1/authority/create` and `/v1/authority/delegate`. use crate::{ api::NodeRuntime, From f64724ad232d8ba444b49b1c0208451fcbbc87f5 Mon Sep 17 00:00:00 2001 From: bordumb Date: Mon, 17 Aug 2026 04:12:48 +0100 Subject: [PATCH 60/61] ci: allow superseded runs to cancel --- .github/workflows/ci.yml | 38 +++---- formal/assurance-manifest-v1.toml | 6 +- .../qualification/aeneas/source-closure.json | 4 +- release/semantic-freeze-versions.toml | 10 +- release/semantic-freeze.json | 18 +-- xtask/ci-plan/src/lib.rs | 105 ++++++++++++++++++ 6 files changed, 143 insertions(+), 38 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c4c28cc..82fbecb9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -112,7 +112,7 @@ jobs: formal-update-gate: name: generated artifacts current - if: always() + if: ${{ !cancelled() }} needs: [ci-plan, formal-update-preflight] runs-on: ubuntu-latest steps: @@ -139,7 +139,7 @@ jobs: name: repository preflight needs: [ci-plan, formal-update-gate] if: >- - always() && + !cancelled() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' runs-on: ubuntu-latest @@ -160,7 +160,7 @@ jobs: name: authoritative implementation needs: [ci-plan, formal-update-gate, repository-preflight] if: >- - always() && + !cancelled() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && needs.repository-preflight.result == 'success' && @@ -227,7 +227,7 @@ jobs: name: formal-translation implementation needs: [ci-plan, formal-update-gate, repository-preflight] if: >- - always() && + !cancelled() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && needs.repository-preflight.result == 'success' && @@ -359,7 +359,7 @@ jobs: name: compliance implementation needs: [ci-plan, formal-update-gate, repository-preflight] if: >- - always() && + !cancelled() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && needs.repository-preflight.result == 'success' && @@ -417,7 +417,7 @@ jobs: name: dependencies implementation needs: [ci-plan, formal-update-gate, repository-preflight] if: >- - always() && + !cancelled() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && needs.repository-preflight.result == 'success' && @@ -452,7 +452,7 @@ jobs: name: secrets implementation needs: [ci-plan, formal-update-gate, repository-preflight] if: >- - always() && + !cancelled() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && needs.repository-preflight.result == 'success' && @@ -487,7 +487,7 @@ jobs: name: opentofu-live implementation needs: [ci-plan, formal-update-gate, repository-preflight] if: >- - always() && + !cancelled() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && needs.repository-preflight.result == 'success' && @@ -566,7 +566,7 @@ jobs: name: postgresql-live implementation needs: [ci-plan, formal-update-gate, repository-preflight] if: >- - always() && + !cancelled() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && needs.repository-preflight.result == 'success' && @@ -680,7 +680,7 @@ jobs: name: records-api-live implementation needs: [ci-plan, formal-update-gate, repository-preflight] if: >- - always() && + !cancelled() && needs.ci-plan.result == 'success' && needs.formal-update-gate.result == 'success' && needs.repository-preflight.result == 'success' && @@ -758,7 +758,7 @@ jobs: authoritative: name: authoritative - if: always() + if: ${{ !cancelled() }} needs: [ci-plan, authoritative-run] runs-on: ubuntu-latest steps: @@ -775,7 +775,7 @@ jobs: formal-translation: name: formal-translation - if: always() + if: ${{ !cancelled() }} needs: [ci-plan, formal-translation-run] runs-on: ubuntu-latest steps: @@ -793,7 +793,7 @@ jobs: compliance: name: compliance - if: always() + if: ${{ !cancelled() }} needs: [ci-plan, compliance-run] runs-on: ubuntu-latest steps: @@ -810,7 +810,7 @@ jobs: dependencies: name: dependencies - if: always() + if: ${{ !cancelled() }} needs: [ci-plan, dependencies-run] runs-on: ubuntu-latest steps: @@ -827,7 +827,7 @@ jobs: secrets: name: secrets - if: always() + if: ${{ !cancelled() }} needs: [ci-plan, secrets-run] runs-on: ubuntu-latest steps: @@ -844,7 +844,7 @@ jobs: opentofu-live: name: opentofu-live - if: always() + if: ${{ !cancelled() }} needs: [ci-plan, opentofu-live-run] runs-on: ubuntu-latest steps: @@ -861,7 +861,7 @@ jobs: postgresql-live: name: postgresql-live - if: always() + if: ${{ !cancelled() }} needs: [ci-plan, postgresql-live-run] runs-on: ubuntu-latest steps: @@ -878,7 +878,7 @@ jobs: records-api-live: name: records-api-live - if: always() + if: ${{ !cancelled() }} needs: [ci-plan, records-api-live-run] runs-on: ubuntu-latest steps: @@ -895,7 +895,7 @@ jobs: ci-qualified: name: CI qualified - if: always() + if: ${{ !cancelled() }} needs: - authoritative - formal-translation diff --git a/formal/assurance-manifest-v1.toml b/formal/assurance-manifest-v1.toml index 4bb0fad5..5e129395 100644 --- a/formal/assurance-manifest-v1.toml +++ b/formal/assurance-manifest-v1.toml @@ -6503,7 +6503,7 @@ sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" [[claims.evidence]] kind = "source-closure" artifact = "formal/qualification/aeneas/source-closure.json" -sha256 = "5166f5a29bcea85f90c44e22a89df2f4505c65de117f112757bc80cd6b74f135" +sha256 = "25ec3a68e50a63dc4e000da8f4477355b2b9018bc555164ea4d44a18aa830159" [[claims]] claim_id = "AP-FORMAL-RICH-055" @@ -6606,7 +6606,7 @@ sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" [[claims.evidence]] kind = "source-closure" artifact = "formal/qualification/aeneas/source-closure.json" -sha256 = "5166f5a29bcea85f90c44e22a89df2f4505c65de117f112757bc80cd6b74f135" +sha256 = "25ec3a68e50a63dc4e000da8f4477355b2b9018bc555164ea4d44a18aa830159" [[claims]] claim_id = "AP-FORMAL-RICH-056" @@ -6714,7 +6714,7 @@ sha256 = "28587f3637469e8d86b991ad74e3ad418d1d690011732be5b2a3f80fe33c93b2" [[claims.evidence]] kind = "source-closure" artifact = "formal/qualification/aeneas/source-closure.json" -sha256 = "5166f5a29bcea85f90c44e22a89df2f4505c65de117f112757bc80cd6b74f135" +sha256 = "25ec3a68e50a63dc4e000da8f4477355b2b9018bc555164ea4d44a18aa830159" [[claims]] claim_id = "AP-FORMAL-RICH-104" diff --git a/formal/qualification/aeneas/source-closure.json b/formal/qualification/aeneas/source-closure.json index 9aa2a23d..c13034f8 100644 --- a/formal/qualification/aeneas/source-closure.json +++ b/formal/qualification/aeneas/source-closure.json @@ -1,6 +1,6 @@ { "schema": "auths-proof-translation-source-closure/v2", - "digest": "0fbb1153de69314c95db7cb138b9f9bf0c2c885ab09a27164039aacd3fa0dad2", + "digest": "8ab669d3bd6e56897f79dffbdeb112adca9f304493023cb0225b0277d26000f8", "files": [ { "path": ".cargo/config.toml", @@ -189,7 +189,7 @@ }, { "path": "xtask/ci-plan/src/lib.rs", - "sha256": "42ca248e1c169063e3f782c0250a16f03b750a1baf3425f80d36deab411dc2db" + "sha256": "4eb58cb21ef8006b121121deaceda029bd66d4a6955fb40c12813fc3af758e9a" }, { "path": "xtask/ci-plan/src/main.rs", diff --git a/release/semantic-freeze-versions.toml b/release/semantic-freeze-versions.toml index f9146c20..da24e31e 100644 --- a/release/semantic-freeze-versions.toml +++ b/release/semantic-freeze-versions.toml @@ -1,4 +1,4 @@ -freeze_version = 133 +freeze_version = 135 # Semantic identity counters live outside the xtask source tree deliberately. # The formal source closure binds xtask's executable code, while the semantic @@ -16,10 +16,10 @@ freeze_version = 133 "auths.frozen-bytes/core/fixtures/v1/manifest.json" = 4 "auths.frozen-bytes/core/formal-vectors/v1/manifest.json" = 1 "auths.frozen-bytes/demos/benchmarks/profiles/release.toml" = 1 -"auths.frozen-bytes/formal/assurance-manifest-v1.toml" = 16 +"auths.frozen-bytes/formal/assurance-manifest-v1.toml" = 17 "auths.frozen-bytes/formal/qualification/aeneas/generated" = 6 "auths.frozen-bytes/formal/qualification/aeneas/qualification.toml" = 5 -"auths.frozen-bytes/formal/qualification/aeneas/source-closure.json" = 23 +"auths.frozen-bytes/formal/qualification/aeneas/source-closure.json" = 24 "auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json" = 1 "auths.frozen-bytes/product/conformance/v1/simplified-product-waist.json" = 2 "auths.frozen-bytes/product/fixtures/v1/bounded-policy/manifest.json" = 2 @@ -67,5 +67,5 @@ freeze_version = 133 "auths.product.simplified-waist" = 8 "auths.product.vocabulary" = 9 "auths.release.benchmark-contract" = 1 -"auths.release.evolution-contract" = 18 -"auths.release.public-surface" = 132 +"auths.release.evolution-contract" = 19 +"auths.release.public-surface" = 134 diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index d468a1b5..8eed97dd 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 133, + "freezeVersion": 135, "publicSurface": { "rustRoots": [ "auths", @@ -199,7 +199,7 @@ }, { "id": "auths.frozen-bytes/formal/assurance-manifest-v1.toml", - "version": 16, + "version": 17, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -207,7 +207,7 @@ "owners": [ "formal/assurance-manifest-v1.toml" ], - "sha256": "5ad03fd0b99091be06fd82784979aa34abdba91b0132f52d89783ba69402c81a" + "sha256": "c3918c9c4f4aa655efac439658f1e4ea3c2d9d70db562888d0c3926d465fa0aa" }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/generated", @@ -235,7 +235,7 @@ }, { "id": "auths.frozen-bytes/formal/qualification/aeneas/source-closure.json", - "version": 23, + "version": 24, "classification": "frozen-bytes", "categories": [ "canonical-generated-evidence" @@ -243,7 +243,7 @@ "owners": [ "formal/qualification/aeneas/source-closure.json" ], - "sha256": "919dff5a4d619f76e8d22884ed200b2c3838890d984bc16258b213f5897dca57" + "sha256": "57e23e20ee95ddd488bde6a21e7270e57c55517a205a6d05b9e2f7874f0e3cbd" }, { "id": "auths.frozen-bytes/product/conformance/v1/mechanism-profile-conformance.json", @@ -1013,7 +1013,7 @@ }, { "id": "auths.release.evolution-contract", - "version": 18, + "version": 19, "classification": "frozen-meaning", "categories": [ "version-axes", @@ -1034,11 +1034,11 @@ "release/fixtures/evolution", "xtask/src/evolution_policy.rs" ], - "sha256": "cc426b66fa161ef75e8115028d809b373ffa741d99363c88c9df9a25bc337c53" + "sha256": "8f74e3791acdd7e63b28b37ce812eff3ef5a339d5bfbe4ea81247af79dc5fe12" }, { "id": "auths.release.public-surface", - "version": 132, + "version": 134, "classification": "release-metadata", "categories": [ "package-names", @@ -1133,7 +1133,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "59a77d5c8089c1ffe7bfd3d2fa14c3add091ff11a38631468d834da58c13b797" + "sha256": "478566fd33b6f729c26b857cc96a5142baf85379f7e7de93890eb949df3ac3af" } ] } diff --git a/xtask/ci-plan/src/lib.rs b/xtask/ci-plan/src/lib.rs index 0d718af8..9ada59b0 100644 --- a/xtask/ci-plan/src/lib.rs +++ b/xtask/ci-plan/src/lib.rs @@ -520,6 +520,12 @@ fn generate_plan(options: &Options) -> Result<(), String> { let baseline = CostBaseline::load(&options.root.join(DEFAULT_BASELINE))?; let model = RepositoryModel::load(&options.root)?; validate_manifest(&loaded.manifest)?; + if options.workflow == "ci" { + let path = options.root.join(".github/workflows/ci.yml"); + let source = fs::read_to_string(&path) + .map_err(|error| format!("could not read {}: {error}", path.display()))?; + validate_ci_cancellation_policy(&source)?; + } let comprehensive = loaded .manifest @@ -1199,6 +1205,9 @@ fn validate_repository( for workflow_file in workflow_files { let source = fs::read_to_string(root.join(workflow_file)) .map_err(|error| format!("could not read {workflow_file}: {error}"))?; + if workflow_file == ".github/workflows/ci.yml" { + validate_ci_cancellation_policy(&source)?; + } let actual_jobs = workflow_job_ids(&source); let declared_jobs: BTreeSet<_> = manifest .workflow_jobs @@ -1215,6 +1224,65 @@ fn validate_repository( Ok(()) } +fn validate_ci_cancellation_policy(source: &str) -> Result<(), String> { + if !source.contains("cancel-in-progress: true") { + return Err("CI must cancel superseded workflow runs".to_owned()); + } + + let mut in_jobs = false; + let mut current_job = None; + let mut in_job_condition = false; + for line in source.lines() { + if line == "jobs:" { + in_jobs = true; + continue; + } + if !in_jobs { + continue; + } + if !line.starts_with(' ') && !line.trim().is_empty() { + break; + } + if line.starts_with(" ") + && !line.starts_with(" ") + && let Some(job) = line + .strip_prefix(" ") + .and_then(|line| line.strip_suffix(':')) + { + current_job = Some(job); + in_job_condition = false; + continue; + } + if let Some(condition) = line.strip_prefix(" if: ") { + in_job_condition = condition == ">-" || condition == "|"; + if condition.contains("always()") { + return Err(format!( + "CI job `{}` uses cancellation-resistant `always()`; use `!cancelled()`", + current_job.unwrap_or("unknown") + )); + } + continue; + } + if in_job_condition { + let Some(condition) = line.strip_prefix(" ") else { + in_job_condition = false; + continue; + }; + if condition.starts_with(' ') { + in_job_condition = false; + continue; + } + if condition.contains("always()") { + return Err(format!( + "CI job `{}` uses cancellation-resistant `always()`; use `!cancelled()`", + current_job.unwrap_or("unknown") + )); + } + } + } + Ok(()) +} + fn workflow_job_ids(source: &str) -> BTreeSet<&str> { let mut in_jobs = false; let mut jobs = BTreeSet::new(); @@ -2209,6 +2277,43 @@ serde = "2" assert!(usage().contains("auths-ci-plan formal-update-artifact ")); } + #[test] + fn ci_jobs_allow_superseding_runs_to_cancel_them() { + let workflow = r#"name: CI +concurrency: + cancel-in-progress: true +jobs: + formal: + if: >- + !cancelled() && + needs.preflight.result == 'success' + steps: + - name: Preserve diagnostics + if: always() + run: echo diagnostics +"#; + validate_ci_cancellation_policy(workflow) + .expect("job conditions must cancel while diagnostic steps may still run"); + } + + #[test] + fn ci_jobs_cannot_resist_superseding_run_cancellation() { + let workflow = r#"name: CI +concurrency: + cancel-in-progress: true +jobs: + formal: + if: >- + always() && + needs.preflight.result == 'success' + steps: + - run: cargo xtask formal qualify aeneas +"#; + let error = validate_ci_cancellation_policy(workflow) + .expect_err("job-level always must not defeat concurrency cancellation"); + assert!(error.contains("cancellation-resistant `always()`")); + } + #[test] fn unrelated_workspace_and_lock_packages_do_not_drift_formal_closure() { let root = Path::new("/repo"); From 33670e36d10b0ef4564ef92056ca61eb41691d6e Mon Sep 17 00:00:00 2001 From: bordumb Date: Mon, 17 Aug 2026 04:34:25 +0100 Subject: [PATCH 61/61] fix: keep Python docs within public API --- bindings/python/src/lib.rs | 4 ++-- release/semantic-freeze-versions.toml | 6 +++--- release/semantic-freeze.json | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index 3d89652f..b0a37f30 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -3,8 +3,8 @@ //! This crate is a transport, not a tier. It carries meaning that Rust already //! owns across the pyo3 call boundary, and it defines none of its own: //! -//! * Failures cross as [`errors::NativeAuthsError`], carrying the stable code -//! and the registry's own effect state, retry class, and recommended action. +//! * Failures cross as native Python exceptions carrying the stable code and +//! the registry's own effect state, retry class, and recommended action. //! * It projects no generic reference vertical. `auths-profile-domains` is //! tier-1 reference Rust and is not reachable from Python, so a Python caller //! cannot introduce a vertical whose canonical form lives in Python. diff --git a/release/semantic-freeze-versions.toml b/release/semantic-freeze-versions.toml index da24e31e..7f589e08 100644 --- a/release/semantic-freeze-versions.toml +++ b/release/semantic-freeze-versions.toml @@ -1,4 +1,4 @@ -freeze_version = 135 +freeze_version = 136 # Semantic identity counters live outside the xtask source tree deliberately. # The formal source closure binds xtask's executable code, while the semantic @@ -48,7 +48,7 @@ freeze_version = 135 "auths.frozen-bytes/product/integrations/auths-stripe/fixtures/v1/manifest.sha256.json" = 1 "auths.identity.protocol" = 31 "auths.modular-components" = 8 -"auths.portable-abi-bindings" = 55 +"auths.portable-abi-bindings" = 56 "auths.product.bounded-domains" = 7 "auths.product.bounded-policy" = 2 "auths.product.configuration-commitments" = 1 @@ -68,4 +68,4 @@ freeze_version = 135 "auths.product.vocabulary" = 9 "auths.release.benchmark-contract" = 1 "auths.release.evolution-contract" = 19 -"auths.release.public-surface" = 134 +"auths.release.public-surface" = 135 diff --git a/release/semantic-freeze.json b/release/semantic-freeze.json index 8eed97dd..40bdb91d 100644 --- a/release/semantic-freeze.json +++ b/release/semantic-freeze.json @@ -1,6 +1,6 @@ { "schema": "auths.semantic-freeze/1", - "freezeVersion": 135, + "freezeVersion": 136, "publicSurface": { "rustRoots": [ "auths", @@ -620,7 +620,7 @@ }, { "id": "auths.portable-abi-bindings", - "version": 55, + "version": 56, "classification": "frozen-meaning", "categories": [ "portable-abi", @@ -637,7 +637,7 @@ "core/crates/auths-model/src/lib.rs", "core/spec/v1/auths-proof.cddl" ], - "sha256": "bee86e4b434721b6eedb065416cf6c4c44e2fbfe062a00692fc7b19e8591cafa" + "sha256": "d578e912196bc03ac2c4bcd776692477eb109b0a63ace9a62aa786a18fcffad0" }, { "id": "auths.product.bounded-domains", @@ -1038,7 +1038,7 @@ }, { "id": "auths.release.public-surface", - "version": 134, + "version": 135, "classification": "release-metadata", "categories": [ "package-names", @@ -1133,7 +1133,7 @@ "xtask/src/release_control.rs", "xtask/src/semantic_freeze.rs" ], - "sha256": "478566fd33b6f729c26b857cc96a5142baf85379f7e7de93890eb949df3ac3af" + "sha256": "e72e4df29318aa8945962cbbfff93db3e5f8297bbc50e7cd794088e95bb70bc4" } ] }