mreg-<hex> tokens for legacy worker self-registration
node_provisioning_tokens
mprov-<hex> single-use provisioning tokens (hashed, with expiry)
domains
Base domains an org routes on. Each carries its own dns_mode (delegation or ondemand); is_primary marks the one whose platform subdomains serve and that new routes default to; retiring_at stops new routes attaching while everything already on it keeps serving
Polymorphic workload: application or database. slug is the Kubernetes object name, fixed at creation and suffixed when the plain name is taken in the project; empty on pre-slug rows, which fall back to the display name
service_ports
Exposed ports per service
build_configs
Git source, builder type, registry target (1:1 with service)
db.Migrate() runs GORM AutoMigrate for all models, then applyConstraints(), which creates the unique indexes GORM cannot express as struct tags and runs a few idempotent data migrations (column cleanups and backfills):
Index
Constraint
idx_one_owner_per_org
Exactly one owner per organisation (partial: WHERE role = 'owner')
idx_users_email_unique
Email unique among humans only (partial: WHERE email <> ''); agents carry an empty email
idx_variable_group_service
At most one system-managed variable group per service (partial)
idx_variable_group_item_key
Item keys unique within a variable group
idx_service_variable_group
A service attaches a given group at most once
idx_jobs_project_name
Job names unique within a project
idx_route_target_path
One path rule per route
idx_resource_permission_grant
No duplicate permission grants
Domain names are unique across all organisations through the uniqueIndex tag on domains.base_domain, so one org cannot claim another’s domain.
idx_one_primary_domain_per_org is a partial unique index on domains(organization_id) WHERE is_primary: every base domain routes, and primary only decides whose platform subdomains serve, so two of them would leave that undecided.
Migrations run automatically on API startup; no migration CLI is needed.
EncryptedString is a custom GORM type that transparently encrypts on write and decrypts on read using AES-256-GCM. Call db.SetEncryptionKey(key) before any DB operation — the key must be exactly 32 characters.
Fields using this type (registry credentials, storage keys, git tokens) are stored as base64-encoded ciphertext and are never readable as plaintext in the database.
db.RegisterMigration(fn) registers additional schema migrations that run after AutoMigrate and applyConstraints. Call it from any package’s init() to extend the schema without modifying packages/db directly.