Overview
Shannon uses PostgreSQL with pgvector extension for persistent storage. The database is organized into two schemas:auth- Authentication, multi-tenancy, and securitypublic- Core application tables (tasks, agents, sessions)
Entity Relationship Diagram
Schema: auth (Authentication & Multi-Tenancy)
auth.tenants
Purpose: Multi-tenant organization management| Column | Type | Constraints | Description |
|---|---|---|---|
| id | UUID | PK, DEFAULT gen_random_uuid() | Tenant identifier |
| name | VARCHAR(255) | NOT NULL | Organization name |
| slug | VARCHAR(100) | UNIQUE, NOT NULL | URL-safe identifier |
| plan | VARCHAR(50) | DEFAULT ‘free’ | Subscription plan (free, pro, enterprise) |
| token_limit | INTEGER | DEFAULT 10000 | Monthly token allowance |
| monthly_token_usage | INTEGER | DEFAULT 0 | Current month usage |
| rate_limit_per_hour | INTEGER | DEFAULT 1000 | API rate limit |
| is_active | BOOLEAN | DEFAULT true | Tenant status |
| created_at | TIMESTAMP | DEFAULT NOW() | Creation time |
| updated_at | TIMESTAMP | DEFAULT NOW() | Last update time |
| metadata | JSONB | DEFAULT '' | Additional tenant data |
- Primary key on
slug(UNIQUE)
auth.users
Purpose: User authentication and profile management| Column | Type | Constraints | Description |
|---|---|---|---|
| id | UUID | PK, DEFAULT gen_random_uuid() | User identifier |
| VARCHAR(255) | UNIQUE, NOT NULL | User email | |
| username | VARCHAR(100) | UNIQUE, NOT NULL | Username |
| password_hash | VARCHAR(255) | NOT NULL | Bcrypt password hash |
| full_name | VARCHAR(255) | Full name | |
| tenant_id | UUID | FK → auth.tenants, NOT NULL | Organization |
| role | VARCHAR(50) | DEFAULT ‘user’ | Role (user, admin, owner) |
| is_active | BOOLEAN | DEFAULT true | Account status |
| is_verified | BOOLEAN | DEFAULT false | Email verification |
| email_verified_at | TIMESTAMP | Verification timestamp | |
| created_at | TIMESTAMP | DEFAULT NOW() | Account creation |
| updated_at | TIMESTAMP | DEFAULT NOW() | Last update |
| last_login | TIMESTAMP | Last login time | |
| metadata | JSONB | DEFAULT '' | Additional user data |
idx_users_emailON (email)idx_users_usernameON (username)idx_users_tenant_idON (tenant_id)
auth.api_keys
Purpose: API key management for programmatic access| Column | Type | Constraints | Description |
|---|---|---|---|
| id | UUID | PK, DEFAULT gen_random_uuid() | Key identifier |
| key_hash | VARCHAR(255) | UNIQUE, NOT NULL | SHA256 hash of key |
| key_prefix | VARCHAR(20) | NOT NULL | First 8 chars (for display) |
| user_id | UUID | FK → auth.users | Key owner |
| tenant_id | UUID | FK → auth.tenants, NOT NULL | Organization |
| name | VARCHAR(100) | NOT NULL | Key name/description |
| description | TEXT | Detailed description | |
| scopes | TEXT[] | DEFAULT […] | Permissions array |
| rate_limit_per_hour | INTEGER | DEFAULT 1000 | Key-specific rate limit |
| last_used | TIMESTAMP | Last usage timestamp | |
| expires_at | TIMESTAMP | Expiration time | |
| is_active | BOOLEAN | DEFAULT true | Key status |
| created_at | TIMESTAMP | DEFAULT NOW() | Creation time |
| metadata | JSONB | DEFAULT '' | Additional key data |
idx_api_keys_tenant_idON (tenant_id)idx_api_keys_key_prefixON (key_prefix)
tasks:run: Allow task executiontasks:read: Allow reading task status and resultssessions:manage: Allow managing sessions
auth.refresh_tokens
Purpose: JWT refresh token storage and revocation| Column | Type | Constraints | Description |
|---|---|---|---|
| id | UUID | PK, DEFAULT gen_random_uuid() | Token identifier |
| token_hash | VARCHAR(255) | UNIQUE, NOT NULL | SHA256 hash |
| user_id | UUID | FK → auth.users | Token owner |
| tenant_id | UUID | FK → auth.tenants, NOT NULL | Organization |
| expires_at | TIMESTAMP | NOT NULL | Expiration time |
| revoked | BOOLEAN | DEFAULT false | Revocation status |
| revoked_at | TIMESTAMP | Revocation time | |
| ip_address | INET | Client IP | |
| user_agent | TEXT | Client user agent | |
| created_at | TIMESTAMP | DEFAULT NOW() | Creation time |
idx_refresh_tokens_user_idON (user_id)idx_refresh_tokens_token_hashON (token_hash)
auth.audit_logs
Purpose: Security event audit trail| Column | Type | Constraints | Description |
|---|---|---|---|
| id | UUID | PK, DEFAULT gen_random_uuid() | Log entry ID |
| event_type | VARCHAR(100) | NOT NULL | Event type |
| user_id | UUID | FK → auth.users | User (nullable) |
| tenant_id | UUID | FK → auth.tenants | Tenant (nullable) |
| ip_address | INET | Client IP | |
| user_agent | TEXT | Client user agent | |
| details | JSONB | DEFAULT '' | Event details |
| created_at | TIMESTAMP | DEFAULT NOW() | Event time |
idx_audit_logs_user_idON (user_id)idx_audit_logs_tenant_idON (tenant_id)idx_audit_logs_event_typeON (event_type)idx_audit_logs_created_atON (created_at)
login- User loginlogout- User logoutapi_key_created- API key generationpermission_changed- Role/permission modification
Schema: public (Core Application)
users
Purpose: Application user profiles (legacy, linked to auth.users)| Column | Type | Constraints | Description |
|---|---|---|---|
| id | UUID | PK, DEFAULT uuid_generate_v4() | User identifier |
| external_id | VARCHAR(255) | UNIQUE, NOT NULL | External system ID |
| VARCHAR(255) | Email address | ||
| tenant_id | UUID | Tenant reference | |
| metadata | JSONB | DEFAULT '' | User metadata |
| created_at | TIMESTAMPTZ | DEFAULT NOW() | Creation time |
| updated_at | TIMESTAMPTZ | DEFAULT NOW() | Last update |
idx_users_tenant_idON (tenant_id)idx_users_external_idON (external_id)
auth.users.
sessions
Purpose: User session management and context| Column | Type | Constraints | Description |
|---|---|---|---|
| id | UUID | PK, DEFAULT uuid_generate_v4() | Session identifier |
| user_id | UUID | FK → users | Session owner |
| tenant_id | UUID | Tenant reference | |
| context | JSONB | DEFAULT '' | Session context data |
| token_budget | INTEGER | DEFAULT 10000 | Token allocation |
| tokens_used | INTEGER | DEFAULT 0 | Tokens consumed |
| created_at | TIMESTAMPTZ | DEFAULT NOW() | Session start |
| updated_at | TIMESTAMPTZ | DEFAULT NOW() | Last activity |
| expires_at | TIMESTAMPTZ | Expiration time | |
| deleted_at | TIMESTAMPTZ | DEFAULT NULL | Soft delete timestamp |
| deleted_by | UUID | DEFAULT NULL | User who deleted session |
idx_sessions_user_idON (user_id)idx_sessions_tenant_idON (tenant_id)idx_sessions_expires_atON (expires_at)idx_sessions_external_idON ((context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULLidx_sessions_user_external_idUNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULLidx_sessions_not_deletedON (id) WHERE deleted_at IS NULLidx_sessions_deleted_atON (deleted_at) WHERE deleted_at IS NOT NULL
- External IDs (non-UUID session IDs) are stored in
context->>'external_id'to enable dual-ID lookups - Soft delete support via
deleted_atcolumn - sessions with non-NULLdeleted_atare considered deleted
task_executions
Purpose: Task/workflow execution history and metrics| Column | Type | Constraints | Description |
|---|---|---|---|
| id | UUID | PK, DEFAULT uuid_generate_v4() | Task identifier |
| workflow_id | VARCHAR(255) | UNIQUE, NOT NULL | Temporal workflow ID |
| user_id | UUID | FK → users | Task creator |
| tenant_id | UUID | Tenant reference | |
| session_id | VARCHAR(255) | Session identifier | |
| query | TEXT | NOT NULL | Task query/prompt |
| mode | VARCHAR(50) | Execution mode (SIMPLE, STANDARD, COMPLEX) | |
| status | VARCHAR(50) | NOT NULL | Task status |
| started_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | Start time |
| completed_at | TIMESTAMPTZ | Completion time | |
| result | TEXT | Final result | |
| response | JSONB | DEFAULT '' | Structured response |
| error_message | TEXT | Error details | |
| total_tokens | INTEGER | DEFAULT 0 | Total tokens used |
| prompt_tokens | INTEGER | DEFAULT 0 | Input tokens |
| completion_tokens | INTEGER | DEFAULT 0 | Output tokens |
| total_cost_usd | DECIMAL(10,6) | DEFAULT 0 | Total cost |
| duration_ms | INTEGER | Execution time (ms) | |
| agents_used | INTEGER | DEFAULT 0 | Number of agents |
| tools_invoked | INTEGER | DEFAULT 0 | Number of tool calls |
| cache_hits | INTEGER | DEFAULT 0 | Cache hit count |
| complexity_score | DECIMAL(3,2) | Complexity (0.0-1.0) | |
| metadata | JSONB | Additional metadata | |
| created_at | TIMESTAMPTZ | DEFAULT NOW() | Record creation |
idx_task_user_sessionON (user_id, session_id)idx_task_created_atON (created_at DESC)idx_task_statusON (status)idx_task_workflow_idON (workflow_id)idx_task_executions_tenant_idON (tenant_id)idx_task_executions_session_idON (session_id)
RUNNING- Task in progressCOMPLETED- Task finished successfullyFAILED- Task failed with errorCANCELLED- Task cancelled by user
agent_executions
Purpose: Individual agent execution details within tasks| Column | Type | Constraints | Description |
|---|---|---|---|
| id | UUID | PK, DEFAULT uuid_generate_v4() | Execution ID |
| task_execution_id | UUID | FK → task_executions | Parent task |
| agent_id | VARCHAR(255) | NOT NULL | Agent identifier |
| execution_order | INTEGER | NOT NULL | Execution sequence |
| input | TEXT | NOT NULL | Agent input |
| output | TEXT | Agent output | |
| mode | VARCHAR(50) | Execution mode | |
| state | VARCHAR(50) | FSM state | |
| tokens_used | INTEGER | DEFAULT 0 | Tokens consumed |
| cost_usd | DECIMAL(10,6) | DEFAULT 0 | Execution cost |
| model_used | VARCHAR(100) | LLM model | |
| duration_ms | INTEGER | Execution time | |
| memory_used_mb | INTEGER | Memory usage | |
| created_at | TIMESTAMPTZ | DEFAULT NOW() | Start time |
| completed_at | TIMESTAMPTZ | Completion time |
idx_agent_task_executionON (task_execution_id)idx_agent_created_atON (created_at DESC)idx_agent_stateON (state)
IDLE- Agent idleANALYZING- Analyzing inputPLANNING- Planning executionRETRIEVING- Retrieving informationEXECUTING- Executing taskVALIDATING- Validating resultsSYNTHESIZING- Synthesizing responseCOMPLETED- Execution completeFAILED- Execution failed
tool_executions
Purpose: Tool invocation history and performance| Column | Type | Constraints | Description |
|---|---|---|---|
| id | UUID | PK, DEFAULT uuid_generate_v4() | Execution ID |
| agent_execution_id | UUID | FK → agent_executions | Parent agent |
| task_execution_id | UUID | FK → task_executions | Parent task |
| tool_name | VARCHAR(255) | NOT NULL | Tool identifier |
| tool_version | VARCHAR(50) | Tool version | |
| category | VARCHAR(100) | Tool category | |
| input_params | JSONB | Input parameters | |
| output | JSONB | Tool output | |
| success | BOOLEAN | DEFAULT true | Success status |
| error_message | TEXT | Error details | |
| duration_ms | INTEGER | Execution time | |
| tokens_consumed | INTEGER | DEFAULT 0 | Tokens used |
| sandboxed | BOOLEAN | DEFAULT true | WASI sandbox used |
| memory_used_mb | INTEGER | Memory usage | |
| executed_at | TIMESTAMPTZ | DEFAULT NOW() | Execution time |
idx_tool_nameON (tool_name)idx_tool_executed_atON (executed_at DESC)idx_tool_task_executionON (task_execution_id)idx_tool_successON (success)
search- Search and lookup toolscalculation- Mathematical and computational toolsfile- File system operationsapi- API calls and integrationscode- Code execution and analysis
event_logs
Purpose: Streaming event storage for audit and replay| Column | Type | Constraints | Description |
|---|---|---|---|
| id | UUID | PK, DEFAULT uuid_generate_v4() | Event ID |
| workflow_id | VARCHAR(255) | NOT NULL | Workflow identifier |
| task_id | UUID | Task reference (nullable) | |
| type | VARCHAR(100) | NOT NULL | Event type |
| agent_id | VARCHAR(255) | Agent identifier | |
| message | TEXT | Event message | |
| payload | JSONB | DEFAULT '' | Event payload |
| timestamp | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | Event time |
| seq | BIGINT | Sequence number | |
| stream_id | VARCHAR(64) | Redis stream ID | |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | Record creation |
idx_event_logs_workflow_idON (workflow_id)idx_event_logs_task_idON (task_id)idx_event_logs_typeON (type)idx_event_logs_tsON (timestamp DESC)idx_event_logs_seqON (workflow_id, seq)idx_event_logs_workflow_tsON (workflow_id, timestamp DESC)uq_event_logs_wf_type_seqUNIQUE ON (workflow_id, type, seq) WHERE seq IS NOT NULL
task.started- Task execution startedtask.completed- Task execution completedagent.thinking- Agent processingtool.called- Tool invocation
token_usage
Purpose: Detailed token usage tracking per task| Column | Type | Constraints | Description |
|---|---|---|---|
| id | UUID | PK, DEFAULT uuid_generate_v4() | Record ID |
| user_id | UUID | FK → users | User |
| task_id | UUID | FK → task_executions | Task |
| provider | VARCHAR(50) | NOT NULL | LLM provider |
| model | VARCHAR(255) | NOT NULL | Model name |
| prompt_tokens | INTEGER | NOT NULL | Input tokens |
| completion_tokens | INTEGER | NOT NULL | Output tokens |
| total_tokens | INTEGER | NOT NULL | Total tokens |
| cost_usd | DECIMAL(10,6) | NOT NULL | Cost in USD |
| created_at | TIMESTAMPTZ | DEFAULT NOW() | Record time |
idx_token_usage_user_idON (user_id)idx_token_usage_created_atON (created_at DESC)idx_token_usage_provider_modelON (provider, model)idx_token_usage_task_idON (task_id)
usage_daily_aggregates
Purpose: Pre-computed daily usage statistics| Column | Type | Constraints | Description |
|---|---|---|---|
| id | UUID | PK, DEFAULT uuid_generate_v4() | Record ID |
| user_id | UUID | FK → users | User |
| date | DATE | NOT NULL | Aggregation date |
| total_tasks | INTEGER | DEFAULT 0 | Total task count |
| successful_tasks | INTEGER | DEFAULT 0 | Successful count |
| failed_tasks | INTEGER | DEFAULT 0 | Failed count |
| total_tokens | INTEGER | DEFAULT 0 | Total tokens |
| total_cost_usd | DECIMAL(10,6) | DEFAULT 0 | Total cost |
| model_usage | JSONB | Model distribution | |
| tools_invoked | INTEGER | DEFAULT 0 | Tool invocation count |
| tool_distribution | JSONB | Tool usage distribution | |
| avg_duration_ms | INTEGER | Average task duration | |
| cache_hit_rate | DECIMAL(3,2) | Cache hit percentage | |
| created_at | TIMESTAMPTZ | DEFAULT NOW() | Record time |
idx_usage_daily_user_dateON (user_id, date)idx_usage_daily_dateON (date DESC)
Additional Tables
tool_calls
Purpose: Legacy tool call tracking (use tool_executions instead)prompts
Purpose: Prompt versioning and A/B testinglearning_cases
Purpose: Reinforcement learning case storagesession_archives
Purpose: Long-term session snapshots from Redisaudit_logs (public)
Purpose: Application audit trail (separate from auth.audit_logs)Database Functions
update_updated_at_column()
Purpose: Automatically updateupdated_at on row changes
Usage: Attached as trigger to users and sessions tables
update_daily_aggregate(user_id, date)
Purpose: Update or create daily usage aggregate for a user Usage: Called automatically via trigger on task completiontrigger_update_daily_aggregate()
Purpose: Trigger function to update aggregates on task status changes Usage: Automatically fires on task_executions INSERT/UPDATESample Queries
Task Analytics
Cost Analysis
Performance Monitoring
Best Practices
1. Indexing
Always use indexes for:- Foreign key columns (user_id, tenant_id, task_execution_id, etc.)
- Frequently filtered columns (status, created_at, expires_at)
- JSONB queries (context->>‘external_id’)
- Time-based queries (created_at DESC)
2. Partitioning
For high-volume tables, consider partitioning:3. Data Retention
Recommended policies:event_logs: 90 days (high volume, archivable)task_executions: 1 year (archive older)audit_logs: 2 years (compliance)token_usage: Aggregate to daily, archive raw data after 90 days