-
Notifications
You must be signed in to change notification settings - Fork 0
Reference ReadyStackGo Migration Spec
This document describes how ReadyStackGo (RSGO) handles database migrations – particularly for ams.project – and how different database users (Least Privilege) are used in the process.
Goals:
- Clean, reproducible migration model for stacks (especially ams.project).
- Clear separation between:
- App-User (restricted rights, runtime operation),
- Migration-User (schema/migration rights),
- optionally Provisioning-/Admin-User (DB/login creation).
- Orchestration by RSGO:
- Migrations are executed as Jobs (Migration Containers).
- RSGO evaluates exit codes and updates status/health.
- Integration with the RSGO health and deployment model:
- e.g.,
OperationMode = Migrating,MigrationStatus.
- e.g.,
This document is intended as an implementation template (e.g., for Claude) to implement migrations and DB permissions in RSGO.
For each business database (e.g., ams_project, ams_identity, etc.), three levels of users are distinguished:
Purpose: Runtime user for all regular services/containers.
- Typical permissions:
-
SELECT,INSERT,UPDATE,DELETE -
EXECon Stored Procedures/Functions
-
-
No DDL rights:
- no
CREATE TABLE,ALTER TABLE,DROP TABLE, etc.
- no
- Used only in app containers (e.g.,
ams-api,ams-bff, Worker, …).
Purpose: Execution of schema/data migrations.
- May:
- create/modify/delete tables/views/procs/functions,
- create/modify/delete indexes,
- perform DB-specific migration operations if needed.
- Ideally restricted to a specific DB (e.g., only
ams_project). - Used only by migration containers.
Purpose: One-time initial setup / "provisioning".
- May:
- create databases
- create logins/users
- grant permissions
- Used only for:
- initial setup,
- rare special cases.
- Should not remain permanently active; either:
- locked by the customer after provisioning,
- or stored only as a highly sensitive secret in RSGO.
The RSGO manifest describes which parameters are required for a stack and how they are used in services/migrations.
parameters:
- key: PROJECT_DB_APP
description: "App connection string to the ams.project database (read/write rights only)"
required: true
secret: true
- key: PROJECT_DB_MIGRATION
description: "Migration connection string to the ams.project database (schema changes)"
required: false # Required in expert mode, otherwise optional
secret: true
- key: PROJECT_DB_PROVISIONING
description: "Optional provisioning connection string (DB/user creation)"
required: false
secret: true
- key: PROJECT_DB_APP_USERNAME
description: "Login/username of the App-User (may be created by migration)"
required: false
secret: falseNote: The exact names (
PROJECT_DB_*) are example names; they should match the naming convention of the respective domain/DB.
Regular services use only the App connection string:
services:
- name: ams-api
image: registry.example.com/ams.api:0.5.0
env:
- name: ConnectionStrings__Main
valueFrom: param:PROJECT_DB_APP
- name: ams-worker
image: registry.example.com/ams.worker:0.5.0
env:
- name: ConnectionStrings__Main
valueFrom: param:PROJECT_DB_APPMigrations use the Migration connection string (or the provisioning string in provisioning scenarios):
migrations:
- name: ams-project-db-migration
image: registry.example.com/ams.migrations:0.5.0
runPolicy: before-services # wird vor dem Start der Services ausgeführt
env:
- name: PROJECT_DB_MIGRATION
valueFrom: param:PROJECT_DB_MIGRATION
- name: PROJECT_DB_PROVISIONING
valueFrom: param:PROJECT_DB_PROVISIONING
- name: PROJECT_DB_APP_USERNAME
valueFrom: param:PROJECT_DB_APP_USERNAME
- name: RSGO_TARGET_VERSION
valueFrom: system:StackTargetVersion
- name: RSGO_ORG_ID
valueFrom: system:OrgId
- name: RSGO_ENV_ID
valueFrom: system:EnvironmentIdMigrations are executed as one-shot jobs in dedicated containers.
- Starts, executes migrations, terminates.
- No long-running background processes.
-
Idempotent:
- Multiple executions should not cause damage.
- Already applied migrations are skipped (e.g., Flyway, EF Migrations History).
The container receives all necessary information via env vars:
Minimal:
PROJECT_DB_MIGRATION=...
RSGO_TARGET_VERSION=0.5.0
RSGO_ORG_ID=...
RSGO_ENV_ID=...
RSGO_MIGRATION_RUN_ID=... # optional, GUID from RSGO
Provisioning mode (optional):
PROJECT_DB_PROVISIONING=... # higher privileged connection string
PROJECT_DB_APP=... # optional, if App-User should be tested with it
PROJECT_DB_APP_USERNAME=ams_app # Name the migration job uses for App-User
APP_DB_PASSWORD=... # Password for App-User to be created (from Secret)
-
Exit-Code:
-
0→ Migration successful -
!= 0→ Migration failed
-
- Optional: Write result to JSON file (e.g., in volume
/rsgo/migration-result.json):
{
"status": "Succeeded",
"fromVersion": "0.4.2",
"toVersion": "0.5.0",
"appliedMigrations": [
"V0_5_0__Create_Table_X",
"V0_5_0__Add_Index_Y"
],
"durationSeconds": 37
}RSGO can:
- always evaluate the exit code (mandatory),
- optionally read the JSON output to show history/details in the UI.
Optional "convenience mode": The migration creates (with sufficient rights) the App-User directly in the database.
-
In the RSGO wizard/deployment for a new installation, the admin selects:
- Mode:
- "Users managed by Database Admin"
- "Users created by ReadyStackGo/Migration" (Provisioning Mode)
- specifies:
-
PROJECT_DB_PROVISIONING(highly privileged connection string) -
PROJECT_DB_APP_USERNAME(e.g.,ams_app) - optionally App password (or lets it be generated)
-
- Mode:
-
RSGO starts the migration container with:
-
PROJECT_DB_PROVISIONINGset -
PROJECT_DB_APP_USERNAMEset - optionally
APP_DB_PASSWORDvia env var
-
-
Migration logic:
- If DB doesn't exist yet → create it.
- If App-Login/User doesn't exist yet:
- Create login (e.g.,
CREATE LOGIN [ams_app] WITH PASSWORD = @AppUserPassword;). - Create user for the DB (
CREATE USER [ams_app] FOR LOGIN [ams_app];). - Assign roles (e.g.,
db_datareader,db_datawriter, possibly custom role forEXEC).
- Create login (e.g.,
- Execute schema migrations.
-
Result:
- DB exists.
- App-User exists.
- Migration history is up to date.
-
RSGO notes in the configuration:
-
PROJECT_DB_APP→ Connection string with App-User. -
PROJECT_DB_MIGRATION→ Connection string with Migration-/Provisioning-User (depending on setup).
-
The provisioning script must be written so that it can safely run again.
Example (SQL Server, pseudocode):
-- Create login + user only if they don't already exist
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = @AppUserName)
BEGIN
IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = @AppUserName)
BEGIN
DECLARE @CreateLoginSql nvarchar(max);
SET @CreateLoginSql = N'CREATE LOGIN [' + @AppUserName + N'] WITH PASSWORD = @AppUserPassword;';
EXEC sp_executesql @CreateLoginSql, N'@AppUserPassword nvarchar(256)', @AppUserPassword=@AppUserPassword;
END;
DECLARE @CreateUserSql nvarchar(max);
SET @CreateUserSql = N'CREATE USER [' + @AppUserName + N'] FOR LOGIN [' + @AppUserName + N'];';
EXEC(@CreateUserSql);
EXEC sp_addrolemember N'db_datareader', @AppUserName;
EXEC sp_addrolemember N'db_datawriter', @AppUserName;
-- possibly custom role for EXEC rights
END;Passwords are not hardcoded, but passed to the migration container as env var.
When deploying/upgrading a stack (e.g., ams-project):
-
Set status (in SQLite):
DeploymentStatus = UpgradingMigrationStatus = RunningOperationMode = Migrating- set
TargetVersionif applicable.
-
Stop services (if
runPolicyrequires it):- stop affected services of the stack (e.g., Worker/APIs),
- depending on policy.
-
Start migration container:
- via Docker API (
restartPolicy = "no"):- Image:
ams.project.migrations:<TargetVersion> - Env: DB connection strings, RSGO IDs etc.
- Image:
- Store container ID in RSGO.
- via Docker API (
-
Monitor migration run:
- periodically check container status:
-
running→ Migration is running -
exited→ Migration finished
-
- read exit code.
- periodically check container status:
-
Process result:
- Exit-Code
0:MigrationStatus = SucceededCurrentVersion = TargetVersion-
OperationMode = Normal(or transition to "start services")
- Exit-Code
!= 0:MigrationStatus = FailedOperationMode = FailedDeploymentStatus = Failed- UI: Note "Migration failed – check logs".
- Exit-Code
-
Start services (on success):
- Start stack services with APP connection strings.
DeploymentStatus = Idle- Re-evaluate health checks.
- UI option: "Retry migration":
- restarts the same migration container (new
RSGO_MIGRATION_RUN_ID). - assumes idempotency of the migration logic.
- restarts the same migration container (new
The migration feature integrates into the Health/OperationMode model of RSGO:
- During migration:
OperationMode = Migrating- Health engine sets:
overall = Degraded- UI shows: "Migration running (from X to Y)"
- After successful migration:
OperationMode = Normal-
overallis recalculated from Bus/Infra/Self.
- After failed migration:
OperationMode = FailedMigrationStatus = Failedoverall = Unhealthy- UI shows:
- "Migration failed – manual intervention required."
This clearly distinguishes between planned restrictions (migration/maintenance) and unplanned outages.
For DB parameters (especially MIGRATION/PROVISIONING):
- Only SystemOwner / OrgAdmin:
- may set/change values for MIGRATION/PROVISIONING,
- may even see whether these are set (displayed only as
*****).
-
Operator:
- may start/stop stacks,
- may trigger migrations again (if allowed),
- may see status/logs,
- may not view or change connection strings.
- All DB connection strings are treated as Secrets in SQLite:
- Encryption with ASP.NET Data Protection or separate key,
- no plaintext storage.
- Logs:
- Migration logs and RSGO logs must not contain complete connection strings or passwords.
- Error texts should be "sanitized" (e.g., mask offending connection strings).
When .env files are used:
PROJECT_DB_APP=...
PROJECT_DB_MIGRATION=...
PROJECT_DB_PROVISIONING=...
PROJECT_DB_APP_USERNAME=ams_app- RSGO:
- reads the values,
- stores them encrypted,
- shows only masking (
******) for secrets.
RSGO should support two broad operating modes per database/stack:
- For smaller customers / simple setups.
- RSGO/Migration containers:
- create database (if needed),
- create App-User,
- assign roles.
- Customer only needs to provide a provisioning/admin connection string.
- After completion, it is recommended to:
- reduce provisioning rights,
- use only Migration-/App-User for normal operation.
- For customers with strict policies.
- DBAs create:
- Database,
- Logins,
- Users,
- Permissions themselves.
- RSGO receives:
- App connection string,
- optionally Migration connection string,
- RSGO/Migration containers do not change logins/users, only schema/data.
In the wizard, this can be presented as an option:
- "Users & DB managed by Database Admin"
- "Users & DB created by ReadyStackGo/Migration"
- Migrations in RSGO are executed as jobs in dedicated migration containers.
- There are at least two DB users:
- App-User (Least Privilege, runtime only),
- Migration-User (schema changes).
- Optionally, a Provisioning-/Admin-User can be used so that the migration can:
- create database,
- create App-User,
- assign permissions itself.
- RSGO:
- orchestrates start/monitoring/evaluation of migration containers,
- sets
DeploymentStatus,MigrationStatus,OperationMode, - cleanly integrates migrations into the health model (
Degraded (Migrating)).
- RBAC ensures that:
- only authorized roles (SystemOwner/OrgAdmin) manage DB credentials,
- operators only work with status/health/start/stop.
- This specification is sufficient for an implementer (e.g., Claude) to consistently implement:
- the manifest,
- the migration container integration,
- and the RSGO orchestration.
Getting Started
Architecture
Configuration
Security
Setup Wizard
Development
Operations
CI/CD
Reference
- Roadmap
- API Reference
- Configuration Reference
- Manifest Schema
- Multi-Environment
- Stack Sources
- Plugin System
- Technical Specification
- Full Specification
Specifications
Release Notes