How to point your coding agents at AuditTrace-AI.
- Stack is running:
./scripts/start-full-stack.sh - mkcert CA is installed locally:
mkcert -install(already run during cert generation)
| Legacy | Current |
|---|---|
http://localhost:8765/v1/chat/completions |
https://localhost/v1/chat/completions |
http://localhost:8765/invoke |
https://localhost/context (POST {"query": "..."}) |
http://localhost:8765/health |
https://localhost/health |
The mkcert CA is trusted system-wide, so HTTPS works without -k for tools that
read the system trust store. Some tools bundle their own — see notes per agent.
File: ~/.config/opencode/config.json
Since ADR-026 Phase 5b the memory-server defaults to
AUDITTRACE_AUTH_REQUIRED=true, which means every request must carry
a valid Keycloak JWT in the Authorization: Bearer header. The
@ai-sdk/openai-compatible provider maps its apiKey field to exactly
that header, so we reuse it as the JWT carrier.
Use the sovereign-memory-dev client via the helper script:
# One-time setup: copy script + read the secret
docker cp scripts/mint-dev-jwt.sh audittrace-ai:/tmp/
TOKEN=$(docker exec \
-e CLIENT_SECRET="$(cat secrets/dev_client_secret.txt)" \
audittrace-ai bash /tmp/mint-dev-jwt.sh)
echo "$TOKEN" > ~/.config/opencode/sovereign-jwt.txtThe token lasts ~10 hours (client-level access.token.lifespan=86400
is capped by the realm's default SSO session max of 10h). Re-mint
once per workday.
Why not Traefik HTTPS +
apiKeyfrom the host? Keycloak's issuer ishttp://keycloak:8080/realms/sovereign-ai(the internal docker-network hostname the memory-server trusts). Running the mint script from inside the audittrace-net viadocker execguarantees the JWT'sissclaim matches whatrequire_uservalidates. A host-side mint againstlocalhostor Traefik would produce a token with a different issuer that the memory-server would reject.
{
"$schema": "https://opencode.ai/config.json",
"model": "sovereign/qwen3.8",
"provider": {
"sovereign": {
"npm": "@ai-sdk/openai-compatible",
"name": "audittrace-ai",
"options": {
"baseURL": "https://localhost/v1",
"apiKey": "<paste the JWT from sovereign-jwt.txt here>",
"headers": {
"X-Project": "default"
}
},
"models": {
"qwen3.8": {
"name": "Qwen3.8-27B (dense) + MTP",
"tools": true
}
}
}
},
"instructions": ["~/.config/opencode/AGENTS.md"]
}Without an X-Project header every interaction lands with
project="default" — recall_recent_sessions then has no way to scope
cross-session memory to the project you're actually working on. The
memory-server reads the header on every /v1/chat/completions, falls
back to body.metadata.project, then body.project, then
"default".
One helper script ships with the server to set the tag without editing JSON by hand:
# Before starting a new project session
scripts/configure-project.py AuditTrace-AI
# Preview without writing
scripts/configure-project.py SMC --dry-run
# Inspect the current value per provider
scripts/configure-project.py --show
# Set the tag AND launch opencode in one go
scripts/configure-project.py SMC --launchThe script walks every provider.*.options block in
~/.config/opencode/config.json and sets
headers["X-Project"] = <name>. A timestamped backup is written
alongside the config; the new file replaces the old atomically
(os.replace), so a partial write cannot leave the config broken.
Restart OpenCode after running the script — it reads config at launch, so a session that is already open is still using the old header.
A single-line refresh wrapper (optional, nicer DX) — save as
~/.config/opencode/refresh-jwt.sh:
#!/usr/bin/env bash
set -euo pipefail
REPO="${1:-$HOME/work/AuditTrace-AI}"
cd "$REPO"
docker cp scripts/mint-dev-jwt.sh audittrace-ai:/tmp/ >/dev/null
TOKEN=$(docker exec -e CLIENT_SECRET="$(cat secrets/dev_client_secret.txt)" \
audittrace-ai bash /tmp/mint-dev-jwt.sh)
# Patch the apiKey in OpenCode's config in place (requires jq)
CONF=~/.config/opencode/config.json
tmp=$(mktemp)
jq --arg t "$TOKEN" \
'.provider.sovereign.options.apiKey = $t' \
"$CONF" > "$tmp" && mv "$tmp" "$CONF"
echo "Refreshed OpenCode JWT (sub: $(echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq -r .sub))"Run once per day (or hook into your shell's session start).
If you hit self signed certificate in certificate chain, set
NODE_EXTRA_CA_CERTS before launching OpenCode:
export NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem"
opencodeAdd this to your ~/.zshrc to make it permanent.
If Keycloak is down or you need to debug without auth, flip the
memory-server env back to AUDITTRACE_AUTH_REQUIRED=false for the
duration of the debug session:
# k3s + Helm is the sole app-deploy path (docker-compose was stopped).
# Toggle the flag on the running Deployment and roll it:
kubectl set env deploy/audittrace-memory-server AUDITTRACE_AUTH_REQUIRED=false
kubectl rollout status deploy/audittrace-memory-server
# Durable change: set memoryServer env in charts/audittrace/values.yaml + helm upgrade.The sentinel UserContext takes over and requests without JWTs
get the admin-by-construction fallback. Flip back to true when
done (kubectl set env deploy/audittrace-memory-server AUDITTRACE_AUTH_REQUIRED=true).
File: ~/.continue/config.yaml
name: Sovereign Memory Server
version: 1.0.0
schema: v1
models:
- name: Qwen3.8-27B dense+MTP (sovereign)
provider: openai
model: qwen3.8
apiBase: https://localhost/v1
apiKey: dummy
contextLength: 65536
defaultCompletionOptions:
temperature: 0.7
maxTokens: 4096
tabAutocompleteModel:
name: Qwen3.8-27B dense+MTP (sovereign)
provider: openai
model: qwen3.8
apiBase: https://localhost/v1
apiKey: dummy
# Memory context injection — calls /context before each query
contextProviders:
- name: http
params:
url: https://localhost/context
title: "Sovereign Memory"
description: "five-layer memory — sessions, ADRs, skills, semantic RAG, shared corpus"
displayTitle: "Memory"
systemMessage: |
You are working with a Solutions Architect specialized in IAM/OAuth2.
Local stack: llama-server :11435 (Qwen3.8-27B dense+MTP, Vulkan).
Memory server: audittrace-ai (five-layer memory, PostgreSQL + ChromaDB).
Always answer in English.Continue uses Node's HTTPS stack and respects the system CA store on Linux.
If TLS errors persist, set NODE_EXTRA_CA_CERTS in your VS Code launcher.
Settings → Roo Code → API Configuration:
| Field | Value |
|---|---|
| API Provider | OpenAI Compatible |
| Base URL | https://localhost/v1 |
| API Key | dummy |
| Model | qwen3.8 |
| Custom Headers | (none) |
If TLS errors appear, launch VS Code with:
NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem" code .After updating any agent, test with a simple query:
curl -s https://localhost/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"qwen3.8","messages":[{"role":"user","content":"What is ADR-009 about?"}]}' \
| python3 -m json.toolIf memory augmentation works, the answer should reference KV cache compression (content from your migrated ADR-009 file).
Current default since ADR-026 Phase 5b:
AUDITTRACE_AUTH_REQUIRED=true — every request needs a valid
Keycloak JWT in Authorization: Bearer <jwt>. See the per-agent
sections above for how to plug a token into each client.
Two auth flags exist:
| Flag | Purpose | Default |
|---|---|---|
AUDITTRACE_AUTH_ENABLED |
Legacy require_scope gate (ADR-022, ADR-023). Gates specific scope strings per route |
false |
AUDITTRACE_AUTH_REQUIRED |
New require_user gate (ADR-026 §15). Validates JWT against JWKS, populates UserContext, pushes app.current_user_id into Postgres RLS |
true |
The two are independent but today both live on the memory-server;
require_user is the authoritative path for per-user identity.
JWT sources:
| Client type | Flow | Client |
|---|---|---|
| Dev / curl / Bruno | client_credentials grant with a service account |
sovereign-memory-dev |
| OpenCode / Continue / Roo Code (daily use) | Client-credentials JWT via scripts/mint-dev-jwt.sh, paste into client config |
Same as above |
| Production humans | OAuth2 device flow (deferred — Phase 8+) | Dedicated public client TBD |
See ADR-026 §15 for the full Keycloak-delegated identity design and §16 for the end-of-2026-04-11 shipped status with commit SHAs.