import { Callout } from "../components/Callout"; import { CodeBlock } from "../components/CodeBlock"; export function RbacGuide() { return (

RBAC & Permissions

Veylant IA enforces Role-Based Access Control on every request. Roles are stored in the{" "} users table and embedded in the HS256 JWT at login time. A role cannot be elevated at runtime — a new token must be issued after a role change.

Roles

{[ { role: "admin", color: "bg-red-100 dark:bg-red-900/40 text-red-700 dark:text-red-300", description: "Full access. Can manage routing policies, users, providers, feature flags, and read all compliance/audit data. Has unrestricted model access.", }, { role: "manager", color: "bg-amber-100 dark:bg-amber-900/40 text-amber-700 dark:text-amber-300", description: "Read-write access to routing policies and user profiles. Can run AI inference with any model. Cannot manage feature flags or access compliance reports.", }, { role: "user", color: "bg-blue-100 dark:bg-blue-900/40 text-blue-700 dark:text-blue-300", description: "Inference only. Restricted to the model list in rbac.user_allowed_models (default: gpt-4o-mini, mistral-medium). No admin API access.", }, { role: "auditor", color: "bg-purple-100 dark:bg-purple-900/40 text-purple-700 dark:text-purple-300", description: "Read-only access to audit logs, costs, and compliance data. Cannot call /v1/chat/completions. Intended for compliance officers and DPOs.", }, ].map((item) => (
{item.role}

{item.description}

))}

Permission Matrix

{[ { ep: "POST /v1/auth/login", admin: "✓", manager: "✓", user: "✓", auditor: "✓" }, { ep: "POST /v1/chat/completions", admin: "✓", manager: "✓", user: "✓ (limited models)", auditor: "✗" }, { ep: "POST /v1/pii/analyze", admin: "✓", manager: "✓", user: "✓", auditor: "✓" }, { ep: "GET /v1/admin/policies", admin: "✓", manager: "✓", user: "✗", auditor: "✗" }, { ep: "POST/PUT/DELETE /v1/admin/policies", admin: "✓", manager: "✓", user: "✗", auditor: "✗" }, { ep: "GET/POST/PUT/DELETE /v1/admin/users", admin: "✓", manager: "read only", user: "✗", auditor: "✗" }, { ep: "GET /v1/admin/logs", admin: "✓", manager: "✓", user: "✗", auditor: "✓" }, { ep: "GET /v1/admin/costs", admin: "✓", manager: "✓", user: "✗", auditor: "✓" }, { ep: "GET /v1/admin/compliance/*", admin: "✓", manager: "✗", user: "✗", auditor: "✓" }, { ep: "POST/PUT/DELETE /v1/admin/compliance/*", admin: "✓", manager: "✗", user: "✗", auditor: "✗" }, { ep: "GET/PUT/DELETE /v1/admin/flags", admin: "✓", manager: "✗", user: "✗", auditor: "✗" }, { ep: "GET /v1/admin/providers/status", admin: "✓", manager: "✓", user: "✗", auditor: "✗" }, { ep: "GET/POST/PUT/DELETE /v1/admin/providers", admin: "✓", manager: "✗", user: "✗", auditor: "✗" }, { ep: "GET/PUT/DELETE /v1/admin/rate-limits", admin: "✓", manager: "✗", user: "✗", auditor: "✗" }, ].map((row) => ( {[row.admin, row.manager, row.user, row.auditor].map((v, i) => ( ))} ))}
Endpoint admin manager user auditor
{row.ep} {v}

Model Restrictions

Users with the user role can only access models listed in{" "} rbac.user_allowed_models. Requests to other models are rejected with 403:

The admin and manager roles have unrestricted model access —{" "} user_allowed_models does not apply to them.

Managing User Roles via the Admin API

Roles are managed through the /v1/admin/users endpoints. After updating a user's role, they must log in again to receive a new token with the updated claims.

JWT tokens embed the role at login time. After changing a user's role via the API, the user must log out and log in again. The old token remains valid until its exp claim (controlled by auth.jwt_ttl_hours).

Bulk User Import

For tenant onboarding, use the provided script to bulk-import users from a CSV file:

users.csv << 'EOF' alice@acme.com,Alice,Martin,Legal,user bob@acme.com,Bob,Dupont,Finance,manager carol@acme.com,Carol,Lefebvre,IT,auditor EOF # Import (requires make dev to be running) ./deploy/onboarding/import-users.sh users.csv`} />
); }