diff --git a/.env.example b/.env.example index 5102e9e..c214c41 100644 --- a/.env.example +++ b/.env.example @@ -2,6 +2,9 @@ # 敏感配置同时支持NAME_FILE=/容器内绝对路径;NAME与NAME_FILE只能设置一个。 APP_ENV=local LOG_LEVEL=debug +PLATFORM_BUILD_VERSION=dev +PLATFORM_BUILD_COMMIT=unknown +PLATFORM_BUILD_TIME=unknown HTTP_ADDR=0.0.0.0:58080 API_PORT=58080 CALLBACKS_PORT=58081 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57fc6be..0da4d5a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,10 +42,27 @@ jobs: npm run build - name: Validate compose run: docker compose config --quiet + - name: Resolve immutable build identity + run: | + openapi_version="$(awk '/^ version:/{print $2; exit}' openapi/openapi.yaml)" + test -n "$openapi_version" + echo "PLATFORM_BUILD_VERSION=${openapi_version}-ci.${GITHUB_RUN_NUMBER}" >> "$GITHUB_ENV" + echo "PLATFORM_BUILD_COMMIT=${GITHUB_SHA}" >> "$GITHUB_ENV" + echo "PLATFORM_BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_ENV" - name: Build backend image - run: docker build -f deploy/backend.Dockerfile -t data-terminal-cloud-backend:test . + run: >- + docker build -f deploy/backend.Dockerfile + --build-arg VERSION="$PLATFORM_BUILD_VERSION" + --build-arg COMMIT="$PLATFORM_BUILD_COMMIT" + --build-arg BUILD_TIME="$PLATFORM_BUILD_TIME" + -t data-terminal-cloud-backend:test . - name: Build web image - run: docker build -f deploy/web.Dockerfile -t data-terminal-cloud-web:test . + run: >- + docker build -f deploy/web.Dockerfile + --build-arg VERSION="$PLATFORM_BUILD_VERSION" + --build-arg COMMIT="$PLATFORM_BUILD_COMMIT" + --build-arg BUILD_TIME="$PLATFORM_BUILD_TIME" + -t data-terminal-cloud-web:test . - name: Go vulnerability and license policy run: | go install golang.org/x/vuln/cmd/govulncheck@v1.1.4 @@ -79,7 +96,19 @@ jobs: run: | backend_digest="$(docker image inspect --format='{{.Id}}' data-terminal-cloud-backend:test)" web_digest="$(docker image inspect --format='{{.Id}}' data-terminal-cloud-web:test)" - jq -n --arg source_commit "$GITHUB_SHA" --arg run_id "$GITHUB_RUN_ID" --arg run_attempt "$GITHUB_RUN_ATTEMPT" --arg backend_digest "$backend_digest" --arg web_digest "$web_digest" '{source_commit:$source_commit,run_id:$run_id,run_attempt:$run_attempt,backend_image_digest:$backend_digest,web_image_digest:$web_digest}' > artifacts/build-metadata.json + backend_version="$(docker image inspect --format='{{index .Config.Labels "org.opencontainers.image.version"}}' data-terminal-cloud-backend:test)" + backend_commit="$(docker image inspect --format='{{index .Config.Labels "org.opencontainers.image.revision"}}' data-terminal-cloud-backend:test)" + backend_time="$(docker image inspect --format='{{index .Config.Labels "org.opencontainers.image.created"}}' data-terminal-cloud-backend:test)" + web_version="$(docker image inspect --format='{{index .Config.Labels "org.opencontainers.image.version"}}' data-terminal-cloud-web:test)" + web_commit="$(docker image inspect --format='{{index .Config.Labels "org.opencontainers.image.revision"}}' data-terminal-cloud-web:test)" + web_time="$(docker image inspect --format='{{index .Config.Labels "org.opencontainers.image.created"}}' data-terminal-cloud-web:test)" + test "$backend_version" = "$PLATFORM_BUILD_VERSION" + test "$web_version" = "$PLATFORM_BUILD_VERSION" + test "$backend_commit" = "$GITHUB_SHA" + test "$web_commit" = "$GITHUB_SHA" + test "$backend_time" = "$PLATFORM_BUILD_TIME" + test "$web_time" = "$PLATFORM_BUILD_TIME" + jq -n --arg candidate_version "$PLATFORM_BUILD_VERSION" --arg source_commit "$GITHUB_SHA" --arg build_time "$PLATFORM_BUILD_TIME" --arg run_id "$GITHUB_RUN_ID" --arg run_attempt "$GITHUB_RUN_ATTEMPT" --arg backend_digest "$backend_digest" --arg web_digest "$web_digest" '{candidate_version:$candidate_version,source_commit:$source_commit,build_time:$build_time,run_id:$run_id,run_attempt:$run_attempt,backend_image_digest:$backend_digest,web_image_digest:$web_digest}' > artifacts/build-metadata.json - uses: actions/upload-artifact@v4 with: name: security-and-sbom diff --git a/README.md b/README.md index 376610f..75cec5d 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,8 @@ Set-Location E:\Codex\story\platform .\scripts\dev.ps1 ``` +默认本地镜像使用`dev/unknown/unknown`构建标识。需要冻结可追溯候选时,应在构建前设置`PLATFORM_BUILD_VERSION`、40位`PLATFORM_BUILD_COMMIT`和UTC RFC3339格式的`PLATFORM_BUILD_TIME`;Compose会把同一标识写入后端二进制和后端/Web镜像OCI标签,远程CI会根据OpenAPI版本、完整提交和运行时间自动设置并复核这些值。 + 启动成功后访问: - Web: diff --git a/compose.yaml b/compose.yaml index 3d9a5cd..a0a89a3 100644 --- a/compose.yaml +++ b/compose.yaml @@ -66,6 +66,14 @@ x-backend-environment: &backend-environment ARK_EMBEDDING_MODEL: ${ARK_EMBEDDING_MODEL:-} ARK_REQUEST_TIMEOUT: ${ARK_REQUEST_TIMEOUT:-30s} +x-backend-build: &backend-build + context: . + dockerfile: deploy/backend.Dockerfile + args: + VERSION: ${PLATFORM_BUILD_VERSION:-dev} + COMMIT: ${PLATFORM_BUILD_COMMIT:-unknown} + BUILD_TIME: ${PLATFORM_BUILD_TIME:-unknown} + x-backend-dependencies: &backend-dependencies postgres: condition: service_healthy @@ -194,9 +202,7 @@ services: restart: "no" api: - build: - context: . - dockerfile: deploy/backend.Dockerfile + build: *backend-build entrypoint: ["/app/api"] environment: <<: *backend-environment @@ -208,9 +214,7 @@ services: depends_on: *backend-dependencies callbacks: - build: - context: . - dockerfile: deploy/backend.Dockerfile + build: *backend-build entrypoint: ["/app/callbacks"] environment: <<: *backend-environment @@ -223,9 +227,7 @@ services: depends_on: *backend-dependencies worker: - build: - context: . - dockerfile: deploy/backend.Dockerfile + build: *backend-build entrypoint: ["/app/worker"] environment: <<: *backend-environment @@ -255,9 +257,7 @@ services: condition: service_started mock-provider: - build: - context: . - dockerfile: deploy/backend.Dockerfile + build: *backend-build entrypoint: ["/app/mock-provider"] environment: HTTP_ADDR: 0.0.0.0:8083 @@ -273,9 +273,7 @@ services: condition: service_started mock-alert-receiver: - build: - context: . - dockerfile: deploy/backend.Dockerfile + build: *backend-build entrypoint: ["/app/mock-alert-receiver"] environment: HTTP_ADDR: 0.0.0.0:8084 @@ -285,9 +283,7 @@ services: sim-device: profiles: [simulation] - build: - context: . - dockerfile: deploy/backend.Dockerfile + build: *backend-build entrypoint: ["/app/sim-device"] environment: SIM_API_URL: http://api:8080 @@ -301,6 +297,10 @@ services: build: context: . dockerfile: deploy/web.Dockerfile + args: + VERSION: ${PLATFORM_BUILD_VERSION:-dev} + COMMIT: ${PLATFORM_BUILD_COMMIT:-unknown} + BUILD_TIME: ${PLATFORM_BUILD_TIME:-unknown} environment: WEB_OIDC_AUTHORITY: ${WEB_OIDC_AUTHORITY:-http://localhost:58180/realms/voice-local} WEB_OIDC_CLIENT_ID: ${WEB_OIDC_CLIENT_ID:-voice-platform-web} diff --git a/deploy/backend.Dockerfile b/deploy/backend.Dockerfile index 517b873..2da7f61 100644 --- a/deploy/backend.Dockerfile +++ b/deploy/backend.Dockerfile @@ -33,6 +33,12 @@ RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w \ CGO_ENABLED=0 go build -trimpath -o /out/mock-alert-receiver ./cmd/mock-alert-receiver FROM gcr.io/distroless/static-debian12:nonroot +ARG VERSION=dev +ARG COMMIT=unknown +ARG BUILD_TIME=unknown +LABEL org.opencontainers.image.version="${VERSION}" \ + org.opencontainers.image.revision="${COMMIT}" \ + org.opencontainers.image.created="${BUILD_TIME}" WORKDIR /app COPY --from=build /out/ /app/ EXPOSE 8080 8081 8082 8083 8084 diff --git a/deploy/web.Dockerfile b/deploy/web.Dockerfile index f49beab..10a69a7 100644 --- a/deploy/web.Dockerfile +++ b/deploy/web.Dockerfile @@ -7,6 +7,12 @@ COPY web ./ RUN npm run generate:api && npm run build FROM nginxinc/nginx-unprivileged:1.30.4-alpine +ARG VERSION=dev +ARG COMMIT=unknown +ARG BUILD_TIME=unknown +LABEL org.opencontainers.image.version="${VERSION}" \ + org.opencontainers.image.revision="${COMMIT}" \ + org.opencontainers.image.created="${BUILD_TIME}" USER root RUN apk upgrade --no-cache COPY --chown=101:101 deploy/web.nginx.conf /etc/nginx/conf.d/default.conf diff --git a/docs/development-status.md b/docs/development-status.md index 9e82668..80478a4 100644 --- a/docs/development-status.md +++ b/docs/development-status.md @@ -246,7 +246,8 @@ | 真实终端RTC/语音供应商精简矩阵与发布门禁 | 新增`real-device-rtc-capture-v1`、`real-device-rtc-conformance-report-v1`和`cmd/rtc-conformance`。真实报告以至少3台候选终端执行6通:`s2s_pure/s2s_hybrid/cascade`各正常完成1通,再覆盖凭证续签、网络切换恢复和受控停止;每通必须证明房间/用户/任务绑定、双向媒体、固定状态序列、供应商启停、最终字幕、用量、质量通过、任务去重和资源回收。协议模拟固定为`PROTOCOL_SIMULATION/external_validation_required=true`。v3证据包新增`real_device_rtc_validation`,该门禁不可豁免且首个文件必须为合格真实报告。按低强度策略执行采集器/证据包定向测试和一条协议结构工具链冒烟:1台模拟终端1通结构结果生成`PASS/PROTOCOL_SIMULATION`并继续外部待验;证据包保留真实报告、设备/通话数量、三方式和四场景共10项缺口,临时文件已清理。 | | 真实Agent生成、Embedding和内容安全质量门禁 | 新增`real-agent-models-capture-v1`、`real-agent-models-conformance-report-v1`和`cmd/agent-models-conformance`。内容生成复用现有固定10类Agent评测,并使用评测集、脱敏观测和报告重新计算防篡改;Embedding按版本化阈值覆盖精确/改写召回、跨长者隔离、撤销/敏感排除和无匹配降级六类;内容安全覆盖验证码、金融账户、提示注入、敏感医疗、正常内容和模型输出六类。真实证据还必须绑定受控提供器、账号/区域、数据条款、额度和审计引用;本地生成器、哈希向量和规则固定为`LOCAL_AGENT_TOOLCHAIN/external_validation_required=true`。v3证据包新增`real_agent_models_validation`,该门禁不可豁免。按低强度策略执行聚合器/证据包定向测试和一条重算现有模拟报告的本地工具链冒烟:既有10类模拟报告重新计算一致,两组六类结构完整,输出`PASS/LOCAL_AGENT_TOOLCHAIN`并继续外部待验;证据包保留真实报告、三提供器和12类别共16项缺口,临时文件已清理。 | | 托管内容安全外部预检与告警 | OpenAPI 0.64.0把`content_safety_tls`纳入外部依赖查询和候选验收快照。生产Worker仅执行DNS、TCP和受信TLS握手,不发送待分类正文;结果复用依赖可用性指标、数据库脱敏状态和运营诊断页面,连续5分钟不可用进入统一CRITICAL告警,记录缺失/失败/过期均可阻断候选技术快照但不驱逐Pod。按低强度策略执行App/探针/诊断定向测试、前端类型生成/类型检查/生产构建和Prometheus规则校验。 | -| 阶段8外部执行轻量预检 | 新增`scripts/stage8-preflight.ps1`,以单一只读入口核对候选Git提交、远程仓库、Compose本地基线、外部配置变量名称和十类证据索引结构/状态;报告明确不读取密钥值,也不把环境变量或本地服务可用性当作验收证据。默认仅报告缺口,`-Strict`可供自动化阻断未就绪交接;当前实测本地运行基线就绪、索引结构有效,但无候选提交/远程仓库、构建摘要仍为占位值且十类证据均为`PENDING`。 | +| 阶段8外部执行轻量预检 | 新增`scripts/stage8-preflight.ps1`,以单一只读入口核对候选Git提交、远程仓库、Compose本地基线、外部配置变量名称和十类证据索引结构/状态;报告明确不读取密钥值,也不把环境变量或本地服务可用性当作验收证据。默认仅报告缺口,`-Strict`可供自动化阻断未就绪交接;当前已建立本地`main`候选提交,本地运行基线就绪且索引结构有效,仍缺远程仓库,样例构建摘要为占位值,十类外部证据均为`PENDING`。 | +| 候选构建身份贯通 | Compose新增统一候选构建参数,后端二进制和后端/Web镜像OCI标签共享版本、完整Git提交及UTC构建时间;远程CI从OpenAPI版本和运行编号形成候选版本,构建后重新读取两个镜像标签并与`GITHUB_SHA`和构建时间逐项比对,再把候选版本、提交、时间和双镜像摘要写入构建元数据。修复了此前Dockerfile虽支持注入但Compose/CI未传参、运行版本仍为`dev/unknown`的问题。 | ## 下一开发批次 diff --git a/docs/remote-ci-supply-chain-evidence.md b/docs/remote-ci-supply-chain-evidence.md index 76f1c9d..ab7b7c7 100644 --- a/docs/remote-ci-supply-chain-evidence.md +++ b/docs/remote-ci-supply-chain-evidence.md @@ -25,6 +25,8 @@ 仓库CI会上传三份SBOM和`build-metadata.json`,其中记录完整提交、运行编号和两个本次构建镜像ID。正式发布若将镜像推入制品库,应以制品库返回的不可变清单摘要为准,并保证采集中的`build_digest`与证据包候选一致。 +CI从OpenAPI版本生成候选版本,并把候选版本、`GITHUB_SHA`和UTC构建时间同时写入后端二进制及后端/Web镜像的OCI `version/revision/created`标签。生成`build-metadata.json`前会重新读取并逐项比对两个镜像标签;标签为空、提交不一致或构建时间漂移时,流水线直接失败,不能产出候选供应链证据。 + 下载运行日志、构建元数据、SBOM和制品库摘要后,按`remote-ci-supply-chain-capture-v1`整理采集,再执行: ```powershell