Database Schema Reference

Engine: PostgreSQL 16+
Schema: public
Migration: migrations/0001_initial.sql


Enum Types

undolog_tool_tier

Value Description
safe Read-only or idempotent. Execute freely, no logging needed.
compensable Has a defined compensation/undo. Log + register undo stack entry.
irreversible Cannot be undone. Require human approval gate before execution.

undolog_effect_state

Value Description
pending Intercepted, awaiting execution (or awaiting human approval).
executing Currently running.
committed Completed successfully, result cached.
compensating Compensation is running (undo in progress).
compensated Compensation completed successfully.
compensation_failed Compensation itself failed; requires manual intervention.
approved Human approved an irreversible action; ready for proxy execution.
rejected Human rejected an irreversible action; session halted.
replayed Result was served from cache (exactly-once replay path).

undolog_session_state

Value Description
active Session is running.
completed Session completed normally.
failed Session failed (non-compensated).
compensating Rolling back the undo stack.
compensated All compensations completed.
awaiting_approval Waiting for human approval of an irreversible action.
halted Fatal: compensation failed, requires manual intervention.

undolog_approval_state

Value Description
pending Awaiting human decision.
approved Human approved.
rejected Human rejected.
timed_out Approval window expired.
auto_approved Approved by policy (e.g. low-risk auto-approve timeout).

undolog_approval_action

Value Description
approve Approve as-is.
reject Reject; execution halted.
modify Approver modified the arguments before approving.
timeout Approval timed out without a decision.

undolog_orgs

Tenant root table.

Column Type Constraints Description
org_id uuid PK DEFAULT uuidv7_or_random() Unique organisation identifier.
name text NOT NULL Organisation name.
slug text NOT NULL UNIQUE URL-safe identifier used in API paths. Format: ^[a-z0-9-]{2,64}$.
created_at timestamptz NOT NULL DEFAULT now() Creation timestamp.
updated_at timestamptz NOT NULL DEFAULT now() Last update timestamp.
default_approval_timeout_seconds int NOT NULL DEFAULT 3600 Default approval timeout (1 hour).
auto_approve_on_timeout bool NOT NULL DEFAULT false Auto-approve when approval times out.

Indexes:

Name Columns Type
idx_undolog_orgs_slug slug B-tree

RLS policy: org_id = current_setting('undolog.current_org_id')::uuid


undolog_projects

Grouping of agent deployments within an org.

Column Type Constraints Description
project_id uuid PK DEFAULT uuidv7_or_random() Unique project identifier.
org_id uuid NOT NULL FK → undolog_orgs(org_id) ON DELETE CASCADE Owning organisation.
name text NOT NULL Project name.
description text , Human-readable description.
created_at timestamptz NOT NULL DEFAULT now() Creation timestamp.
updated_at timestamptz NOT NULL DEFAULT now() Last update timestamp.

Constraints:

Name Definition
undolog_projects_org_name_unique UNIQUE (org_id, name)

Indexes:

Name Columns
idx_undolog_projects_org org_id

RLS policy: org_id = current_setting('undolog.current_org_id')::uuid


undolog_tool_registry

Tool definitions with tier annotations.

Column Type Constraints Description
tool_id uuid PK DEFAULT uuidv7_or_random() Unique tool identifier.
org_id uuid NOT NULL FK → undolog_orgs(org_id) ON DELETE CASCADE Owning organisation.
project_id uuid FK → undolog_projects(project_id) ON DELETE SET NULL Optional project grouping.
tool_name text NOT NULL Logical tool name (e.g. "transfer_funds").
tool_version text NOT NULL DEFAULT '1.0.0' Semantic version.
tier undolog_tool_tier NOT NULL Tool tier classification.
irreversibility_reason text , Required for irreversible tier. Human-readable explanation.
compensation_ref text , Required for compensable tier. References undolog_compensation_registry.fn_name.
tool_schema jsonb NOT NULL Full MCP tool JSON schema.
risk_tags text[] NOT NULL DEFAULT '{}' Risk categorisation tags (e.g. ['financial']).
estimated_impact text , Human-readable impact description.
registered_at timestamptz NOT NULL DEFAULT now() Registration timestamp.
updated_at timestamptz NOT NULL DEFAULT now() Last update timestamp.
is_active bool NOT NULL DEFAULT true Whether the tool is currently active.

Constraints:

Name Definition
undolog_tool_registry_unique UNIQUE (org_id, tool_name, tool_version)
undolog_tool_compensable_has_ref CHECK (tier != 'compensable' OR compensation_ref IS NOT NULL)
undolog_tool_irreversible_has_reason CHECK (tier != 'irreversible' OR irreversibility_reason IS NOT NULL)

Indexes:

Name Columns
idx_undolog_tool_registry_org org_id
idx_undolog_tool_registry_project project_id
idx_undolog_tool_registry_name tool_name
idx_undolog_tool_registry_tier tier

RLS policy: org_id = current_setting('undolog.current_org_id')::uuid


undolog_compensation_registry

Compensation function definitions.

Column Type Constraints Description
compensation_id uuid PK DEFAULT uuidv7_or_random() Unique compensation identifier.
org_id uuid NOT NULL FK → undolog_orgs(org_id) ON DELETE CASCADE Owning organisation.
fn_name text NOT NULL Function name matching tool_registry.compensation_ref.
fn_version text NOT NULL DEFAULT '1.0.0' Semantic version.
description text NOT NULL Human-readable description (e.g. "Cancels a payment").
fn_body jsonb NOT NULL Serialized compensation descriptor (endpoint, method, arg mapping, idempotency key).
args_schema jsonb NOT NULL JSON Schema for compensation arguments.
max_retries int NOT NULL DEFAULT 3 Max retries before escalating to compensation_failed.
retry_backoff_ms int NOT NULL DEFAULT 1000 Base retry backoff in milliseconds.
registered_at timestamptz NOT NULL DEFAULT now() Registration timestamp.
is_active bool NOT NULL DEFAULT true Whether compensation is active.

Constraints:

Name Definition
undolog_compensation_registry_unique UNIQUE (org_id, fn_name, fn_version)

Indexes:

Name Columns
idx_undolog_compensation_org org_id
idx_undolog_compensation_fn_name fn_name

RLS policy: org_id = current_setting('undolog.current_org_id')::uuid


undolog_sessions

One agent execution session.

Column Type Constraints Description
session_id uuid PK DEFAULT uuidv7_or_random() Unique session identifier.
org_id uuid NOT NULL FK → undolog_orgs(org_id) ON DELETE CASCADE Owning organisation.
project_id uuid FK → undolog_projects(project_id) ON DELETE SET NULL Optional project grouping.
external_run_id text , External correlation ID (LangGraph run ID, Temporal workflow ID).
agent_name text , Human-readable agent name.
state undolog_session_state NOT NULL DEFAULT 'active' Current lifecycle state.
tool_calls_total int NOT NULL DEFAULT 0 Counter maintained by trigger.
compensations_total int NOT NULL DEFAULT 0 Counter maintained by trigger.
approvals_pending int NOT NULL DEFAULT 0 Counter maintained by trigger.
started_at timestamptz NOT NULL DEFAULT now() Session start timestamp.
completed_at timestamptz , Session completion timestamp.
failed_at timestamptz , Session failure timestamp.
failure_reason text , Human-readable failure reason.
metadata jsonb NOT NULL DEFAULT '{}' Arbitrary agent metadata.

Indexes:

Name Columns Condition
idx_undolog_sessions_org org_id
idx_undolog_sessions_project project_id
idx_undolog_sessions_state state WHERE state != 'completed'
idx_undolog_sessions_external external_run_id WHERE external_run_id IS NOT NULL
idx_undolog_sessions_started started_at DESC

RLS policy: org_id = current_setting('undolog.current_org_id')::uuid


undolog_effect_log (PARTITIONED)

Append-only log of every tool call. This is the heart of UndoLog.

Partition key: RANGE (executed_at) monthly.

Column Type Constraints Description
effect_id uuid NOT NULL DEFAULT uuidv7_or_random() Unique effect identifier.
org_id uuid NOT NULL Tenant organisation.
session_id uuid NOT NULL Session identifier.
tool_id uuid , Registered tool identifier (NULL if unknown).
call_signature char(64) NOT NULL BLAKE3 deduplication key. UNIQUE across all partitions.
tool_name text NOT NULL Tool name at call time (denormalised).
tool_version text NOT NULL DEFAULT '1.0.0' Tool version at call time.
tier undolog_tool_tier NOT NULL Tool tier at call time.
step_index int NOT NULL Monotonically increasing per session.
args_snapshot jsonb NOT NULL Input captured before execution (canonical JSON).
result_snapshot jsonb , Output captured after execution (NULL until committed).
state undolog_effect_state NOT NULL DEFAULT 'pending' Current lifecycle state.
compensation_args jsonb , Compensation args captured before execution (NULL for safe/irreversible).
executed_at timestamptz NOT NULL DEFAULT now() Partition key. Execution timestamp.
committed_at timestamptz , Commit timestamp.
compensated_at timestamptz , Compensation completion timestamp.
replay_count smallint NOT NULL DEFAULT 0 Number of times this effect was replayed.
last_replayed_at timestamptz , Most recent replay timestamp.
approval_request_id uuid , FK to undolog_approval_requests for irreversible actions.

Primary key: (effect_id, executed_at)

Indexes

Name Columns Condition Type
idx_undolog_effect_log_session session_id, step_index B-tree
idx_undolog_effect_log_state state, org_id WHERE state IN ('pending', 'executing', 'compensating', 'compensation_failed') B-tree
idx_undolog_effect_log_org_time org_id, executed_at DESC B-tree

Partitions (2025-11, 2027-04)

Monthly partitions spanning November 2025 through April 2027 (6 months back + 12 months forward from May 2026). Each partition carries a BRIN index on executed_at (pages_per_range = 32) and a unique index on call_signature:

-- Example: partition DDL for a single month
CREATE TABLE undolog_effect_log_2026_06 PARTITION OF undolog_effect_log
  FOR VALUES FROM ('2026-06-01') TO ('2026-07-01');
CREATE INDEX idx_vel_2026_06_brin ON undolog_effect_log_2026_06
  USING BRIN (executed_at) WITH (pages_per_range = 32);
CREATE UNIQUE INDEX idx_vel_2026_06_sig ON undolog_effect_log_2026_06 (call_signature);

RLS policy: org_id = current_setting('undolog.current_org_id')::uuid

Trigger: undolog_effect_log_counter

Fires AFTER INSERT OR UPDATE. Increments undolog_sessions.tool_calls_total on INSERT. Increments compensations_total when state transitions to compensated.


undolog_undo_stack

Ordered stack of pending compensations per session.

Column Type Constraints Description
undo_id uuid PK DEFAULT uuidv7_or_random() Unique undo entry identifier.
org_id uuid NOT NULL FK → undolog_orgs(org_id) ON DELETE CASCADE Owning organisation.
session_id uuid NOT NULL FK → undolog_sessions(session_id) ON DELETE CASCADE Session identifier.
effect_id uuid NOT NULL References undolog_effect_log.effect_id.
stack_position int NOT NULL LIFO ordering: higher = pushed later = compensated first.
compensation_fn text NOT NULL Matches undolog_compensation_registry.fn_name.
compensation_version text NOT NULL DEFAULT '1.0.0' Compensation function version.
compensation_args jsonb NOT NULL Argument snapshot captured before action execution.
state text NOT NULL DEFAULT 'pending' CHECK (state IN ('pending','running','compensated','failed','skipped')) Compensation execution state.
retry_count smallint NOT NULL DEFAULT 0 Current retry attempt count.
last_error text , Most recent error message.
registered_at timestamptz NOT NULL DEFAULT now() Must be before action executes.
compensated_at timestamptz , Compensation completion timestamp.

Constraints:

Name Definition
undolog_undo_stack_session_position UNIQUE (session_id, stack_position)

Indexes:

Name Columns Condition
idx_undolog_undo_stack_session session_id, stack_position DESC WHERE state = 'pending'
idx_undolog_undo_stack_effect effect_id

RLS policy: org_id = current_setting('undolog.current_org_id')::uuid


undolog_approval_requests

Human-in-the-loop gate for irreversible actions.

Column Type Constraints Description
approval_request_id uuid PK DEFAULT uuidv7_or_random() Unique approval request identifier.
org_id uuid NOT NULL FK → undolog_orgs(org_id) ON DELETE CASCADE Owning organisation.
session_id uuid NOT NULL FK → undolog_sessions(session_id) ON DELETE CASCADE Suspended session.
effect_id uuid NOT NULL The pending effect waiting for approval.
tool_name text NOT NULL Tool requesting approval.
tier undolog_tool_tier NOT NULL DEFAULT 'irreversible' Tool tier (always irreversible).
irreversibility_reason text NOT NULL Human-readable explanation.
risk_tags text[] NOT NULL DEFAULT '{}' Risk categorisation tags.
estimated_impact text , Human-readable impact description.
proposed_args jsonb NOT NULL The agent's proposed tool arguments.
agent_context jsonb NOT NULL DEFAULT '{}' Session context for UI (last N tool calls + reasoning trace).
state undolog_approval_state NOT NULL DEFAULT 'pending' Current approval state.
timeout_at timestamptz NOT NULL Deadline for approval decision.
auto_approve_on_timeout bool NOT NULL DEFAULT false Auto-execute on timeout.
resolved_at timestamptz , Resolution timestamp.
resolved_by text , User ID or 'system:timeout'.
approved_args jsonb , Final approved args (may differ from proposed).
created_at timestamptz NOT NULL DEFAULT now() Creation timestamp.

Indexes:

Name Columns Condition
idx_undolog_approval_org_state org_id, state, created_at DESC WHERE state = 'pending'
idx_undolog_approval_session session_id
idx_undolog_approval_timeout timeout_at WHERE state = 'pending'

RLS policy: org_id = current_setting('undolog.current_org_id')::uuid

Trigger: undolog_approval_counter

Fires AFTER INSERT OR UPDATE. Increments undolog_sessions.approvals_pending on INSERT with state = 'pending'. Decrements when state leaves pending.


undolog_approval_events

Full audit trail of every action on an approval request.

Column Type Constraints Description
event_id uuid PK DEFAULT uuidv7_or_random() Unique event identifier.
approval_request_id uuid NOT NULL FK → undolog_approval_requests(approval_request_id) ON DELETE CASCADE Parent approval request.
org_id uuid NOT NULL Organisation identifier.
action undolog_approval_action NOT NULL Action taken.
actor text NOT NULL User ID or 'system:timeout'.
actor_ip inet , Actor IP address.
note text , Human-readable note.
args_diff jsonb , For modify actions: changes to proposed args.
occurred_at timestamptz NOT NULL DEFAULT now() Event timestamp.

Indexes:

Name Columns
idx_undolog_approval_events_request approval_request_id, occurred_at ASC

RLS policy: org_id = current_setting('undolog.current_org_id')::uuid


undolog_session_snapshots

Periodic state snapshots for fast session replay.

Column Type Constraints Description
snapshot_id uuid PK DEFAULT uuidv7_or_random() Unique snapshot identifier.
session_id uuid NOT NULL FK → undolog_sessions(session_id) ON DELETE CASCADE Session identifier.
org_id uuid NOT NULL Organisation identifier.
up_to_step int NOT NULL Step index up to which this snapshot reflects state.
state_bytes bytea NOT NULL Serialized Rust session state (MessagePack or CBOR).
state_format text NOT NULL DEFAULT 'msgpack' Serialization format identifier.
effect_ids uuid[] NOT NULL DEFAULT '{}' Included effect IDs (for cache invalidation).
created_at timestamptz NOT NULL DEFAULT now() Creation timestamp.

Constraints:

Name Definition
undolog_session_snapshots_unique UNIQUE (session_id, up_to_step)

Indexes:

Name Columns
idx_undolog_session_snapshots_session session_id, up_to_step DESC

RLS policy: org_id = current_setting('undolog.current_org_id')::uuid


undolog_schema_migrations

Migration version tracking.

Column Type Constraints Description
version text PK Migration version identifier.
description text NOT NULL Human-readable description.
applied_at timestamptz NOT NULL DEFAULT now() Application timestamp.

Row-Level Security

RLS is enabled on all data tables. The application layer must set undolog.current_org_id at the start of every transaction:

SET LOCAL undolog.current_org_id = '<uuid>';
Table Policy name Policy definition
undolog_orgs undolog_orgs_isolation USING (org_id = current_setting('undolog.current_org_id')::uuid)
undolog_projects undolog_projects_isolation USING (org_id = current_setting('undolog.current_org_id')::uuid)
undolog_tool_registry undolog_tool_registry_isolation USING (org_id = current_setting('undolog.current_org_id')::uuid)
undolog_compensation_registry undolog_compensation_registry_isolation USING (org_id = current_setting('undolog.current_org_id')::uuid)
undolog_sessions undolog_sessions_isolation USING (org_id = current_setting('undolog.current_org_id')::uuid)
undolog_effect_log undolog_effect_log_isolation USING (org_id = current_setting('undolog.current_org_id')::uuid)
undolog_undo_stack undolog_undo_stack_isolation USING (org_id = current_setting('undolog.current_org_id')::uuid)
undolog_approval_requests undolog_approval_requests_isolation USING (org_id = current_setting('undolog.current_org_id')::uuid)
undolog_approval_events undolog_approval_events_isolation USING (org_id = current_setting('undolog.current_org_id')::uuid)
undolog_session_snapshots undolog_session_snapshots_isolation USING (org_id = current_setting('undolog.current_org_id')::uuid)

Utility View: undolog_active_sessions

CREATE VIEW undolog_active_sessions AS
SELECT
  s.session_id,
  s.org_id,
  s.project_id,
  s.agent_name,
  s.external_run_id,
  s.state,
  s.tool_calls_total,
  s.compensations_total,
  s.approvals_pending,
  s.started_at,
  p.name AS project_name,
  (SELECT json_build_object(
      'tool_name', el.tool_name,
      'tier', el.tier,
      'state', el.state,
      'executed_at', el.executed_at
   ) FROM undolog_effect_log el
   WHERE el.session_id = s.session_id
   ORDER BY el.executed_at DESC
   LIMIT 1) AS latest_effect
FROM undolog_sessions s
LEFT JOIN undolog_projects p ON p.project_id = s.project_id
WHERE s.state IN ('active', 'awaiting_approval', 'compensating');