{"record":{"id":"6136fd5cef1be76c","repo":"BloopAI/vibe-kanban","slug":"server-signing-key-file-has-invalid-length-expect","errorCode":null,"errorMessage":"server signing key file has invalid length (expected 32 bytes)","messagePattern":"server signing key file has invalid length \\(expected 32 bytes\\)","errorType":"validation","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/relay-control/src/signing.rs","lineNumber":145,"sourceCode":"\n#[derive(Clone)]\npub struct RelaySigningService {\n    sessions: Arc<RwLock<HashMap<Uuid, RelaySigningSession>>>,\n    server_signing_key: Arc<SigningKey>,\n}\n\nimpl RelaySigningService {\n    pub fn new(server_signing_key: SigningKey) -> Self {\n        Self {\n            sessions: Arc::new(RwLock::new(HashMap::new())),\n            server_signing_key: Arc::new(server_signing_key),\n        }\n    }\n\n    pub fn load_or_generate(key_path: &Path) -> io::Result<Self> {\n        let key = if let Ok(bytes) = fs::read(key_path) {\n            let arr: [u8; 32] = bytes.try_into().map_err(|_| {\n                io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    \"server signing key file has invalid length (expected 32 bytes)\",\n                )\n            })?;\n            SigningKey::from_bytes(&arr)\n        } else {\n            let key = SigningKey::generate(&mut OsRng);\n\n            if let Some(parent) = key_path.parent() {\n                fs::create_dir_all(parent)?;\n            }\n\n            let tmp = key_path.with_extension(\"tmp\");\n            fs::write(&tmp, key.to_bytes())?;\n\n            #[cfg(unix)]\n            {\n                use std::os::unix::fs::PermissionsExt;","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/crates/relay-control/src/signing.rs#L127-L163","documentation":"load_or_generate loads the relay server's Ed25519 signing key from a file that must contain exactly 32 bytes of raw key material. If the file exists but its length differs (corruption, truncation, base64/PEM-encoded text, wrong file), the bytes cannot fit [u8; 32] and this error is returned.","triggerScenarios":"Relay control startup with a key_path whose file content is not exactly 32 raw bytes — e.g. a key saved as hex/base64 text, a truncated file, or a different key file placed at the expected path.","commonSituations":"Manually generating a key and writing it encoded (openssl output) instead of raw; a partially failed write leaving a truncated file; mounting a config secret that is PEM/ASCII-armored; copying the wrong file to the key path.","solutions":["Delete the invalid key file and let load_or_generate regenerate a fresh one (re-register any clients that pinned the old key).","If the key must be preserved, decode it and write exactly 32 raw bytes to the file (no hex, base64, or newlines).","Verify the file: wc -c < key_path must print 32.","Ensure secret mounts/copy scripts don't inject encoding or trailing newlines into the key file."],"exampleFix":"// before (writes base64 text)\nopenssl genpkey ... | base64 > key_path\n// after (writes raw 32 bytes)\nopenssl genpkey -algorithm ed25519 | openssl pkey -outform DER -out /tmp/k.der\ntail -c 32 /tmp/k.der > key_path","handlingStrategy":"validation","validationCode":"fn key_file_valid(path: &Path) -> bool {\n    std::fs::metadata(path).map(|m| m.len() == 32).unwrap_or(false)\n}\nif !key_file_valid(key_path) {\n    std::fs::remove_file(key_path).ok(); // let load_or_generate regenerate\n}","typeGuard":"fn is_raw_32_byte_key(bytes: &[u8]) -> bool {\n    bytes.len() == 32\n}","tryCatchPattern":"match SigningKey::load_or_generate(key_path) {\n    Err(e) if e.kind() == ErrorKind::InvalidData\n        && e.to_string().contains(\"invalid length\") => {\n        std::fs::remove_file(key_path)?;\n        SigningKey::load_or_generate(key_path)? // regenerated fresh\n    }\n    other => other,\n}","preventionTips":["Never write encoded (hex/base64/PEM) key material to the key path.","Verify key files are exactly 32 bytes after provisioning scripts run.","Backup the key file atomically so it can never be truncated mid-write."],"tags":["cryptography","signing-key","configuration"],"backgroundTag":"invalid-signing-key","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}