Migrations
This workflow runs in the ce repo, not here. The split moved
ent/,migrations/andatlas.hclthere, and themake migratetarget moved with them — every command below is run from a ce checkout. This repo consumes ce as a pinned Go module and the daemon applies the embedded migrations at startup, so there is nothing schema-related to run on the shc side. File paths below are ce-repo-relative.
SHC uses Atlas (ariga/atlas) with Ent (entgo.io/ent) as the
schema source. Tables are declared as Go structs under
ent/schema/; Atlas reads those declarations directly (via the
ent:// URL provider) and generates versioned migration files. The
SHC daemon applies the generated migrations/*.sql at startup via
the runner at core/db/migrate.go.
Atlas is a dev-time tool only — operators don’t need it installed on production hosts.
Layout
Section titled “Layout”ent/ generate.go # go generate ./ent entrypoint entc.go # codegen driver schema/ <table>.go # one file per table (30 schemas) <table>.go, <table>_create.go, … # generated typed client (don't edit)atlas.hcl # src = "ent://ent/schema"migrations/ 20260520000000_initial.sql # bootstrap migration for new installs atlas.sum # integrity manifest (checksums) embed.go # //go:embed *.sql for the runtimeent/schema/*.go is the source of truth. The Go schema definitions
drive both the typed CRUD client (used by every module’s repo via
core/entclient) AND the migration generation.
Making schema changes
Section titled “Making schema changes”- Edit
ent/schema/<table>.go(add / remove / change fields, edges, indexes). - Run
go generate ./entto regenerate the typed client. - Run
make migrate NAME=<slug>— Atlas materializes the ent declarations into a fresh sqlite dev DB, diffs it against the current migration head, and writesmigrations/<UTC-timestamp>_<slug>.sqlplus an updatedatlas.sum. (NAMEdefaults toautoif omitted.) - Review the generated SQL, commit alongside the schema change.
# Edit schema$EDITOR ent/schema/deployment.go
# Regenerate typed clientgo generate ./ent
# Generate migrationmake migrate NAME=add_deployment_priority
# Verifysqlite3 .shc/state/shc.db ".schema"Running migrations
Section titled “Running migrations”The daemon applies migrations automatically on startup — there is no make target for applying. For ad-hoc local runs use atlas directly:
atlas migrate apply --env local --url "sqlite://${PWD}/.shc/state/shc.db"Schema authoring conventions
Section titled “Schema authoring conventions”Every ent/schema/<table>.go:
- Type name = table CamelCase (
mesh_token→MeshToken,storage_provider_node→StorageProviderNode). Annotations()pins the SQL table name explicitly viaentsql.Annotation{Table: "<name>"}— Ent pluralizes by default.- VARCHAR(N) UUID PKs: redeclare via
field.String("id").MaxLen(N).Immutable(). - VARCHAR(N) non-UUID PKs: use
field.String("id").StorageKey("<col>").Immutable(). - Field names match column names verbatim (snake_case); Ent derives Go field names from those.
- Edges: parent declares
edges.To("children", Child.Type)withAnnotations(entsql.OnDelete(...)); child declaresedges.From("parent", Parent.Type).Ref("children").Field("col").Unique().
The canonical exemplar lives in ent/schema/kv.go.
Best practices
Section titled “Best practices”- Forward-only — Atlas does not generate down migrations and SHC does not roll back in production. Restore from a backup instead.
- Small migrations — one logical change per migration.
- Data migrations — separate from schema migrations when
possible. Schema migrations are SQL; data migrations belong in Go
startup code (
core/db/maintenance.gostyle). - No breaking changes — add columns as nullable first, migrate data, then add constraints in a follow-up migration.
SQLite limitations (both engines)
Section titled “SQLite limitations (both engines)”rqlite replicates plain SQLite statements over raft, so the same ALTER TABLE caveats apply on both engines:
ADD COLUMN,CREATE INDEX,DROP INDEX— supported directly.ALTER COLUMN,DROP COLUMN— require table recreation (create-new + copy + drop-old + rename pattern). Atlas’s declarative diff handles this for you automatically when you edit the Ent schema and runmake migrate.