# AI Gateway Go

AI Gateway 的全量 Go 重构工程。M0–M6 工程实现已完成，当前可运行基线包含 PostgreSQL、双 Redis、独立迁移器、Art Design Pro 管理端/门户端、OpenAI 兼容网关和可发布的 AI 资产编排运行时。

## 当前能力

- `GET /healthz`：进程存活检查。
- `GET /readyz`：PostgreSQL、critical Redis 和 cache Redis 状态；前两者失败会返回 `503`。
- `GET /metrics`：不依赖外部组件的基础 Prometheus 文本指标。
- `POST /v1/chat/completions`、`POST /v1/responses`、`POST /v1/embeddings`、`POST /v1/messages`：透明代理，支持 SSE。
- `GET /v1/models`：透明代理。
- 独立 `gateway-migrator`，API 启动时不会修改数据库结构。
- PostgreSQL 权威管理员/门户身份、PBKDF2 旧密码兼容和 Redis 可撤销会话。
- 管理端与门户端 RFC 6238 TOTP 两步验证、5 分钟挑战令牌、时间步防重放和一次性备用码。
- Provider 管理 API；凭据使用 AES-256-GCM 加密，变更与 outbox 同事务提交。
- PostgreSQL Provider 原子运行时快照、默认/显式路由、能力校验和共享 HTTP 连接池；刷新失败保留上一有效版本。
- Provider 连接测试和模型目录同步；上游缺失模型保留历史记录并自动停用，同步事件与目录更新同事务提交。
- 管理员/门户账号管理、内置角色与可扩展 `resource:action` 权限字符串；账号和权限变更即时生效。
- 层级部门管理和门户账号部门绑定；循环层级、停用父部门和停用在用部门均由服务端约束。
- OIDC Authorization Code + PKCE 登录、RS256 ID Token 校验、外部账号映射与自动开户。
- SAML 2.0 SP 发起登录、SP metadata、签名断言验证、RelayState/Request ID 绑定、断言防重放与自动开户。
- 旧系统 38 个持久化实体的数据字典，以及幂等的旧 ID → UUIDv5 兼容映射基础设施。
- PostgreSQL API Key（明文仅显示一次、scope、过期时间、Redis 共享缓存、即时撤销）及 Art 管理页面。
- 按 API Key 配置的每分钟请求上限、UTC 自然月请求配额和 Token 配额；critical Redis Lua 原子执行，Token 采用请求前预留、JSON/SSE usage 回写校准，返回 429、`Retry-After` 和配额响应头。
- Provider 级响应头超时、幂等安全重试与熔断；POST 只有在客户端提供 `Idempotency-Key` 且请求体可重放时才会重试。
- PostgreSQL 模型别名和条件路由，按 endpoint、API Key、tenant 过滤，并在最高优先级组内做确定性加权 Provider 选择；规则通过不可变快照运行并由 Redis 通知多实例刷新。
- 有界异步调用审计和按日 usage 聚合：批量写 PostgreSQL，记录状态、延迟及输入/输出 Token；管理端提供权限隔离的筛选查询，审计存储异常不会阻塞模型调用。
- 独立事务 Outbox Worker：多实例 `SKIP LOCKED` 抢占、Redis Stream 原子去重投递、指数退避与死信；管理端可审阅并人工重试，消费者可用同事务幂等入口。
- PostgreSQL 审计月分区维护：自动预创建、default 分区在线迁移、过期整分区快速丢弃、边界精确清理和更长期的 usage 保留策略。
- 可扩展内容策略：Go RE2 不可变编译快照，按端点、模型/API Key 匹配，支持仅审计、阻断与提示词文本脱敏；默认保护常见 API Key、Token、密码和 secret。
- 带时间版本的模型价格与成本核算：按 Provider/模型选择价格，输入与输出 Token 分别计价，结果进入调用审计和 PostgreSQL 按日聚合。
- Prompt 分类、模板和不可变版本，支持显式变量定义、必填校验、历史版本激活与 API Key 渲染接口。
- PostgreSQL 知识库：2 MiB 有界文本正文、段落感知重叠分块、FTS + 中文二元词片混合检索，以及可替换的 `Retriever` 接口；向量化由 M8 的 pgvector/Ollama 阶段提供。
- 声明式 HTTP 工具：JSON Schema 基础校验、KEK 加密请求头、注册和拨号双层 SSRF 防护、禁止重定向、1 MiB 响应限制与调用记录。
- AI 应用草稿和不可变发布版本，将模型、Prompt、知识库、工具组合为 `/v1/applications/{code}/chat/completions`；所有模型轮次继续经过鉴权、配额、内容策略、路由、成本和审计。
- 独立通知 Worker 消费可靠 outbox，按精确事件或末尾 `*` 模式投递 HMAC-SHA256 Webhook；内容策略命中由审计批处理异步产生脱敏事件，失败投递可在 Art 管理端重试。
- 门户自助工作台：部门范围资产目录、Prompt 搜索/收藏、个人审计/用量/成本、模型访问申请与管理员审批。
- M8 对象存储：自托管 MinIO，上传/下载全部经网关代理（不暴露主机端口），管理端文件管理与门户个人文件仓库，`sha256` 完整性校验与严格归属隔离。
- 门户应用托管会话：服务端加密运行凭证、单会话租约、不可变消息序列和 SHA-256 哈希链，不向浏览器暴露应用 API Key。
- 独立事实核验配置、作用域策略与事件契约，复用 Provider 加密凭据和知识库引用，为同步/异步执行器保留清晰模块边界。
- 旧 Python 源码 201 条路由全部有覆盖、替代或退役决策，未决契约缺口为 0；OpenAPI 0.10.0 覆盖全部 Go 字面量路由。

M8 起 MinIO（对象存储）纳入基线部署并由 compose 提供，但不作为启动依赖：网关启动时 MinIO 未就绪只告警、上传请求得到明确报错，服务不会因对象存储缺失而崩溃。ClickHouse 不属于基线，审计与统计继续存放在 PostgreSQL。

## 本地启动

1. 复制 `.env.example` 为 `.env` 并修改密钥。
2. 启动 PostgreSQL 和两套 Redis：`docker compose -f deploy/docker-compose.yml up -d postgres redis-critical redis-cache`。
3. 执行迁移：`go run ./cmd/gateway-migrator`。
4. 设置 `BOOTSTRAP_ADMIN_PASSWORD` 后执行 `go run ./cmd/gateway-bootstrap` 创建初始管理员。
5. 启动 API：`go run ./cmd/gateway-api`。
6. 启动可靠事件投递：`go run ./cmd/gateway-outbox-worker`。
7. 启动审计分区与保留维护：`go run ./cmd/gateway-maintenance`。
8. 启动通知投递：`go run ./cmd/gateway-notification-worker`。

完整容器部署可执行 `docker compose -f deploy/docker-compose.yml up -d --build`。Art Design Pro 管理端默认暴露在 `http://127.0.0.1:8081`，门户端在 `http://127.0.0.1:8082`；两者同源代理 `/api/*`、`/v1/*`、`/healthz` 和 `/readyz` 到 Go API，Go API 仍可从 `8080` 端口直接访问。

生产部署请使用 `deploy/docker-compose.production.yml` 和
`deploy/production.env.example`，完整步骤见
[`deploy/PRODUCTION.md`](deploy/PRODUCTION.md)。生产编排默认仅绑定回环地址、要求显式提供数据库/Redis/加密密钥，并关闭迁移期 bootstrap API key。

如果主机未安装 Go，可以在 Compose 依赖启动和迁移完成后执行：

```bash
docker compose -f deploy/docker-compose.yml run --rm \
  -e BOOTSTRAP_ADMIN_PASSWORD='替换为强口令' \
  --entrypoint gateway-bootstrap gateway-api
```

未设置 `HTTP_ADDR` 时只监听 `127.0.0.1:8080`；容器配置会显式监听 `:8080`。

生产环境必须设置 `APP_ENV=production`，并使用带 TLS 的数据库和 Redis 地址。启用环境变量上游回退时必须设置 `UPSTREAM_API_KEY`。数据库 API Key 已可用于网关请求；bootstrap key 默认关闭，仅用于迁移兼容，需要时显式设置 `GATEWAY_BOOTSTRAP_API_KEY_ENABLED=true` 并配置强随机值，迁移结束后立即关闭，此时无需再配置 bootstrap key。`gateway_bootstrap_api_key_uses_total` 指标可用于确认旧客户端是否已经清零。bootstrap key 会绕过按 key 的限流与配额，切勿长期开启。

内置管理员角色为 `superadmin`、`operator`、`auditor`。超级管理员拥有通配权限；运维管理员默认管理 Provider 和 API Key；审计员只有读取权限。账号还可获得独立的 `resource:action` 权限，服务端每次请求都会从 PostgreSQL 复核账号状态和权限，因此停用或权限回收即时生效。

内容策略和模型价格以 PostgreSQL 为权威源，管理端保存后本实例立即刷新；其他实例分别按 `CONTENT_POLICY_REFRESH_INTERVAL`、`PRICING_REFRESH_INTERVAL`（默认 30 秒）加载不可变快照。正则采用 Go RE2，不支持回溯、环视等可能导致灾难性耗时的表达式。价格金额以微货币单位整数保存，避免浮点累计误差；例如 USD 2.50/百万 Token 存为 `2500000`。

部门采用稳定 UUID 与邻接表层级。部门仍包含启用门户用户或启用子部门时不能停用；移动部门时会在事务内检测自身引用和任意深度循环。门户账号只能绑定启用部门，解绑不会删除任何历史身份数据。

OIDC 登录使用 Redis 保存 5 分钟一次性 `state`、`nonce` 和 PKCE verifier；回调校验发现文档、JWKS/RS256 签名、issuer、audience/azp、iat/nbf/exp 与 nonce。门户会话令牌不进入回调 URL，只使用 60 秒一次性交换码。OIDC Client Secret 使用 `identity-provider-credentials` 独立 AEAD 用途加密。

SAML 当前只开放 SP-initiated Redirect 登录和 POST ACS，显式拒绝 IdP-initiated 与 Artifact 绑定。IdP metadata 通过 SSRF 安全客户端获取并短时缓存，启用前校验 Entity ID、Redirect SSO 端点、签名证书和过期时间。ACS 依次验证 XML 签名、issuer、audience、destination、recipient、InResponseTo 和时间窗，只接受 SHA-256 及以上的签名/摘要算法，并使用 Redis 防止 RelayState 和 Assertion ID 重放。

登录防爆破采用两层叠加：账号锁定（`LOGIN_MAX_FAILURES` 次失败后锁定 `LOGIN_LOCK_DURATION`，针对单账号）与按 IP 的 Redis 滑动窗口限流（`LOGIN_RATE_LIMIT_MAX` 次/`LOGIN_RATE_LIMIT_WINDOW`，超出返回 429，针对跨账号撞库）。限流在 Redis 不可用时 fail-open，账号锁定仍然生效。来源 IP 取自 `X-Forwarded-For`：`deploy/nginx-web.conf` 在 `/api/` 路径用 `$remote_addr` 覆盖该头，避免客户端伪造头绕过限流；直接访问网关端口绕过 nginx 的请求仍可伪造该头，因此生产建议将管理面代理收敛在受信网络内。

旧 Python 数据结构的完整字段字典见 `docs/legacy-data-dictionary.md`。迁移使用 `gateway.legacy_id_mappings` 保存来源、实体、旧 ID 与新 UUID 的对应关系；新 UUID 由固定 namespace 的 UUIDv5 生成，同一条旧数据可安全重跑而不会生成不同主键。字段字典可用 `scripts/export_legacy_dictionary.py` 从旧工程重新生成。

`CREDENTIAL_MASTER_KEY` 必须是 32 字节密钥的 Base64 编码。Compose 中的默认值只允许本地开发，生产环境必须替换并纳入密钥托管与备份；丢失该密钥会导致 Provider 凭据、TOTP、工具请求头、通知签名密钥和应用运行凭证无法解密。每类密文使用独立用途标签，不能相互替换。

工具端点和通知 Webhook 默认只允许公网 HTTP(S) 地址，并在实际拨号时重新解析与校验地址。确有内网服务时分别显式设置 `ALLOW_PRIVATE_TOOL_URLS=true`、`ALLOW_PRIVATE_WEBHOOK_URLS=true`；这两个开关与 Provider 私网开关相互独立。

影子流量默认关闭。设置 `SHADOW_BASE_URL`、专用 `SHADOW_API_KEY` 和大于 0 的 `SHADOW_SAMPLE_RATE` 后，只复制确定性采样的非流式 JSON POST；生产客户端凭据不会转发。主响应不等待影子请求，Prometheus 指标只比较 HTTP 状态和 JSON 结构签名，不记录提示词或模型正文。压测使用独立 `gateway-loadtest` 二进制，可用错误率和 p95 阈值直接控制退出码。

旧路由矩阵由 `scripts/compare_route_contracts.py` 从 Python AST 与 Go OpenAPI 生成。旧源码当前实际包含 201 个路由装饰器，并非早期估算的 141 个；矩阵明确区分同契约覆盖、新契约替代、退役和真实缺口。切换步骤见 `docs/cutover-runbook.md`，旧数据暂存与密文边界见 `docs/legacy-import-runbook.md`。

KEK 轮换时，将 `CREDENTIAL_KEK_VERSION` 和 `CREDENTIAL_MASTER_KEY` 设置为新活动版本与新密钥，并用 `CREDENTIAL_KEK_KEYRING` 加载历史密钥，例如 `{"1":"<old-base64-key>"}`。服务重启并确认可解密后，在供应商页面执行“轮换凭据”；所有旧版本 Provider 凭据和对应 outbox 事件会在一个事务内提交。确认全部 Provider 已切换后才能从 keyring 移除旧密钥。TOTP 同样通过 keyring 保持历史版本可解密，新配置会使用活动版本。

Provider 以 PostgreSQL 为权威源，在进程内构建不可变快照。管理 API 修改后会刷新本实例并通过 critical Redis Pub/Sub 通知其他实例立即刷新；默认每 5 秒轮询仍作为通知丢失或 Redis 短暂异常时的兜底。请求可通过 `X-Gateway-Provider` 指定 Provider；未指定时使用 `config.default=true` 的启用项，否则使用按 code 排序的第一项。`UPSTREAM_FALLBACK_ENABLED=false` 可关闭环境变量上游回退。

供应商连接测试和模型同步访问 OpenAI-compatible `/v1/models`。控制面请求限制为 10 秒、4 MiB 响应，拒绝重定向，并在禁用私网 Provider 时对实际拨号地址再次执行 SSRF 校验。模型同步不会物理删除旧模型，本次未出现的模型会标记为停用，为后续别名和路由规则保留引用稳定性。

## 目录

- `api/openapi`：对外契约。
- `cmd`：可部署二进制。
- `internal/platform`：配置、数据库、缓存、HTTP 运行时。
- `internal/provider`：上游 Provider 扩展接口及实现。
- `internal/gateway`：协议入口和代理编排。
- `internal/workbench`：Prompt、知识、工具、应用编排和通知。
- `internal/portal`：门户目录、申请、个人统计、加密应用凭证与托管会话。
- `internal/factcheck`：事实核验设置、策略和事件契约。
- `migrations`：仅由 migrator 执行的 PostgreSQL 迁移。
- `web`：Art Design Pro 管理端和门户端。
