Skip to content

Database

Each environment gets an Aurora PostgreSQL Serverless v2 cluster. Application connections use IAM authentication rather than long-lived database passwords, while RDS maintains an AWS-managed master-user secret that Pulumi uses through the Data API for provisioning. With the default hawk:dbMinCapacity: "0", the cluster scales to zero after a few minutes of inactivity to save costs; the first connection after a pause takes ~30 seconds to wake up.

Database Roles

These login roles are created automatically:

Role Purpose
inspect_admin Migrations (rds_superuser)
inspect API read/write
inspect_ro Read-only access
middleman LLM proxy model configuration reads and admin API writes
inspect-importer Import pipeline (eval-log and scan importers); bypasses RLS. Name configurable via hawk:warehouseSystemUser

Row-level security is managed through NOLOGIN group roles that the login roles are granted into: rls_bypass (system pipelines), rls_reader (RLS-filtered reads), and model_access_all. See Security: Access Control for how permissions flow.

Connecting

After logging Pulumi in to the correct S3 backend, name the target stack explicitly. Resolve both the database endpoint and AWS region from that stack, then generate an IAM auth token for your database role:

export AWS_PROFILE="<profile-for-the-target-account>"
STACK="<stack>"
ENDPOINT="$(pulumi stack output database_url_admin -s "$STACK" | sed -E 's#.*@([^:/]+).*#\1#')"
REGION="$(pulumi stack output region -s "$STACK")"
TOKEN="$(aws rds generate-db-auth-token \
  --hostname "$ENDPOINT" --port 5432 --region "$REGION" --username inspect)"
PGPASSWORD="$TOKEN" psql "host=$ENDPOINT dbname=inspect user=inspect sslmode=require"

Running Migrations

After logging Pulumi in to the correct S3 backend, get the database URL for an isolated development stack. Production and shared staging migrations run as part of deployment; do not apply them manually.

export AWS_PROFILE="<profile-for-the-target-account>"
STACK="dev-<name>"
export DATABASE_URL="$(pulumi stack output database_url_admin -s "$STACK")"

Run migrations:

cd hawk
uv run alembic upgrade head

Creating a New Migration

After changing the SQLAlchemy models in hawk/core/db/models.py, use an isolated development database—never staging or production—to generate the migration:

cd hawk
uv run alembic revision --autogenerate -m "description of change"

Validate the complete migration chain against the disposable test database:

cd hawk
uv run pytest tests/core/db/test_alembic_migrations.py

Schema Conventions

  • All tables have a pk UUID primary key, and created_at/updated_at timestamps
  • All timestamps are timezone-aware and stored in UTC
  • Model names are singular