This episode covers ChromaDB authentication and authorization: the default no-auth condition, token-based auth, BasicAuth, auth configuration on the server side, and per-collection and per-tenant access control in shared deployments.

In episode 12 you opened your server to the network. The question that immediately follows: who is allowed to use it? ChromaDB's default answer may surprise you: there is no authentication. A server running without auth settings accepts requests from anyone who can reach it.
Episode 13 covers two concepts that are often mixed up: authentication (proving who you are) and authorization (what you are allowed to do). We will enable token-based auth and BasicAuth on the server, configure clients to authenticate, then discuss access control for shared deployments.
When you run chroma run without auth settings, every HTTP request is accepted. If the server is exposed to the internet — a single port misconfiguration in the cloud — anyone can read and delete your collections:
curl http://server:8000/api/v2/tenants/default_tenant/databases/default_database/collectionsA curl .../collections request without a token will succeed on a server without auth. This is what must be avoided in production. The first step is always: never expose a server without auth to a public network.
If auth is not yet active, at least make sure the server can only be reached from the application host — restrict it with a firewall as covered in episode 15. But do not stop there: this episode teaches real auth.
Danger
ChromaDB's default is indeed no authentication. Do not assume your server is safe just because "no one knows its IP". Port scanning and internet bots are a reality — enable auth before exposing anything.
The easiest way to enable auth: a static token. The server stores a list of tokens, and requests must carry one. Prepare a credentials file:
token:rahasia-kuat-2026Then configure the server when starting it:
export CHROMA_SERVER_AUTHN_CREDENTIALS_FILE="/etc/chroma/creds.txt"
export CHROMA_SERVER_AUTHN_PROVIDER="chromadb.auth.token_authn.TokenAuthenticationServerProvider"
chroma runCHROMA_SERVER_AUTHN_PROVIDER=...TokenAuthenticationServerProvider tells the server to use token auth with credentials from the file. The token rahasia-kuat-2026 is now required for all requests.
Clients send the token via the Authorization: Bearer header:
client = chromadb.HttpClient(
host="localhost",
port=8000,
headers={"Authorization": "Bearer rahasia-kuat-2026"},
)chromadb.HttpClient(host="localhost", port=8000, headers={"Authorization": "Bearer rahasia-kuat-2026"}) includes the token on every request. Without a token, requests are rejected with status 401.
For curl:
curl -H "Authorization: Bearer rahasia-kuat-2026" \
http://localhost:8000/api/v2/heartbeatcurl -H "Authorization: Bearer rahasia-kuat-2026" adds the auth header manually — useful for quick debugging.
Besides tokens, ChromaDB supports BasicAuth: a username and password sent as the Authorization: Basic header. Server configuration:
export CHROMA_SERVER_AUTHN_CREDENTIALS_FILE="/etc/chroma/creds.txt"
export CHROMA_SERVER_AUTHN_PROVIDER="chromadb.auth.basic_authn.BasicAuthenticationServerProvider"
chroma runFor BasicAuth, the creds.txt file holds one username:password pair per line:
arman:password-rahasia-1
ci:password-rahasia-2Python clients send base64-encoded credentials automatically:
import base64
kred = base64.b64encode(b"arman:password-rahasia-1").decode()
client = chromadb.HttpClient(
host="localhost",
port=8000,
headers={"Authorization": f"Basic {kred}"},
)base64.b64encode(b"arman:password-rahasia-1").decode() encodes the credentials, then headers={"Authorization": f"Basic {kred}"} sends them. Tokens and BasicAuth are two entry paths with nearly identical configuration — pick the one that fits your team's habits.
Authentication proves who you are — authorization decides what you may do. In ChromaDB, the authn above secures the entry gate, but by default every authenticated user can still see all collections. For shared (multi-tenant) deployments, separation is done via the tenant model from episode 12: each team uses its own tenant, and servers are separated when stricter control is needed.
The most common and safest pattern today:
create_tenant and set_tenant.def buat_client(tenant, token):
return chromadb.HttpClient(
host="chroma-" + tenant,
port=8000,
headers={"Authorization": f"Bearer {token}"},
)
client_tim_a = buat_client("tim-a", "token-tim-a")
client_tim_b = buat_client("tim-b", "token-tim-b")buat_client(tenant, token) produces a client isolated per tenant. The combination of tenant + token + network isolation is a layered defense for shared deployments.
Episode 13 turned your server from an open door into a guarded one: understanding that ChromaDB's default has no auth, enabling token-based auth and BasicAuth on both server and client, distinguishing authentication from authorization, and building a multi-tenant pattern with token and network isolation.
Key takeaways:
CHROMA_SERVER_AUTHN_PROVIDER and a credentials file.Authorization: Bearer header.In the next episode, episode 14, we will discuss security best practices and CVE-2026-45829 — the pre-auth RCE vulnerability in the Python FastAPI server versions 1.0.0 through 1.5.8, why the Rust server is safe, and full mitigation: server migration, network isolation, zero-trust, and upgrade routines. This is an episode you should not skip before production.