{"record":{"id":"1d527fb62b4100c3","repo":"astrid-runtime/astrid","slug":"capsule-operation-is-outside-the-explicit-w","errorCode":null,"errorMessage":"capsule {operation} '{}' is outside the explicit workspace portal and has no durable registry authority","messagePattern":"capsule (.+?) '(.+?)' is outside the explicit workspace portal and has no durable registry authority","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/capsule_materialization.rs","lineNumber":141,"sourceCode":"        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,\n                principal,\n                manifest,\n                &snapshot,\n            )?;\n            return Ok(Some(BoundMaterialization {\n                snapshot,\n                runtime_dir,\n                manifest: bound_manifest,\n            }));\n        }\n        if !self.verify_registry_materialization(dir, principal, manifest)? {\n            if self.principal_store.is_some()\n                && !dir.starts_with(self.workspace_selection.state_dir())\n            {\n                anyhow::bail!(\n                    \"capsule {operation} '{}' is outside the explicit workspace portal and \\\n                     has no durable registry authority\",\n                    manifest.package.name\n                );\n            }\n            self.verify_installed_authority_for_runtime(dir, manifest).map_err(|error| {\n                anyhow::anyhow!(\n                    \"capsule {operation} '{}' exceeds or cannot prove its installed authority: {error:#}\",\n                    manifest.package.name\n                )\n            })?;\n        }\n        Ok(None)\n    }\n\n    /// Repair a stale or missing cache generation from one exact snapshot.\n    #[cfg(not(all(target_arch = \"wasm32\", target_os = \"unknown\")))]\n    pub(crate) fn ensure_published_materialization(","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/capsule_materialization.rs#L123-L159","documentation":"capture_bound_materialization first tries to bind the capsule to a durable registry publication (published_capsule_snapshot + repair) and then falls back to verifying the directory's installed authority. If neither applies — the registry verification fails AND a durable principal store exists but the target directory lies outside the explicit workspace state dir — the kernel refuses the operation: the capsule would run from an arbitrary path with no durable authority backing it, bypassing the workspace portal.","triggerScenarios":"load_capsule or prepare_runtime_replacement is called with a `dir` such that published_capsule_snapshot finds no publication, verify_registry_materialization returns false, self.principal_store is Some, and dir does not start with self.workspace_selection.state_dir() — e.g. loading a capsule from /tmp/my-capsule or another project folder while a durable registry is configured.","commonSituations":"A developer points the loader at a local checkout/build-output directory instead of an installed or workspace-portal capsule; the workspace state dir is misconfigured (different path than where capsules were installed); a capsule was installed before the durable principal store was enabled and now lives outside the portal; CI copies capsule directories to scratch paths outside the workspace.","solutions":["Load the capsule from inside the explicit workspace portal (a path under workspace_selection.state_dir()) or install/publish it into the durable registry so verify_registry_materialization succeeds.","Publish the capsule (register its snapshot and authority with the principal store) before attempting load_capsule/prepare_runtime_replacement on that path.","Verify the workspace state_dir configuration matches where the capsule is actually materialized; a path-prefix mismatch alone triggers this bail.","If a locally built capsule must run, route it through the workspace portal directory (copy/materialize it under state_dir) rather than passing its build path directly."],"exampleFix":"// before: loading a capsule from an arbitrary path\nlet manifest = kernel.load_capsule(Path::new(\"/tmp/build/my-capsule\"), &principal)?;\n\n// after: load from inside the workspace portal state dir\nlet portal_dir = kernel.workspace_selection.state_dir().join(\"my-capsule\");\nlet manifest = kernel.load_capsule(&portal_dir, &principal)?;","handlingStrategy":"validation","validationCode":"// Confirm the capsule lives in the workspace portal before loading\nlet state_dir = kernel.workspace_selection.state_dir();\nlet dir = std::fs::canonicalize(&dir)?;\nlet state_dir = std::fs::canonicalize(state_dir)?;\nif !dir.starts_with(state_dir) {\n    anyhow::bail!(\"capsule must be published or placed under the workspace state dir\");\n}","typeGuard":"fn inside_workspace(dir: &Path, state_dir: &Path) -> bool {\n    match (std::fs::canonicalize(dir), std::fs::canonicalize(state_dir)) {\n        (Ok(d), Ok(s)) => d.starts_with(s),\n        _ => false,\n    }\n}","tryCatchPattern":"match kernel.load_capsule(&dir, &principal) {\n    Err(e) if e.to_string().contains(\"outside the explicit workspace portal\") => {\n        publish_to_registry(&manifest)?; // or move capsule under state_dir\n        kernel.load_capsule(&dir, &principal)\n    }\n    other => other,\n}","preventionTips":["Always publish capsules to the durable registry before loading them by path.","Configure the workspace state_dir to exactly match the installation root.","Never point loaders at build-output or scratch directories when a principal store is active.","Re-install capsules created before the durable registry was enabled."],"tags":["security","authorization","path-validation","workspace"],"backgroundTag":"permission-denied","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"}