Skip to content

LLM RBAC — Role-Based Access Control for AI models

AKKO maps the 5 AKKO platform roles (akko-admin, akko-engineer, akko-analyst, akko-user, akko-viewer) to LiteLLM model access groups and quotas, centralising the AI access matrix in a single ConfigMap that is exposed via the Cockpit UI under Governance → LLM Access Matrix.


Default matrix

Role Chat (qwen2.5:3b) Embed (nomic) Code (qwen-coder) GPU (vLLM) Daily quota Rate limit
akko-admin unlimited unlimited
akko-engineer 50 k tokens 120 req/min
akko-analyst 20 k tokens 60 req/min
akko-user 5 k tokens 30 req/min
akko-viewer none

Source: helm/akko/charts/akko-cockpit/templates/configmap-llm-rbac.yaml (rendered ConfigMap akko-llm-rbac in namespace akko).


Architecture

Cockpit UI (Governance → LLM Access tab)
GET /api/cockpit/llm-rbac.json     ← served by nginx cockpit from mounted ConfigMap
  ├── read by browser → renders the matrix (read-only)
  └── write: GitOps only — edit the Helm-templated ConfigMap, commit, push,
       let CI re-deploy. No runtime mutation endpoint. This avoids the
       need for a K8s ServiceAccount with patch-ConfigMap rights inside
       the cockpit, and keeps every change auditable in Git history.

File layout

File Role
helm/akko/charts/akko-cockpit/templates/configmap-llm-rbac.yaml Source of truth (Helm-templated)
helm/akko/charts/akko-cockpit/templates/deployment.yaml Mounts ConfigMap at /usr/share/nginx/html/api/cockpit/llm-rbac.json
branding/cockpit/index.html Governance page with matrix UI (Sprint 15.5)
branding/cockpit/app.js loadLlmRbac() fetches the JSON + renders

Using the matrix from code

From a notebook (jupyter-alice)

import os
import requests
# LiteLLM respects the access_groups based on the bearer token user
resp = requests.post(
    f"http://akko-akko-litellm:4000/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['LITELLM_USER_KEY']}"},
    json={"model": "akko-chat", "messages": [{"role": "user", "content": "hi"}]}
)

If the user's role doesn't match the model's access_groups, LiteLLM returns 403 Forbidden with a message indicating the missing group.

From Trino (via ai_*() functions)

-- The ai_service middleware passes the Trino user's Keycloak role to LiteLLM.
-- A user with role akko-viewer gets an empty result (no model access).
SELECT akko_ai_sentiment(comment) FROM reviews LIMIT 10;

From Claude Desktop (via MCP)

MCP Trino server propagates the user's role via HTTP header. The LiteLLM gateway enforces it the same way.


Modifying the matrix

Edit helm/akko/charts/akko-cockpit/templates/configmap-llm-rbac.yaml → commit → push → CI auto-deploy → ConfigMap updated.

Via Cockpit UI (Sprint 15.5, admin only — read-only view)

  1. Sign in as alice (akko-admin)
  2. Navigate to Governance → LLM Access tab
  3. The full matrix renders: 4 models × 5 roles, with quotas and rate limits per role
  4. To change the matrix, edit the Helm ConfigMap and push (GitOps only — no runtime edits)

The read-only design is deliberate: every change goes through code review and lands as a Git commit, so access-control history is auditable forever. The configmap-llm-rbac.yaml file is the single source of truth.


Adding a new model

  1. Edit helm/examples/values-dev.yaml — add the model to akko-litellm.config.model_list
  2. Edit helm/akko/charts/akko-cockpit/templates/configmap-llm-rbac.yaml — add the model to models section with its default access_groups
  3. Commit + push → CI deploy
  4. LiteLLM picks up the new model automatically; matrix shows the new column

Auditing LLM usage

Every LLM call is logged in logs layer by the AI Service middleware:

{service="ai-service"} |= "llm_call" | json | line_format
"{{.user}} {{.model}} {{.status}} tokens={{.tokens}}"

Dashboard Dashboards LLM Usage by Role (planned Sprint 15.7) shows: - Tokens/hour per role - Top 10 users by volume - Error rate per model - Cost estimate (when commercial models are added)


Emergency lockout

Set a role to access_groups: [] to immediately deny all LLM access for that role, without changing Keycloak or redeploying services:

kubectl edit configmap akko-llm-rbac -n akko
# Find "akko-analyst" → set "access_groups": []
# Save → LiteLLM reload → effective immediately

See also