From c92fbad94be27337d5e0d04b6751e0ab8c29a77e Mon Sep 17 00:00:00 2001 From: Sharang Parnerkar Date: Tue, 19 May 2026 12:01:37 +0200 Subject: [PATCH] =?UTF-8?q?feat(schema):=20M4.1=20=E2=80=94=20full=20tenan?= =?UTF-8?q?t=5Fregistry=20schema=20+=20migrate=20binary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PLATFORM_ARCHITECTURE.md §5c schema, end-to-end: enums: tenant_status (demo/trial/active/frozen/archived), tenant_kind (customer/demo), idp_kind (oidc/saml), tenant_project_status (active/archived) tables: tenants id/slug/name/status/kind/plan/erp_id/ stripe_id/trial_ends_at/contract_dates/ sales_owner tenant_projects sub-tenancy (GCP-Project style); opt-in via product manifest.supports_projects=true tenant_products tenant ↔ product matrix + JSONB config tenant_idp_config enterprise SSO (OIDC/SAML metadata + verified) api_keys argon2 hash + prefix + scopes + revoked_at; single source of truth across all products audit_log Retraced-compatible: actor/action/target/ product/metadata; indexed for cross-product filtering (PRODUCT_INTEGRATION_SPEC.md §8.4) triggers: updated_at auto-bump on every mutable table. fks: ON DELETE CASCADE for owned rows; SET NULL for audit_log so forensic history outlives the tenant delete. cmd/migrate (new binary): golang-migrate as a library with the migrations embedded via migrations/embed.go → embed.FS. Sub-commands: up / down / version / force. Ships as a self-contained binary; in prod it's the Orca init container per IMPLEMENTATION_PLAN.md §1.7. Dockerfile builds both binaries; the migrate one runs as the init step. Tests (require Docker; gated by -short): TestMigrate_upDownRoundTrip schema → 6 tables + 4 enums; down→empty; up-after-down succeeds (round-trip clean) TestSeed_canInsertAndQuery insert across every table; FK cascade works; audit_log SET-NULL keeps the row TestSlugConstraint tenant.slug regex rejects too-short / leading dash / trailing dash / uppercase / underscore Makefile: make migrate-up / down / down-all / version / create NAME=... make test-short → skip integration when Docker isn't around make build-migrate → just the migrator binary The handler-layer in-memory store is unchanged; M4.2 swaps it for the pgx-backed implementation against this schema. Refs: M4.1 --- Dockerfile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0645f05..d83fa9f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,20 @@ # Multi-stage build for tenant-registry. +# Produces two binaries: +# /tenant-registry — long-running API server +# /migrate — one-shot schema migrator (Orca init container in prod) FROM golang:1.24-alpine AS build WORKDIR /src -COPY go.mod ./ +COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /out/tenant-registry ./cmd/server +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /out/tenant-registry ./cmd/server && \ + CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /out/migrate ./cmd/migrate FROM gcr.io/distroless/static-debian12:nonroot WORKDIR / COPY --from=build /out/tenant-registry /tenant-registry +COPY --from=build /out/migrate /migrate USER nonroot:nonroot EXPOSE 8090 ENTRYPOINT ["/tenant-registry"]