ekzhang/bore · error

server requires authentication, but no client secret was…

Error message

server requires authentication, but no client secret was provided

What it means

In `Client::new`, the server replied to the client's `Hello` with a `ServerMessage::Challenge`, meaning it requires authentication, but this client was constructed without a secret so it can never answer. The client aborts rather than proceeding unauthenticated.

Solutions

  1. Pass the server's secret to the client: `bore local <port> --to <host> --secret <server-secret>`.
  2. Ask the server operator for the secret if you don't have it.
  3. If auth is not wanted, restart the server without `--secret`.

Example fix

// before
bore local 3000 --to bore.example.com
// after
bore local 3000 --to bore.example.com --secret mysecret
Defensive patterns

Strategy: validation

Validate before calling

if server_requires_secret && std::env::var("BORE_SECRET").map(|s| s.is_empty()).unwrap_or(true) {
    bail!("BORE_SECRET must be set; the server requires authentication");
}

Try / catch

match Client::new(...).await {
    Err(e) if e.to_string().contains("requires authentication") => fetch_secret_and_retry(),
    other => other,
}

Prevention

When it happens

Trigger: Connecting to a bore server started with `--secret` while creating the client without a secret (`bore local <port> --to <host>` with no `--secret` flag).

Common situations: Server operator enabled a secret but the user was not informed or forgot it; CI scripts or old shell history predating the server's secret; secrets added server-side as a security hardening step.

Understand the failure class

Related errors


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

Appendix: source

Thrown at src/client.rs:54

    pub async fn new(
        local_host: &str,
        local_port: u16,
        to: &str,
        port: u16,
        secret: Option<&str>,
    ) -> Result<Self> {
        let mut stream = Delimited::new(connect_with_timeout(to, CONTROL_PORT).await?);
        let auth = secret.map(Authenticator::new);
        if let Some(auth) = &auth {
            auth.client_handshake(&mut stream).await?;
        }

        stream.send(ClientMessage::Hello(port)).await?;
        let remote_port = match stream.recv_timeout().await? {
            Some(ServerMessage::Hello(remote_port)) => remote_port,
            Some(ServerMessage::Error(message)) => bail!("server error: {message}"),
            Some(ServerMessage::Challenge(_)) => {
                bail!("server requires authentication, but no client secret was provided");
            }
            Some(_) => bail!("unexpected initial non-hello message"),
            None => bail!("unexpected EOF"),
        };
        info!(remote_port, "connected to server");
        info!("listening at {to}:{remote_port}");

        Ok(Client {
            conn: Some(stream),
            to: to.to_string(),
            local_host: local_host.to_string(),
            local_port,
            remote_port,
            auth,
        })
    }

    /// Returns the port publicly available on the remote.

View on GitHub (pinned to 00a735a899)