Files
test-doc/Gitea知识库/v1.1-技术栈与详细实施设计.md
2026-08-11 15:40:11 +08:00

559 lines
33 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Gitea知识库 v1.1 技术栈与详细实施设计
date: 2026-08-10
type: 设计spec
status: draft
content_status: discussion
owner: Verlit
last_updated_at: 2026-08-10
last_updated_by: Codex
hash: sha256:943f3d865bf228e8adef23b6c9064079f0a3573ada0ff9fbae2560defc0b727b
hash_scope: Markdown 正文(从一级标题开始至文件末尾)的 UTF-8 SHA-256
belongs_to:
- "[[3-业务线/Gitea知识库/_context|Gitea知识库]]"
depends_knowledge:
- "[[3-业务线/Gitea知识库/公司共享 Context 项目仓库发布方案 v1|公司共享 Context 项目仓库发布方案 v1]]"
- "[[3-业务线/Gitea知识库/公司共享 Context MCP-first 身份与审计方案 v1.1|公司共享 Context MCP-first 身份与审计方案 v1.1]]"
- "[[3-业务线/Gitea知识库/v1-工程构建规格|v1.1 工程构建规格]]"
- "[[3-业务线/Gitea知识库/v1-开发任务分解|v1.1 开发任务分解]]"
source_refs:
- "https://developers.openai.com/codex/codex-manual.md"
- "https://code.claude.com/docs/en/mcp"
- "https://code.claude.com/docs/en/permissions"
- "https://github.com/modelcontextprotocol/go-sdk/blob/main/docs/protocol.md"
- "https://github.com/modelcontextprotocol/go-sdk/releases/tag/v1.7.0"
- "https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http"
- "https://modelcontextprotocol.io/docs/tutorials/security/authorization"
- "https://go.dev/doc/devel/release"
- "https://www.postgresql.org/support/versioning/"
- "https://open.feishu.cn/document/authentication-management/access-token/get-user-access-token"
- "https://open.feishu.cn/document/server-docs/contact-v3/user/field-overview"
- "https://docs.gitea.com/usage/permissions"
- "https://github.com/go-gitea/gitea/releases/tag/v1.26.2"
---
# Gitea知识库 v1.1 技术栈与详细实施设计
## 1. 结论
本项目可以确定技术栈并拆解到可下发的开发与实施步骤。推荐采用“一个模块化 Go 代码库、两个运行角色、一个 PostgreSQL 控制数据底座、一个独立 Gitea、一个远程 MCP 用户入口”的方案:
```text
Agent Host
→ 飞书 OAuth 登录
→ 远程 MCP Gateway
→ Identity/AuthZ/Audit 中间件
→ Read / Build / Validate / Release 业务模块
→ Gitea 私有项目仓库
→ 不可变 release + 唯一 current
```
本方案不建设自定义业务 Web 页面。HTTP 仅用于 MCP transport、OAuth 协议回调、健康检查和内部运维端点,不提供发布驾驶舱、权限后台或审计后台。
Agent 产品不进入业务核心。Codex、Claude Code(本文把用户所称 `CC` 按 Claude Code 记录)和后续 Agent 都通过统一兼容层调用同一个远程 MCP;服务端只信任标准 OAuth、框架确认凭证和受控 Selection Manifest,不信任具体 Agent 的品牌或模型声明。若 `CC` 实际指其他客户端,只新增适配器,不改变核心方案。
## 2. 推荐技术栈
| 层 | 确定方案 | 当前基线 | 选择理由 | 明确不采用 |
|---|---|---|---|---|
| 服务端语言 | Go | Go 1.26.x,构建时固定最新安全补丁 | 官方 MCP SDK、标准库 HTTP、单二进制、并发和部署边界清晰 | 首期不做多语言服务 |
| MCP SDK | 官方 Go SDK | `modelcontextprotocol/go-sdk` v1.7.x,依赖锁定 | 已覆盖 Streamable HTTP、Bearer auth 和 2026-07-28 协议 | 不使用非官方协议实现 |
| MCP 协议 | 当前协议+兼容协商 | 目标 `2026-07-28`,兼容 `2025-11-25` | 当前协议已无服务端会话,便于多实例;兼容尚未升级的 Agent Host | 不新增旧 HTTP+SSE 实现 |
| OAuth 授权服务器 | 成熟 OAuth/OIDC Authorization Server+飞书身份联邦 | 产品在 C1-04 前锁定;必须支持 PKCE、PRM、resource/audience、撤销和逐请求校验 | 飞书完成员工认证,授权服务器完成 MCP 客户端协议与令牌生命周期 | 不在 `contextd` 内从零自研密码学、客户端注册和令牌签发体系 |
| HTTP 层 | Go `net/http` | 标准库 `ServeMux` 和中间件 | 只需协议端点,无业务页面,无须 Web 框架 | 不引入前端、SSR、模板和 Web 管理系统 |
| 主数据存储 | PostgreSQL | PostgreSQL 18.x,部署时锁定最新小版本 | 同时支撑身份、权限、release、幂等、outbox、审计和全文索引 | 首期不引入 MySQL、MongoDB |
| SQL 访问 | `pgx/v5``sqlc` | 锁定版本 | 显式 SQL、类型生成、事务边界可审查 | 不用隐藏 SQL/权限条件的重型 ORM |
| 数据迁移 | `goose` | 锁定版本,迁移只走运维身份 | 简单、可版本化、可回滚说明 | 不允许 Agent/MCP 执行迁移 |
| Gitea | 自建 Gitea | 1.26.x 支持分支,G0 时复核最新安全补丁 | 项目权限、私仓、分支保护和 Git 历史真源 | 不把 Gitea Admin 暴露给 Agent |
| Git 写入 | 系统 Git CLI | 固定最低版本,临时 worktreeHTTPS | 多文件单 commit 和标准 non-fast-forward/CAS 语义成熟 | 不开放通用 Git tool;不把 token 放命令行或日志 |
| Markdown | Go AST 解析器 | `goldmark`+自有规范化层 | 可确定性解析标题、链接和正文 | 不用 LLM 生成确定性发布物 |
| Schema | JSON Schema 2020-12 | schema+合法/非法样例 | 与 v1/v1.1 契约包一致 | 不用仅靠 Go struct 代替外部契约 |
| 搜索 | PostgreSQL `pg_trgm`+元数据过滤 | 规范化正文、标题、标签、路径;只索引授权项目的 current | 中文不依赖空格分词,首期可获得可预测的子串/相似度检索并保持项目隔离 | 首期不把原生英文 FTS 当中文主检索,也不引入 Elasticsearch/向量数据库 |
| 异步任务 | PostgreSQL outbox worker | 同一代码库、独立 worker 运行角色 | 保证业务状态与事件一致,减少基础设施 | 首期不引入 Kafka/RabbitMQ |
| 审计 | PostgreSQL 独立 schema/角色 | append-only、事件 hash、每日签名 checkpoint | 查询和完整性验证可落地 | 不把普通日志当审计真源 |
| 可观测 | OpenTelemetry | OTLP 输出到公司既有监控 | 不绑定具体监控产品 | 审计正文和 token 不进入 telemetry |
| 测试 | `go test`Testcontainers | fake Feishu/Gitea+临时 PostgreSQL | 默认离线、可重复、正反用例统一 | 普通 CI 不连接生产环境 |
| 打包 | OCI 镜像 | 多阶段构建、固定 digest、SBOM | 可复现、便于测试环境部署 | 首期不要求 Kubernetes |
### 2.1 为什么选择 Go 而不是 Web 技术栈
- 当前产品没有自定义页面,主要工作是协议接入、身份、权限、事务、文件构建、Git 和审计。
- 官方 Go MCP SDK 可以直接挂在 `net/http`,不需要业务 Web 框架。
- 一个代码库可编译成同一镜像的 `serve``worker` 两个运行角色,减少微服务和跨语言维护成本。
- Go 的标准库、显式错误和静态类型适合安全门、状态机和可审查中间件。
若现有开发团队不能维护 Go,TypeScript 可以作为替代语言,但必须重新确认 SDK 版本、运行时、依赖供应链和部署基线;不能在编码中途混用两套主栈。
## 3. 部署拓扑与进程边界
```mermaid
flowchart LR
U[飞书用户] --> C[Codex / Claude Code / Other Agent]
C --> H[Agent Compatibility Layer]
H --> A[OAuth Authorization Server]
A -->|Feishu federation| F[飞书 OAuth]
A -->|Per-user OAuth bearer| P[Reverse Proxy]
P --> M[contextd serve]
M --> DB[(PostgreSQL)]
M --> R[Current Reader]
M --> O[Outbox]
O --> W[contextd worker]
W --> G[Gitea 1.26.x]
W --> FS[Immutable Releases/current]
W --> DB
M --> OT[OTel Collector]
W --> OT
```
| 运行角色 | 面向谁 | 允许能力 | 禁止能力 |
|---|---|---|---|
| `contextd serve` | Agent Host | OAuth/MCP、身份、授权、读取、构建请求、发布请求、审计查询 | 不持有 Gitea 写 token,不执行部署/备份/迁移 |
| `contextd worker` | 内部 outbox | 构建、检查、Git commit/push、导出、读回、审计完成事件 | 不接受用户网络请求,不具有 Gitea Admin |
| `migrator` | 受控运维任务 | 数据库迁移 | 不作为常驻服务,不可由 MCP 调用 |
| `audit-reader` | 授权审计查询 | 脱敏 select/view | 无 insert/update/delete |
| `backup/restore` | 受控运维任务 | 备份和隔离恢复 | 不作为普通 Agent 工具 |
首期采用模块化单体代码库,不拆成身份、权限、发布和审计微服务。`serve/worker` 的运行身份和凭据分离,但共享领域契约和代码版本。
## 4. 协议端点而非 Web 页面
| 路径/能力 | 用途 | 可见范围 |
|---|---|---|
| `POST /mcp` | 远程 Streamable HTTP MCP | Agent Host |
| `/.well-known/oauth-protected-resource` | MCP Protected Resource Metadata | Agent Host |
| OAuth metadata/authorize/token/revoke | MCP 客户端授权与撤销 | Agent Host/OAuth Authorization Server |
| `/oauth/feishu/callback` | 飞书授权码回调 | 飞书 OAuth 浏览器跳转 |
| `/health/live``/health/ready` | 存活与依赖健康 | 内部探针 |
| `/metrics` 或 OTLP | 指标/trace 输出 | 内网监控 |
这些均是机器协议端点。项目不实现 HTML 页面、导航、表单、角色后台和审计后台。
`2026-07-28` 连接使用 stateless Streamable HTTP;内部 `AgentSession` 是应用身份对象,不是 MCP transport session。`/mcp` 必须验证 Origin/Host、协议 header 与 body 一致性、Content-Type、请求大小、超时和 bearer resource;旧协议兼容只通过官方 SDK 协商,不自行维护两套业务逻辑。
## 5. Agent 无关的兼容层契约
### 5.1 架构判断
Agent 品牌与模型能力不是业务权限依据,因此不需要把 Codex、Claude Code 或其他 Agent 写进发布、权限和审计核心。Agent 的差异只影响:MCP 配置方式、OAuth callback、工具确认事件和本地文件读取适配。
当前官方能力证明 Codex 和 Claude Code 都能连接远程 Streamable HTTP MCP 并完成 OAuth。Codex 还提供 MCP server/tool approval 配置,Claude Code 提供 MCP permission ask/allow/deny 和企业 managed MCP 配置。这些客户端能力用于交互和部署,但服务端仍必须逐次验证 token、权限和确认。
### 5.2 Verlit 已确认的接入条件
| 条件 | 当前结论 | 设计处理 |
|---|---|---|
| Agent 选择 | 框架自行兼容,优先 Codex、Claude Code | Agent adapter 可插拔,业务工具 schema 保持一致 |
| MCP 连接 | 每位用户独立连接同一远程 MCP | 每人独立 OAuth token、AgentSession 和审计主体 |
| OAuth | 必须完成 OAuth | MCP resource server+成熟授权服务器+飞书 Authorization Code/PKCE 身份联邦 |
| 人类确认 | 框架支持 | A3/A4 使用框架签发的一次性 `confirmation_id` |
| 本地文件 | 可以读取并传输,但必须受控 | 采用下述 Local Source Gate,不允许 Agent 自由扫描或上传 |
这四项已经从“待提供信息”转为“已确认架构约束”。C1-00 仍需对 Codex、Claude Code 的实际版本做契约测试,但它是适配验收,不再决定核心架构是否成立。
### 5.3 兼容层最小接口
每个 Agent adapter 只负责:
1. 注册远程 MCP endpoint 和支持的协议版本。
2. 发起逐用户 OAuth,并安全保存/刷新该用户的 MCP 凭据。
3.`client_kind``client_version``adapter_version` 和本地 session 绑定到 AgentSession。
4. 在 A3/A4 前展示固定的 project、action、candidate hash、`base_current` 和 diff 摘要。
5. 只有真实用户完成确认后,向框架确认服务换取一次性 `confirmation_id`
6. 将用户明确选择的本地内容经过 Local Source Gate 后调用 MCP。
Codex/Claude Code 自带的 tool prompt 是第一层交互保护,但不直接成为发布授权真源;服务端只接受框架确认服务签发、与精确请求 hash 绑定的一次性凭证。
### 5.4 Local Source Gate:本地文件传输控制
```text
用户明确选择路径/章节
→ adapter 解析真实路径
→ 允许根、类型、大小、隐藏/控制文件检查
→ 本地 secret/PII/路径预检查
→ 展示文件数、相对路径、大小和 hash 供用户确认
→ 生成不可变 Selection Manifest
→ 通过 MCP 分块传输选中内容
→ 服务端复算 hash、再做完整 validation
```
强制规则:
- 允许根由框架/企业配置确定,Agent 和正文不能修改。
- 不递归扫描未选择目录;不允许工作区外路径、符号链接逃逸、`.git`、凭据目录、隐藏控制文件和未允许二进制。
- 只传相对 source label、内容、大小和 hash,不传个人绝对路径。
- 默认只允许 Markdown;附件使用类型 allowlist、单文件上限和 bundle 总上限。
- 工具参数和传输正文不进入普通日志;审计只保存 selection ID、文件数、字节数、规则结果和 hash。
- 客户端预检查不是最终放行依据;服务端必须重新执行路径语义、secret/PII、链接和内容策略检查。
- 任何分块缺失、hash 不一致、用户取消、会话变化或超时都废弃整个 selection,不做部分发布。
- 临时缓冲区使用独立目录、最小权限和完成后清理;不得成为 Agent 后续默认检索面。
Local Source Gate 可以由 Verlit 的兼容框架统一实现,Codex 和 Claude Code 只提供文件读取能力。这样 Agent 可替换,而本地数据控制保持一致。
## 6. 飞书登录与 MCP 会话
### 6.1 推荐身份模型
- 内部 `user_id` 是唯一长期主体,不直接采用邮箱、手机号、姓名或 Gitea username 作为主键。
- `FeishuBinding` 保存 `tenant_key`、当前应用的 `open_id`,在权限允许时同时保存租户内 `user_id`;外部 ID 变化或应用迁移通过绑定版本处理。
- `GiteaBinding` 保存 Gitea 数字用户 ID、username 和绑定状态。
- 飞书 user token 只用于登录时取得身份,不作为 MCP token,不透传给 Gitea。
### 6.2 登录流程
```text
Agent Host 请求 /mcp
→ 401 + Protected Resource Metadata
→ Host 进入 OAuth Authorization Code + PKCE
→ OAuth Authorization Server 重定向飞书授权页
→ 飞书 callback 返回一次性 code
→ 身份联邦适配器换取 user_access_token 并读取稳定用户 ID
→ 查找内部 UserIdentity、FeishuBinding 和预配置 GiteaBinding
→ 校验用户/绑定状态
→ 授权服务器签发短期 MCP access token
→ Host 携带 token 调用 /mcp
```
### 6.3 Token 规则
- access token 由成熟授权服务器签发;可采用短期签名 JWT 或可内省的 opaque token,但必须校验 issuer、audience/resource、scope、client、expiry 和撤销状态。
- 若采用 opaque token,随机强度至少 256 bit,业务数据库只保存不可逆 hash;若采用 JWT,业务数据库不落原始 token,撤权仍通过会话和权限版本即时生效。
- 建议 access token 15 分钟、refresh/session 8 小时;最终值在 C1 安全评审确认。
- token 绑定 `user_id`、Agent Host client、MCP resource、scope、permission version 和 expiry。
- 每次工具调用重新检查用户、绑定、AgentSession 和权限版本;撤权不等待 token 自然到期。
- Feishu、MCP 和 Gitea token 三者永不复用或互相透传。
### 6.4 首次登录与飞书—Gitea 绑定
- 首次飞书登录只证明“这个人是谁”,不会根据用户输入的 Gitea username 自动授予项目权限。
- v1.1 试点采用受控预配置:治理人员把 `tenant_key+飞书稳定 ID` 与 Gitea 数字用户 ID 建立待激活映射,并同时配置 ProjectGrant;该动作走迁移/受控运维流程,不暴露为普通 MCP tool。
- 用户首次登录时只激活已存在且唯一匹配的映射;未配置、重复、停用或不一致时进入 `binding_pending/denied`,只允许读取本人身份与处理提示,不能列出或读取项目。
- Gitea 绑定必须以 Gitea 数字用户 ID 为真源,username 只用于展示;后续若建设自助绑定,必须通过 Gitea OAuth/一次性所有权验证另行评审,不能用“用户自行填写账号名”替代。
- 项目授权和解绑同样不暴露为普通 MCP 写工具。撤销 FeishuBinding、GiteaBinding、ProjectGrant 或 AgentSession 任一项,下一次调用立即拒绝并写审计事件。
## 7. PostgreSQL 数据模型
### 7.1 身份与授权
| 表 | 核心字段 | 约束 |
|---|---|---|
| `user_identities` | internal user ID、status、display name | 显示名变化不改主键 |
| `feishu_bindings` | tenant、open/user ID、user ID、status、version | tenant+外部 ID 唯一;不保存明文 token |
| `gitea_bindings` | Gitea numeric ID、username、user ID、status | Gitea numeric ID 唯一 |
| `projects` | project ID、repo mapping、sensitivity、status | repo mapping 受保护 |
| `project_grants` | user、project、role、actions、expiry、version | 第一层权限真源 |
| `agent_sessions` | session、user、agent/client、scope、expiry | 短期,可撤销 |
| `mcp_tokens` | token hash、session、resource、scope、expiry | 不保存明文 bearer token |
| `confirmations` | user/session、tool、project、request hash、expiry | 一次性,不能跨 hash 复用 |
### 7.2 发布与读取
| 表 | 作用 |
|---|---|
| `selections` | 不可变选择范围、来源 revision 和 hash |
| `artifacts` | artifact manifest、bundle hash、builder version |
| `validation_reports` | policy version、finding、结果和例外引用 |
| `release_records` | release 状态、幂等键、`base_current`、commit/readback |
| `current_versions` | 每项目唯一有效 current 和版本引用 |
| `current_search_documents` | 仅 current 的 project-scoped 规范化检索文档与 trigram 索引 |
### 7.3 审计与可靠任务
| 表 | 作用与权限 |
|---|---|
| `outbox_events` | 业务事务内写入待执行/待投递事件;worker claim |
| `audit_events` | 追加写三主体事件;运行角色无 update/delete |
| `audit_checkpoints` | 按日/分区保存事件链摘要和签名引用 |
数据库至少使用 `runtime``worker``audit_writer``audit_reader``migrator``backup` 六类角色。运行时不得拥有迁移、审计删除或任意 schema 权限。
当前版本不创建 `skills``skill_reviews``skill_registry` 表;它们属于独立后续版本。
## 8. 双层权限算法
每次工具调用按固定顺序执行:
```text
Bearer token 有效
∩ UserIdentity active
∩ FeishuBinding active
∩ AgentSession active and bound to client
∩ ProjectGrant allows action/resource
∩ Project/sensitivity/policy allows
∩ GiteaBinding active
∩ Gitea confirms current project permission
∩ exact Confirmation valid when A3/A4
```
- 服务端不读取工具参数中的 `user_id`、role、grant、session 作为权威数据。
- 首期每次项目访问实时查询 Gitea 权限,不使用允许结果的长期缓存;Gitea 不可用时 fail closed。
- 后续只有在撤权失效机制验证后,才可增加短 TTL 缓存。
- Gitea 权限查询使用独立 read-scoped 服务凭据;发布写入使用独立 project-scoped 网关凭据。
- 用户本人不需要向 Agent 提供 Gitea token。
## 9. MCP 工具实现分组
### 9.1 第一批:身份与只读
- `get_my_identity`
- `list_authorized_projects`
- `get_my_project_roles`
- `get_current_manifest`
- `read_artifact`
- `search_current`
- `verify_release_hash`
### 9.2 第二批:选择、构建与检查
- `create_selection`
- `append_selection_content`(仅在 Host 需分块传输时)
- `build_preview`
- `validate_release_request`
- `preview_release_diff`
Markdown 内容通过 MCP 加密传输,但不得进入访问日志和审计正文;审计仅记录内容 hash、大小、文件数和脱敏标签。首期设置单文件和 bundle 大小上限,超限时停止,不自动改走通用上传接口。
### 9.3 第三批:受控发布
- `submit_discussion_release`
- `submit_confirmed_release`
- `get_release_status`
- `cancel_precommit_release`
A3/A4 请求必须引用一次性 `confirmation_id`,并绑定 tool、user、session、project、candidate hash、`base_current` 和过期时间。Host 不能提供可靠确认时不注册提交工具。
### 9.4 第四批:审计查询
- `list_my_actions`
- `get_action_detail`
- `trace_release`
- `trace_correlation`
- `list_project_audit_events`
- `verify_audit_event`
审计生成不是 MCP tool;它由服务端中间件和业务事务自动完成。
## 10. 强制审计实现
### 10.1 每次调用顺序
```text
解析 bearer/token context
→ 持久化 request_received
→ 身份、双层权限、策略和确认判断
→ 持久化 allowed/denied
→ 执行业务逻辑或 outbox 任务
→ postcheck/readback
→ 持久化 completed/failed
→ 返回 MCP 结果
```
### 10.2 可靠性
- `request_received` 无法持久化时拒绝工具调用。
- 发布等写动作与 outbox 在同一个 PostgreSQL 事务提交。
- worker 使用 `FOR UPDATE SKIP LOCKED`/等价机制 claim 任务,按幂等键重复安全执行。
- completed 只有在 Git、current、MCP readback 和审计结果均落盘后产生。
- 每个事件记录 human、agent、service、session、tool、action、project、request hash、policy、authorization、confirmation、result 和 correlation。
- `audit_events` 使用 canonical JSON hash;每日产生签名 checkpoint 并进入备份,便于发现离线篡改。
## 11. Gitea 发布与 current
### 11.1 Gitea 权限
- 每个项目一个私有 Context 仓库。
- 普通成员按项目获得 Code Read;无需 Write。
- 只有 `publish-gateway:<project>` 可写受保护 `main`
- Gitea Admin、组织 Owner 和仓库 Settings 权限不进入 MCP 工具。
### 11.2 发布事务
1. worker 读取服务端 policy、validation、confirmation、幂等和 `base_current`
2. 使用临时 worktree 获取指定 `main`,再次校验远端 commit 与 `base_current`
3. 生成完整新 `current/`,不在服务目录逐文件修改。
4. 创建单一 Git commit,记录 release ID、human、agent、service 和 manifest hash。
5. 非 force push 到受保护 `main`non-fast-forward 返回 `STALE_BASE`
6. 从该 commit 构建不可变 `releases/<release-id>/`
7. 校验 manifest/checksum 后原子切换唯一 current。
8. 通过 MCP 内部读取路径执行 readback;一致后才 completed。
### 11.3 Search 索引
current 切换时为新 release 生成规范化检索文档和 PostgreSQL `pg_trgm` GIN/GiST 索引,以 `project_idrelease_id` 为版本键;标题、标签、相对路径和正文分别保留权重字段。切换 current 与索引激活必须保持同一版本。查询先鉴权、再限定 project、最后执行精确匹配、前缀/子串与 trigram 相似度排序,不跨项目检索后在模型端过滤。
原生 PostgreSQL FTS 可作为英文或明确分词语料的补充,但不作为 v1.1 中文主检索。只有基准测试证明 `pg_trgm` 无法满足真实规模和相关性时,才评审中文分词扩展或外部检索服务。
## 12. 代码仓库结构
```text
context-platform/
├─ cmd/contextd/
├─ contracts/
│ ├─ v1/
│ └─ v1.1/
├─ internal/
│ ├─ domain/
│ ├─ identity/
│ ├─ authorization/
│ ├─ mcpserver/
│ ├─ selection/
│ ├─ builder/
│ ├─ validation/
│ ├─ release/
│ ├─ gitea/
│ ├─ current/
│ ├─ search/
│ ├─ audit/
│ └─ observability/
├─ migrations/
├─ config/
├─ tests/
│ ├─ contract/
│ ├─ integration/
│ ├─ security/
│ └─ fixtures/
├─ deploy/
└─ docs/
```
同一镜像通过子命令运行:
```text
contextd serve
contextd worker
contextd migrate # 仅运维任务
contextd verify # 离线配置/契约自检
```
## 13. C1 代码开发执行顺序
| WP | 工作包 | 主要动作 | 交付物 | 通过门 |
|---|---|---|---|---|
| C1-00 | Agent adapter 契约测试 | 分别用 Codex、Claude Code adapterfake OAuth/MCP 验证逐用户 token、协议、框架确认和 Local Source Gate | 通用 adapter contract、两份兼容性报告 | 两个客户端通过同一 schema;不接真实业务数据 |
| C1-01 | 工程基线 | 创建代码仓库、Go module、依赖锁、CI、秘密扫描、制品规则 | 仓库骨架、构建命令 | 离线 build/test 通过,无真实配置 |
| C1-02 | 契约与领域 | 落地 v1/v1.1 schema、状态机、ID、错误和样例 | contracts、domain tests | CT/UT 通过,未知 major/状态拒绝 |
| C1-03 | PostgreSQL 与迁移 | 表、索引、约束、DB roles、迁移和测试库 | migration、sqlc、repository ports | 回滚说明、最小权限和并发测试通过 |
| C1-04 | 身份与 OAuth | 选定成熟授权服务器,接入 fake Feishu、binding、token/session、PRM/auth metadata | identity federation adapter、auth middleware | 参数冒充、未预配绑定和撤权后访问均拒绝 |
| C1-05 | 双层授权 | ProjectGrant、Gitea permission port、policy 和确认 | authorization engine、fake Gitea | M01M04、M07 通过 |
| C1-06 | MCP 与审计骨架 | Streamable HTTP、工具 registry、三主体中间件、outbox | serve/worker、audit events | M05、M08M10 通过 |
| C1-07 | current 读取与搜索 | manifest/read/search/hash verify、project filter | 第一批 MCP 工具 | 未授权、跨项目和历史读取均失败 |
| C1-08 | Selection/Build/Validate | 实现 Local Source Gate、分块传输、规范化、附件/链接、扫描和 diff | 第二批 MCP 工具 | 未选内容不读取;确定性 hash、安全阻断和大小限制通过 |
| C1-09 | Release/Export/Readback | 幂等、CAS、fake Git/Gitea、原子 current、readback | 第三批 MCP 工具 | 并发、失败状态、恢复发布通过 |
| C1-10 | 审计查询与观测 | 查询工具、event verify、指标/trace、脱敏 | 第四批工具、OTel | 三主体链完整,日志无正文/秘密 |
| C1-11 | 安全与恢复测试 | 冒充、越权、注入、审计中断、DB/Git/current 恢复 | 测试报告 | M01~M10 和开发态 T 用例通过 |
| C1-12 | 打包交接 | OCI、SBOM、checksum、配置样例、运行手册 | 不连接环境的制品 | 可复现、无秘密、不会自动部署 |
C1-00 某个客户端未通过时,只阻断该客户端 adapter,不阻断核心和已通过客户端;不得为兼容单个 Agent 降级 OAuth、确认、Local Source Gate 或审计语义。
## 14. G0G5 真实实施顺序
### G0:参数与责任确认
1. 固定 Codex、Claude Code 的受支持版本和 adapter version,并确认飞书自建应用、试点用户和管理员。
2. 选择已有或独立部署的成熟 OAuth/OIDC Authorization Server,确认 MCP PRM、PKCE、resource/audience、客户端注册、撤销和飞书身份联邦能力。
3. 确认目标 Linux/容器环境、域名、TLS、反向代理、PostgreSQL、存储和备份。
4. 确认 Gitea 是新建还是复用、版本、管理员和维护窗口。
5. 固定试点 project、成员、飞书—Gitea 预配置绑定、publisher 和安全/备份责任人。
6. 确认 RPO/RTO、审计保留、脱敏和事故联系人。
### G1:测试环境基础设施
1. 只读盘点目标环境。
2. 准备 PostgreSQL 数据库/角色和密钥引用。
3. 部署固定版本 Gitea,关闭公开注册并保护 `main`
4. 部署 reverse proxy、`contextd serve/worker` 和 OTel 输出。
5. 配置飞书 OAuth redirect、最小 scope 和 MCP resource metadata。
6. 创建一个合成项目和合成用户,先运行 M01~M10。
### G2:身份、权限与仓库
1. 建立试点用户的内部、飞书和 Gitea 绑定。
2. 建立 ProjectGrant 和 Gitea Code Read 权限。
3. 创建项目级 reader、permission-checker 和 publish-gateway 服务身份。
4. 验证双层权限不一致、用户停用、解绑和 Gitea 撤权。
5. 创建试点私仓、受保护 `main` 和备份链。
### G3:发布闭环
1. 只开放身份与 current 读取工具。
2. 读取稳定后开放 selection/build/validate/diff。
3. Host 精确确认验证通过后再开放 discussion 发布。
4. confirmed/恢复作为最后一批 A4 工具开放。
5. 每次发布验证 Git/current/MCP/Agent 四段一致和三主体审计。
### G4:安全、备份与恢复
1. 完成越权、提示注入、秘密/PII、路径和审计中断测试。
2. 轮换 Feishu app secret、MCP 签发密钥、Gitea token 和数据库凭据。
3. 恢复 PostgreSQL、Gitea、current、audit checkpoints 和服务配置。
4. 在隔离环境完成空环境恢复并验证撤销凭据失效。
### G5:真实试点
1. 只接一个 internal、Markdown 为主的真实项目。
2. 完成 M01M10 和 T01T12。
3. 至少两名非平台维护者完成真实读取、发布或纠错。
4. 观察使用和维护成本,命中止损即停止扩项目。
5. 根据证据决定保持 v1.1,还是单独立项 Skill Registry、飞书内容来源或其他版本。
## 15. 默认不引入的复杂度
- 不建设自定义 Web/移动端页面。
- 不使用 Kubernetes 作为首期前置。
- 不拆身份、权限、发布、审计为多个代码仓库或微服务。
- 不引入 Redis、Kafka、RabbitMQ、Elasticsearch 或向量数据库。
- 不让 LLM 决定权限、审核策略、Git commit 或 current 切换。
- 不让 MCP 工具执行部署、迁移、备份、恢复、密钥和 Gitea Admin。
- 不创建 Skill Registry 数据表或发布链。
只有真实负载、可靠性或检索效果证明 PostgreSQL/模块化单体不足时,才为后续版本增加基础设施。
## 16. 工作量量级
在一名熟悉 Go/安全后端的主开发+一名测试/平台协作者条件下,C1 代码与测试基线约为 45~70 人日;G0~G4 环境接入、加固和恢复验证约为 15~30 人日,真实试点观察另计。
该估算包含 Codex、Claude Code 两个 adapter 的契约适配和 Local Source Gate 基础实现,但不包括:第三种 Agent 的特殊兼容、公司没有可用飞书自建应用权限、目标服务器需要重新采购、历史 Gitea 迁移或 Skill Registry 扩展。若某客户端违反统一契约,只重新评估该 adapter,不扩大核心权限。
## 17. 已确认条件与后续实施参数
| 优先级 | 信息 | 当前状态 | 后续处理 |
|---|---|---|---|
| P0 | Agent 架构 | confirmed:框架兼容,优先 Codex、Claude Code | C1-00 固定受支持版本和 adapter contract |
| P0 | 每人独立远程 MCP | confirmed:必须支持 | C1-00 测试两名用户 token/subject 隔离 |
| P0 | OAuth | confirmed:必须完成 | C1-04 集成成熟 OAuth AS+飞书身份联邦 |
| P0 | A3/A4 人类确认 | confirmed:框架支持 | C1-00/05 验证一次性 confirmation 不可伪造/复用 |
| P0 | 本地文件 | confirmed:可读可传但必须控制 | C1-08 实现并验收 Local Source Gate |
| P1 | 团队能否维护 Go;如不能,主要语言是什么 | open | C1-01 前决定是否接受默认 Go 栈 |
| P1 | 可复用的 OAuth/OIDC Authorization Server;如无,批准独立部署哪一成熟产品 | open | C1-04 前锁定;不得在业务服务内临时自研完整授权服务器 |
| P1 | 是否已有飞书自建应用、App 管理员和可用 OAuth redirect 域名 | open | G0 前确认真实 OAuth 参数和 scope |
| P1 | Gitea 是新建还是复用,当前版本和管理员 | open | G0 盘点、升级和权限 API 兼容性 |
| P1 | 目标服务器是否允许 Linux 容器、PostgreSQL 18 和反向代理 | open | 决定部署配置,不影响领域设计 |
| P1 | 飞书—Gitea 绑定和 ProjectGrant 的首批预配置名单与治理责任人 | open | G2 前导入;未预配置用户 fail closed |
| P1 | Local Source Gate 的允许根、类型、单文件/bundle 上限、secret/PII 规则和临时保留期 | open | C1-08 前按 PRD 默认值评审并配置化 |
| P2 | 既有秘密、日志、指标、备份产品 | open | 优先复用,避免重复建设 |
Agent 相关架构信息已经足以关闭概念设计缺口。尚未提供的 P1/P2 信息不阻断当前文档设计,只在对应 C1/G0 任务开始前阻断具体代码栈确认或部署命令。
## 18. 与 v1/v1.1 的一致性
| 原则 | 本方案实现方式 | 是否偏移 |
|---|---|---|
| 一项目一私仓 | Gitea private repositoryProjectGrant | 否 |
| 发布网关唯一写 `main` | 只有 worker 的 project-scoped gateway credential | 否 |
| 唯一 `main/current` | 不可变 release+原子 current 指针 | 否 |
| 内容/事务状态分离 | v1 状态机原样落地 | 否 |
| MCP 唯一用户入口 | 仅 `/mcp` 暴露业务工具 | 否 |
| Agent 可替换 | Codex/Claude Code 通过统一 adapter contract,服务端不信任 Agent 自报身份 | 否 |
| 飞书绑定内部主体 | 成熟授权服务器完成 MCP OAuth,飞书联邦到内部 UserIdentity | 否 |
| 飞书—Gitea 绑定 | Gitea 数字 ID 受控预配置;未绑定、冲突或停用即拒绝 | 否,补齐安全落地方式 |
| 双层权限 | ProjectGrant+实时 Gitea 权限复核 | 否 |
| 三主体强制审计 | MCP 中间件+业务 outboxappend-only store | 否 |
| 无自建 Web | 只有协议和内部健康端点 | 否 |
| 私人来源明确选择 | Local Source Gate 只传用户明确选择的文件/章节 | 否 |
| 搜索只暴露 current | PostgreSQL project-scoped `pg_trgm`+元数据过滤 | 否,补齐中文检索基线 |
| Skill Registry 后置 | 当前无 skill 表、工具和流程 | 否 |
因此,本技术栈是 v1.1 的实现选择,不构成新的业务版本。只有改成共享用户身份、取消 Gitea 二次复核、绕过强制审计、开放 Web 旁路或把 Skill Registry 加入首期,才属于需要重新确认的版本变更。