OpenClaw Enterprise -- Data Model Reference¶
This document defines every data entity in the OpenClaw Enterprise system, including field types, validation constraints, retention policies, and relationships.
All entities are stored in PostgreSQL. The audit subsystem uses a separate PostgreSQL database with append-only constraints (see ADR-006).
Table of Contents¶
- Policy
- Task
- Connector
- AgentIdentity
- Exchange
- AuditEntry
- Briefing
- DataClassification
- Entity Relationship Diagram
- Database Indexes
Policy¶
Policies define rules that govern system behavior across 7 domains. Policies are scoped hierarchically: enterprise > org > team > user. A lower scope can restrict but never expand permissions granted by a parent scope.
| Field | Type | Constraints | Description |
|---|---|---|---|
id |
UUID | Primary key, auto-generated | Unique identifier |
scope |
VARCHAR(20) | enterprise, org, team, user |
Hierarchy level at which this policy applies |
scopeId |
VARCHAR(255) | NOT NULL | Identifier of the scope target (e.g., org ID, team ID, user ID) |
domain |
VARCHAR(20) | models, actions, integrations, agent-to-agent, features, data, audit |
Policy domain this rule governs |
name |
VARCHAR(255) | NOT NULL | Human-readable policy name |
version |
VARCHAR(50) | NOT NULL, must follow semver | Semantic version of this policy revision |
content |
TEXT | NOT NULL, YAML format | Policy definition in YAML |
status |
VARCHAR(20) | draft, active, deprecated (default: draft) |
Lifecycle status |
createdBy |
VARCHAR(255) | NOT NULL | User ID of the policy author |
createdAt |
TIMESTAMPTZ | NOT NULL, auto-set | Creation timestamp |
updatedAt |
TIMESTAMPTZ | NOT NULL, auto-set | Last modification timestamp |
changeReason |
TEXT | NOT NULL | Explanation for the creation or most recent change |
Validation Rules:
versionmust follow semantic versioning (e.g.,1.0.0,2.1.3).- A lower-scope policy cannot expand permissions beyond what the parent scope allows. For example, if the enterprise policy sets
max_classification: confidential, an org-level policy cannot setmax_classification: restricted. - Hierarchy validation is enforced at write time by
PolicyHierarchyValidator.
Source: db/migrations/001_policies.sql, plugins/shared/src/types.ts
Task¶
Tasks represent work items discovered from connected systems (email, calendar, Jira, GitHub, Google Drive). Tasks are correlated across sources using multi-signal scoring.
| Field | Type | Constraints | Description |
|---|---|---|---|
id |
UUID | Primary key | Unique identifier |
userId |
VARCHAR(255) | NOT NULL | Owner of this task |
title |
VARCHAR(255) | NOT NULL | Task title |
description |
TEXT | Detailed description | |
priorityScore |
INTEGER | 0--100 | Computed priority based on urgency signals |
status |
VARCHAR(20) | discovered, active, completed, archived, purged |
Lifecycle status |
sources |
JSONB | Array of {system, id, url} |
Source systems where this task was found |
correlationId |
UUID | Nullable, FK to Task | ID of the correlated/parent task if merged |
correlationConfidence |
DECIMAL | 0.0--1.0, nullable | Confidence score of the correlation match |
deadline |
TIMESTAMPTZ | Nullable | Task deadline if known |
urgencySignals |
JSONB | Object | Signals used for priority scoring |
classification |
VARCHAR(20) | public, internal, confidential, restricted |
Data classification level |
discoveredAt |
TIMESTAMPTZ | NOT NULL | When the task was first discovered |
completedAt |
TIMESTAMPTZ | Nullable | When the task was marked complete |
archivedAt |
TIMESTAMPTZ | Nullable | When the task was archived |
purgeAt |
TIMESTAMPTZ | Nullable | Scheduled purge timestamp |
Urgency Signals Object:
| Field | Type | Description |
|---|---|---|
senderSeniority |
number or null | Seniority level of the task requester |
followUpCount |
number | Number of follow-up messages about this task |
slaTimer |
string or null | ISO 8601 duration or timestamp for SLA |
blockingRelationships |
string[] | IDs of tasks/items this task is blocking |
Retention Policy:
| Phase | Duration | Trigger |
|---|---|---|
| Active | 90 days | From discoveredAt |
| Archived | 30 days after completion | Task moves to archived status |
| Purged | 90 days after archival | Task data is permanently deleted |
Correlation Thresholds:
| Score Range | Action |
|---|---|
| >= 0.8 | Auto-merge as duplicate |
| 0.5 -- 0.8 | Flag as "possibly related" for human review |
| < 0.5 | Treat as separate tasks |
Source: db/migrations/002_tasks.sql, plugins/shared/src/types.ts, plugins/shared/src/constants.ts
Connector¶
Connectors represent authenticated connections to external systems.
| Field | Type | Constraints | Description |
|---|---|---|---|
id |
UUID | Primary key | Unique identifier |
type |
VARCHAR(20) | gmail, gcal, jira, github, gdrive |
Connector type |
tenantId |
VARCHAR(255) | NOT NULL | Tenant that owns this connector |
userId |
VARCHAR(255) | Nullable | User-level connector (null = tenant-wide) |
permissions |
VARCHAR(10) | read, write, admin (default: read) |
Access level granted to the connector |
defaultClassification |
VARCHAR(20) | public, internal, confidential, restricted |
Default classification for data from this connector |
status |
VARCHAR(20) | active, disabled, error |
Current connector status |
credentialsRef |
VARCHAR(255) | NOT NULL | Reference to credentials in secret store |
lastSyncAt |
TIMESTAMPTZ | Nullable | Last successful synchronization timestamp |
errorDetails |
TEXT | Nullable | Error message if status is error |
config |
JSONB | Connector-specific configuration |
Default Classification by Connector Type:
| Connector | Default Classification |
|---|---|
gmail |
internal |
gcal |
internal |
jira |
internal |
github |
public |
gdrive |
internal |
Source: db/migrations/003_connectors.sql, plugins/shared/src/types.ts, plugins/shared/src/constants.ts
AgentIdentity¶
AgentIdentity describes the capabilities and constraints of an OpenClaw agent instance. Agent identities are derived from policies at runtime, not stored directly.
| Field | Type | Constraints | Description |
|---|---|---|---|
instanceId |
string | Required | Unique agent instance identifier |
userId |
string | Required | User this agent acts on behalf of |
tenantId |
string | Required | Tenant the agent belongs to |
orgUnit |
string | Required | Organizational unit within the tenant |
canReceiveQueries |
boolean | Required | Whether this agent accepts incoming queries |
canAutoRespond |
boolean | Required | Whether this agent can auto-respond without human approval |
canMakeCommitments |
boolean | Always false unless human approves |
Whether the agent can make commitments on behalf of the user |
maxClassificationShared |
DataClassificationLevel | Required | Maximum classification level this agent may share externally |
supportedExchangeTypes |
ExchangeType[] | Required | Exchange types this agent can participate in |
maxRoundsAccepted |
number | Required | Maximum conversation rounds before escalation |
Important: canMakeCommitments is always false for agent-generated messages. A human must explicitly approve any commitment before it can be made. This is enforced by the OCIP commitment detector.
Note: AgentIdentity also includes a humanAvailability field at the application layer that indicates whether the user is currently online, away, or offline, used to determine auto-response behavior.
Source: plugins/shared/src/types.ts
Exchange¶
Exchanges track structured agent-to-agent conversations via the OCIP protocol.
| Field | Type | Constraints | Description |
|---|---|---|---|
exchangeId |
UUID | Primary key | Unique exchange identifier |
conversationId |
UUID | NOT NULL | Groups related exchanges into a conversation |
initiatorAgentId |
VARCHAR(255) | NOT NULL | Agent instance that initiated the exchange |
initiatorUserId |
VARCHAR(255) | NOT NULL | User behind the initiating agent |
responderAgentId |
VARCHAR(255) | NOT NULL | Agent instance responding to the exchange |
responderUserId |
VARCHAR(255) | NOT NULL | User behind the responding agent |
exchangeType |
VARCHAR(30) | information_query, commitment_request, meeting_scheduling |
Type of exchange |
currentRound |
INTEGER | >= 0 | Current message round in the exchange |
maxRounds |
INTEGER | Default: 3 | Maximum rounds before mandatory escalation |
classificationLevel |
VARCHAR(20) | public, internal, confidential, restricted |
Highest classification of data in this exchange |
outcome |
VARCHAR(20) | in_progress, resolved, escalated, denied, expired |
Current exchange outcome |
escalationReason |
TEXT | Nullable | Reason for escalation to human |
dataShared |
JSONB | Array of {source, fields[]} |
Data items shared during the exchange |
dataWithheld |
JSONB | Array of {reason, description} |
Data items withheld and why |
policyApplied |
VARCHAR(255) | NOT NULL | Policy that governed this exchange |
transcript |
JSONB | Array of objects | Full message transcript |
channel |
VARCHAR(50) | NOT NULL | Communication channel used |
startedAt |
TIMESTAMPTZ | NOT NULL | Exchange start time |
endedAt |
TIMESTAMPTZ | Nullable | Exchange end time (null if in progress) |
Source: db/migrations/005_exchanges.sql, plugins/shared/src/types.ts
AuditEntry¶
Audit entries form a tamper-evident, append-only log of all significant system actions. Stored in a separate PostgreSQL database, partitioned by month.
| Field | Type | Constraints | Description |
|---|---|---|---|
id |
VARCHAR(26) | ULID, part of composite PK | Unique identifier (ULID for time-ordered IDs) |
tenantId |
VARCHAR(255) | NOT NULL | Tenant context |
userId |
VARCHAR(255) | NOT NULL | User who performed the action |
timestamp |
TIMESTAMPTZ | NOT NULL, part of composite PK | When the action occurred |
actionType |
VARCHAR(30) | One of 6 types (see below) | Category of action |
actionDetail |
JSONB | Default: {} |
Structured details about the action |
dataAccessed |
JSONB | Default: [] |
Array of {source, classification, purpose} records |
modelUsed |
VARCHAR(255) | Nullable | AI model identifier if a model was invoked |
modelTokens |
JSONB | Nullable, {input, output} |
Token counts for model calls |
dataClassification |
VARCHAR(20) | public, internal, confidential, restricted |
Highest classification of data involved |
policyApplied |
VARCHAR(255) | NOT NULL | Policy that was evaluated |
policyResult |
VARCHAR(20) | allow, deny, require_approval |
Policy decision |
policyReason |
TEXT | Default: '' |
Human-readable explanation of the policy decision |
outcome |
VARCHAR(20) | success, denied, error, pending_approval |
Final outcome of the action |
requestId |
VARCHAR(255) | NOT NULL | Correlation ID for request tracing |
Action Types:
| Action Type | Description |
|---|---|
tool_invocation |
A tool was called by the agent |
data_access |
Data was read from a connected system |
model_call |
An AI model was invoked |
policy_decision |
A policy evaluation was performed |
agent_exchange |
An agent-to-agent OCIP exchange occurred |
policy_change |
A policy was created, updated, or deprecated |
Storage Properties:
- Append-only: UPDATE and DELETE are blocked by database triggers.
- Monthly partitioned: Table is partitioned by
timestampusingPARTITION BY RANGE. Partitions are created for 12 months ahead. - Retention: 1-year minimum retention. Old partitions can be archived or dropped.
- GDPR compliance: Achieved via anonymization of user identifiers, not deletion of audit records.
Source: db/migrations/004_audit_entries.sql, plugins/shared/src/types.ts
Briefing¶
Daily briefings aggregate tasks, calendar blocks, auto-response summaries, organization news, and system alerts for a user.
| Field | Type | Constraints | Description |
|---|---|---|---|
id |
UUID | Primary key | Unique identifier |
userId |
VARCHAR(255) | NOT NULL | User this briefing is for |
tenantId |
VARCHAR(255) | NOT NULL | Tenant context |
generatedAt |
TIMESTAMPTZ | NOT NULL | When the briefing was generated |
tasks |
JSONB | Array of {taskId, rank} |
Priority-ranked task list |
timeBlocks |
JSONB | Array of {start, end, taskId, label} |
Suggested time blocks for the day |
autoResponseSummary |
JSONB | Object | Summary of auto-responses sent on behalf of the user |
orgNewsItems |
JSONB | Array of {title, relevance, source} |
Organization news items with relevance scoring |
docChangeAlerts |
JSONB | Array of {docId, summary, impact} |
Alerts about changed documents relevant to the user |
alerts |
JSONB | Array of {type, message, severity} |
System and workflow alerts |
connectorStatus |
JSONB | Map of ConnectorType to status | Health status of each connector |
deliveryChannel |
VARCHAR(20) | slack, email, web_ui |
How the briefing was delivered |
deliveredAt |
TIMESTAMPTZ | Nullable | When the briefing was delivered (null if pending) |
News Relevance Levels:
| Level | Description |
|---|---|
must-read |
Critical news the user must see |
should-read |
Important news relevant to the user's role |
nice-to-know |
Informational, not urgent |
skip |
Filtered out, not included in briefing |
Connector Status Values: ok, partial, error, unreachable
Source: db/migrations/006_briefings.sql, plugins/shared/src/types.ts
DataClassification¶
Tracks the classification level assigned to every piece of data, including the assignment method and any overrides.
| Field | Type | Constraints | Description |
|---|---|---|---|
dataRef |
VARCHAR(255) | Primary key | Reference to the data item |
level |
VARCHAR(20) | public, internal, confidential, restricted |
Current classification level |
assignedBy |
VARCHAR(30) | connector_default, ai_reclassification, admin_override |
How the classification was assigned |
originalLevel |
VARCHAR(20) | Nullable | Original classification before reclassification or override |
overrideBy |
VARCHAR(255) | Nullable | User ID of the admin who performed the override |
overrideReason |
TEXT | Nullable | Reason for the admin override |
assessedAt |
TIMESTAMPTZ | NOT NULL | When the classification was last assessed |
Classification Levels (lowest to highest sensitivity):
| Level | Order | Description |
|---|---|---|
public |
0 | Publicly available information |
internal |
1 | Internal company information |
confidential |
2 | Sensitive business information |
restricted |
3 | Highly sensitive, strictly controlled access |
Three-Layer Classification Process:
- Connector default: Baseline classification set by the connector type (see Connector defaults).
- AI reclassification: AI model reviews content and may upgrade (never downgrade) the classification if sensitive content is detected.
- Admin override: Human administrators can set any classification level, overriding both connector defaults and AI reclassification.
Source: db/migrations/007_data_classifications.sql, plugins/shared/src/types.ts
Entity Relationship Diagram¶
erDiagram
Policy {
UUID id PK
VARCHAR scope
VARCHAR scopeId
VARCHAR domain
VARCHAR name
VARCHAR version
TEXT content
VARCHAR status
VARCHAR createdBy
TIMESTAMPTZ createdAt
TIMESTAMPTZ updatedAt
TEXT changeReason
}
Task {
UUID id PK
VARCHAR userId
VARCHAR title
TEXT description
INTEGER priorityScore
VARCHAR status
JSONB sources
UUID correlationId FK
DECIMAL correlationConfidence
TIMESTAMPTZ deadline
JSONB urgencySignals
VARCHAR classification
TIMESTAMPTZ discoveredAt
TIMESTAMPTZ completedAt
TIMESTAMPTZ archivedAt
TIMESTAMPTZ purgeAt
}
Connector {
UUID id PK
VARCHAR type
VARCHAR tenantId
VARCHAR userId
VARCHAR permissions
VARCHAR defaultClassification
VARCHAR status
VARCHAR credentialsRef
TIMESTAMPTZ lastSyncAt
TEXT errorDetails
JSONB config
}
Exchange {
UUID exchangeId PK
UUID conversationId
VARCHAR initiatorAgentId
VARCHAR initiatorUserId
VARCHAR responderAgentId
VARCHAR responderUserId
VARCHAR exchangeType
INTEGER currentRound
INTEGER maxRounds
VARCHAR classificationLevel
VARCHAR outcome
TEXT escalationReason
JSONB dataShared
JSONB dataWithheld
VARCHAR policyApplied
JSONB transcript
VARCHAR channel
TIMESTAMPTZ startedAt
TIMESTAMPTZ endedAt
}
AuditEntry {
VARCHAR id PK
VARCHAR tenantId
VARCHAR userId
TIMESTAMPTZ timestamp PK
VARCHAR actionType
JSONB actionDetail
JSONB dataAccessed
VARCHAR modelUsed
JSONB modelTokens
VARCHAR dataClassification
VARCHAR policyApplied
VARCHAR policyResult
TEXT policyReason
VARCHAR outcome
VARCHAR requestId
}
Briefing {
UUID id PK
VARCHAR userId
VARCHAR tenantId
TIMESTAMPTZ generatedAt
JSONB tasks
JSONB timeBlocks
JSONB autoResponseSummary
JSONB orgNewsItems
JSONB docChangeAlerts
JSONB alerts
JSONB connectorStatus
VARCHAR deliveryChannel
TIMESTAMPTZ deliveredAt
}
DataClassification {
VARCHAR dataRef PK
VARCHAR level
VARCHAR assignedBy
VARCHAR originalLevel
VARCHAR overrideBy
TEXT overrideReason
TIMESTAMPTZ assessedAt
}
Task ||--o| Task : "correlationId"
Task }o--|| Connector : "discovered via"
Task }o--|| DataClassification : "classification"
Exchange }o--|| Policy : "policyApplied"
AuditEntry }o--|| Policy : "policyApplied"
Briefing ||--|{ Task : "tasks (ranked)"
Connector }o--|| DataClassification : "defaultClassification"
Exchange }o--|| DataClassification : "classificationLevel"
Database Indexes¶
The following indexes are defined across the schema migrations to support common query patterns.
| Table | Index Name | Columns | Purpose |
|---|---|---|---|
policies |
idx_policies_scope_domain |
scope, scope_id, domain, status |
Policy lookup by scope hierarchy and domain |
policies |
idx_policies_status |
status |
Filter policies by lifecycle status |
audit.audit_entries |
idx_audit_tenant_user_ts |
tenant_id, user_id, timestamp DESC |
Audit queries filtered by tenant + user with time ordering |
audit.audit_entries |
idx_audit_tenant_action_ts |
tenant_id, action_type, timestamp DESC |
Audit queries filtered by tenant + action type with time ordering |
audit.audit_entries |
idx_audit_request_id |
request_id |
Request tracing correlation |
Partitioning:
audit.audit_entriesis partitioned byRANGE (timestamp)with monthly partitions. Partitions are namedaudit_entries_YYYY_MM(e.g.,audit_entries_2026_03).- Partition pruning is automatic for queries that include a
timestampfilter.
Source: db/migrations/001_policies.sql, db/migrations/004_audit_entries.sql