{"record":{"id":"9c645d31e52026cb","repo":"jdx/mise","slug":"encrypt-an-external-dotfile-source-instead-of-conf","errorCode":null,"errorMessage":"encrypt an external dotfile source instead of configuration: {path}","messagePattern":"encrypt an external dotfile source instead of configuration: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/sync/files.rs","lineNumber":339,"sourceCode":"        .map(|recipient| {\n            agecrypt::parse_recipient_mode(recipient, interactive)?\n                .ok_or_else(|| eyre::eyre!(\"invalid age recipient: {recipient}\"))\n        })\n        .collect::<Result<Vec<_>>>()?;\n    encrypt(repo, path, object, &scheme, &recipients)\n}\n\n/// Encrypt bytes before they enter Git. Callers may store only the returned\n/// envelope, never the input or the decrypted payload in repository objects.\npub(crate) fn encode(\n    path: &str,\n    mode: &str,\n    content: &[u8],\n    scheme: &str,\n    recipients: &[Box<dyn age::Recipient + Send>],\n) -> Result<Vec<u8>> {\n    if control_file(path) {\n        bail!(\"encrypt an external dotfile source instead of configuration: {path}\");\n    }\n    if !matches!(mode, \"100644\" | \"100755\" | \"120000\") {\n        bail!(\"unsupported encrypted file mode: {path}\");\n    }\n    let inner = Plaintext {\n        path: path.into(),\n        mode: mode.into(),\n        scheme: scheme.into(),\n        content: Bytes(content.to_vec()),\n    };\n    let bytes = rmp_serde::to_vec_named(&inner)?;\n    let outer = Envelope {\n        path: path.into(),\n        mode: mode.into(),\n        scheme: scheme.into(),\n        ciphertext: Bytes(agecrypt::encrypt_bytes(&bytes, recipients)?),\n    };\n    let mut encoded = MAGIC.to_vec();","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/sync/files.rs#L321-L357","documentation":"encode() refuses to encrypt a path that control_file() identifies as mise setup configuration. Same policy as the decrypt-side guard: only external dotfile sources may be encrypted, so the setup store remains bootstrappable. The error fires before any recipients/ciphertext work is done.","triggerScenarios":"Calling encrypt() (and the audit/test paths rechecking policy) with a path for which control_file(path) returns true — i.e. trying to encrypt the setup repository's own configuration.","commonSituations":"Over-broad include globs matching mise's config files; user explicitly listing the setup config as an encrypted dotfile source; tooling that walks the whole store and encrypts everything.","solutions":["Exclude setup configuration paths from encrypted sources (tighten globs or remove the explicit entry).","Track configuration files as plain files in the setup store instead of encrypted ones.","Split configuration out of the dotfile source directory being encrypted."],"exampleFix":"# before\ndotfiles.enc_globs = [\"**/*\"]\n# after\ndotfiles.enc_globs = [\".bashrc\", \".gitconfig\"]  # exclude mise setup config","handlingStrategy":"validation","validationCode":"let sources: Vec<_> = config.dotfiles.sources\n    .into_iter()\n    .filter(|p| !control_file(p))\n    .collect();","typeGuard":null,"tryCatchPattern":"match encrypt(path, mode, content, scheme, recipients) {\n    Ok(bytes) => store(bytes),\n    Err(e) if e.to_string().starts_with(\"encrypt an external dotfile source\") => {\n        eprintln!(\"skipping mise config file {path}\");\n        Ok(())\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Enumerate encrypted sources explicitly rather than with catch-all globs.","Run a dry-run listing of files to be encrypted and inspect for config paths.","Keep setup configuration in a directory excluded from dotfile sync."],"tags":["encryption","configuration","policy"],"backgroundTag":"unsupported-operation","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}