Skip to main content
This reference documents the technical specifications of Char’s security controls. For conceptual understanding of why these controls exist, see the Security Architecture explanation.

Token Validation

Required Claims

ClaimPurposeValidation
issIssuerMust match configured IDP issuer exactly
subSubjectMust be present and non-empty
audAudienceMust include configured client ID
expExpirationMust be in the future (60s clock skew tolerance)

Optional Claims

ClaimPurposeWhen Used
tidTenant ID (Azure AD)Multi-tenant Azure configurations
hdHosted domain (Google)Google Workspace configurations
org_idOrganization IDOkta with organizations enabled
nbfNot beforeValidated if present
emailUser emailExtracted for user identification

Supported Algorithms

AlgorithmStatus
RS256, RS384, RS512Supported
ES256, ES384, ES512Supported
HS256, HS384, HS512Blocked (symmetric)
noneBlocked
Symmetric algorithms are blocked to prevent key confusion attacks. Only asymmetric (public key) algorithms are permitted.

JWKS Caching

ParameterValue
Cache TTL1 hour
Refresh on 401Immediate
StorageIn-memory

Identity Provider Support

Supported IDPs

ProviderIDP TypeIssuer Pattern
Oktaoktahttps://{domain}
Azure ADazurehttps://login.microsoftonline.com/{tenant}/v2.0
Auth0auth0https://{domain}/
Googlegooglehttps://accounts.google.com
AWS Cognitocustom_oidchttps://cognito-idp.{region}.amazonaws.com/{pool}
Firebasecustom_oidchttps://securetoken.google.com/{project}
Clerkcustom_oidchttps://{instance}.clerk.accounts.dev
Keycloakcustom_oidchttps://{host}/realms/{realm}
Custom OIDCcustom_oidcConfigurable

OIDC Discovery

For each IDP, Char fetches the OpenID Connect discovery document at:
{issuer}/.well-known/openid-configuration
Required discovery fields:
  • issuer - Must match token iss claim
  • jwks_uri - Endpoint for public keys

Origin Validation

Allowed Domains

RuleBehavior
Exact match onlyNo wildcard support
Protocol requiredhttps://app.example.com
Localhost always allowedDevelopment convenience
Empty listAll origins rejected

Validation Order

  1. Extract Origin header from request
  2. Look up organization by matching allowed_domains
  3. If found, validate JWT against that organization’s IDP
  4. If not found, reject request

Localhost Fallback

When origin is localhost:
  1. Extract iss claim from token (unverified)
  2. Match against organization’s idp_issuer field
  3. If matched, use that organization’s configuration
Localhost fallback is for development only. Production deployments should configure explicit allowed domains.

Durable Object Isolation

Identity Scoping

Durable ObjectKeyed ByContains
ThreadManager{external_user_id}Thread metadata, tool connections, active sessions
ThreadAgent{thread_id}Messages, tool selection, conversation state

State Initialization

Each Durable Object is initialized with:
{
  organizationId: string  // Required
  endUserId: string       // Internal ID (from end_users table)
  externalId: string      // External user ID (sub claim)
}

Access Guards

Every operation calls requireMetadata() which:
  1. Verifies organization context is set
  2. Verifies user context is set
  3. Throws if either is missing
Cross-organization access is rejected in createThread() with an explicit check.

Tool Execution

Tool Sources

SourceDescriptionScope
Client toolsRegistered via WebSocket from browser tabsPer-connection
SkillsSKILL.md files stored in D1Per-organization
UI toolsBuilt-in tools (done, form)Global

Tool Registry

Tools are stored per-connection in the ThreadManager:
connections: Map<connectionId, {
  ws: WebSocket
  tools: Map<toolName, ToolSchema>
  tabId: string
  origin: string
}>
When a connection closes, its tools are removed.

Tool Invocation

  1. Agent requests tool call with tool_name and arguments
  2. ThreadManager finds connection that registered this tool
  3. Request is routed to that specific browser tab via WebSocket
  4. Result is returned through the same connection

Database Schema

Organization Scoping

All major tables include organization_id:
TableOrganization ColumnPurpose
organizationsid (PK)Organization identity
organization_membersorganization_id (FK)Membership
end_usersorganization_id (FK)Per-org end users
threadsorganization_id (FK)Conversation threads
organization_skillsorganization_id (FK)SKILL.md storage
idp_configsorganization_id (FK)IDP configuration

Row-Level Filtering

All queries filter by organization_id. Example pattern:
SELECT * FROM threads
WHERE organization_id = ?
  AND end_user_id = ?
ORDER BY updated_at DESC

Error Codes

Authentication Errors

CodeHTTP StatusMeaning
UNAUTHORIZED401Missing or invalid token
FORBIDDEN403Valid token, insufficient permissions

MCP Error Mapping

oRPC ErrorMCP Error Code
UNAUTHORIZEDInvalidRequest
FORBIDDENInvalidRequest
NOT_FOUNDInvalidRequest
BAD_REQUESTInvalidParams
INTERNAL_SERVER_ERRORInternalError

Enterprise Features

The following security controls are available on the Enterprise plan:
FeatureDescription
Rate limitingPer-user and per-endpoint throttling with configurable thresholds
Audit loggingCentralized logging of tool invocations for compliance
Content guardrailsPII detection, prompt injection filtering
Approval workflowsHuman-in-the-loop for sensitive operations
Kill switchesInstant disable of tools or connectors
Tool risk classificationL0-L3 classification with configurable approval levels
The Hub-mediated architecture makes these controls straightforward to enable—they plug into the existing request flow without changing tool implementations.
Need these features? Contact us to discuss Enterprise pricing.

Further Reading