Skip to content

Database schema

Shared GORM models and database utilities. Imported by apps/api and apps/proxy via the Go workspace replace directive.


FilePurpose
db.goOpen(), Migrate(), FromEnv(), RegisterMigration()
models.goAll 36 table definitions
types.goCustom JSONB types: EnvVarsMap, JSONObject, StringArray
crypto.goEncryptedString — AES-256-GCM GORM type

TablePurpose
usersIdentity
trusted_devicesRemembered devices — skips 2FA prompt on re-login
recovery_codesOne-time 2FA recovery codes (hashed)
organizationsTenancy root
organization_membersUser ↔ Org join (roles: owner / admin / member)
resource_permissionsPer-resource ACL grants (service, stack, job, project)
org_invitationsEmail invitations to join an org
TablePurpose
projectsK8s namespace — slug becomes the namespace name
nodesMesh worker nodes + K3s + Headscale metadata
node_registration_tokensmreg-<hex> tokens for legacy worker self-registration
node_provisioning_tokensmprov-<hex> single-use provisioning tokens (hashed, with expiry)
domainsCustom domains attached to an org
TablePurpose
stacksDocker Compose stacks — parsed spec + services
servicesPolymorphic workload: application or database
service_portsExposed ports per service
build_configsGit source, builder type, registry target (1:1 with service)
database_configsEngine, version, storage size (1:1 with service)
volumesPersistent volumes
volume_mountsVolume ↔ Service mount (path + read-only flag)
volume_backup_configsBackup schedule for individual volumes
TablePurpose
variable_groupsNamed collections of key/value variables (project-scoped)
variable_group_itemsIndividual variable items within a group
service_variable_groupsService ↔ VariableGroup join
TablePurpose
routesHostname → service routing rule
route_targetsTarget service + path-strip config per route
TablePurpose
deploymentsDeployment history + K8s artefacts + build log
TablePurpose
jobsJob definition (image, command, schedule, concurrency)
job_runsIndividual run records (status, logs, started/finished at)
TablePurpose
storage_integrationsS3-compatible storage credentials (org-scoped)
registry_integrationsContainer registry credentials (org-scoped)
git_integrationsGit provider connections (GitHub App, GitLab OAuth, Gitea OAuth)
TablePurpose
backup_configsScheduled DB backup config (service-scoped)
system_backup_configsOrg-wide system backup config
notification_channelsSlack / Discord / webhook / email event routing
org_email_configsSMTP credentials per org
TablePurpose
templates1-click deployment blueprints (official + user-created)

db.Migrate() runs GORM AutoMigrate for all models followed by applyConstraints() which creates partial unique indexes that GORM cannot express as struct tags:

IndexConstraint
idx_one_owner_per_orgExactly one owner per organisation
idx_unique_domain_per_orgDomain names unique within an org

Migrations run automatically on API startup — no migration CLI 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.


import dbpkg "github.com/meshploy/packages/db"
// Open from DATABASE_URL env var
db, err := dbpkg.FromEnv()
// Or explicit DSN
db, err := dbpkg.Open(dsn)
// Run migrations
dbpkg.Migrate(db)
// Set encryption key before any encrypted field access
dbpkg.SetEncryptionKey(os.Getenv("ENCRYPTION_KEY"))