--- 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 | 固定最低版本,临时 worktree+HTTPS | 多文件单 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:` 可写受保护 `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//`。 7. 校验 manifest/checksum 后原子切换唯一 current。 8. 通过 MCP 内部读取路径执行 readback;一致后才 completed。 ### 11.3 Search 索引 current 切换时为新 release 生成规范化检索文档和 PostgreSQL `pg_trgm` GIN/GiST 索引,以 `project_id+release_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 adapter+fake 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 | M01~M04、M07 通过 | | C1-06 | MCP 与审计骨架 | Streamable HTTP、工具 registry、三主体中间件、outbox | serve/worker、audit events | M05、M08~M10 通过 | | 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. G0~G5 真实实施顺序 ### 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. 完成 M01~M10 和 T01~T12。 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 repository+ProjectGrant | 否 | | 发布网关唯一写 `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 中间件+业务 outbox+append-only store | 否 | | 无自建 Web | 只有协议和内部健康端点 | 否 | | 私人来源明确选择 | Local Source Gate 只传用户明确选择的文件/章节 | 否 | | 搜索只暴露 current | PostgreSQL project-scoped `pg_trgm`+元数据过滤 | 否,补齐中文检索基线 | | Skill Registry 后置 | 当前无 skill 表、工具和流程 | 否 | 因此,本技术栈是 v1.1 的实现选择,不构成新的业务版本。只有改成共享用户身份、取消 Gitea 二次复核、绕过强制审计、开放 Web 旁路或把 Skill Registry 加入首期,才属于需要重新确认的版本变更。