ekzhang/bore · error

server requires secret, but no secret was provided

Error message

server requires secret, but no secret was provided

What it means

During `Authenticator::server_handshake`, the server sent a challenge and expected the client to reply with a `ClientMessage::Authenticate(tag)`. Instead, the first message received (or an EOF/timeout) was anything else, so the server cannot authenticate the connection. This library throws it because the server was started with a secret and will not accept unauthenticated clients.

Solutions

  1. Start the client with the same secret as the server: `bore local <port> --to <host> --secret mysecret`.
  2. If the server should not require auth, restart it without `--secret`.
  3. Ensure client and server are the same bore version so the handshake protocol matches.
  4. Check that nothing between client and server (proxy, tunnel) interferes with the framed protocol messages.

Example fix

// before (server has --secret)
bore local 3000 --to bore.example.com
// after
bore local 3000 --to bore.example.com --secret mysecret
Defensive patterns

Strategy: validation

Validate before calling

// Before connecting, confirm the server requires a secret and you have one configured
if server_requires_secret && client_secret.is_none() {
    bail!("server requires --secret; provide the same secret as 'bore server --secret'");
}

Try / catch

match Client::new(...).await {
    Err(e) if e.to_string().contains("no secret was provided") => configure_secret_and_retry(),
    other => other,
}

Prevention

When it happens

Trigger: Client connected to a server started with `--secret` but the client never sent `Authenticate`: the client sent `Hello` directly (no secret configured on the client), sent some other unexpected message, or the connection closed/ephemeral message arrived before authentication.

Common situations: Server run with `bore server --secret mysecret` while the client omits `--secret`; mismatched client/server versions where one side does not authenticate; a proxy or misconfigured port forwarding stripping/reordering the handshake; a non-bore client (e.g. plain TCP scanner or health check) hitting the port.

Related errors


AI-assisted analysis of ekzhang/bore@00a735a899 (2026-09-08). Data as JSON: /api/errors/3fcc59530c26cc68. Report an issue: GitHub.

Appendix: source

Thrown at src/auth.rs:62

            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)