Domain names become an allocatable resource with one authority. A new core module (schema `domains`, own migration stream between core and the integrations) owns claims — a DNS node plus its whole subtree, mutually disjoint: operator shared-domain roots, member claims carved from them, and bring-your-own names proven by TXT verification — and placements, which bind a name inside a claim to a provider slug and resource ref. Verification moves to the claim and decouples from creation. A member proves control of a domain once; afterwards every name inside it places instantly, wildcard-CNAME friendly, with no further DNS work. The claim workflow activates the claim and stops — it no longer creates a site — so the sites list offers a one-click create once a domain verifies. /domains/ask answers from placements and is registered by core rather than the FedWiki adapter; its HTTP contract is unchanged. A configured `domains-ask-fallback-url` forwards names the registry does not know to a legacy answerer, the strangler seam wiki.cafe's migration needs; a name the registry knows but has archived is refused locally. FedWiki's create saga reserves the name before the farm call, carrying a workflow-minted site id so retries are idempotent, and compensates on failure. Sync places only names it owns, never stealing a member's; lifecycle transitions and the retention purge maintain servability. An unconditional boot pass seeds operator roots, releases orphaned placements, and adopts pre-existing sites — grandfathering member-owned external domains shortest-name-first, and skipping name policy, so a live single-letter site cannot lose its certificate. Members manage domains at /domains: claims with verification status, DNS records including an optional wildcard row, check-now, cancel, release. Name policy (reserved, blocked, premium, plus a single-letter guard) is operator data; refusals collapse to a plain "unavailable" so the console never becomes an oracle for who holds what. BREAKING (pre-release): `fedwiki.custom_domain_verifications` and `sites.is_custom_domain` are dropped, the flag now derived from the placement's claim kind; resource key `fedwiki_custom_domains` migrates to the platform-owned `external_domain_claims`; running verify-custom-domain workflows must be terminated before deploy.
7.8 KiB
module-migrations
Purpose
Defines per-module migration infrastructure: each module owns its schema via embedded migration files, assembled into a global sequence by the db package.
Requirements
Requirement: Dependency-ordered migration execution
RunMigrations SHALL apply the core stream first, then core-module streams
(domains), then integration streams. Integration streams SHALL be mutually
order-independent, but MAY depend on core and core-module objects (their FKs and
grants reference only core and domains). Each stream SHALL be applied by its own
goose run against its own ledger. Execution SHALL remain idempotent. Rollback SHALL
undo one step of the most-recently-populated stream in reverse source order
(integrations before core-module streams before core); status SHALL report each
stream's ledger separately.
Scenario: Migrations run in dependency order
- WHEN
RunMigrationsexecutes on an empty database - THEN the core stream SHALL complete before the domains stream begins
- AND the domains stream SHALL complete before any integration stream begins
Scenario: Idempotent migration execution
- WHEN
RunMigrationsexecutes twice against the same database - THEN the second run SHALL apply nothing and SHALL NOT error
Scenario: Rollback steps integrations back before core
- WHEN
RollbackMigrationis invoked on a fully migrated database - THEN it SHALL roll back one step of the last integration stream with applied migrations, not the core or domains stream
Requirement: Per-module sqlc configuration
Each Go package with queries SHALL keep its own sqlc.yaml, with schema:
paths pointing at the new baseline migration files (plus
internal/db/sqlc_schemas.sql declaring exactly core, domains, stripe,
fedwiki). A package whose queries join another module's tables SHALL include that
module's migrations in its schema: list (the fedwiki store includes the domains
migrations). Rename maps SHALL be pinned so that generated Go type names are
unchanged by the schema consolidation.
Scenario: Billing package sqlc config after consolidation
- WHEN sqlc generates for
internal/billing - THEN queries reference
core.*tables - AND generated type names are identical to their pre-consolidation names
Scenario: FedWiki sqlc config with schema awareness
- WHEN sqlc generates for the fedwiki store
- THEN provider tables resolve in the
fedwikischema, core references resolve incore, and registry joins resolve indomains
Requirement: Cross-module FK references in migrations use schema-qualified notation
When a module migration references a table in another module, the FK constraint SHALL use schema-qualified notation (e.g., REFERENCES identity.persons(person_id)) (Decision 114). Intra-module FK references SHALL omit the schema prefix.
Scenario: Organization migration references identity schema
- WHEN the organization module's init migration creates a FK to
persons - THEN the FK SHALL use
REFERENCES identity.persons(person_id) - AND intra-module FKs like
REFERENCES organizations(org_id)SHALL omit the schema prefix
Scenario: Billing migration references organization and identity schemas
- WHEN the billing module's migration creates FKs to
organizationsandpersons - THEN the FKs SHALL use
REFERENCES organization.organizations(org_id)andREFERENCES identity.persons(person_id)
Requirement: Three ordered migration streams
Migrations SHALL be organized as the core stream (internal/db/migrations/), plus
core-module streams registered explicitly in internal/migrate (domains from
internal/domains/migrations/), plus one stream per registered integration
(internal/integrations/<slug>/migrations/). migrate.Sources() SHALL enumerate
integration streams from the integration registry, after core and core-module
streams. Adding a new integration SHALL add its stream via registration alone; the
core stream SHALL always run first, core-module streams next, and integration
streams SHALL be mutually order-independent.
Scenario: Fresh database migrates core first
- WHEN migrations run against an empty database
- THEN the core stream SHALL apply before the domains stream and any integration stream
- AND integration-stream foreign keys and grants into
coreanddomainsSHALL resolve because their targets already exist
Scenario: Adding a new integration
- WHEN a new provider integration is registered
- THEN its migration stream SHALL be picked up from the registry without
edits to
internal/migrate - AND existing streams' ledgers and version numbers SHALL be unaffected
Requirement: Cross-schema FK direction is integration to core only
Cross-schema foreign keys SHALL follow the dependency direction only:
integration-stream migrations MAY declare foreign keys referencing core tables (and
MAY reference domains objects in grants or data migrations); the domains stream MAY
reference core only; core-stream migrations SHALL NOT reference any other
application schema.
Scenario: Stripe mapping table references core
- WHEN a stripe-stream migration creates a mapping table
- THEN it MAY declare
REFERENCES core.products(product_id)
Scenario: Core migration attempts integration reference
- WHEN a core-stream migration is reviewed
- THEN any reference to
stripe.*,fedwiki.*, ordomains.*SHALL be rejected
Scenario: Domains migration attempts integration reference
- WHEN a domains-stream migration is reviewed
- THEN any reference to an integration schema SHALL be rejected
Requirement: Baseline migration creates core schema and roles
The first core-stream migration SHALL create the core schema, the core
role triple, the core table grants, the member_console login role's
membership in core_writer, all 38 end-state domain tables (plus the
product_kinds view) in FK-dependency order, and the shared public
functions. Integration schemas, role triples, grants, and member_console
memberships SHALL NOT be created by the core stream — each integration's
own baseline creates them. The combined baselines SHALL remain structurally
complete against the superseded 10-source chain — the same object set
(tables, columns, constraints, indexes, triggers, functions, net grant
effects, seed rows) modulo the rename map and the documented drop list.
Scenario: Baseline on fresh install
- WHEN all baseline migrations run on an empty database
- THEN the normalized object set SHALL equal that of a database migrated through the superseded per-module chain (reachable via git history), after applying the five-schemas→core rename and the documented drop list
Scenario: Core baseline creates no integration objects
- WHEN only the core stream has run
- THEN no
stripe_*orfedwiki_*role and nostripeorfedwikischema SHALL exist
Requirement: Per-stream version ledgers
Each migration stream SHALL track its applied versions in its own goose
ledger table, named goose_db_version_<stream> (_core, _domains, _fedwiki,
_stripe). Streams SHALL keep their native 00001… file numbering; no
global version namespace SHALL exist, and a stream's version numbers SHALL
be independent of its position in the source list.
Scenario: Fresh install creates one ledger per stream
- WHEN migrations run against an empty database
- THEN
goose_db_version_core,goose_db_version_domains,goose_db_version_fedwiki, andgoose_db_version_stripeSHALL exist - AND no shared
goose_db_versiontable SHALL be created
Scenario: Adding an integration renumbers nothing
- WHEN a new integration stream is registered in
migrate.Sources() - THEN existing streams' ledgers and version numbers SHALL be unchanged
- AND the new stream SHALL start at its own version 1