Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ jobs:
- name: Set up Helm
uses: azure/setup-helm@v4

- name: Build dependencies
run: helm dependency build .

- name: Helm lint
run: helm lint . --set citadel.secretKey=test

Expand Down
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,29 @@ All notable changes to the Citadel Helm chart will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.1] - 2026-03-08

### Changed

- **Go runtime migration**: Removed Python/uvicorn command override and `PYTHONPATH` env var from deployment — the Go image's `CMD ["/app/citadel"]` is now used as the entrypoint
- **Init container**: Replaced Python/asyncpg database wait script with a lightweight `busybox` + `nc` TCP check (no longer pulls the full app image for the init container)
- **`passthrough.enabled`**: Default changed from `false` to `true` to match the Go app default
- **`guardrails.openaiModeration`**: Default changed from `false` to `true` to match the Go app default
- Bumped `appVersion` to `0.2.0`

### Added

- **`citadel.uiSessionSecret`** value: Exposes `UI_SESSION_SECRET` in the Helm secret so production deployments don't silently CrashLoop when the Go app rejects the default session secret
- **NOTES.txt**: Added post-install guidance for creating an API key and PVC cleanup reminder

### Fixed

- Docs: Removed unnecessary "Add Bitnami repo" step (chart uses OCI dependencies)
- Docs: Fixed health response format from `{"status": "healthy"}` to `{"status":"ok"}`
- Docs: Replaced Python `asyncpg` troubleshooting command with `curl` health check
- Docs: Updated image tag references from `0.1.0` to `0.2.0`
- Docs: Added `uiSessionSecret` to production hardening checklist

## [0.1.0] - 2025-03-03

### Added
Expand Down
4 changes: 2 additions & 2 deletions Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ description: Helm chart for Citadel AI Gateway. Big Bang ready.
type: application

# Chart version — increment on each release
version: 0.1.0
version: 0.2.1

# Application version deployed by this chart
appVersion: "0.1.0"
appVersion: "0.2.1"

dependencies:
- name: postgresql
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ Required keys in your secret: `DATABASE_URL`, `SECRET_KEY`. Optional: `OPENROUTE
| `citadel.logLevel` | Log level | `INFO` |
| `citadel.autoProvisionUsers` | Auto-create users from headers | `true` |
| `citadel.guardrails.enabled` | Enable guardrails | `true` |
| `citadel.passthrough.enabled` | Enable API key passthrough | `false` |
| `citadel.passthrough.enabled` | Enable API key passthrough | `true` |
| `citadel.plugins.enabled` | Enable plugin system | `true` |
| `citadel.okta.enabled` | Enable Okta OIDC | `false` |
| `providers.openrouter.apiKey` | OpenRouter API key | `""` |
Expand Down
13 changes: 4 additions & 9 deletions docs/GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@ This guide walks you through deploying Citadel AI Gateway on Kubernetes and conf

## Step 1: Install the Chart

### Add the Bitnami dependency repo

```bash
helm repo add bitnami https://charts.bitnami.com/bitnami
```

### Install in evaluation mode

Evaluation mode enables dev login so you can explore the UI, create users, and generate API keys without configuring OIDC.
Expand Down Expand Up @@ -55,7 +49,7 @@ kubectl port-forward svc/citadel 8000:8000 &

# Liveness check
curl -s http://localhost:8000/health | jq .
# Expected: {"status": "healthy", "version": "0.1.0"}
# Expected: {"status":"ok","version":"0.2.0"}

# Readiness check (verifies database connectivity)
curl -s http://localhost:8000/health/ready | jq .
Expand Down Expand Up @@ -252,6 +246,7 @@ Before deploying to production, review this checklist:
### Security

- [ ] **Set a strong `secretKey`**: `openssl rand -hex 32`
- [ ] **Set `uiSessionSecret`** for production: `openssl rand -hex 32`
- [ ] **Disable dev login**: `citadel.devLoginEnabled: false` (default)
- [ ] **Set environment to production**: `citadel.environment: production` (default)
- [ ] **Configure OIDC** (Okta) for user authentication
Expand Down Expand Up @@ -346,7 +341,7 @@ The container image may not be accessible:
kubectl describe pod -l app.kubernetes.io/name=citadel
```

- Verify image exists: `docker pull ghcr.io/radiusmethod/citadel:0.1.0`
- Verify image exists: `docker pull ghcr.io/radiusmethod/citadel:0.2.0`
- For private registries, set `imagePullSecrets`

### Database connection failures
Expand Down Expand Up @@ -380,5 +375,5 @@ This means the database is not connected. Check:

```bash
# PostgreSQL connectivity
kubectl exec -it citadel-XXXXX -- python -c "import asyncio, asyncpg, os; asyncio.run(asyncpg.connect(os.environ['DATABASE_URL']))"
kubectl exec -it citadel-XXXXX -- curl -s http://localhost:8000/health/ready
```
12 changes: 12 additions & 0 deletions templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,21 @@ Dev login is ENABLED. Click "Dev Login" on the UI to get started.
Disable for production: --set citadel.devLoginEnabled=false
{{- end }}

Next steps — create your first API key:
1. Open the UI and navigate to API Keys
2. Click "Create Key" and copy the generated key (starts with sk-citadel-)
3. Configure your client:

Configure Claude Code to use Citadel:
claude config set --global apiBaseUrl http://localhost:8000/v1

Configure OpenAI-compatible clients:
export OPENAI_API_BASE=http://localhost:8000/v1
export OPENAI_API_KEY=<your-citadel-api-key>

{{- if .Values.postgresql.enabled }}

NOTE: The bundled PostgreSQL uses a PersistentVolumeClaim that is NOT deleted
on helm uninstall. To fully clean up after uninstalling:
kubectl delete pvc data-{{ .Release.Name }}-postgresql-0
{{- end }}
40 changes: 13 additions & 27 deletions templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,33 +42,22 @@ spec:
{{- if .Values.postgresql.enabled }}
initContainers:
- name: wait-for-db
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
image: busybox:1.37
command:
- python
- sh
- -c
- |
import asyncio, asyncpg, os, sys, time
async def wait():
url = os.environ["DATABASE_URL"]
for i in range(30):
try:
conn = await asyncpg.connect(url)
await conn.close()
print("Database is ready")
return
except Exception as e:
print(f"Waiting for database... ({e})")
time.sleep(2)
print("Database not ready after 60s")
sys.exit(1)
asyncio.run(wait())
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ include "citadel.secretName" . }}
key: DATABASE_URL
echo "Waiting for {{ .Release.Name }}-postgresql:5432..."
for i in $(seq 1 30); do
if nc -z {{ .Release.Name }}-postgresql 5432; then
echo "Database is ready"
exit 0
fi
echo "Attempt $i/30 — waiting 2s..."
sleep 2
done
echo "Database not ready after 60s"
exit 1
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
{{- end }}
Expand All @@ -78,10 +67,7 @@ spec:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["uvicorn", "citadel.main:app", "--host", "0.0.0.0", "--port", "8000"]
env:
- name: PYTHONPATH
value: /app/src
{{- with .Values.extraEnv }}
{{- toYaml . | nindent 12 }}
{{- end }}
Expand Down
3 changes: 3 additions & 0 deletions templates/secrets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ stringData:
DATABASE_URL: {{ .Values.externalDatabase.url | quote }}
{{- end }}
SECRET_KEY: {{ .Values.citadel.secretKey | quote }}
{{- if .Values.citadel.uiSessionSecret }}
UI_SESSION_SECRET: {{ .Values.citadel.uiSessionSecret | quote }}
{{- end }}
{{- if .Values.citadel.okta.enabled }}
OKTA_CLIENT_SECRET: {{ .Values.citadel.okta.clientSecret | quote }}
UI_SESSION_SECRET: {{ .Values.citadel.okta.sessionSecret | quote }}
Expand Down
5 changes: 3 additions & 2 deletions values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@ citadel:
environment: production
logLevel: INFO
secretKey: ""
uiSessionSecret: ""
apiKeyPrefix: "sk-citadel"
autoProvisionUsers: true
devLoginEnabled: false
guardrails:
enabled: true
openaiModeration: false
openaiModeration: true
piiFilter: false
passthrough:
enabled: false
enabled: true
plugins:
enabled: true
# OIDC / Okta (optional)
Expand Down