{"record":{"id":"abb9d3cb5fcff8c5","repo":"astrid-runtime/astrid","slug":"materialized-capsule-manifest-exceeds-durable-auth","errorCode":null,"errorMessage":"materialized capsule manifest exceeds durable authority approval","messagePattern":"materialized capsule manifest exceeds durable authority approval","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/capsule_materialization.rs","lineNumber":108,"sourceCode":"        if actual.directories != expected_directories {\n            anyhow::bail!(\"materialized capsule directory inventory differs from durable package\");\n        }\n        for (relative, expected) in &expected_files {\n            let materialized =\n                Self::read_projection_file_nofollow(&dir.join(relative)).map_err(|error| {\n                    anyhow::anyhow!(\"read materialized capsule member {relative}: {error}\")\n                })?;\n            if materialized != *expected {\n                anyhow::bail!(\n                    \"materialized capsule member {relative} differs from durable archive\"\n                );\n            }\n        }\n        let expansions = manifest\n            .capabilities\n            .expansions_from(&verified.authority().approved_capabilities);\n        if !expansions.is_empty() {\n            anyhow::bail!(\"materialized capsule manifest exceeds durable authority approval\");\n        }\n        Ok(())\n    }\n\n    /// Bind durable activation or authorize the explicit workspace portal.\n    #[cfg(not(all(target_arch = \"wasm32\", target_os = \"unknown\")))]\n    pub(crate) fn capture_bound_materialization(\n        &self,\n        dir: &Path,\n        principal: &astrid_core::principal::PrincipalId,\n        manifest: &astrid_capsule_types::manifest::CapsuleManifest,\n        operation: &str,\n    ) -> anyhow::Result<Option<BoundMaterialization>> {\n        let snapshot = self.published_capsule_snapshot(principal, manifest)?;\n        if let Some(snapshot) = snapshot {\n            let runtime_dir = self.published_cache_target(principal, manifest, &snapshot)?;\n            let bound_manifest = self.repair_published_materialization(\n                &runtime_dir,","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/capsule_materialization.rs#L90-L126","documentation":"As the final verification step, the materialized manifest's capabilities are compared via capabilities::expansions_from against the approved_capabilities recorded in the durable authority document. This bail fires when the Capsule.toml being bound requests capabilities beyond what the signing authority actually approved — i.e. the manifest is an expansion (privilege escalation) relative to the durable grant, so the kernel refuses to bind or activate it.","triggerScenarios":"verify_published_materialization is invoked with a manifest whose `capabilities` struct contains grants not present/covered in verified.authority().approved_capabilities — typically after a Capsule.toml was modified post-signing, an authority document was replaced with a narrower one, or the caller passed a manifest from a newer version against an authority approved for an older, less-privileged version.","commonSituations":"A developer adds a new capability (e.g. network or filesystem access) to Capsule.toml locally without re-approval; a package is republished with expanded capabilities but the durable authority document was not regenerated; a downgrade or version pin causes the registry's authority to authorize fewer capabilities than the manifest declares.","solutions":["Re-obtain or regenerate the durable authority: have the authority re-approve the expanded capability set and republish the capsule so approved_capabilities covers the manifest.","Remove the unapproved capability entries from the manifest so it fits within approved_capabilities, then re-publish/re-materialize.","Check which expansions were rejected: compute manifest.capabilities.expansions_from(&approved) yourself to see the offending grants before changing anything.","If the capsule is intended to be less privileged, align the manifest version with the one the authority document was issued for (the version/name are checked earlier in the same function)."],"exampleFix":"// before: manifest requests more than the authority approved\n# Capsule.toml\n[capabilities]\nnetwork = [\"api.example.com\"]\nstorage = [\"secrets/*\"]   # not in approved_capabilities\n\n// after: only declare what the authority signed for, or re-approve\n[capabilities]\nnetwork = [\"api.example.com\"]","handlingStrategy":"validation","validationCode":"// Check for capability expansion before binding the capsule\nlet expansions = manifest.capabilities\n    .expansions_from(&verified_authority.approved_capabilities);\nif !expansions.is_empty() {\n    anyhow::bail!(\"manifest expands beyond authority: {expansions:?}\");\n}","typeGuard":null,"tryCatchPattern":"match kernel.load_capsule(&dir, &principal) {\n    Err(e) if e.to_string().contains(\"exceeds durable authority approval\") => {\n        // request re-approval or trim capabilities in Capsule.toml\n        request_authority_reapproval(&manifest)?;\n    }\n    other => other.map(|_| ()),\n}","preventionTips":["Regenerate the authority document whenever you add capabilities to Capsule.toml.","Run capabilities.expansions_from locally in CI to catch unapproved grants before publish.","Keep manifest versions aligned with the authority document version that approved them.","Review capability diffs as part of the release checklist."],"tags":["security","capabilities","authorization","privilege-escalation"],"backgroundTag":"insufficient-permissions","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}