{"record":{"id":"f9a8ff7d49fa6c3a","repo":"ekzhang/bore","slug":"invalid-secret","errorCode":null,"errorMessage":"invalid secret","messagePattern":"invalid secret","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/auth.rs","lineNumber":59,"sourceCode":"        if let Ok(tag) = hex::decode(tag) {\n            let mut hmac = self.0.clone();\n            hmac.update(challenge.as_bytes());\n            hmac.verify_slice(&tag).is_ok()\n        } else {\n            false\n        }\n    }\n\n    /// As the server, send a challenge to the client and validate their response.\n    pub async fn server_handshake<T: AsyncRead + AsyncWrite + Unpin>(\n        &self,\n        stream: &mut Delimited<T>,\n    ) -> Result<()> {\n        let challenge = Uuid::new_v4();\n        stream.send(ServerMessage::Challenge(challenge)).await?;\n        match stream.recv_timeout().await? {\n            Some(ClientMessage::Authenticate(tag)) => {\n                ensure!(self.validate(&challenge, &tag), \"invalid secret\");\n                Ok(())\n            }\n            _ => bail!(\"server requires secret, but no secret was provided\"),\n        }\n    }\n\n    /// As the client, answer a challenge to attempt to authenticate with the server.\n    pub async fn client_handshake<T: AsyncRead + AsyncWrite + Unpin>(\n        &self,\n        stream: &mut Delimited<T>,\n    ) -> Result<()> {\n        let challenge = match stream.recv_timeout().await? {\n            Some(ServerMessage::Challenge(challenge)) => challenge,\n            _ => bail!(\"expected authentication challenge, but no secret was required\"),\n        };\n        let tag = self.answer(&challenge);\n        stream.send(ClientMessage::Authenticate(tag)).await?;\n        Ok(())","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/ekzhang/bore/blob/00a735a89917642df62d84336a90d9476fa175b5/src/auth.rs#L41-L77","documentation":"In `Authenticator::server_handshake`, the client replied to the challenge with an `Authenticate(tag)` whose HMAC did not match the challenge under the server's secret (`validate` returned false). The server rejects the client as knowing the wrong secret.","triggerScenarios":"Client configured with a different secret string than the server's `--secret`; garbled or whitespace-damaged secret in env/config; a stale client cached from before a server secret rotation.","commonSituations":"Secret typo or copy-paste including quotes/trailing whitespace; secret rotated on the server but not the client (or vice versa); secrets sourced from different env vars per environment (staging vs production).","solutions":["Re-enter the exact server secret on the client (watch for quotes, spaces, newlines): `bore local <port> --to <host> --secret <exact-secret>`.","Compare secrets on both sides (e.g. hash them) to confirm they match.","If the secret was rotated, update all clients with the new secret.","Fix the env var / config file supplying the secret if it's malformed."],"exampleFix":"// before (trailing whitespace from shell)\nbore local 3000 --to bore.example.com --secret 'mysecret '\n// after\nbore local 3000 --to bore.example.com --secret mysecret","handlingStrategy":"validation","validationCode":"let secret = std::env::var(\"BORE_SECRET\").unwrap_or_default();\nif secret != secret.trim() || secret.is_empty() {\n    bail!(\"BORE_SECRET is empty or contains stray whitespace\");\n}","typeGuard":null,"tryCatchPattern":"match Client::new(...).await {\n    Err(e) if e.to_string().contains(\"invalid secret\") => {\n        eprintln!(\"secret mismatch: compare BORE_SECRET with the server's --secret\");\n        std::process::exit(3);\n    }\n    other => other,\n}","preventionTips":["Trim secrets read from env/files before use.","Compare hashes of client and server secrets when debugging mismatches.","Version/rotate secrets on both sides atomically."],"tags":["authentication","hmac","secret-mismatch"],"backgroundTag":"invalid-credentials","analyzedSha":"00a735a89917642df62d84336a90d9476fa175b5","analyzedAt":"2026-09-08T13:27:32.996Z","contentChangedAt":"2026-09-08T13:27:32.996Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}