{"record":{"id":"b3f0eca144998e4b","repo":"astrid-runtime/astrid","slug":"capsule-install-authority-was-not-approved","errorCode":null,"errorMessage":"capsule install authority was not approved","messagePattern":"capsule install authority was not approved","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/capsule/install/authority.rs","lineNumber":78,"sourceCode":"        for expansion in &inspection.capability_expansions {\n            let semantic = semantic_expansion(expansion);\n            eprintln!(\"    - {}\", semantic.action);\n            if !semantic.scope.is_empty() {\n                eprintln!(\"      Scope: {}\", semantic.scope.join(\"; \"));\n            }\n            eprintln!(\"      Impact: {}\", semantic.impact);\n        }\n    }\n    eprint!(\"Approve this exact install once? [y/N] \");\n    std::io::Write::flush(&mut std::io::stderr())?;\n    let mut answer = String::new();\n    std::io::stdin().read_line(&mut answer)?;\n    if matches!(answer.trim().to_ascii_lowercase().as_str(), \"y\" | \"yes\") {\n        Ok(AuthorityDecision::ExplicitApproval {\n            content_digest: inspection.content_digest.clone(),\n        })\n    } else {\n        bail!(\"capsule install authority was not approved\")\n    }\n}\n\npub(super) fn daemon_install_authority(\n    source: &str,\n    principal: &astrid_core::PrincipalId,\n    prompt: &ManualInstallOptions,\n) -> anyhow::Result<CapsuleInstallAuthority> {\n    let home = AstridHome::resolve()?;\n    let path = Path::new(source.strip_prefix(\"file://\").unwrap_or(source));\n    let inspection = if path.is_file() {\n        inspect_archive_for_principal_with_layout(\n            path,\n            &home,\n            principal,\n            false,\n            crate::workspace_layout::current(),\n        )?","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/capsule/install/authority.rs#L60-L96","documentation":"When no automated or flag-based approval applies, authority_decision prints the capsule's provenance, signer, content digest, and any new capability expansions, then asks 'Approve this exact install once? [y/N]'. If the operator's stdin answer is anything other than y/yes (case-insensitive), the install is aborted with this error. It is the normal refusal outcome of the interactive consent gate, not a malfunction.","triggerScenarios":"Interactive capsule install reaches the approval prompt (non-LocalRuntime provenance, not BATCH_MODE, no --approve-untrusted, no --yes) and stdin yields a line that does not trim/lowercase to \"y\" or \"yes\" — e.g. pressing Enter, typing \"n\", \"no\", or EOF (empty read, as when stdin is /dev/null or a closed pipe).","commonSituations":"User intentionally declines an untrusted capsule; running the command in a non-interactive shell where stdin returns EOF immediately, so the read yields an empty string and counts as refusal; scripting around the prompt by piping nothing; user misreads the prompt and types \"no\" expecting a different flow.","solutions":["Re-run the install interactively and answer `y` or `yes` at the 'Approve this exact install once?' prompt if you trust the artifact.","Ensure stdin is a TTY or an open pipe; EOF (e.g. `cmd < /dev/null` or nohup without stdin) is treated as refusal.","For scripted trusted installs, pass `--approve-untrusted` (after reviewing the digest) instead of relying on piped input.","Verify the capsule's signer and content digest printed before the prompt; if they are unexpected, keep declining and investigate the source.","For operator distribution scenarios, use the batch mode path which grants OperatorDistribution authority without prompting."],"exampleFix":"# before (non-interactive stdin, instant refusal)\necho -n | astrid capsule install ./x.capsule\n# after\nprintf 'y\\n' | astrid capsule install ./x.capsule   # or run interactively / use --approve-untrusted","handlingStrategy":"try-catch","validationCode":"# guard: only run when interactive, or pre-approve via flag\n[ -t 0 ] || { echo \"non-interactive stdin: pass --approve-untrusted\"; exit 1; }","typeGuard":null,"tryCatchPattern":"match authority_decision(&inspection, &prompt) {\n    Ok(decision) => install_with(decision),\n    Err(e) if e.to_string().contains(\"not approved\") => eprintln!(\"install declined by operator\"),\n    Err(e) => return Err(e),\n}","preventionTips":["Run installs from an interactive terminal when you intend to answer the approval prompt.","Pipe an explicit 'y\\n' only when you have verified the artifact's digest.","Use --approve-untrusted for scripted trusted installs instead of relying on stdin.","Check that the capsule's signer and capability expansions match expectations before approving."],"tags":["security","approval","interactive","cli"],"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"}