HKUDS/Vibe-Trading · error · HTTPException

API_AUTH_KEY is required for non-local API access

Error message

API_AUTH_KEY is required for non-local API access

What it means

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.

Source

Thrown at agent/src/api/security.py:448

    if origin and not (_is_loopback_origin(origin) or _origin_matches_request_host(origin, request)):
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Cross-site request denied")


def _require_shutdown_authorization(
    *,
    request: Request,
    cred: Optional[HTTPAuthorizationCredentials],
) -> None:
    """Authorize the local shutdown control-plane action."""
    _reject_cross_site_browser_request(request)
    api_key = _configured_api_key()
    if api_key:
        token = _auth_credential_from_header_or_query(cred, None, allow_query=False)
        if not token or not hmac.compare_digest(token, api_key):
            raise HTTPException(status_code=401, detail="Invalid or missing API key")
        return
    if not _is_local_client(request):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="API_AUTH_KEY is required for non-local API access",
        )


#: Subject recorded when the caller proved possession of the shared API key.
#: It is a role, not a person: every holder of that one secret authenticates
#: identically, so this string must never be presented as an identity.
SHARED_KEY_SUBJECT = "shared-key-holder"

#: Subject recorded when no key is configured and a loopback client was trusted.
LOOPBACK_SUBJECT = "loopback-operator"


def _validate_api_auth(
    *,
    request: Request,
    cred: Optional[HTTPAuthorizationCredentials],

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Set API_AUTH_KEY in the server environment and send it in the Authorization header
  2. If you intend local access, call the endpoint from 127.0.0.1/localhost (or ::1) rather than an external interface
  3. 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
  4. Never expose the shutdown route publicly without a key — this error is the guard preventing that

Example fix

# before
# server started with no API_AUTH_KEY, client on another host
curl http://192.168.1.10:8000/shutdown -X POST   # 403

# after
export API_AUTH_KEY="$(openssl rand -hex 32)"
# restart server, then
curl http://192.168.1.10:8000/shutdown -X POST -H "Authorization: Bearer $API_AUTH_KEY"
Defensive patterns

Strategy: validation

Validate before calling

import socket, os
BASE_HOST = "127.0.0.1"  # use this for key-less local calls
HAS_KEY = bool(os.environ.get("API_AUTH_KEY"))
assert HAS_KEY or BASE_HOST in ("127.0.0.1", "localhost", "::1"), \
    "No API_AUTH_KEY set and target is not loopback; shutdown will 403"

Try / catch

try:
    resp = requests.post(f"{BASE}/shutdown", headers=headers)
    resp.raise_for_status()
except requests.HTTPError as e:
    if e.response.status_code == 403 and "API_AUTH_KEY" in e.response.text:
        raise RuntimeError("Set API_AUTH_KEY on the server, or call from localhost") from e
    raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/8751d068d8486266. Report an issue: GitHub.