Skip to main content

Database

AgentHiFive uses PostgreSQL 15+ as its sole data store and Drizzle ORM for type-safe database access. This page covers naming conventions, schema organization, and migration workflow.

Naming Conventions

Table Prefixes

Every table name uses a single-letter prefix to indicate its category:

PrefixCategoryPurposeExample
t_TransactionalCore business entities (mutable)t_workspaces, t_connections
d_DictionaryStatic reference/lookup datad_provider_types
l_LogAppend-only audit and event recordsl_audit_events
r_RelationshipJunction tables for many-to-manyr_policy_scopes

Column Naming

All columns use snake_case:

PatternUsageExamples
idPrimary key (UUID)id
{entity}_idForeign keyworkspace_id, user_id
{verb}_atTimestampscreated_at, updated_at, expires_at
{verb}_byActor UUIDcreated_by, revoked_by
is_{adjective}Boolean flagsis_active, is_revoked

Standard Audit Columns

All transactional tables include:

created_at  TIMESTAMPTZ  NOT NULL  DEFAULT now()
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()

Tables with soft-delete add deleted_at and deleted_by.

Index and Constraint Naming

TypePatternExample
Indexidx_{table}_{columns}idx_connections_workspace_id
Primary key{table}_pkeyt_users_pkey
Unique{table}_{columns}_uniquet_users_email_unique
Foreign key{table}_{column}_fkeyt_connections_workspace_id_fkey
Check{table}_{column}_checkt_policies_rate_limit_check

Current Tables

Transactional Tables (t_)

TableDrizzle VariableDescription
t_usersusersUser accounts (auth + profile)
t_sessionssessionsAuth sessions
t_accountsaccountsOAuth provider accounts
t_verificationsverificationsEmail verification tokens
t_workspacesworkspacesTenant organizations
t_connectionsconnectionsOAuth provider connections
t_pending_connectionspendingConnectionsIn-progress OAuth flows
t_agentsagentsRegistered AI agents/apps
t_policiespoliciesAccess policies
t_approval_requestsapprovalRequestsStep-up approval workflow
t_personal_access_tokenspersonalAccessTokensAPI tokens for agents

Log Tables (l_)

TableDrizzle VariableDescription
l_audit_eventsauditEventsAppend-only audit trail

Drizzle Schema Conventions

Table Definitions

// File: apps/api/src/db/schema/users.ts
import { pgTable, uuid, text, timestamp, boolean } from "drizzle-orm/pg-core";

export const users = pgTable("t_users", {
id: uuid("id").primaryKey().defaultRandom(),
email: text("email").notNull().unique(),
name: text("name").notNull(),
emailVerified: boolean("email_verified").notNull().default(false),
image: text("image"),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
});

Variable Naming

ConventionPatternExample
Table variablecamelCase plural nounusers, auditEvents
Column propertycamelCaseworkspaceId, createdAt
Column DB namesnake_case (in string)"workspace_id", "created_at"

Schema File Organization

apps/api/src/db/schema/
index.ts # Re-exports all tables
users.ts # t_users, t_sessions, t_accounts, t_verifications
workspaces.ts # t_workspaces
connections.ts # t_connections
pending-connections.ts # t_pending_connections
agents.ts # t_agents
policies.ts # t_policies
approval-requests.ts # t_approval_requests
personal-access-tokens.ts # t_personal_access_tokens
audit-events.ts # l_audit_events
enums.ts # PostgreSQL enum types

Migration Workflow

AgentHiFive uses Drizzle Kit for schema migrations.

Development

# Push schema changes directly to the dev database (no migration files)
make migrate

# This runs: pnpm --filter @agenthifive/api run migrate

Adding a New Table

  1. Determine the category prefix (t_, d_, l_, or r_).
  2. Name the table as {prefix}_{plural_snake_case_noun}.
  3. Include standard audit columns (created_at, updated_at).
  4. Create or update the Drizzle schema file under apps/api/src/db/schema/.
  5. Export the table from schema/index.ts.
  6. Run make migrate to push changes to the database.
Auth Tables

Authentication tables (t_users, t_sessions, t_accounts, t_verifications) are defined in the Drizzle schema and follow the same conventions. Better Auth uses our schema via the Drizzle adapter -- we own the tables, not the framework.