ekzhang/bore · error
invalid secret
Error message
invalid secret
What it means
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.
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.
Example fix
// before (trailing whitespace from shell) bore local 3000 --to bore.example.com --secret 'mysecret ' // after bore local 3000 --to bore.example.com --secret mysecret
Defensive patterns
Strategy: validation
Validate before calling
let secret = std::env::var("BORE_SECRET").unwrap_or_default();
if secret != secret.trim() || secret.is_empty() {
bail!("BORE_SECRET is empty or contains stray whitespace");
} Try / catch
match Client::new(...).await {
Err(e) if e.to_string().contains("invalid secret") => {
eprintln!("secret mismatch: compare BORE_SECRET with the server's --secret");
std::process::exit(3);
}
other => other,
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- server requires secret, but no secret was provided
- expected authentication challenge, but no secret was…
- server requires authentication, but no client secret was…
AI-assisted analysis of ekzhang/bore@00a735a899 (2026-09-08).
Data as JSON: /api/errors/f9a8ff7d49fa6c3a.
Report an issue: GitHub.
Appendix: source
Thrown at src/auth.rs:59
if let Ok(tag) = hex::decode(tag) {
let mut hmac = self.0.clone();
hmac.update(challenge.as_bytes());
hmac.verify_slice(&tag).is_ok()
} else {
false
}
}
/// As the server, send a challenge to the client and validate their response.
pub async fn server_handshake<T: AsyncRead + AsyncWrite + Unpin>(
&self,
stream: &mut Delimited<T>,
) -> Result<()> {
let challenge = Uuid::new_v4();
stream.send(ServerMessage::Challenge(challenge)).await?;
match stream.recv_timeout().await? {
Some(ClientMessage::Authenticate(tag)) => {
ensure!(self.validate(&challenge, &tag), "invalid secret");
Ok(())
}
_ => bail!("server requires secret, but no secret was provided"),
}
}
/// As the client, answer a challenge to attempt to authenticate with the server.
pub async fn client_handshake<T: AsyncRead + AsyncWrite + Unpin>(
&self,
stream: &mut Delimited<T>,
) -> Result<()> {
let challenge = match stream.recv_timeout().await? {
Some(ServerMessage::Challenge(challenge)) => challenge,
_ => bail!("expected authentication challenge, but no secret was required"),
};
let tag = self.answer(&challenge);
stream.send(ClientMessage::Authenticate(tag)).await?;
Ok(())View on GitHub (pinned to 00a735a899)