{"record":{"id":"8751d068d8486266","repo":"HKUDS/Vibe-Trading","slug":"api-auth-key-is-required-for-non-local-api-access","errorCode":null,"errorMessage":"API_AUTH_KEY is required for non-local API access","messagePattern":"API_AUTH_KEY is required for non-local API access","errorType":"http","errorClass":"HTTPException","httpStatus":403,"severity":"error","filePath":"agent/src/api/security.py","lineNumber":448,"sourceCode":"    if origin and not (_is_loopback_origin(origin) or _origin_matches_request_host(origin, request)):\n        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=\"Cross-site request denied\")\n\n\ndef _require_shutdown_authorization(\n    *,\n    request: Request,\n    cred: Optional[HTTPAuthorizationCredentials],\n) -> None:\n    \"\"\"Authorize the local shutdown control-plane action.\"\"\"\n    _reject_cross_site_browser_request(request)\n    api_key = _configured_api_key()\n    if api_key:\n        token = _auth_credential_from_header_or_query(cred, None, allow_query=False)\n        if not token or not hmac.compare_digest(token, api_key):\n            raise HTTPException(status_code=401, detail=\"Invalid or missing API key\")\n        return\n    if not _is_local_client(request):\n        raise HTTPException(\n            status_code=status.HTTP_403_FORBIDDEN,\n            detail=\"API_AUTH_KEY is required for non-local API access\",\n        )\n\n\n#: Subject recorded when the caller proved possession of the shared API key.\n#: It is a role, not a person: every holder of that one secret authenticates\n#: identically, so this string must never be presented as an identity.\nSHARED_KEY_SUBJECT = \"shared-key-holder\"\n\n#: Subject recorded when no key is configured and a loopback client was trusted.\nLOOPBACK_SUBJECT = \"loopback-operator\"\n\n\ndef _validate_api_auth(\n    *,\n    request: Request,\n    cred: Optional[HTTPAuthorizationCredentials],","sourceCodeStart":430,"sourceCodeEnd":466,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/api/security.py#L430-L466","documentation":"The shutdown endpoint raises 403 when no API_AUTH_KEY is configured at all and the request does not originate from a local loopback client (as determined by _is_local_client). This is a fail-closed design: destructive shutdown is only allowed either from localhost on a key-less dev setup, or with a valid key from anywhere.","triggerScenarios":"Calling shutdown_local_api from a non-loopback address (container-to-host, LAN IP, remote host, or via a proxy that rewrites the client address) while API_AUTH_KEY is unset or empty.","commonSituations":"Running the API in Docker where the client IP seen by the server is the bridge network gateway, not 127.0.0.1; a reverse proxy (nginx/traefik) forwarding requests so the server sees the proxy IP; forgetting to set API_AUTH_KEY in production deployment configs; calling via the machine's external hostname instead of localhost.","solutions":["Set API_AUTH_KEY in the server environment and send it in the Authorization header","If you intend local access, call the endpoint from 127.0.0.1/localhost (or ::1) rather than an external interface","If behind a reverse proxy, configure it to pass the real client IP (X-Forwarded-For) and ensure the app trusts/uses it, or always authenticate with a key","Never expose the shutdown route publicly without a key — this error is the guard preventing that"],"exampleFix":"# before\n# server started with no API_AUTH_KEY, client on another host\ncurl http://192.168.1.10:8000/shutdown -X POST   # 403\n\n# after\nexport API_AUTH_KEY=\"$(openssl rand -hex 32)\"\n# restart server, then\ncurl http://192.168.1.10:8000/shutdown -X POST -H \"Authorization: Bearer $API_AUTH_KEY\"","handlingStrategy":"validation","validationCode":"import socket, os\nBASE_HOST = \"127.0.0.1\"  # use this for key-less local calls\nHAS_KEY = bool(os.environ.get(\"API_AUTH_KEY\"))\nassert HAS_KEY or BASE_HOST in (\"127.0.0.1\", \"localhost\", \"::1\"), \\\n    \"No API_AUTH_KEY set and target is not loopback; shutdown will 403\"","typeGuard":null,"tryCatchPattern":"try:\n    resp = requests.post(f\"{BASE}/shutdown\", headers=headers)\n    resp.raise_for_status()\nexcept requests.HTTPError as e:\n    if e.response.status_code == 403 and \"API_AUTH_KEY\" in e.response.text:\n        raise RuntimeError(\"Set API_AUTH_KEY on the server, or call from localhost\") from e\n    raise","preventionTips":["Always configure API_AUTH_KEY on any host reachable beyond loopback","Automate provisioning so production deployments always receive a generated key","Log request.client.host server-side when debugging loopback detection"],"tags":["auth","shutdown","http-403","local-only","security"],"backgroundTag":"api-key-required-non-local","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}