Multi-Tenancy
Greentic 从设计之初就是为 多租户部署 而构建的。平台的每一个方面都支持租户隔离,使单个部署能够安全地服务多个组织。
Workspace└── Tenant (organization) └── Environment (prod, staging, dev) └── Team (department, group) └── Channel (messaging provider instance) └── Session (user conversation)TenantCtx
Section titled “TenantCtx”TenantCtx 结构体会贯穿所有操作:
pub struct TenantCtx { pub tenant_id: String, pub env_id: String, pub team_id: Option<String>,}每次 API 调用、flow 执行和数据访问都限定在某个 TenantCtx 范围内。
tenant: id: acme name: "ACME Corporation" settings: timezone: "America/New_York" language: "en-US"
environments: - id: prod name: "Production" - id: staging name: "Staging"Team 定义
Section titled “Team 定义”team: id: support name: "Customer Support" tenant_id: acme
channels: slack: provider: messaging-slack config: workspace_id: "T123456" channel_id: "C789012"
telegram: provider: messaging-telegram config: chat_id: "-1001234567890"Bundle 配置
Section titled “Bundle 配置”tenants: acme: name: "ACME Corporation" teams: support: name: "Customer Support" channels: slack: provider: messaging-slack telegram: provider: messaging-telegram
sales: name: "Sales Team" channels: teams: provider: messaging-teams
bigcorp: name: "BigCorp Inc." teams: helpdesk: channels: webchat: provider: messaging-webchatSession 存储
Section titled “Session 存储”Sessions 按租户上下文隔离:
sessions/├── acme/│ ├── prod/│ │ ├── support/│ │ │ ├── session_001.cbor│ │ │ └── session_002.cbor│ │ └── sales/│ │ └── session_003.cbor│ └── staging/│ └── support/│ └── session_004.cbor└── bigcorp/ └── prod/ └── helpdesk/ └── session_005.cbor工作内存(state)按每个 session 进行作用域隔离:
// State key formatlet key = format!( "state:{}:{}:{}:{}", tenant_ctx.tenant_id, tenant_ctx.env_id, tenant_ctx.team_id.unwrap_or("default"), session_id);NATS Subject 隔离
Section titled “NATS Subject 隔离”消息路由使用带租户作用域的 subjects:
greentic.messaging.ingress.{env}.{tenant}.{team}.{channel} │ │ │ │ │ │ │ └─ Channel ID (slack, telegram) │ │ └──────── Team ID │ └─────────────── Tenant ID └────────────────────── Environment (prod, staging)Secret 隔离
Section titled “Secret 隔离”Secrets 会按租户范围进行存储和读取:
// Secret retrieval includes tenant contextlet secret = secrets_client .get_secret(&tenant_ctx, "api_key") .await?;Secret 命名空间
Section titled “Secret 命名空间”secrets/├── global/ # Platform-wide secrets│ └── signing_key├── acme/ # Tenant: ACME│ ├── slack_bot_token│ ├── openai_api_key│ └── teams/│ └── support/│ └── webhook_secret└── bigcorp/ # Tenant: BigCorp └── telegram_bot_tokenFlow 隔离
Section titled “Flow 隔离”每个租户都可以拥有不同的 flows 和配置:
name: acme_support_flowversion: "1.0"
# ACME-specific support flownodes: - id: greet type: reply config: message: "Welcome to ACME Support! How can I help?"name: bigcorp_helpdesk_flowversion: "1.0"
# BigCorp-specific helpdesk flownodes: - id: greet type: reply config: message: "BigCorp Helpdesk here. What's your issue?"Component 访问
Section titled “Component 访问”Components 会接收 TenantCtx,并且必须遵守边界:
impl Guest for MyComponent { fn process(input: Input, ctx: &TenantCtx) -> Output { // Verify tenant has access to requested resource if !has_permission(ctx, &input.resource_id) { return Output::error("Access denied"); }
// Process with tenant scope process_for_tenant(ctx, input) }}API 访问
Section titled “API 访问”REST API 会强制执行租户作用域:
GET /api/v1/sessionsAuthorization: Bearer <token>X-Tenant-ID: acmeX-Team-ID: support每个租户一个 Greentic 实例:
┌─────────────────┐│ Greentic (ACME) │└─────────────────┘
┌─────────────────┐│ Greentic (BigCorp)│└─────────────────┘共享式多租户
Section titled “共享式多租户”多个租户共享同一套基础设施:
┌───────────────────────────────────┐│ Greentic Instance ││ ┌─────────┐ ┌─────────────────┐││ │ ACME │ │ BigCorp │││ │ (prod) │ │ (prod) │││ └─────────┘ └─────────────────┘││ ┌─────────┐ ┌─────────────────┐││ │ ACME │ │ BigCorp │││ │(staging)│ │ (staging) │││ └─────────┘ └─────────────────┘│└───────────────────────────────────┘混合使用专属资源和共享资源:
┌───────────────────────────────────┐│ Shared Greentic Instance ││ ┌─────────┐ ┌─────────────────┐││ │ Small │ │ Medium │││ │ Tenants │ │ Tenants │││ └─────────┘ └─────────────────┘│└───────────────────────────────────┘
┌───────────────────────────────────┐│ Dedicated: Enterprise Tenant ││ (Custom SLA, dedicated resources)│└───────────────────────────────────┘- 始终传递 TenantCtx - 不要硬编码 tenant ID
- 在边界处校验 - 在 API 和 component 层检查租户访问权限
- 使用带租户作用域的日志 - 所有日志项都包含 tenant ID
- 隔离 secrets - 不要在租户之间共享 secrets
- 测试隔离性 - 验证一个租户无法访问另一个租户的数据
- 按租户监控 - 按租户跟踪使用情况和错误