Skip to main content

Overview

Shannon uses PostgreSQL for persistent storage. The database is organized into two schemas:
  • auth - Authentication, multi-tenancy, and security
  • public - Core application tables (tasks, agents, sessions)
Total Tables: 25 tables Extensions: uuid-ossp, pg_trgm, btree_gin, pgcrypto

Entity Relationship Diagram

Schema: auth (Authentication & Multi-Tenancy)

auth.tenants

Purpose: Multi-tenant organization management Indexes:
  • Primary key on slug (UNIQUE)
Example:

auth.users

Purpose: User authentication and profile management Indexes:
  • idx_users_email ON (email)
  • idx_users_tenant_id ON (tenant_id)
Example:

auth.api_keys

Purpose: API key management for programmatic access Indexes:
  • idx_api_keys_tenant_id ON (tenant_id)
  • idx_api_keys_key_prefix ON (key_prefix)
Default Scopes:
  • workflows:read: Allow reading workflow/task status and results
  • workflows:write: Allow submitting and managing workflows/tasks
  • agents:execute: Allow agent execution
Example:

auth.refresh_tokens

Purpose: JWT refresh token storage and revocation Indexes:
  • idx_refresh_tokens_user_id ON (user_id)
  • idx_refresh_tokens_token_hash ON (token_hash)
Example:

auth.audit_logs

Purpose: Security event audit trail Indexes:
  • idx_audit_logs_user_id ON (user_id)
  • idx_audit_logs_tenant_id ON (tenant_id)
  • idx_audit_logs_event_type ON (event_type)
  • idx_audit_logs_created_at ON (created_at)
Event Types:
  • login - User login
  • logout - User logout
  • api_key_created - API key generation
  • permission_changed - Role/permission modification
Example:

Schema: public (Core Application)

users

Purpose: Application user profiles (legacy, linked to auth.users) Indexes:
  • idx_users_tenant_id ON (tenant_id)
  • idx_users_external_id ON (external_id)
Note: This table exists for backward compatibility. New code should use auth.users.

sessions

Purpose: User session management and context Indexes:
  • idx_sessions_user_id ON (user_id)
  • idx_sessions_tenant_id ON (tenant_id)
  • idx_sessions_expires_at ON (expires_at)
  • idx_sessions_external_id ON ((context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL
  • idx_sessions_user_external_id UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • idx_sessions_not_deleted ON (id) WHERE deleted_at IS NULL
  • idx_sessions_deleted_at ON (deleted_at) WHERE deleted_at IS NOT NULL
Notes:
  • External IDs (non-UUID session IDs) are stored in context->>'external_id' to enable dual-ID lookups
  • Soft delete support via deleted_at column - sessions with non-NULL deleted_at are considered deleted
Example:

task_executions

Purpose: Task/workflow execution history and metrics Indexes:
  • idx_task_user_session ON (user_id, session_id)
  • idx_task_created_at ON (created_at DESC)
  • idx_task_status ON (status)
  • idx_task_workflow_id ON (workflow_id)
  • idx_task_executions_tenant_id ON (tenant_id)
  • idx_task_executions_session_id ON (session_id)
Status Values:
  • RUNNING - Task in progress
  • COMPLETED - Task finished successfully
  • FAILED - Task failed with error
  • CANCELLED - Task cancelled by user
Example:

agent_executions

Purpose: Individual agent execution details within tasks Indexes:
  • idx_agent_executions_workflow_id ON (workflow_id)
  • idx_agent_executions_created_at ON (created_at DESC)
  • idx_agent_executions_state ON (state)
State Values:
  • IDLE - Agent idle
  • ANALYZING - Analyzing input
  • PLANNING - Planning execution
  • RETRIEVING - Retrieving information
  • EXECUTING - Executing task
  • VALIDATING - Validating results
  • SYNTHESIZING - Synthesizing response
  • COMPLETED - Execution complete
  • FAILED - Execution failed
Example:

tool_executions

Purpose: Tool invocation history and performance Indexes:
  • idx_tool_executions_workflow_id ON (workflow_id)
  • idx_tool_executions_tool_name ON (tool_name)
  • idx_tool_executions_created_at ON (created_at DESC)
  • idx_tool_executions_success ON (success)
Example:

event_logs

Purpose: Streaming event storage for audit and replay Indexes:
  • idx_event_logs_workflow_id ON (workflow_id)
  • idx_event_logs_task_id ON (task_id)
  • idx_event_logs_type ON (type)
  • idx_event_logs_ts ON (timestamp DESC)
  • idx_event_logs_seq ON (workflow_id, seq)
  • idx_event_logs_workflow_ts ON (workflow_id, timestamp DESC)
  • uq_event_logs_wf_type_seq UNIQUE ON (workflow_id, type, seq) WHERE seq IS NOT NULL
Event Types:
  • task.started - Task execution started
  • task.completed - Task execution completed
  • agent.thinking - Agent processing
  • tool.called - Tool invocation
Example:

token_usage

Purpose: Detailed token usage tracking per task Indexes:
  • idx_token_usage_user_id ON (user_id)
  • idx_token_usage_created_at ON (created_at DESC)
  • idx_token_usage_provider_model ON (provider, model)
  • idx_token_usage_task_id ON (task_id)
Example:

usage_daily_aggregates

Purpose: Pre-computed daily usage statistics Indexes:
  • idx_usage_daily_user_date ON (user_id, date)
  • idx_usage_daily_date ON (date DESC)
Unique Constraint: (user_id, date) Example:

Additional Tables

tool_calls

Purpose: Legacy tool call tracking (use tool_executions instead)

prompts

Purpose: Prompt versioning and A/B testing

learning_cases

Purpose: Reinforcement learning case storage

session_archives

Purpose: Long-term session snapshots from Redis

audit_logs (public)

Purpose: Application audit trail (separate from auth.audit_logs)

Database Functions

update_updated_at_column()

Purpose: Automatically update updated_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 completion

trigger_update_daily_aggregate()

Purpose: Trigger function to update aggregates on task status changes Usage: Automatically fires on task_executions INSERT/UPDATE

Sample 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)
Current index coverage: Excellent (all foreign keys and common queries indexed)

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
Cleanup script:

4. Query Optimization

Use EXPLAIN ANALYZE:
Optimize with covering indexes:

5. Connection Pooling

Configuration (already configured in Shannon):

Maintenance

Vacuum and Analyze

Statistics

Environment Variables

Database configuration

Performance Tuning

Query optimization

Monitoring

Database monitoring