Workforce OS uses Alembic to version-control the PostgreSQL schema and an automated bootstrapper that seeds the RBAC permission matrix and creates the initial admin account on API startup. This page covers running migrations, creating new migration files, the bootstrap flow, seed data for development, and production-specific database considerations.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Paramount-Intelligence/HR_Monitoring_System/llms.txt
Use this file to discover all available pages before exploring further.
PostgreSQL is required. SQLite is explicitly rejected at startup — the
DATABASE_URL validator raises a ValueError if a sqlite:// URL is detected. All environments, including staging, should use PostgreSQL.Alembic Migration System Overview
Alembic tracks schema changes in versioned migration scripts stored underapps/api/alembic/versions/. Each script contains upgrade() and downgrade() functions. The Alembic configuration file (apps/api/alembic.ini) points at the database via sqlalchemy.url, which is overridden at runtime by the DATABASE_URL environment variable so the same scripts work across environments.
The Procfile in apps/api runs migrations automatically before the application server starts on every deploy:
Procfile
Running Migrations
Apply all pending migrations
From theapps/api directory with the virtual environment active:
alembic upgrade head will:
- Connect to the PostgreSQL database using
DATABASE_URL. - Inspect the
alembic_versiontable to determine which migrations have already been applied. - Apply any pending migrations in order, creating all required tables and indexes.
Check current migration state
View migration history
Roll back one migration
Creating New Migration Files
Auto-generate from model changes
After modifying a SQLAlchemy model, generate a migration script automatically:upgrade() / downgrade() script to alembic/versions/. Review the generated file before committing — auto-generation does not detect all changes (for example, renamed columns or custom constraints may need manual edits).
Create a blank migration script
For data migrations or DDL that Alembic cannot auto-detect:upgrade() and downgrade() logic manually.
Bootstrap: Permission Seeding and Admin Creation
How it works
When the FastAPI application starts, it runs two bootstrapping phases before accepting requests:-
seed_permissions— Inserts or reconciles the base RBAC permission matrix required for role-based access control. This is idempotent and safe to run on every startup. -
Admin user creation — Checks whether a user with
BOOTSTRAP_ADMIN_EMAILalready exists. If not, it:- Hashes
BOOTSTRAP_ADMIN_PASSWORDwith bcrypt. - Creates the user with the
adminrole andactivestatus. - Sets the display name to
BOOTSTRAP_ADMIN_NAME. - Logs a confirmation message with the configured email.
- Hashes
Bootstrap credentials
Configure these three variables in your environment before first startup:| Variable | Default | Notes |
|---|---|---|
BOOTSTRAP_ADMIN_EMAIL | [email protected] | The login email for the first admin account. |
BOOTSTRAP_ADMIN_PASSWORD | change-this-password | Must be changed from the default in production. The startup validator rejects change-this-password, changeme, and password when APP_ENV=production. |
BOOTSTRAP_ADMIN_NAME | HR Admin | Display name for the admin account. |
First login
Ensure migrations have run
Confirm
alembic upgrade head has completed successfully. If using the Railway Procfile, this happens automatically on each deploy.Start the API
Start the backend service. Watch the startup logs for the bootstrap success message confirming the admin email.
Log in with bootstrap credentials
Open the frontend, enter
A successful login redirects to
BOOTSTRAP_ADMIN_EMAIL and BOOTSTRAP_ADMIN_PASSWORD, and submit.A successful login redirects to
/admin/dashboard.Verify the user list
Navigate to Users & Teams. You should see at least one user (the bootstrap admin).
Seed Script for Development
Aseed.py script is provided for populating a local development database with representative sample data (users, teams, projects, attendance records). This is separate from the production bootstrap and should never be run against a production database.
alembic upgrade head has already been run and that the database is accessible via DATABASE_URL.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
Relation "users" does not exist | Migrations have not been run | Run alembic upgrade head |
IntegrityError during bootstrap | Admin user already exists with different credentials | The bootstrapper is idempotent and will attempt to sync; check startup logs for details |
Password is too short or bootstrap password rejected | Leading/trailing whitespace in BOOTSTRAP_ADMIN_PASSWORD | Remove surrounding spaces from the value in .env or the Railway dashboard |
DATABASE_URL is missing | DATABASE_URL not set and PG* variables incomplete | Set DATABASE_URL directly or ensure all of PGHOST, PGUSER, PGPASSWORD, PGDATABASE are configured |
SQLite is no longer supported | DATABASE_URL starts with sqlite:// | Switch to a PostgreSQL connection string |
Production vs Development Databases
| Concern | Development | Production |
|---|---|---|
| Database engine | PostgreSQL (local or Docker) | PostgreSQL (Railway managed or external) |
| SQLite | ✗ Not supported | ✗ Not supported |
| Encoding | UTF-8 required | UTF-8 required |
| Migration trigger | Manual: alembic upgrade head | Automatic via Procfile on each deploy |
| Seed data | seed.py optional | Never run seed.py in production |
| Backups | Not required | Required — enable in Railway dashboard |
UTF-8 Encoding Requirement
The database must use UTF-8 encoding to correctly store emoji and Unicode characters in message bodies, call notes, and other text fields. Railway’s managed PostgreSQL uses UTF-8 by default. For self-managed instances:Backup Guidance
Railway automated backups:In the Railway dashboard, navigate to your PostgreSQL service → Settings → Backups and enable scheduled backups. Railway retains multiple restore points and supports point-in-time recovery on paid plans. Manual backup with
pg_dump:
