Proxy RBAC & Cloud Auth
The proxy enforces role-based access control using policies embedded in JWT tokens from the Quint auth service. Evaluation is deny-first — every action must pass all checks to be allowed.
Token Types
Quint uses 6 token types, each identified by a prefix:
| Prefix | Type | Purpose |
|---|
qt_app_ | app | Application-level access |
qt_bearer_ | bearer | Environment-scoped access (has env field) |
qt_agent_ | agent | Agent identity with RBAC policy |
qt_subagent_ | subagent | Child agent with narrowed permissions |
qt_session_ | session | Time/event-limited session |
qt_override_ | override | One-time decision override for a specific event |
All tokens are ES256 (ECDSA P-256) JWTs. The proxy validates signatures against the auth service’s public key, which is fetched and cached with a configurable TTL (default: 5 minutes).
Deny-First Evaluation
Every action goes through 6 sequential checks. The first failure stops evaluation.
Step 1: Denied Actions
If the action matches any pattern in denied_actions → DENY.
Step 2: Allowed Actions
If allowed_actions is non-empty and the action matches none → DENY.
Step 3: Denied Resources
If the resource matches any pattern in denied_resources → DENY.
Step 4: Allowed Resources
If allowed_resources is non-empty and the resource matches none → DENY.
Step 5: Sensitivity Level
If the resource’s sensitivity level exceeds the policy’s limit → DENY.
Step 6: Allowed
All checks passed → ALLOW.
Glob Pattern Matching
Actions and resources are matched using glob patterns with colon-aware wildcards:
| Pattern | Matches | Does Not Match |
|---|
mcp:github:* | mcp:github:list_repos.list | mcp:slack:post.send |
mcp:** | mcp:github:list_repos.list | http:api.openai.com:POST.chat |
mcp:*:*.read | mcp:postgres:query.read | mcp:postgres:query.write |
*:*:*.delete | mcp:s3:remove_object.delete | mcp:s3:list_objects.list |
* matches within a single colon-separated segment
** matches across segments (recursive)
RBAC Policy Structure
Each agent/subagent token carries an RBAC policy in its JWT claims:
{
"sub": "customer-uuid",
"typ": "agent",
"agent_id": "support-bot",
"rbac": {
"allowed_actions": ["mcp:slack:*", "mcp:notion:*"],
"denied_actions": ["mcp:**:*.delete", "mcp:**:*.execute"],
"allowed_resources": ["*"],
"denied_resources": ["vault/*", "*/credentials"],
"sensitivity_level": 2,
"max_risk_score": 75
}
}
| Field | Type | Description |
|---|
allowed_actions | string[] | Glob patterns for permitted actions (empty = allow all) |
denied_actions | string[] | Glob patterns for blocked actions (checked first) |
allowed_resources | string[] | Glob patterns for permitted resources |
denied_resources | string[] | Glob patterns for blocked resources |
sensitivity_level | int | Max data sensitivity the agent can access (0-4) |
max_risk_score | int | Max risk score before blocking (0-100) |
Subagent Policy Narrowing
When a parent agent spawns a child, the child’s RBAC policy is automatically narrowed — it can never exceed the parent’s permissions:
| Constraint | Rule |
|---|
| Allowed actions | child ⊆ parent (intersection) |
| Denied actions | child ⊇ parent (union — child inherits all parent denials) |
| Sensitivity level | child ≤ parent (minimum) |
| Max risk score | child ≤ parent (minimum) |
Parent: allowed_actions = ["mcp:github:*", "mcp:slack:*"]
Child: allowed_actions = ["mcp:github:*.read"]
Result: valid (child is a subset)
Parent: denied_actions = ["mcp:**:*.delete"]
Child: denied_actions = ["mcp:**:*.delete", "mcp:**:*.execute"]
Result: valid (child is a superset)
Parent: sensitivity_level = 3
Child: sensitivity_level = 4
Result: INVALID (child exceeds parent)
Narrowing is validated cryptographically — a subagent token with wider permissions than its parent will be rejected during JWT validation.
Local Scope Hierarchy
For agents without cloud tokens, the proxy uses a local scope system:
| Scope | Implies |
|---|
tools:admin | tools:write, tools:read |
tools:execute | tools:read |
tools:write | tools:read |
tools:read | (base level) |
Tool names are mapped to required scopes:
| Tool Prefix/Keyword | Required Scope |
|---|
delete, remove, drop | tools:admin |
execute, shell, bash, run | tools:execute |
write, create, update, edit | tools:write |
read, get, list, search | tools:read |
| Unknown | tools:write (fail-closed) |
Cloud Key Validation
The proxy fetches the auth service’s public key to validate JWT signatures:
GET {auth_service_url}/keys/public/{customer_id}
| Setting | Default | Description |
|---|
base_url | — | Auth service URL |
customer_id | — | Customer ID for key lookup |
timeout_ms | 5000 | Validation timeout |
key_refresh_seconds | 300 | Public key cache TTL |
{
"auth_service": {
"base_url": "https://auth.quintai.com",
"customer_id": "a1b2c3d4",
"enabled": true,
"timeout_ms": 5000,
"key_refresh_seconds": 300
}
}
Environment variable QUINT_TOKEN can provide a cloud JWT token for stdio relay mode.
Token Resolution Flow