Skip to content

Add multi-tenant protocol with isolation levels and quota management - #82

Merged
hotlong merged 2 commits into
mainfrom
copilot/add-tenant-schema-file
Jan 23, 2026
Merged

hotlong merged 2 commits into
mainfrom
copilot/add-tenant-schema-file

Conversation

Copilot AI commented Jan 23, 2026 •

Copy link
Copy Markdown
Contributor

Implements tenant protocol for SaaS deployments with configurable data isolation strategies and resource quotas.

Changes

  • src/system/tenant.zod.ts: Core tenant schema with three isolation modes

    • shared_schema: Row-level isolation (single DB, single schema)
    • isolated_schema: Schema-per-tenant (single DB, separate schemas)
    • isolated_db: Database-per-tenant (maximum isolation)
  • Quota management: maxUsers, maxStorage (bytes), apiRateLimit (requests/min)

  • Customization support: Arbitrary tenant-specific configuration via customizations record

Usage

import { TenantSchema, TenantIsolationLevel } from '@objectstack/spec/system';

const tenant = TenantSchema.parse({
  id: 'tenant_acme',
  name: 'Acme Corp',
  isolationLevel: 'isolated_schema',
  quotas: {
    maxUsers: 100,
    maxStorage: 10737418240, // 10GB
    apiRateLimit: 1000
  },
  customizations: {
    theme: 'dark',
    features: { analytics: true }
  }
});

Follows established patterns: Zod-first definitions, camelCase config keys, snake_case enum values, inferred TypeScript types.

Original prompt
  1. 多租户协议 (优先级: 高)

// 建议新增: src/system/tenant.zod.ts
export const TenantSchema = z.object({
id: z.string(),
name: z.string(),
isolationLevel: z.enum(['shared_schema', 'isolated_schema', 'isolated_db']),
customizations: z.record(z.any()).optional(),
quotas: z.object({
maxUsers: z.number().optional(),
maxStorage: z.number().optional(),
apiRateLimit: z.number().optional(),
}).optional(),
});
影响: SaaS 部署必需


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@vercel

vercel Bot commented Jan 23, 2026 •

Copy link
Copy Markdown

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

Project Deployment Review Updated (UTC)
spec Ready Ready Preview, Comment Jan 23, 2026 7:35am

Request Review

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Add multi-tenant schema implementation Add multi-tenant protocol with isolation levels and quota management Jan 23, 2026
Copilot AI requested a review from hotlong January 23, 2026 07:37
@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:system tests size/m labels Jan 23, 2026
@hotlong
hotlong marked this pull request as ready for review January 23, 2026 07:58
Copilot AI review requested due to automatic review settings January 23, 2026 07:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements a multi-tenant system protocol so SaaS deployments can declare how tenants are isolated and what quotas apply, with corresponding JSON Schemas and reference docs.

Changes:

  • Added TenantIsolationLevel, TenantQuotaSchema, and TenantSchema Zod definitions plus inferred types in src/system/tenant.zod.ts.
  • Added comprehensive Vitest coverage for tenant isolation, quotas, and customization behaviors in src/system/tenant.test.ts, and exported the new schema from src/system/index.ts.
  • Introduced JSON Schema artifacts (Tenant.json, TenantQuota.json, TenantIsolationLevel.json) and reference documentation pages for these types under content/docs/references/system.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/spec/src/system/tenant.zod.ts Defines the core tenant, isolation level enum, and quota schemas following existing Zod-first, camelCase/snake_case conventions.
packages/spec/src/system/tenant.test.ts Adds unit tests validating allowed/denied isolation levels, quota constraints, and required tenant properties.
packages/spec/src/system/index.ts Re-exports the new tenant schema module from the system barrel file.
packages/spec/json-schema/TenantQuota.json JSON Schema for the tenant quota object, matching the Zod quota fields and constraints.
packages/spec/json-schema/TenantIsolationLevel.json JSON Schema enum for tenant isolation levels aligning with the Zod enum.
packages/spec/json-schema/Tenant.json JSON Schema for the tenant object, including isolation level, customizations, and quotas.
content/docs/references/system/TenantQuota.mdx Reference doc for the TenantQuota schema (note: table header currently uses an extra leading pipe and should be normalized).
content/docs/references/system/TenantIsolationLevel.mdx Reference doc documenting the allowed tenant isolation level values.
content/docs/references/system/Tenant.mdx Reference doc for the Tenant schema (note: properties table uses `

Comment on lines +8 to +9
| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |

Copilot AI Jan 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The markdown table header has an extra leading pipe (|| Property / || :---), which will render an empty first column and is inconsistent with other reference docs (e.g., Organization.mdx). Drop the extra pipe so the header rows start with a single | to match the established table format.

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +9
| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |

Copilot AI Jan 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The properties table starts each row with a double pipe (|| ...), which introduces an empty column and differs from the standard reference-table format used elsewhere (e.g., Organization.mdx). Please change these rows to start with a single | so the table renders correctly and consistently.

Copilot uses AI. Check for mistakes.
@hotlong
hotlong merged commit 1320d12 into main Jan 23, 2026
17 checks passed
os-justin pushed a commit that referenced this pull request Sep 10, 2026
`os generate <type> <name>` accepted any name at all until #16724 taught it
to refuse names whose emitted TypeScript does not parse. It now also refuses,
ahead of that check and ahead of every derivation, any name the object-`name`
declaration in `@objectstack/spec` rejects — maintainer ruling, decision batch
#82, option A: a gate, no sanitiser, no third charset.

The judge is the schema itself (`ObjectSchema.shape.name`), so the charset is
asked rather than transcribed, and the refusal quotes the schema's own message
so the rule the author is shown is the rule that judged them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DapQyvYrFb1MxSYe7BL2nt
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 17, 2026
…for an object name (objectstack-ai#17408)

* feat(cli)!: refuse a generate name outside spec's object-name charset

`os generate <type> <name>` accepted any name at all until objectstack-ai#16724 taught it
to refuse names whose emitted TypeScript does not parse. It now also refuses,
ahead of that check and ahead of every derivation, any name the object-`name`
declaration in `@objectstack/spec` rejects — maintainer ruling, decision batch
objectstack-ai#82, option A: a gate, no sanitiser, no third charset.

The judge is the schema itself (`ObjectSchema.shape.name`), so the charset is
asked rather than transcribed, and the refusal quotes the schema's own message
so the rule the author is shown is the rule that judged them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DapQyvYrFb1MxSYe7BL2nt

* test(cli): measure the parse-check layer through a name that reaches it

The objectstack-ai#16726 charset gate answers first for `foo.bar`, so objectstack-ai#16541's pin now
measures its own subject through `class` — inside the charset, refused by the
compiler — and keeps every `foo.bar` assertion that is still about the command.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DapQyvYrFb1MxSYe7BL2nt

* chore(changeset): record the ADR-0087 disposition for the generate name gate

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DapQyvYrFb1MxSYe7BL2nt

---------

Co-authored-by: Claude <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 972bcde2 Deployed Jan 23, 2026 by vercel[bot]
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/m tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants