Skip to content

Refactor multi-tenant architecture to per-environment database isolation#1186

Merged
xuyushun441-sys merged 2 commits intomainfrom
copilot/refactor-database-isolation-model
Apr 19, 2026
Merged

Refactor multi-tenant architecture to per-environment database isolation#1186
xuyushun441-sys merged 2 commits intomainfrom
copilot/refactor-database-isolation-model

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 19, 2026

The v3.4/v4.0 tenant model provisions one DB per organization and separates environments via an env_id column. This couples schema evolution across dev/test/prod, makes backup/DR and solution publishing fragile, and leaves a single missing WHERE env_id predicate away from cross-environment data corruption. Turso/libSQL/Neon/D1 have made per-environment databases essentially free, so the isolation boundary should be physical, not logical.

This PR lands the v4.1 protocol + service foundation for per-environment database isolation, with a hard Control Plane ↔ Data Plane split, a v4.x deprecation shim, and a v5.0 migration path. See docs/adr/0002-environment-database-isolation.md for the full rationale.

Protocol (packages/spec/src/cloud/environment.zod.ts)

  • New Zod schemas: Environment, EnvironmentDatabase (1:1 with environment), DatabaseCredential (rotatable, active/rotating/revoked), EnvironmentMember (owner/admin/maker/reader/guest), plus EnvironmentType/Status/Role enums and ProvisionEnvironment* / ProvisionOrganization* request-response schemas.
  • TenantDatabaseSchema tagged @deprecated with a pointer to the migration.

Control-plane objects (packages/services/service-tenant/src/objects/)

  • sys_environment — UNIQUE (organization_id, slug), enforces one default env per org.
  • sys_environment_database — UNIQUE environment_id, exactly one physical DB per environment.
  • sys_database_credential — N:1 with the DB mapping; supports rotation without touching addressing.
  • sys_environment_member — UNIQUE (environment_id, user_id); per-environment RBAC.
  • All sys_-prefixed, every field .describe()-annotated, every uniqueness constraint explicit.

Service (packages/services/service-tenant/src/environment-provisioning.ts)

  • EnvironmentProvisioningService with provisionOrganization(), provisionEnvironment(), rotateCredential().
  • Pluggable EnvironmentDatabaseAdapter keyed by driver (turso shipped; libsql/sqlite/postgres plug in without core changes) and pluggable SecretEncryptor for KMS integration.
  • Default-environment invariant enforced at the service layer before any physical allocation.
const svc = new EnvironmentProvisioningService({
  controlPlaneDriver,
  adapters: [new TursoEnvironmentAdapter(tursoConfig)],
  encryptor: kmsEncryptor,
});

// Bootstrap a new org → creates `prod` env + dedicated DB + encrypted credential atomically
await svc.provisionOrganization({ organizationId: 'org_1', createdBy: 'user_1' });

// Later: allocate a sandbox on its own physical DB
await svc.provisionEnvironment({
  organizationId: 'org_1', slug: 'sandbox', envType: 'sandbox', createdBy: 'user_1',
});

Compatibility & migration

  • createTenantPlugin() registers the four new objects out of the box; sys_tenant_database stays registered as a v4.x shim (opt out via registerLegacyTenantDatabase: false) and is scheduled for removal in v5.0.
  • packages/services/service-tenant/migrations/v4-to-v5-env-migration.ts — idempotent, non-destructive: reuses each org's existing physical DB as its new prod environment DB (no data movement), re-encrypts the legacy auth token with the current KMS key, archives the legacy row.
  • Also fixed a latent Field.picklistField.select bug in the legacy sys_tenant_database / sys_package_installation object defs that would have crashed plugin init once exercised.

Tests

  • 22 new tests across three files covering Zod round-trips + rejections, organization bootstrap, environment creation, default-environment invariant, adapter routing (including a custom postgres adapter), encryption hook, credential rotation, and object-definition invariants (sys_ naming, UNIQUE indexes, .describe() coverage).

Follow-ups (out of scope for this PR)

  • better-auth session active_environment_id integration.
  • Per-environment quota enforcement via sys_quota; solution publishing on env DBs via sys_solution_history.
  • v5.0: remove the legacy shim and run the migration on production tenants.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectstack-demo Ready Ready Preview, Comment Apr 19, 2026 7:59am
spec Ready Ready Preview, Comment Apr 19, 2026 7:59am

Request Review

…v5 migration skeleton

Agent-Logs-Url: https://github.com/objectstack-ai/framework/sessions/52606e79-56bc-48cb-8a2a-da808a53104d

Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor multi-tenant architecture to per-environment databases Refactor multi-tenant architecture to per-environment database isolation Apr 19, 2026
Copilot AI requested a review from xuyushun441-sys April 19, 2026 07:57
@github-actions github-actions bot added documentation Improvements or additions to documentation tests size/xl labels Apr 19, 2026
@xuyushun441-sys xuyushun441-sys marked this pull request as ready for review April 19, 2026 08:07
@xuyushun441-sys xuyushun441-sys merged commit b78b426 into main Apr 19, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

重构多租户架构为“每环境单独数据库”高隔离模型

2 participants