Skip to main content
Quint uses a 6-level token hierarchy where each level derives from its parent. Tokens are prefixed for O(1) type detection and contain ES256-signed JWT claims.

Hierarchy Diagram

                    ┌───────────┐
                    │ App Token │  qt_app_*    (management plane)
                    │  1 year   │
                    └─────┬─────┘

                    ┌─────▼─────┐
                    │  Bearer   │  qt_bearer_*  (data plane root)
                    │  90 days  │
                    └─────┬─────┘

                    ┌─────▼─────┐
              ┌─────│   Agent   │  qt_agent_*   (per-agent identity)
              │     │  24 hours │
              │     └─────┬─────┘
              │           │
        ┌─────▼─────┐  ┌─▼──────────┐
        │  Session   │  │  Subagent  │  qt_subagent_*
        │  1 hour    │  │  4 hours   │
        └────────────┘  └─────┬──────┘

                        ┌─────▼─────┐
                        │  Session   │  qt_session_*
                        │  1 hour    │
                        └────────────┘

        ┌────────────┐
        │  Override   │  qt_override_*  (ephemeral, single-use)
        │  5 minutes  │
        └────────────┘

Token Types

1. App Token (qt_app_*)

Purpose: Management-plane operations — creating other tokens, rotating keys, registering webhooks.
FieldValue
Prefixqt_app_
Default TTL1 year
Auth requiredBootstrap / admin
JWT claimsjti, sub (customer_id), typ: "app", iat, exp
StorageHash stored in auth.app_tokens
Create:
POST /tokens/app
{
  "customer_id": "uuid",
  "name": "Production API",
  "scopes": ["*"]
}
App tokens are the root of the derivation chain. They have scopes but no RBAC policy — management tokens bypass RBAC.

2. Bearer Token (qt_bearer_*)

Purpose: Data-plane root token — the entry point for agent authentication. Environment-scoped.
FieldValue
Prefixqt_bearer_
Default TTL90 days
Auth requiredApp token
JWT claimsjti, sub, typ: "bearer", parent_jti (app token), env
Storageauth.bearer_tokens with FK to app_tokens
Create:
POST /tokens/bearer
{
  "customer_id": "uuid",
  "app_token_hash": "sha256-hash-of-app-token",
  "environment": "production"
}
Environment values: development, staging, production

3. Agent Token (qt_agent_*)

Purpose: Per-agent identity with RBAC policy. This is the primary token used in Authorization: Bearer headers when submitting events.
FieldValue
Prefixqt_agent_
Default TTL24 hours
Auth requiredBearer token
JWT claimsjti, sub, typ: "agent", parent_jti (bearer), agent_id, rbac
Storageauth.agent_tokens with FK to bearer_tokens
Create:
POST /tokens/agent
{
  "customer_id": "uuid",
  "bearer_jti": "jti-of-bearer-token",
  "agent_id": "code-review-agent",
  "agent_name": "Code Review Agent",
  "rbac": {
    "allowed_actions": ["data:read:*", "code:review:*"],
    "denied_actions": ["data:write:*"],
    "allowed_resources": ["repo:*"],
    "denied_resources": [],
    "max_sensitivity_level": 3
  }
}
RBAC enforcement: On every event submission, the agent’s RBAC policy is checked against event.action and event.target_resource.

4. Subagent Token (qt_subagent_*)

Purpose: Delegated token for sub-agents with permission narrowing. A sub-agent cannot have broader permissions than its parent agent.
FieldValue
Prefixqt_subagent_
Default TTL4 hours
Auth requiredAgent token
JWT claimsjti, sub, typ: "subagent", parent_jti (agent), agent_id, rbac, depth
Storageauth.subagent_tokens with FK to agent_tokens
Permission narrowing rules:
  • child.allowed_actions must be a subset of parent.allowed_actions
  • child.denied_actions must be a superset of parent.denied_actions
  • child.allowed_resources must be a subset of parent.allowed_resources
  • child.denied_resources must be a superset of parent.denied_resources
  • child.max_sensitivity_level must be <= parent.max_sensitivity_level
Delegation depth: Configurable per customer (default max: 3). Depth is tracked in the depth claim and incremented on each delegation.

5. Session Token (qt_session_*)

Purpose: Short-lived token binding an agent to a specific session with event counting.
FieldValue
Prefixqt_session_
Default TTL1 hour
Auth requiredAgent or subagent token
JWT claimsjti, sub, typ: "session", parent_jti, session_id
Storageauth.session_tokens
CounterRedis INCR on quint:auth:session_events:{"{jti}"}
Create:
POST /tokens/session
{
  "customer_id": "uuid",
  "parent_jti": "jti-of-agent-or-subagent",
  "parent_type": "agent",
  "session_id": "session-2024-01-15-abc",
  "max_events": 1000
}
Event counting: Each event submitted with a session token increments a Redis counter. When event_count exceeds max_events, the API returns SessionExhaustedError. Usage: Sent via X-Quint-Session header alongside the Authorization: Bearer qt_agent_* header.

6. Override Token (qt_override_*)

Purpose: Ephemeral, single-use token for human override decisions on held high-risk events.
FieldValue
Prefixqt_override_
Default TTL5 minutes
Auth requiredInternal (API proxy)
JWT claimsjti, sub, typ: "override", event_id, allowed_decisions
Storageauth.override_tokens with use_count/max_uses
Flow:
  1. Scoring pipeline flags event as high-risk, puts it on hold
  2. System creates override token linked to the event
  3. Human reviewer receives token (via webhook/notification)
  4. Human submits decision via POST /overrides/{"{event_id}"}/decide with override token
  5. Decision is cryptographically attested with HMAC co-signature

Derivation Chain Verification

On every token validation, the _verify_chain() method checks that the JWT claims are consistent with the token type:
Token TypeRequired Claims
App(none — root of chain)
Bearerparent_jti (app), env
Agentparent_jti (bearer), agent_id, rbac
Subagentparent_jti (agent), agent_id, rbac, depth
Sessionparent_jti (agent/subagent), session_id
Overrideevent_id
If any required claim is missing, validation fails with TokenInvalidError.

Token Prefixes

All tokens are prefixed for instant type detection without decoding:
TOKEN_PREFIX_APP       = "qt_app_"
TOKEN_PREFIX_BEARER    = "qt_bearer_"
TOKEN_PREFIX_AGENT     = "qt_agent_"
TOKEN_PREFIX_SUBAGENT  = "qt_subagent_"
TOKEN_PREFIX_SESSION   = "qt_session_"
TOKEN_PREFIX_OVERRIDE  = "qt_override_"
A raw token looks like: qt_agent_eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...