{"record":{"id":"84c400daff5f1966","repo":"astrid-runtime/astrid","slug":"decode-public-key-hex-e","errorCode":null,"errorMessage":"decode public key hex: {e}","messagePattern":"decode public key hex: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/keypair.rs","lineNumber":616,"sourceCode":"    SysRng\n        .try_fill_bytes(&mut bytes)\n        .expect(\"OS CSPRNG unavailable while generating default keypair name\");\n    format!(\"key-{}\", hex::encode(bytes))\n}\n\nfn fingerprint_pubkey(hex_pub: &str) -> Result<String> {\n    PublicKeyFingerprint::from_ed25519_hex(hex_pub)\n        .map(PublicKeyFingerprint::into_inner)\n        .map_err(|e| anyhow::anyhow!(\"fingerprint Ed25519 public key: {e}\"))\n}\n\n/// Convert a 64-char hex ed25519 public key into the `ed25519:<base64>`\n/// wire form that `[distro.signing].pubkey`, `astrid distro seal`, and\n/// the distro trust store consume. Reuses `astrid-crypto`'s encoder so\n/// the base64 variant matches the verifier byte-for-byte.\nfn pubkey_hex_to_wire(pub_hex: &str) -> Result<String> {\n    let pk = astrid_crypto::PublicKey::from_hex(pub_hex.trim())\n        .map_err(|e| anyhow::anyhow!(\"decode public key hex: {e}\"))?;\n    Ok(format!(\"ed25519:{}\", pk.to_base64()))\n}\n\n/// Encode a 32-byte ed25519 public key in the `OpenSSH` wire format\n/// (`ssh-ed25519 <base64>` — RFC 8709 §4). Lets operators paste the\n/// same key into `authorized_keys` if they want to reuse it for SSH.\n/// The body is a length-prefixed type tag followed by the key.\nfn encode_openssh_ed25519(pubkey: &[u8]) -> String {\n    use base64::Engine;\n    let mut blob = Vec::with_capacity(4 + 11 + 4 + 32);\n    let typ = b\"ssh-ed25519\";\n    blob.extend_from_slice(&u32::try_from(typ.len()).unwrap_or(0).to_be_bytes());\n    blob.extend_from_slice(typ);\n    blob.extend_from_slice(&u32::try_from(pubkey.len()).unwrap_or(0).to_be_bytes());\n    blob.extend_from_slice(pubkey);\n    format!(\n        \"ssh-ed25519 {}\",\n        base64::engine::general_purpose::STANDARD.encode(&blob)","sourceCodeStart":598,"sourceCodeEnd":634,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/keypair.rs#L598-L634","documentation":"pubkey_hex_to_wire converts a 64-char hex Ed25519 public key into the `ed25519:<base64>` wire form using astrid_crypto::PublicKey::from_hex. If the hex string cannot be decoded into a 32-byte public key, the decode error is rethrown as \"decode public key hex: {e}\".","triggerScenarios":"Called with a string that is not exactly 64 valid hex characters: wrong length, invalid hex digits, embedded whitespace inside the string, empty input, or a value already in base64/wire form.","commonSituations":"Pasting a `ed25519:<base64>` key where hex is expected (double conversion); truncated keys from terminal copy/paste; keys read from config with quotes or prefixes left in.","solutions":["Ensure the input is the raw 64-hex-char public key; if you have the wire form, extract the base64 part and decode to hex instead","Trim the string and strip prefixes/quotes before calling (the code already trims, but embedded whitespace breaks parsing)","Regenerate the keypair and re-export the key if the stored value is corrupt"],"exampleFix":"// before\nlet wire = pubkey_hex_to_wire(\"ed25519:AbCd...\")?; // already wire form\n// after\nlet wire = pubkey_hex_to_wire(\"a1b2...64-hex-chars\")?;","handlingStrategy":"validation","validationCode":"fn is_hex_pubkey(s: &str) -> bool {\n    let s = s.trim();\n    s.len() == 64 && s.chars().all(|c| c.is_ascii_hexdigit())\n}","typeGuard":"fn convertible_to_wire(s: &str) -> bool {\n    let s = s.trim();\n    s.len() == 64 && s.chars().all(|c| c.is_ascii_hexdigit())\n}","tryCatchPattern":"match pubkey_hex_to_wire(pub_hex) {\n    Ok(wire) => wire,\n    Err(e) if e.to_string().starts_with(\"decode public key hex\") => {\n        eprintln!(\"Input must be 64 hex chars, not the ed25519:<base64> wire form\");\n        return Err(e);\n    }\n}","preventionTips":["Never pass the `ed25519:<base64>` wire form into hex-expecting functions","Copy keys without truncation; validate 64 hex chars before storing","Keep a single source of truth for the key and derive both forms from it"],"tags":["crypto","ed25519","hex-decode"],"backgroundTag":"invalid-argument-format","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"}