Getting Started
Three containers, one command, and four HTTP calls to a working completion.
Prerequisites
- Docker Engine 27+
- Docker Compose v2+
- Nothing else. Go and Java are only needed to work on CogniGate, not to run it.
One command
# Linux / macOS
git clone https://github.com/Life-Experimentalist/CogniGate.git
cd CogniGate
./setup.sh --dev --detach
# Windows (PowerShell)
git clone https://github.com/Life-Experimentalist/CogniGate.git
cd CogniGate
.\setup.ps1 -Mode dev -Detach
The script copies .env.example to .env, replaces both placeholder
credentials with generated ones — GATEWAY_BOOTSTRAP_KEY and
ANALYTICS_TOKEN — and then builds and starts the stack: the gateway, the
analytics service, and PostgreSQL. It waits for all three to report healthy
before it returns, so a successful run is a stack that is actually serving.
Neither credential has a default, and neither is generated anywhere but your
own .env. A reference deployment that shipped with a known admin key would be
no deployment at all.
Verify
curl http://localhost:8080/healthz
/healthz is the only unauthenticated route on the gateway. Everything else
needs a key, including GET /v1/health — the richer readiness view that reports
per-provider state.
Your first completion
A fresh gateway knows nothing: no tenants, no keys, no providers. The bootstrap
key in .env is the one credential that exists before any of that, and its only
job is to create the first of each.
export CG_ADMIN=$(grep '^GATEWAY_BOOTSTRAP_KEY=' .env | cut -d= -f2-)
1. Create a tenant
curl -X POST http://localhost:8080/admin/v1/tenants \
-H "Authorization: Bearer $CG_ADMIN" \
-H "Content-Type: application/json" \
-d '{"name": "my-org"}'
{
"id": "ten_9f2c41ab77d0",
"name": "my-org",
"status": "active",
"created_at": "2026-09-03T10:14:02Z"
}
Every tenant is created with the four portable aliases — fast, balanced,
best and transcribe — so there is something to route to before anything has
been configured.
2. Mint a data-plane key
curl -X POST http://localhost:8080/admin/v1/tenants/ten_9f2c41ab77d0/keys \
-H "Authorization: Bearer $CG_ADMIN" \
-H "Content-Type: application/json" \
-d '{"name": "laptop"}'
{
"key": {
"id": "key_5d80c1e4a933",
"tenant_id": "ten_9f2c41ab77d0",
"plane": "data",
"name": "laptop",
"prefix": "cg-2b91",
"created_at": "2026-09-03T10:15:47Z"
},
"secret": "cg-...",
"warning": "This is the only time the secret is shown. Store it now; it cannot be retrieved again."
}
The gateway keeps a hash, not the key. prefix is the only part it stores in
the clear, and it is there so a key can be recognised in a listing or a usage
record without being reconstructable from one. There is no route that reads a
secret back: a lost key is re-minted, not recovered.
export CG_KEY=cg-... # the "secret" field, verbatim
3. Register a provider
The provider's own credential is given to the tenant, not to the caller. This is
the point of the gateway: your application holds a cg- key, and the upstream
key never leaves the deployment.
curl -X POST http://localhost:8080/admin/v1/tenants/ten_9f2c41ab77d0/providers \
-H "Authorization: Bearer $CG_ADMIN" \
-H "Content-Type: application/json" \
-d '{
"name": "openai",
"kind": "openai",
"base_url": "https://api.openai.com/v1",
"keys": ["<your provider key>"]
}'
keys is a list because a provider may be given several: the gateway rotates
through them and takes a failing one out of circulation without failing the
request.
4. Make the call
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer $CG_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "fast",
"messages": [{"role": "user", "content": "Hello!"}]
}'
The response is the provider's, unchanged — the same shape any OpenAI-compatible client already parses. What CogniGate adds is in the headers:
X-CogniGate-Request-Id: req_3a71f0c9d284
X-CogniGate-Served-By: openai/gpt-4o-mini
X-CogniGate-Fallback-Depth: 0
fast is not a model. It is a constraint — cheapest chat-capable model this
tenant can reach — and X-CogniGate-Served-By says what it resolved to on this
request. A model name works there too, if you would rather be explicit.
Where to go next
- Architecture — the two processes and what happens between them
- API Reference — both planes, route by route
- Configuration — every option and what it changes
- Deployment — what to change before this faces real traffic