Files
cgalo5758 8d05934e93 Add domains registry with claims and placements
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.
2026-07-24 21:25:40 -05:00

4.1 KiB

schema-namespacing

Purpose

Defines PostgreSQL schema separation per design Decisions 85/86/113: domain tables in per-module schemas, integration modules in schema-per-provider, shared infrastructure in public.

Requirements

Requirement: Integration modules get schema-per-provider

Each provider integration SHALL own its own PostgreSQL schema (e.g., fedwiki, stripe) containing only provider-specific state: projections and mappings of core entities, and provider-mechanism tables (e.g., fedwiki.site_swap_policy). Integration tables MAY hold foreign keys into core; core tables SHALL NOT hold foreign keys into integration schemas.

Scenario: FedWiki integration schema

  • WHEN the FedWiki integration is installed
  • THEN its tables SHALL exist in the fedwiki schema
  • AND its foreign keys SHALL reference core tables (e.g., core.workspaces)

Scenario: Integration decommissioning

  • WHEN an integration is decommissioned
  • THEN dropping its schema SHALL remove all provider-specific state
  • AND no core table SHALL be invalidated by the drop

Requirement: Domain tables live in the core schema

All domain tables SHALL reside in a single core schema. The database SHALL contain the following application schemas: core (domain), core-module schemas owned by core modules with their own migration stream (domains — the domains registry), and one schema per active integration (stripe, fedwiki). The public schema SHALL hold only shared extensions and functions (e.g., update_updated_at_column()).

The provider registry tables (providers, provider_operations, provider_states, outbox, webhook_events) are core infrastructure — the contract integrations register against — and SHALL live in core, not in any provider's schema.

Schema layout (as of the 2026-07-04 attribution sweep, amended by this change):

  • core — 38 end-state domain tables + the product_kinds view (formerly billing 14, entitlements 13, organization 6, identity 2, integration 5, as enumerated by the attribution sweep; product_entitlement_rules is created and later dropped within the old chain itself, per Decision 107)
  • domains — 3 tables (claims, placements, name_rules)
  • stripe — 9 tables (Stripe projections/mappings + provider_configs)
  • fedwiki — 2 tables (sites, site_swap_policy)

Scenario: Fresh install creates tables in core

  • WHEN migrations run against an empty database
  • THEN all domain tables SHALL exist in the core schema
  • AND no identity, organization, entitlements, billing, integration, cooperative, or audit schema SHALL exist
  • AND the public schema SHALL contain only the update_updated_at_column() function and the per-stream goose_db_version_<stream> ledgers (no shared goose_db_version table — see module-migrations)

Scenario: Registry tables live in the domains schema

  • WHEN the domains stream has run
  • THEN domains.claims, domains.placements, and domains.name_rules SHALL exist in the domains schema
  • AND no registry table SHALL exist in core or any integration schema

Requirement: search_path resolves the core schema without qualification

The database connection SHALL set search_path to core,public. Domain queries MAY use unqualified table names; core-module-schema and integration-schema queries SHALL use schema-qualified names (e.g., domains.claims, fedwiki.sites), and neither core-module schemas nor integration schemas SHALL appear in search_path.

Scenario: Unqualified query resolves to core

  • WHEN a query references products without schema qualification
  • THEN PostgreSQL SHALL resolve it to core.products

Scenario: Integration queries use explicit schema

  • WHEN a FedWiki query references fedwiki.sites
  • THEN PostgreSQL SHALL resolve it to the fedwiki schema regardless of search_path

Scenario: Registry queries use explicit schema

  • WHEN a registry query references domains.placements
  • THEN PostgreSQL SHALL resolve it to the domains schema regardless of search_path