Getting Started

Docker in 5 Minutes

Start a fully functional Clavex instance locally using Docker Compose — PostgreSQL, Redis, and the Clavex server, wired together in a single command. By the end you'll have a working OIDC issuer and your first tenant registered.

Prerequisites: Docker ≥ 24 and Docker Compose v2 (docker compose). No Go toolchain required. The binary is pre-built in the image.
1
Download docker-compose.yml
The Compose file starts PostgreSQL 16, Redis 7, and the latest Clavex image. A config.dev.yaml file with safe defaults is generated automatically on first run via an entrypoint script.
bash
$ curl -fsSL https://raw.githubusercontent.com/fcraviolatti/clavex/main/docker-compose.yml -o docker-compose.yml $ curl -fsSL https://raw.githubusercontent.com/fcraviolatti/clavex/main/config.example.yaml -o config.yaml # Edit config.yaml — set admin.email and a strong jwt_secret (32+ chars) $ vim config.yaml
Tip: Generate a secure secret with openssl rand -hex 32 and paste it into auth.jwt_secret.
2
Start the stack
Compose starts three containers: postgres, redis, and clavex. The server runs migrations on startup and is ready in a few seconds.
bash
$ docker compose up -d [+] Running 3/3 ✔ Container clavex-postgres-1 Started ✔ Container clavex-redis-1 Started ✔ Container clavex-server-1 Started
$ curl -s http://localhost:8080/health | jq . { "status": "ok", "db": "ok", "redis": "ok" }
3
Create your first tenant (Organization)
The super-admin token is printed to the server logs on first startup. Use it to create an Organization — this is your OIDC tenant. The slug becomes part of the issuer URL: http://localhost:8080/{slug}.
bash
# Grab the initial superadmin token from the logs $ export SUPERADMIN_TOKEN=$(docker compose logs server | grep "superadmin-token" | awk '{print $NF}')
# Create organization $ curl -s -X POST http://localhost:8080/api/v1/organizations \ -H "Authorization: Bearer $SUPERADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Acme Corp","slug":"acme","admin_email":"admin@acme.eu"}' | jq . { "id": "01925f3a-...", "slug": "acme", "name": "Acme Corp" }
# Your OIDC discovery URL is now live $ curl -s http://localhost:8080/acme/.well-known/openid-configuration | jq .issuer "http://localhost:8080/acme"
4
Register an OIDC client
Register your application as an OIDC client within the acme organization. Public clients (SPAs, mobile) use PKCE only — omit the client_secret. Confidential clients (backend) receive a generated secret.
bash
$ export ORG_ID="01925f3a-..." # from step 3 $ export ORG_TOKEN="..." # org admin token from the invitation email
$ curl -s -X POST http://localhost:8080/api/v1/organizations/$ORG_ID/clients \ -H "Authorization: Bearer $ORG_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "client_name": "My Web App", "redirect_uris": ["http://localhost:3000/callback"], "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"], "token_endpoint_auth_method": "none" }' | jq '{client_id, client_secret}' { "client_id": "clavex_01925f...", "client_secret": null }
5
Test the authorization flow
Open the authorization URL in your browser. Log in with the admin user created in step 3. After consent, you'll be redirected to http://localhost:3000/callback?code=....
bash
# Generate PKCE verifier + challenge $ VERIFIER=$(openssl rand -base64 32 | tr -d '=/+' | cut -c1-43) $ CHALLENGE=$(echo -n "$VERIFIER" | openssl dgst -sha256 -binary | base64 | tr '+/' '-_' | tr -d '=')
# Open in browser $ open "http://localhost:8080/acme/authorize?\ client_id=clavex_01925f...&\ redirect_uri=http://localhost:3000/callback&\ response_type=code&scope=openid+email+profile&\ state=$(openssl rand -hex 8)&\ code_challenge=$CHALLENGE&code_challenge_method=S256"
# After login, exchange the code for tokens $ curl -s -X POST http://localhost:8080/acme/token \ -d "grant_type=authorization_code&code=AUTH_CODE&\ redirect_uri=http://localhost:3000/callback&\ client_id=clavex_01925f...&code_verifier=$VERIFIER" | jq . { "access_token": "eyJ...", "id_token": "eyJ...", "token_type": "Bearer", "expires_in": 3600 }
Done! You have a working OIDC flow. The id_token is a signed JWT — verify it with the JWKS at http://localhost:8080/acme/.well-known/jwks.json.

Next Steps