{"record":{"id":"faf4ff52bec6da38","repo":"tinyhumansai/openhuman","slug":"refusing-to-bind-on-non-loopback-address-resolved","errorCode":null,"errorMessage":"refusing to bind on non-loopback address {resolved_host} without an explicit operator-supplied RPC token ({})","messagePattern":"refusing to bind on non-loopback address (.+?) without an explicit operator-supplied RPC token \\((.+?)\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/core/runtime/builder.rs","lineNumber":692,"sourceCode":"        // so treating it as \"explicit\" would be fail-open.\n        if crate::openhuman::security::pairing::is_public_bind(&resolved_host)\n            && !self.has_operator_token\n        {\n            log::error!(\n                \"[core] SECURITY: refusing to bind on public address {resolved_host} without an \\\n                 explicit operator-supplied RPC token. Set {} in your environment (or hand the \\\n                 bearer in-memory via the embedded core handle) to secure the RPC endpoint.\",\n                crate::core::auth::CORE_TOKEN_ENV_VAR\n            );\n            eprintln!(\n                \"\\n\\x1b[1;31m[SECURITY]\\x1b[0m Refusing to bind on {resolved_host} without {}.\\n\\\n                 The auto-generated {{workspace}}/core.token does NOT secure a public bind —\\n\\\n                 remote clients cannot read it. Set {} in your environment to secure the\\n\\\n                 RPC endpoint, or bind on a loopback address.\\n\",\n                crate::core::auth::CORE_TOKEN_ENV_VAR,\n                crate::core::auth::CORE_TOKEN_ENV_VAR\n            );\n            anyhow::bail!(\n                \"refusing to bind on non-loopback address {resolved_host} without an explicit \\\n                 operator-supplied RPC token ({})\",\n                crate::core::auth::CORE_TOKEN_ENV_VAR\n            );\n        }\n\n        let preferred_port = resolved_port;\n        let host = resolved_host;\n        let pick = crate::openhuman::platform::connectivity::rpc::pick_listen_port_for_host(\n            host.as_str(),\n            preferred_port,\n        )\n        .await\n        .map_err(|err| {\n            log::error!(\"[core] Failed to bind to {host}:{preferred_port}: {err}\");\n            anyhow::Error::new(err)\n        })?;\n        let listen_port = pick.port;","sourceCodeStart":674,"sourceCodeEnd":710,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/core/runtime/builder.rs#L674-L710","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nOPENHUMAN_CORE_HOST=0.0.0.0 ./openhuman-core serve   # refuses to bind\n\n# after\nexport OPENHUMAN_CORE_TOKEN=\"$(openssl rand -hex 32)\"\nOPENHUMAN_CORE_HOST=0.0.0.0 ./openhuman-core serve","handlingStrategy":"validation","validationCode":"let host = resolved_bind_host();\nif !host.is_loopback() && std::env::var(\"OPENHUMAN_CORE_TOKEN\").is_err() {\n    anyhow::bail!(\"refusing non-loopback bind: set OPENHUMAN_CORE_TOKEN or bind loopback\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["security","bind","rpc","token","network","authorization"],"backgroundTag":"insecure-bind-refused","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}