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
| Endpoint |
admin |
manager |
user |
auditor |
{[
{ 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.ep} |
{[row.admin, row.manager, row.user, row.auditor].map((v, i) => (
{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`}
/>
);
}