{"record":{"id":"c5bfd823ade29337","repo":"astrid-runtime/astrid","slug":"signing-key-must-be-exactly-32-raw-bytes-got","errorCode":null,"errorMessage":"signing key {} must be exactly 32 raw bytes (got {})","messagePattern":"signing key (.+?) must be exactly 32 raw bytes \\(got (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/distro/seal.rs","lineNumber":198,"sourceCode":"        if candidate.is_file() {\n            return Ok(candidate);\n        }\n        bail!(\"{} contains no Distro.toml\", p.display());\n    }\n    if p.is_file() {\n        return Ok(p.to_path_buf());\n    }\n    bail!(\n        \"seal requires a local Distro.toml path or its directory; {distro:?} is not a file or directory\"\n    )\n}\n\n/// Load a 32-byte raw ed25519 secret key from `path`. Never logged.\nfn load_signing_key(path: &Path) -> anyhow::Result<astrid_crypto::KeyPair> {\n    let bytes = std::fs::read(path)\n        .with_context(|| format!(\"failed to read signing key {}\", path.display()))?;\n    if bytes.len() != 32 {\n        bail!(\n            \"signing key {} must be exactly 32 raw bytes (got {})\",\n            path.display(),\n            bytes.len()\n        );\n    }\n    astrid_crypto::KeyPair::from_secret_key(&bytes)\n        .map_err(|e| anyhow::anyhow!(\"invalid ed25519 secret key: {e}\"))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    struct CurrentDirGuard(PathBuf);\n\n    impl CurrentDirGuard {\n        fn set(path: &Path) -> Self {\n            let original = std::env::current_dir().unwrap();","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/distro/seal.rs#L180-L216","documentation":"`load_signing_key` reads a file expected to contain a raw 32-byte ed25519 secret key and refuses anything else. The library enforces the exact ed25519 seed length before constructing a `KeyPair`; keys of any other size (e.g. base64 text, PEM, or 64-byte expanded keys) are rejected so signing never proceeds with a malformed key. The key file path is never logged to avoid leaking secrets.","triggerScenarios":"Calling `load_signing_key(path)` (via `run_seal` or the test) when the file at `path` does not exist, is empty, is a text/PEM/base64-encoded key instead of raw bytes, or is a 64-byte (seed+public) key file.","commonSituations":"Exporting a key from an SSH/OpenSSL tool that writes PEM, copy-pasting a key into a file (adding trailing newline is fine at 33 bytes — still fails), or pointing at the wrong file such as the public key or a 64-byte pkcs8 artifact.","solutions":["Regenerate or convert the key to exactly 32 raw bytes (e.g. strip a trailing newline, or re-export the seed portion of a 64-byte key).","If the key is base64/hex encoded, decode it to raw 32 bytes before staging: `base64 -d key.b64 > key.raw`.","Verify the file: `wc -c keyfile` must print 32, and confirm it is the secret key, not the public key."],"exampleFix":"// before\n$ head -c 64 expanded.key > signing.key   # 64 bytes -> error\n// after\n$ head -c 32 expanded.key > signing.key   # raw 32-byte ed25519 seed","handlingStrategy":"validation","validationCode":"let bytes = std::fs::read(path)?;\nif bytes.len() != 32 {\n    return Err(anyhow!(\"signing key must be 32 raw bytes, got {}\", bytes.len()));\n}","typeGuard":"fn is_raw_ed25519_seed(bytes: &[u8]) -> bool { bytes.len() == 32 }","tryCatchPattern":"match load_signing_key(path) {\n    Ok(kp) => /* sign */,\n    Err(e) => eprintln!(\"check key file is exactly 32 raw bytes: {e:#}\"),\n}","preventionTips":["Generate keys with the project's own keygen so they are 32 raw bytes","Never store PEM/base64 text where a raw key file is expected","Check file size with `wc -c` before use","Keep secret-key and public-key files clearly named and separated"],"tags":["signing","ed25519","key-format","file-size"],"backgroundTag":"invalid-argument-value","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}