System Admin Control-Plane Bootstrap
This runbook initializes the independent database used by tools/system-admin. It must never be run against a customer endpoint database.
Prerequisites
- Provision a dedicated PostgreSQL database for System Admin.
- Configure
SYSADMIN_DB_*for runtime andSYSADMIN_MIGRATION_DB_*for migrations. Migration commands require all seven split fields: HOST, PORT, USERNAME, PASSWORD, NAME, SCHEMA, and SSL. They do not fall back to runtime or customer credentials. - Prepare an approved bcrypt cost-12 password hash with the application helper
hashSystemAdminPasswordfromtools/system-admin/server/security/password.tson a secure workstation, and prepare the reviewed administrator identity values for the manual SQL scripts. Never put a plaintext password, password hash, OTP secret, or recovery code in source control or deployment configuration. - Configure
SYSADMIN_ENCRYPTION_KEYas a 32-byte Base64 secret outside the database for TOTP secret encryption. - Confirm that the database backup and recovery process covers this control-plane database.
The bootstrap password is not stored in source control, migrations, logs, or the database in plaintext.
Initialize the database
Use database name hereby_works_sysadmin for both SYSADMIN_DB_NAME and SYSADMIN_MIGRATION_DB_NAME, and schema hereby_works for both SYSADMIN_DB_SCHEMA and SYSADMIN_MIGRATION_DB_SCHEMA. The migration command creates this schema when absent; the PostgreSQL database must already exist. Runtime and migration settings must match. Changing these settings does not move existing tables from another database or schema.
For local development, make sysadmin-env adds missing System Admin settings to the root .env, reusing the local DB_* connection credentials with database name hereby_works_sysadmin and schema hereby_works. Existing values and the TOTP encryption key are preserved. This command does not create the PostgreSQL database or apply migrations. Create the dedicated database before running migrations.
make sysadmin-db-migrate runs pnpm install --frozen-lockfile, then pnpm build, then the System Admin migration command. Each step must succeed before the next starts. Both SYSADMIN_MIGRATION_RUN and SYSADMIN_MIGRATION_MODE are required; missing or invalid values fail before connecting. make sysadmin-db-status always shows pending migration status without writes, even when execution is enabled. Both commands use SYSADMIN_MIGRATION_DB_*. Migration jobs only change database structure; they never create accounts, roles, permissions, or audit rows.
For the business backend, make db-migrate uses the same install/build preparation and then invokes root pnpm db:migrate, which reads MIGRATION_DB_* and honors MIGRATION_RUN and MIGRATION_MODE. Set MIGRATION_RUN=true and MIGRATION_MODE=up to apply pending backend migrations; MIGRATION_RUN=false previews them. Direct pnpm migration commands remain available when dependencies and build outputs are already prepared.
Both business and System Admin migration processes use the following controls. They are migration-job settings, not server settings. make sysadmin-env supplies false/up only when these values are absent and preserves existing choices.
| Controls | System Admin behavior |
|---|---|
SYSADMIN_MIGRATION_RUN=false, SYSADMIN_MIGRATION_MODE=up | Preview pending migrations, without creating a schema or tracking table. |
SYSADMIN_MIGRATION_RUN=true, SYSADMIN_MIGRATION_MODE=up | Create the configured schema if needed and apply pending migrations. |
SYSADMIN_MIGRATION_RUN=false, SYSADMIN_MIGRATION_MODE=down | Preview the last executed migration. |
SYSADMIN_MIGRATION_RUN=true, SYSADMIN_MIGRATION_MODE=down | Revert the last executed migration. |
The db:migration:revert command additionally requires SYSADMIN_MIGRATION_MODE=down and never overrides SYSADMIN_MIGRATION_RUN=false. Some migrations are intentionally irreversible; their errors propagate and the command exits unsuccessfully. Preview does not guarantee a migration can be reverted. Review backups and the migration before executing down.
Run with explicit controls (after dependencies/build outputs are ready):
SYSADMIN_MIGRATION_RUN=false SYSADMIN_MIGRATION_MODE=up pnpm --filter @hereby/system-admin db:migrate
SYSADMIN_MIGRATION_RUN=true SYSADMIN_MIGRATION_MODE=up pnpm --filter @hereby/system-admin db:migrate
SYSADMIN_MIGRATION_RUN=false SYSADMIN_MIGRATION_MODE=up pnpm --filter @hereby/system-admin db:migration:showThe System Admin server does not run migrations during normal startup. It refuses to start when the control-plane database is unavailable or has pending migrations.
Initialize security data with DB-tool SQL
The migration job does not initialize security data. After the migrations complete, run the following files from a database client against hereby_works_sysadmin with schema hereby_works:
tools/system-admin/server/db/seeds/inspect-control-plane.sql— read-only preflight and account/catalog inventory.tools/system-admin/server/db/seeds/reconcile-security-catalog.sql— create missing permissions, system roles, and expected system-role links.tools/system-admin/server/db/seeds/bootstrap-local.sql— create the first local Root account in an otherwise empty account table. Replace the four parameter values at the top of the file, including a bcrypt hash generated on an approved secure workstation.tools/system-admin/server/db/seeds/assign-existing-root.sql— on an existing database, replace the reviewed account UUID and assign its Root marker without changing password, OTP, sessions, or existing assignments.
Run the catalog script before either account script. The scripts use explicit transactions and reject the wrong database, missing schema, soft-deleted system records, custom catalog conflicts, incomplete accounts, and multiple Roots. Re-running a complete bootstrap account is a no-op; re-running catalog reconciliation is idempotent. A failed script rolls back its own changes. Do not run the bootstrap script to repair an existing account and do not run both account scripts for the same environment.
Reconcile an existing control plane
When the control-plane database already contains users or roles, the DB-tool catalog script is the primary initialization path. The application-side reconciliation endpoint and optional command remain available for an explicitly authorized administrator, but they are not part of migration or deployment startup:
make sysadmin-permissions-reconcileThe command is read-only by default. To add only missing source permissions, system roles, and their expected system-role links, run the explicit apply mode:
make sysadmin-permissions-reconcile ARGS=--applyThe apply mode is transactional and idempotent. It never resets users, passwords, OTP factors, custom roles, existing assignments, or permissions outside the source catalog. Deleted records or custom roles using a system role code stop the command and require explicit repair.
Local account first login
The browser local-login flow uses the System Admin database only. It supports the bootstrap Root and non-Root local accounts created from the System Admin access screen; it does not make the legacy business-OPERATOR login page accept either account type.
- Sign in with the seeded Root password or the temporary password delivered separately by an authorized account administrator.
- Complete the forced password change.
- Enroll an authenticator-based TOTP factor and store the one-time recovery codes securely.
- Choose a new password of at least 12 characters. Rerunning the SQL will not reset it.
- Keep
SYSADMIN_ENCRYPTION_KEYin the deployment secret manager; it is required to verify existing TOTP factors after a restart. - Remove any temporary password-hash material from the DB tool history or secure workstation after the first successful password change, according to the incident and secret-handling policy.
A successful local login or completed password-and-TOTP reauthentication authorizes sensitive System Admin actions for eight hours. Normal reads and failed authentication attempts do not extend this window. The existing 30-minute idle session timeout and 12-hour absolute session timeout still apply independently, so an expired session always requires a new login. Google SSO sessions keep their existing recent-reauthentication exemption.
The initial bootstrap account is not a Google SSO identity. Google SSO users are linked later by the authentication and user-management flows. External collaborators do not need to belong to an approved Google Workspace: an operator with both admin_user:manage.global and sysadmin_access:manage.global can create a non-Root local account, assign a non-SUPER_ADMIN role and scope, and set a required expiry. The temporary password must be delivered through a separate approved channel and is never shown again or stored in plaintext.
Safety checks
- Verify the database host and database name before running
db:migrateor any manual initialization SQL. - Do not set the control-plane URL to a customer endpoint database.
- Do not pass the bootstrap password as a command-line argument.
- Do not paste the bootstrap password into logs, tickets, or deployment manifests.
- Keep at least one effective
SUPER_ADMIN; later user and role-management flows must reject removal of the last one.