{"record":{"id":"5f5d10644b5b5062","repo":"astrid-runtime/astrid","slug":"invalid-ed25519-public-key-e","errorCode":null,"errorMessage":"invalid ed25519 public key: {e}","messagePattern":"invalid ed25519 public key: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/distro/sign.rs","lineNumber":81,"sourceCode":"    let mut hasher = blake3::Hasher::new();\n    hasher.update(SIG_DOMAIN_TAG);\n    hasher.update(&bytes);\n    Ok(*hasher.finalize().as_bytes())\n}\n\n/// Sign a lock with `keypair`, returning the hex `Distro.sig` contents.\npub(crate) fn sign_lock(lock: &DistroLock, keypair: &KeyPair) -> anyhow::Result<String> {\n    let digest = lock_signing_digest(lock)?;\n    let sig = keypair.sign(&digest);\n    Ok(sig.to_hex())\n}\n\n/// Parse the `ed25519:<base64>` wire form into a [`PublicKey`].\npub(crate) fn parse_pubkey(wire: &str) -> anyhow::Result<PublicKey> {\n    let b64 = wire.strip_prefix(\"ed25519:\").ok_or_else(|| {\n        anyhow::anyhow!(\"public key must be in 'ed25519:<base64>' form, got {wire:?}\")\n    })?;\n    PublicKey::from_base64(b64).map_err(|e| anyhow::anyhow!(\"invalid ed25519 public key: {e}\"))\n}\n\n/// Render a [`PublicKey`] as `ed25519:<base64>`.\npub(crate) fn pubkey_to_wire(pk: &PublicKey) -> String {\n    format!(\"ed25519:{}\", pk.to_base64())\n}\n\n/// Verify a hex `Distro.sig` against a lock and a public key.\n///\n/// # Errors\n///\n/// Returns an error if the signature is malformed (not 64 hex bytes) or\n/// does not verify against the lock's signing digest under `pubkey`.\npub(crate) fn verify_lock(\n    lock: &DistroLock,\n    sig_hex: &str,\n    pubkey: &PublicKey,\n) -> anyhow::Result<()> {","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/distro/sign.rs#L63-L99","documentation":"The second stage of parse_pubkey: after the ed25519: prefix is stripped, the remaining base64 is handed to PublicKey::from_base64. If the crypto layer cannot decode it into a valid ed25519 public key (bad base64 characters, wrong length, invalid point), the error is wrapped as \"invalid ed25519 public key: {e}\".","triggerScenarios":"Calling parse_pubkey with a well-prefixed \"ed25519:<b64>\" string whose payload is not valid base64 or does not decode to a 32-byte valid ed25519 public key — truncated keys, whitespace/typo corruption, or a signature/secret pasted where a public key belongs. Also hit by the pubkey_wire_roundtrips test.","commonSituations":"Copy-paste truncation of a long key string; smart quotes or line breaks introduced by editing a config in a rich-text editor; using a key from a different curve/tool that base64-decodes to the wrong length.","solutions":["Re-copy the public key value carefully — verify it base64-decodes to exactly 32 bytes.","Regenerate the wire string via pubkey_to_wire(pk) from a key you hold rather than manual copy/paste.","Ensure you are using the public key, not the secret key or a signature, in this field.","Strip whitespace/newlines from the value before passing it (trim in scripts)."],"exampleFix":"// before\nparse_pubkey(&conf.publisher_key.trim_end_matches('\"'))?; // value corrupted by editor\n// after\nlet b64: String = conf.publisher_key.trim().chars().filter(|c| !c.is_whitespace()).collect();\nparse_pubkey(&format!(\"ed25519:{b64}\"))?;","handlingStrategy":"validation","validationCode":"use base64::Engine;\nlet b64 = wire.strip_prefix(\"ed25519:\").context(\"missing ed25519: prefix\")?;\nlet raw = base64::engine::general_purpose::STANDARD.decode(b64.trim())?;\nanyhow::ensure!(raw.len() == 32, \"public key must decode to 32 bytes, got {}\", raw.len());","typeGuard":"fn valid_pubkey_wire(s: &str) -> bool {\n    s.strip_prefix(\"ed25519:\")\n        .and_then(|b| base64::engine::general_purpose::STANDARD.decode(b.trim()).ok())\n        .map_or(false, |raw| raw.len() == 32)\n}","tryCatchPattern":"match parse_pubkey(wire) {\n    Ok(pk) => pk,\n    Err(e) if e.to_string().contains(\"invalid ed25519 public key\") => {\n        eprintln!(\"key payload is not valid base64 / not 32 bytes; re-copy the key\");\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Trim whitespace and strip quotes/newlines from pasted keys before parsing.","Verify the value base64-decodes to 32 bytes in config validation.","Use pubkey_to_wire output round-tripped through pubkey_wire_roundtrips-style tests."],"tags":["crypto","ed25519","base64","invalid-key"],"backgroundTag":"invalid-key-material","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"}