tinyhumansai/openhuman · critical
refusing to bind on non-loopback address
Error message
refusing to bind on non-loopback address {resolved_host} without an explicit operator-supplied RPC token ({}) What it means
The embedded HTTP RPC server refuses to bind a non-loopback host unless an explicit operator-supplied token (OPENHUMAN_CORE_TOKEN) is present. The auto-generated {workspace}/core.token is by design insufficient: remote clients cannot read a workspace-local file, so it would leave a LAN or public endpoint effectively unauthenticated. A log line and a highlighted [SECURITY] stderr banner both precede the bail, naming the env var and the loopback alternative.
Solutions
- Set OPENHUMAN_CORE_TOKEN to a strong random value in the core's environment and have clients send it as the bearer token.
- Or keep the default loopback bind and reach the core through an authenticated tunnel or the in-process embedded path.
- Never rely on {workspace}/core.token to secure a non-loopback endpoint.
Example fix
# before OPENHUMAN_CORE_HOST=0.0.0.0 ./openhuman-core serve # refuses to bind # after export OPENHUMAN_CORE_TOKEN="$(openssl rand -hex 32)" OPENHUMAN_CORE_HOST=0.0.0.0 ./openhuman-core serve
Defensive patterns
Strategy: validation
Validate before calling
let host = resolved_bind_host();
if !host.is_loopback() && std::env::var("OPENHUMAN_CORE_TOKEN").is_err() {
anyhow::bail!("refusing non-loopback bind: set OPENHUMAN_CORE_TOKEN or bind loopback");
} Prevention
- Set OPENHUMAN_CORE_TOKEN from a strong random source before any non-loopback bind.
- Default to loopback plus an authenticated tunnel; widen posture only as a deliberate decision.
- Never assume {workspace}/core.token protects remote clients - they cannot read a local file.
When it happens
Trigger: Config or env sets the bind host to 0.0.0.0 or a LAN IP - e.g. external-core debugging, docker/cloud deployment, or LAN access for the iOS client - while OPENHUMAN_CORE_TOKEN is unset in the core's environment.
Common situations: Operators assuming the per-workspace core.token file secures remote binds; docker deployments binding 0.0.0.0 by habit; exposing the core on the LAN without planning auth.
Related errors
- At least one of --expression, --tz, --command, or --name…
- Command blocked by security policy
- Composio action listing failed on v3
- Core rejected the agent_meetings_join request.
- Core rejected the meet_agent_get_call_detail request.
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/faf4ff52bec6da38.
Report an issue: GitHub.
Appendix: source
Thrown at src/core/runtime/builder.rs:692
// so treating it as "explicit" would be fail-open.
if crate::openhuman::security::pairing::is_public_bind(&resolved_host)
&& !self.has_operator_token
{
log::error!(
"[core] SECURITY: refusing to bind on public address {resolved_host} without an \
explicit operator-supplied RPC token. Set {} in your environment (or hand the \
bearer in-memory via the embedded core handle) to secure the RPC endpoint.",
crate::core::auth::CORE_TOKEN_ENV_VAR
);
eprintln!(
"\n\x1b[1;31m[SECURITY]\x1b[0m Refusing to bind on {resolved_host} without {}.\n\
The auto-generated {{workspace}}/core.token does NOT secure a public bind —\n\
remote clients cannot read it. Set {} in your environment to secure the\n\
RPC endpoint, or bind on a loopback address.\n",
crate::core::auth::CORE_TOKEN_ENV_VAR,
crate::core::auth::CORE_TOKEN_ENV_VAR
);
anyhow::bail!(
"refusing to bind on non-loopback address {resolved_host} without an explicit \
operator-supplied RPC token ({})",
crate::core::auth::CORE_TOKEN_ENV_VAR
);
}
let preferred_port = resolved_port;
let host = resolved_host;
let pick = crate::openhuman::platform::connectivity::rpc::pick_listen_port_for_host(
host.as_str(),
preferred_port,
)
.await
.map_err(|err| {
log::error!("[core] Failed to bind to {host}:{preferred_port}: {err}");
anyhow::Error::new(err)
})?;
let listen_port = pick.port;View on GitHub (pinned to 7491200858)