Code Interpreter

Connecting LibreChat

Point LibreChat at this service and get the two sides agreeing on authentication.

LibreChat talks to this service over plain HTTP. There are exactly two things to get right: the base URL, and the authentication settings matching on both sides.

The base URL

Point LibreChat at the API component, which listens on port 3112 and serves everything under the /v1 prefix. Set LIBRECHAT_CODE_BASEURL to that base URL including the /v1 suffix:

LibreChat .env
LIBRECHAT_CODE_BASEURL=http://codeapi-host:3112/v1

Use the internal service name when LibreChat and this service share a Docker network or Kubernetes namespace:

LIBRECHAT_CODE_BASEURL=http://api:3112/v1

Forgetting the /v1 is the single most common setup mistake. Without it every request 404s.

Authentication

Pick one of two modes. The service decides which via CODEAPI_AUTH_PROVIDER.

The legacy X-API-Key header is no longer accepted. Outside local mode the API requires a Bearer JWT.

The default Compose files run with LOCAL_MODE=true, which bypasses authentication entirely. LibreChat needs only the base URL; leave its CODEAPI_JWT_* variables unset. Nothing else is required on either side.

LibreChat .env
LIBRECHAT_CODE_BASEURL=http://localhost:3112/v1

If you run with LOCAL_MODE=false but still want no authentication, say so explicitly on the service:

codeapi .env
LOCAL_MODE=false
CODEAPI_AUTH_PROVIDER=none
CODEAPI_ALLOW_AUTH_PROVIDER_NONE=true

With CODEAPI_AUTH_PROVIDER=none the user id comes from the User-Id header, falling back to the body's user_id, then anonymous. The service refuses to start in this mode unless CODEAPI_ALLOW_AUTH_PROVIDER_NONE=true is also set.

Unauthenticated mode lets any caller execute arbitrary code. Never expose it on a public network or a shared host. Use it only on loopback or a trusted private network.

What must match on both sides

SettingSigner (LibreChat)Verifier (this service)
AlgorithmCODEAPI_JWT_ALGORITHM=EdDSACODEAPI_JWT_ALLOWED_ALGS=EdDSA
Key idCODEAPI_JWT_KIDCODEAPI_JWT_KID
IssuerCODEAPI_JWT_ISSUERCODEAPI_JWT_ISSUER (default librechat)
AudienceCODEAPI_JWT_AUDIENCECODEAPI_JWT_AUDIENCE (default codeapi)

A token whose kid the verifier does not know is rejected outright. Tokens are capped at a 300-second lifetime (CODEAPI_JWT_MAX_TTL_SECONDS) with 30 seconds of clock skew tolerance.

Because LOCAL_MODE=true bypasses authentication entirely, JWT mode requires LOCAL_MODE=false. Setting up JWTs while leaving local mode on gets you unauthenticated access with extra steps.

Key rotation

Instead of a single CODEAPI_JWT_PUBLIC_KEY + CODEAPI_JWT_KID, the verifier also accepts:

  • CODEAPI_JWT_JWKS_JSON (an inline JWKS document
  • CODEAPI_JWT_PUBLIC_KEYS_DIR) a directory of PEM files named by kid

Both let you serve several verifier keys at once, so you can introduce a new signing key before retiring the old one.

Enabling the languages you need

CODEAPI_LANGUAGES selects which runtimes get pre-installed. Installing all of them takes a while and a fair amount of disk; if LibreChat only needs Python, say so:

CODEAPI_LANGUAGES=python

See Languages and runtimes.

Verifying the connection

From the LibreChat host:

curl -s http://codeapi-host:3112/v1/health

Then a real execution. In JWT mode, mint a token the way LibreChat does and:

curl -sX POST http://codeapi-host:3112/v1/exec \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"lang":"py","code":"print(\"connected\")"}' | jq

If that returns stdout: "connected\n", both sides agree. If it returns 401, work through the matching table: issuer, audience, kid, and algorithm all have to line up exactly.

See Troubleshooting for specific failure modes.

On this page