# P3-WP10 多租户与混合交付底座实施计划 > **For agentic workers:** 本计划只构建可验证的本地工程底座;不得把它表述为已批准的 SaaS、共享控制面、企业 UAT 或生产就绪。 **目标:** 在不改变默认私有化单租户交付的前提下,提供版本化、服务端派生并在 PostgreSQL 中强制隔离的租户上下文、生命周期、配额、审计和资源命名空间契约。 **架构:** 请求或后台任务只可从已批准的服务端交付配置/已持久化路由映射获得 `TenantContext`,永不接受 body/query 自报的 `tenant_id`。所有 WP10 新的持久化事实由 `SECURITY DEFINER` 的闭合 JSONB 网关写入,并由 restricted runtime 角色直连 DML 默认拒绝;租户读路径以 RLS 和事务内 `dataops.tenant_id` 设置进行限制。Neo4j、对象存储、缓存和搜索现有部署没有可验证的原生租户隔离,故只提供严格命名空间适配器、manifest 和攻击测试,实际外部服务隔离仍为 `TBD_EXTERNAL`。 **技术栈:** Flask、SQLAlchemy、Alembic、PostgreSQL RLS/`SECURITY DEFINER`/DB clock、pytest、Docker PostgreSQL。 --- ## 只读盘点(2026-08-17) 当前迁移 head 是 `20260813_500`(`20260813_500_runtime_control_claims.py`,source/deployment 字节一致)。工作树已有 WP05–WP09 和用户未提交变更,WP10 不重写它们。 | 面向 | 已有可复用覆盖 | WP10 缺口/本计划闭合 | | --- | --- | --- | | 身份/权限 | `enterprise_identity.py` 提供 fail-closed IdP 映射;`permissions.py` 提供 API 默认拒绝 | 租户域名/路由映射、服务端派生 delivery mode、管理员显式 scope | | 模型/预算/审计 | WP09 已有部分 `tenant_id`、DB-time lease/fence、append-only invocation audit | 这些不是全局 tenant model;新增租户注册表、通用配额、tenant audit 和跨服务传播契约 | | 任务/事件 | 既有 outbox 和 WP05/WP06 任务实现 | 背景任务必须有已验证 tenant scope、冻结时拒绝和事件 envelope 契约 | | 数据库 | WP06/WP09 使用 owner/runtime 角色与受控网关 | 通用 tenant 表强制 RLS、runtime 无直 DML、闭合 tenant gateway、真实两连接验证 | | 图/对象/缓存/索引 | 图、知识缓存和 Compose Neo4j/MinIO 现存 | 无可复核的原生多租户隔离;只增加 namespace adapter/manifest/digest/escape 拒绝,外部 service 验证保留 TBD | | 备份/恢复/插件 | Compose backup 脚本和 WP09 provider contract 已有 | tenant backup/restore/audit-export manifest、approval/hold/fence 及 default-disabled provider 契约;不建设插件仓库 | ## 执行任务 ## 安全整改收口(2026-08-17,工程基线完成但 activation blocked) - 独立复验已证明 501–503 不能作为授权边界;504 保留为历史后续 revoke,legacy repository sealed,不再允许 shared runtime 通过 payload `tenant_id` 或 session GUC 触发写入。 - 505–520 是唯一有效的本地工程控制链:dedicated `dataops_tenant_control` identity、持久 membership+route、DB-clock one-shot claim、claim-bound quota/lifecycle、private-only provision、持久 outbox/worker scope lookup、独立 approver fact 与真实签发 API、只读 status/audit/manifest gateway、final downgrade security fence,以及有界 worker fail/retry terminal state。 - 新 API 为 `/api/system/tenant/provisions`、`quota-claims`、六个 lifecycle action 以及 `status/audit/manifests`;全部由 Flask RBAC、no-store、closed schema 和 server-derived identity/route 保护。OpenAPI 同步,shared/SaaS 仍不可激活。 - 当前实际 head `20260817_525`;独立复验已通过,真实本地验证包括 live-data `525 -> 520` downgrade attack、空库 `525 -> 520 -> 525`、restricted `480 -> 525`、旧 lifecycle gateway 低权拒绝、独立 control/runtime/approver login、两连接 provision/worker race、DB-clock expiry/restart replay、Flask provision/approval/lifecycle/read/outbox 与 quota worker reserve/settle/release。工程状态为 `ENGINEERING_BASELINE_COMPLETE_MULTI_TENANCY_ACTIVATION_BLOCKED`。 ### Task 1: 写入基础行为测试并记录 RED **文件:** - Create: `tests/core/system/test_wp10_tenant_context.py` - Create: `tests/security/test_wp10_tenant_boundaries.py` - [ ] 写失败测试:私有模式只能从 server approved default 解析;共享/saas 未批准时拒绝;请求 payload、后台任务和管理员缺 scope 均拒绝;域名映射、Unicode 同形/路径/URL 逃逸、跨租户 namespace 均拒绝。 - [ ] 运行 `.venv/bin/pytest tests/core/system/test_wp10_tenant_context.py tests/security/test_wp10_tenant_boundaries.py -q`,确认因 WP10 模块不存在而 RED。 ### Task 2: 实现版本化 TenantContext 与资源 namespace 契约 **文件:** - Create: `app/core/system/tenant_context.py` - Create: `app/core/system/tenant_resources.py` - Modify: `app/core/system/permissions.py` - [ ] 仅实现 Task 1 所需最小模型:closed `DeliveryMode`、`TenantContext`、服务端 resolver、后台/管理员 scope validator、统一 namespace builder。 - [ ] namespace 仅接受 ASCII canonical tenant/resource token;对象、图、缓存、索引、keys、connectors、models、plugins、backup、audit export 均产生不可混用的前缀或 manifest;provider 默认 disabled。 - [ ] 重跑 Task 1 测试,确认 GREEN。 ### Task 3: 为 lifecycle/quota/database boundary 先写 RED **文件:** - Create: `tests/core/system/test_wp10_tenant_lifecycle.py` - Create: `tests/integration/test_wp10_tenant_postgres.py` - [ ] 覆盖 `provision -> active -> frozen -> recovering -> active -> deletion_candidate -> deleted`,审批/hold/backup/retention、幂等 replay、changed replay、DB clock lease/fence、冻结任务拒绝、negative/bool/overflow quota 拒绝及跨租户不可借用。 - [ ] 真实 PostgreSQL 测试覆盖 low-privilege migrator、runtime direct DML/trigger/owner/SET ROLE 拒绝、RLS cross-tenant read/write 拒绝、two connection single quota winner、gateway audit append-only 与精确 fixture 清理。 - [ ] 运行上述两文件,确认其因实现/迁移不存在而 RED。 ### Task 4: 实现 PostgreSQL 闭合网关、RLS 与 repository **文件:** - Create: `migrations/versions/20260817_501_tenant_foundation.py` - Create: `app/core/system/tenant_repository.py` - Modify: `app/core/edge_gateway/runtime_roles.py` - Mirror: `deployment/migrations/versions/20260817_501_tenant_foundation.py`, `deployment/app/core/system/tenant_repository.py`, `deployment/app/core/system/tenant_context.py`, `deployment/app/core/system/tenant_resources.py`, `deployment/app/core/edge_gateway/runtime_roles.py` - [ ] 使用 migration 500 作为唯一 down revision;不修改已应用迁移。创建 owner/runtime roles 的初始化前置检查,Tenant registry/routes/quotas/reservations/lifecycle/audit/manifest 表,并对 tenant-scoped 表 `ENABLE/FORCE ROW LEVEL SECURITY`。 - [ ] runtime 无 direct DML、TRUNCATE、trigger/owner 修改和未经授权 `SET ROLE` 权限;只可执行 owner 控制的 closed-schema `tenant_foundation_write`,该函数以 DB clock、advisory lock、lease/fence 和原子 reserve/settle/release 运行。 - [ ] 只保存摘要/manifest;拒绝 raw secret、URL/path traversal、Unicode 同形和跨 tenant ref。将 source/deployment 逐字同步。 - [ ] 重跑 Task 3 的 unit/integration 测试,确认 GREEN;使用 Docker PostgreSQL 做 `500 -> 501 -> 500 -> 501`,保留独立连接输出和 head 证据。 ### Task 5: 接入最小 API/事件契约并同步 OpenAPI **文件:** - Create: `app/api/system/tenant_foundation.py` - Modify: `app/api/system/__init__.py`, `app/core/events/outbox.py`, `docs/architecture/OPENAPI.yaml` - Mirror corresponding deployment paths - Create: `tests/test_wp10_tenant_api.py` - [ ] 测试先行:认证/既有 RBAC 后的 status/read endpoint 不接收 client tenant id;无 scope/no-store/closed response;事件 envelope 无 tenant scope 被拒绝。 - [ ] 添加仅供本地工程验证的最小 status 与 audit/manifest read contract,不开放 SaaS provisioning、真实 key/provider、插件上传或管理 UI。 - [ ] 重新生成 OpenAPI(或明确记录无生成差异)并运行 API/事件定向测试。 ### Task 6: 交付证据与状态台账 **文件:** - Create: `docs/architecture/ADR_P3_WP10_HYBRID_DELIVERY.md` - Create: `docs/runbooks/P3_WP10_TENANT_FOUNDATION_OPERATIONS.md` - Create: `docs/validation/P3_WP10_FAILURE_INJECTION.json` - Create: `docs/validation/P3_WP10_MULTI_TENANCY_EVIDENCE.md` - Modify: `docs/phase3/P3_WP00_REQUIREMENTS.json`, `docs/DATAOPS_PHASE3_6_MONTH_DEVELOPMENT_PLAN_20260802.md` - [ ] 记录 RED/GREEN、真实 PostgreSQL head/round-trip/runtime attack/two-connection/restart-fence、OpenAPI/parity/secret-scan、私有单租户兼容。 - [ ] 状态最高为 `ENGINEERING_BASELINE_COMPLETE_MULTI_TENANCY_ACTIVATION_BLOCKED`;`multi_tenancy`、SaaS/共享控制面决策、租户数、隔离等级、容量/交付 profile、真实外部 graph/object/cache/index/provider 与企业 UAT/production 均保持 `TBD_EXTERNAL`。 ## 验证与不可越过边界 - 仅运行 WP10 与直接 identity/permissions/task/event/audit/backup/knowledge/graph/storage/private delivery 相关测试;不运行全量回归。 - 静态门禁:WP10 Ruff、py_compile、JSON、OpenAPI、source/deployment/migration byte parity、`git diff --check`、WP10 scoped secret scan。 - 迁移和 Docker PostgreSQL 的数据库、角色与精确命名空间清理为本地工程证据;不是独立审计、企业 UAT 或生产验收。 - **外部门禁:** `multi_tenancy = TBD_EXTERNAL`,尚缺 SaaS/共享控制面决策、租户数量、隔离等级、容量和混合交付 profile 的书面批准。因此条件包未正式激活。