This directory contains Alembic migrations for the Bindu PostgreSQL storage backend.
- PostgreSQL database running (local or remote)
- Database URL configured in environment variables
Set the database URL in your environment:
# .env file or environment variable
DATABASE_URL=postgresql://user:password@localhost:5432/bindu # pragma: allowlist secret
# Or using STORAGE__ prefix
STORAGE__POSTGRES_URL=postgresql://user:password@localhost:5432/bindu # pragma: allowlist secret# Install with pip
pip install -e .
# Or with uv
uv pip install -e .# Using psql
createdb bindu
# Or with SQL
psql -U postgres -c "CREATE DATABASE bindu;"# Upgrade to latest version
alembic upgrade head
# Check current version
alembic current
# View migration history
alembic history --verbose# Upgrade to latest
alembic upgrade head
# Upgrade by 1 version
alembic upgrade +1
# Upgrade to specific revision
alembic upgrade 20241207_0001# Downgrade by 1 version
alembic downgrade -1
# Downgrade to specific revision
alembic downgrade 20241207_0001
# Downgrade all (WARNING: destroys all data)
alembic downgrade base# Create a new migration file
alembic revision -m "add_new_feature"
# Auto-generate migration from model changes (if using SQLAlchemy models)
alembic revision --autogenerate -m "auto_generated_changes"# Show current version
alembic current
# Show migration history
alembic history
# Show detailed history with SQL
alembic history --verboseMigrations are located in alembic/versions/:
20241207_0001_initial_schema.py- Initial database schema- Creates
tasks,contexts, andtask_feedbacktables - Adds indexes for performance
- Sets up automatic
updated_attriggers
- Creates
20260418_0001_add_owner_did.py- Per-caller ownership tracking- Adds a nullable
owner_didcolumn + index totasksandcontextsso each row records the DID that created it. - Upgrade ordering matters when enabling the A2A authorization
fix (see slug
idor-task-context-no-ownership-checkinbugs/known-issues.md):- Run
alembic upgrade headto add the nullable columns. - Run
python scripts/backfill_owner_did.py --owner-did did:bindu:<your-legacy-owner>to assign all pre-existing rows to a designated owner — otherwise they become inaccessible to authenticated callers after step 3. Pass--dry-runfirst to count affected rows. If you run per-DID schemas from thecreate_bindu_tables_in_schemahelper below, repeat once per schema with--schema <name>. - Deploy the code revision that enforces ownership on A2A
handlers. Reads/writes now filter by
owner_did.
- Run
- Adds a nullable
Stores A2A protocol tasks with JSONB history and artifacts.
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| context_id | UUID | Reference to context |
| kind | VARCHAR(50) | Task type (default: 'task') |
| state | VARCHAR(50) | Current task state |
| state_timestamp | TIMESTAMPTZ | When state was last updated |
| history | JSONB | Message history array |
| artifacts | JSONB | Task artifacts array |
| metadata | JSONB | Additional metadata |
| created_at | TIMESTAMPTZ | Creation timestamp |
| updated_at | TIMESTAMPTZ | Last update timestamp |
Stores conversation contexts with message history.
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| context_data | JSONB | Context-specific data |
| message_history | JSONB | Message history array |
| created_at | TIMESTAMPTZ | Creation timestamp |
| updated_at | TIMESTAMPTZ | Last update timestamp |
Stores user feedback for tasks.
| Column | Type | Description |
|---|---|---|
| id | INTEGER | Primary key (auto-increment) |
| task_id | UUID | Foreign key to tasks |
| feedback_data | JSONB | Feedback content |
| created_at | TIMESTAMPTZ | Creation timestamp |
Performance indexes are created for:
- Task lookups by context_id, state, timestamps
- JSONB queries on history, metadata, artifacts
- Context lookups by timestamps
- Feedback lookups by task_id
# Test database connection
psql $DATABASE_URL -c "SELECT version();"
# Check if database exists
psql -U postgres -l | grep bindu# If migrations are out of sync, check current version
alembic current
# View pending migrations
alembic history
# Force stamp to specific version (use with caution!)
alembic stamp head# WARNING: This destroys all data!
# Downgrade all migrations
alembic downgrade base
# Drop and recreate database
dropdb bindu && createdb bindu
# Re-run migrations
alembic upgrade head-
Always backup before migrations
pg_dump -U postgres bindu > backup_$(date +%Y%m%d_%H%M%S).sql
-
Test migrations in staging first
# Staging DATABASE_URL=postgresql://staging alembic upgrade head # Neon DATABASE_URL=postgresql://<user>:<Password>@<host>:<port>/<database> uv run alembic upgrade head # Production (after testing) DATABASE_URL=postgresql://production alembic upgrade head
-
Use read-only mode during migrations
- Put application in maintenance mode
- Run migrations
- Verify data integrity
- Resume normal operations
-
Monitor migration performance
# Time the migration time alembic upgrade head
Run migrations as an init container or job:
# Kubernetes Job example
apiVersion: batch/v1
kind: Job
metadata:
name: bindu-migrations
spec:
template:
spec:
containers:
- name: migrations
image: bindu:latest
command: ["alembic", "upgrade", "head"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: bindu-secrets
key: database-url
restartPolicy: OnFailureFor issues or questions:
- Check the Alembic documentation
- Review migration files in
alembic/versions/ - Check application logs for detailed error messages