@aipt/idp-deploy 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +471 -0
- package/bin/idpctl.mjs +8 -0
- package/compose/edge.yaml +61 -0
- package/compose/flow.yaml +34 -0
- package/compose/portal.yaml +38 -0
- package/compose/postgresql.yaml +62 -0
- package/compose/registry.yaml +35 -0
- package/compose/smartgo.yaml +271 -0
- package/compose/tech.yaml +64 -0
- package/contracts/active-profile.schema.json +19 -0
- package/contracts/asset-lifecycle.schema.json +49 -0
- package/contracts/backup-generation.schema.json +60 -0
- package/contracts/component-runtime.schema.json +32 -0
- package/contracts/config-release.schema.json +33 -0
- package/contracts/deployment-evidence.schema.json +43 -0
- package/contracts/deployment-plan.schema.json +67 -0
- package/contracts/release-candidate.schema.json +33 -0
- package/contracts/release-defaults.schema.json +56 -0
- package/contracts/restore-candidate.schema.json +63 -0
- package/contracts/smartgo-component-config.schema.json +79 -0
- package/contracts/tech-backup-boundary.schema.json +61 -0
- package/contracts/tech-source-credentials.schema.json +17 -0
- package/deploy.sh +5 -0
- package/docs/restore-runbook.md +56 -0
- package/governance/asset-lifecycle.v1.json +79 -0
- package/package.json +42 -0
- package/release/defaults.v1.json +64 -0
- package/src/acceptance.mjs +127 -0
- package/src/bindings.mjs +518 -0
- package/src/cli.mjs +360 -0
- package/src/compose.mjs +19 -0
- package/src/config.mjs +903 -0
- package/src/delivery.mjs +123 -0
- package/src/errors.mjs +12 -0
- package/src/foundation-contracts.mjs +128 -0
- package/src/foundation.mjs +107 -0
- package/src/gitops.mjs +285 -0
- package/src/hash.mjs +47 -0
- package/src/image-lock.mjs +160 -0
- package/src/images.mjs +215 -0
- package/src/lifecycle.mjs +58 -0
- package/src/local-source.mjs +354 -0
- package/src/oci-mirror.mjs +10 -0
- package/src/operations.mjs +1484 -0
- package/src/process.mjs +46 -0
- package/src/profiles.mjs +41 -0
- package/src/release.mjs +1760 -0
- package/src/render.mjs +330 -0
- package/src/security.mjs +156 -0
- package/src/source-contracts.mjs +106 -0
- package/src/sources.mjs +78 -0
- package/src/workbench-projects.mjs +118 -0
package/README.md
ADDED
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
# IDP Deploy
|
|
2
|
+
|
|
3
|
+
`idp-deploy` 是企业内部开发者平台的轻量部署与运维控制面。它把 PostgreSQL、私有 npm Registry、Tech、Flow、Portal、SmartGo 和 Caddy 组合为可独立启停的服务片段;Dyyto、Bench、Forge 仍是按需运行的 CLI/MCP 工具,不被强制常驻。SmartGo 是显式选择的业务 Profile,不进入默认 IDP Core。
|
|
4
|
+
|
|
5
|
+
Kubernetes/GitOps 已进入当前 CLI:应用通过 ComponentContract、Bench EnvironmentBinding 和 ReleaseCandidate 生成不可变 DeploymentPlan,再由同一 Kernel 完成确定性 Render、Repository Apply、Git commit、Preimage/CAS 保护和 Repository Verify。它保留历史 `kubernetes-config` 的中央期望状态与 Argo 收敛能力,但不复制跨仓 `sed`、可变 tag、明文 Secret 或宽权限。完整合同与所有权见 [`docs/features/kubernetes-gitops-deployment-backend/DESIGN.md`](docs/features/kubernetes-gitops-deployment-backend/DESIGN.md)。
|
|
6
|
+
|
|
7
|
+
最小生产基座的服务选择、分期部署和管理界面所有权见 [`FOUNDATION-ARCHITECTURE.md`](docs/features/kubernetes-gitops-deployment-backend/FOUNDATION-ARCHITECTURE.md)。Portal 负责统一导航、审批和状态摘要;Argo CD、Grafana、Registry、Secret Manager、Git Provider 及按需配置中心继续使用各自成熟专业界面,Portal 不复制这些控制面。
|
|
8
|
+
|
|
9
|
+
## Kubernetes GitOps 应用入口
|
|
10
|
+
|
|
11
|
+
独立期望状态仓库位于同级 `idp-gitops`。`plan-create`、`plan-update` 和 `plan-remove` 都只生成仓库外不可变 Plan;`apply` 会重新验证三个输入摘要、Git HEAD 与应用目录 Preimage,任何人工或并发修改都会 fail-closed。镜像必须是同时证明 `linux/amd64`、`linux/arm64` 的 OCI digest。
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
export IDP_CONFIG_DIR=/Users/Shared/company/idp
|
|
15
|
+
export IDP_GITOPS_ROOT=/absolute/path/to/idp-gitops
|
|
16
|
+
|
|
17
|
+
node bin/idpctl.mjs gitops app plan-create \
|
|
18
|
+
--component-contract /absolute/project/deploy/component-contract.yaml \
|
|
19
|
+
--environment-binding /absolute/bindings/application.local.yaml \
|
|
20
|
+
--release-candidate /absolute/releases/release-candidate.json \
|
|
21
|
+
--gitops-root "$IDP_GITOPS_ROOT" \
|
|
22
|
+
--output "$IDP_CONFIG_DIR/plans/application-local-create.json" \
|
|
23
|
+
--config-dir "$IDP_CONFIG_DIR"
|
|
24
|
+
|
|
25
|
+
node bin/idpctl.mjs gitops app apply \
|
|
26
|
+
--plan "$IDP_CONFIG_DIR/plans/application-local-create.json" \
|
|
27
|
+
--config-dir "$IDP_CONFIG_DIR"
|
|
28
|
+
|
|
29
|
+
node bin/idpctl.mjs gitops app verify \
|
|
30
|
+
--application application \
|
|
31
|
+
--environment local \
|
|
32
|
+
--gitops-root "$IDP_GITOPS_ROOT" \
|
|
33
|
+
--config-dir "$IDP_CONFIG_DIR"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Apply 只创建本地 Git commit;推送或 PR 仍由目标环境的 Git Provider 权限和分支保护负责,生产不会绕过审批直接写 main。正式本机 fixture 是 `fixtures/gitops-app`,已通过相同命令生成 `foundation-demo`,没有旁路 Renderer。
|
|
37
|
+
|
|
38
|
+
本机基座检查与 Portal 专业界面目录:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
node bin/idpctl.mjs foundation doctor --context kind-idp-foundation
|
|
42
|
+
node bin/idpctl.mjs foundation verify --context kind-idp-foundation
|
|
43
|
+
node bin/idpctl.mjs foundation directory sync --config-dir "$IDP_CONFIG_DIR"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
本机 16GB、Docker 8GB 的默认运行面只常驻 kind、Argo CD/ApplicationSet、Argo Rollouts、cert-manager、External Secrets、`inmemory + dry-run` ExternalDNS 与轻量 OpenTelemetry Collector。Prometheus/Grafana/Loki 不在默认 profile 常驻;生产使用 `idp-gitops` 的 production overlay,并优先部署到 ACK 与阿里云托管监控/日志。没有阿里云凭据时只允许渲染和验证,绝不伪造已创建云资源。
|
|
47
|
+
|
|
48
|
+
本仓库不保存任何真实 Secret、证书或业务数据,也不修改 Tech 和 Aura。
|
|
49
|
+
|
|
50
|
+
## 部署边界
|
|
51
|
+
|
|
52
|
+
| Profile | 服务 | 典型用途 |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| `registry` | Verdaccio | 先建立私有 npm 制品入口 |
|
|
55
|
+
| `flow` | Flow、Caddy | 无状态运行 AI-native SDLC 编排并提供回环入口 |
|
|
56
|
+
| `portal` | PostgreSQL、Portal、Caddy | 独立运行开发者门户 |
|
|
57
|
+
| `tech-only` | PostgreSQL、Tech、Caddy | 只部署中央知识系统 |
|
|
58
|
+
| `foundation` | PostgreSQL、Verdaccio、Tech、Caddy | Registry 验证完成后保留制品服务并优先运行中央知识系统 |
|
|
59
|
+
| `core` / `full` | PostgreSQL、Registry、Tech、Flow、Portal、Caddy | 完整单机 IDP |
|
|
60
|
+
| `business-smartgo` | PostgreSQL、MinIO、SmartGo API/Agent/Worker、Gotology、Operations、Studio、Caddy | 仅运行 SmartGo 业务开发闭包 |
|
|
61
|
+
| `core-smartgo` | `core` 全部服务加 `business-smartgo` 全部服务 | 保留已运行 Core 并并行开发 SmartGo |
|
|
62
|
+
|
|
63
|
+
Bench 原有部署资产已按能力而非目录整体接收:受控运维语义由本仓 Kernel 承担,旧 Compose 只选择性重建为上述独立片段,cnpmcore 由 Verdaccio 替代;未出现真实 Binding 与批准目标环境的实验中间件、GitOps 和 Terraform 保持延后,不进入默认 Profile。机器可读决策与物理归档门禁见 `governance/asset-lifecycle.v1.json`。
|
|
64
|
+
|
|
65
|
+
## 新项目本地 Docker 部署(不推送 ACR)
|
|
66
|
+
|
|
67
|
+
`development/local` 可以使用项目自己的 `deploy/compose.local.yaml` 直接在本机完成受控构建和部署,不执行 Buildx、不推送远程 ACR、不写 GitOps。该模式固定标记为 `local-source`、`promotable: false`;进入 test/production 时仍须从可追溯 Git revision 构建并锁定 amd64/arm64 OCI Index digest。
|
|
68
|
+
|
|
69
|
+
前置条件:项目已经由 Dyyto Apply/Verify,并包含 `project-capabilities.json`、`config/application-config.json`、Dockerfile、`deploy/compose.local.yaml` 和健康检查。真实配置只位于仓库外 Config Dir。
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
export IDP_DEPLOY_ROOT=/Users/dyyto/Desktop/works/sources/spaces/idp-deploy
|
|
73
|
+
export APPLICATION_ROOT=/absolute/path/to/new-project
|
|
74
|
+
export IDP_CONFIG_DIR=/Users/Shared/company/idp/config
|
|
75
|
+
|
|
76
|
+
# 1. 根据Dyyto ApplicationConfigContract创建应用.env、secrets/和certs/;重入不覆盖已有值。
|
|
77
|
+
node "$IDP_DEPLOY_ROOT/bin/idpctl.mjs" app config-init \
|
|
78
|
+
--project "$APPLICATION_ROOT" \
|
|
79
|
+
--config-dir "$IDP_CONFIG_DIR"
|
|
80
|
+
|
|
81
|
+
# 2. 填写命令不能代替外部系统签发的required Secret;不要把值写入项目。
|
|
82
|
+
chmod 600 "$IDP_CONFIG_DIR/applications/<application-id>/secrets/"*
|
|
83
|
+
|
|
84
|
+
# 3. 生成不可变Local DeploymentPlan;本步只执行docker compose config和安全检查。
|
|
85
|
+
node "$IDP_DEPLOY_ROOT/bin/idpctl.mjs" local app plan \
|
|
86
|
+
--project "$APPLICATION_ROOT" \
|
|
87
|
+
--compose-file "$APPLICATION_ROOT/deploy/compose.local.yaml" \
|
|
88
|
+
--output "$IDP_CONFIG_DIR/plans/local-applications/<application-id>-local.json" \
|
|
89
|
+
--config-dir "$IDP_CONFIG_DIR"
|
|
90
|
+
|
|
91
|
+
# 4. 审查Plan中的项目、输入摘要、Compose摘要和promotable:false后执行。
|
|
92
|
+
node "$IDP_DEPLOY_ROOT/bin/idpctl.mjs" local app apply \
|
|
93
|
+
--plan "$IDP_CONFIG_DIR/plans/local-applications/<application-id>-local.json" \
|
|
94
|
+
--config-dir "$IDP_CONFIG_DIR"
|
|
95
|
+
|
|
96
|
+
# 5. 随时重新核验精确Plan与容器健康状态。
|
|
97
|
+
node "$IDP_DEPLOY_ROOT/bin/idpctl.mjs" local app verify \
|
|
98
|
+
--plan "$IDP_CONFIG_DIR/plans/local-applications/<application-id>-local.json" \
|
|
99
|
+
--config-dir "$IDP_CONFIG_DIR"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
代码、Dockerfile、Compose、`.env` 或任一 `*_FILE` 发生变化后,旧 Plan 会 fail-closed;删除旧 Plan 文件并用新的输出文件名重新 `plan`、审查和 `apply`。停止保留容器,移除不删除 Volume:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
node "$IDP_DEPLOY_ROOT/bin/idpctl.mjs" local app stop \
|
|
106
|
+
--plan "$IDP_CONFIG_DIR/plans/local-applications/<application-id>-local.json" \
|
|
107
|
+
--config-dir "$IDP_CONFIG_DIR"
|
|
108
|
+
|
|
109
|
+
node "$IDP_DEPLOY_ROOT/bin/idpctl.mjs" local app remove \
|
|
110
|
+
--plan "$IDP_CONFIG_DIR/plans/local-applications/<application-id>-local.json" \
|
|
111
|
+
--confirm <application-id> \
|
|
112
|
+
--config-dir "$IDP_CONFIG_DIR"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Compose 安全门禁拒绝 privileged、host network、Docker Socket、cap_add、设备直通、显式 container_name、非回环发布端口、项目外绝对 bind mount、项目 Compose Secret 和敏感环境变量字面量;至少一个服务必须提供 healthcheck。应用应原生读取 `*_FILE`,Compose 不负责把 Secret 展开成环境变量。
|
|
116
|
+
|
|
117
|
+
数据库只接入内部 Docker 网络;Registry 默认只绑定宿主机 `127.0.0.1:4873`;Web 入口默认只绑定 `127.0.0.1:8080`。HTTP 是初始模式,TLS 可以通过配置开关启用。
|
|
118
|
+
|
|
119
|
+
## 默认一键发布与部署
|
|
120
|
+
|
|
121
|
+
要求 Node.js 20+、Docker Engine 20.10+、Docker Compose v2 和 Buildx。Tech、Flow、Portal 源码仓库应与 `idp-deploy` 位于同一父目录;真实配置、Secret、证书、数据和发布证据全部位于仓库外。
|
|
122
|
+
|
|
123
|
+
企业 OCI 仓库根作为非敏感发布事实固定在 `release/defaults.v1.json`。部署机只需预先指定仓库外 Config Dir、登录该 Registry,然后运行脚本;初始化、Secret 生成、镜像构建、推送、digest 锁定和部署均由脚本完成:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
export IDP_CONFIG_DIR=/Users/Shared/company/idp/config
|
|
127
|
+
docker login idp-registry.cn-shanghai.cr.aliyuncs.com
|
|
128
|
+
./deploy.sh
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
日常发布同样只需重新登录并执行一个命令:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
docker login idp-registry.cn-shanghai.cr.aliyuncs.com
|
|
135
|
+
./deploy.sh
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
异地或临时 Registry 可通过仓库外 `.env` 的 `IDP_OCI_REPOSITORY_ROOT` 或 `--oci-repository-root` 显式覆盖默认值;脚本不会读取或复制 Docker 凭据文件。`config init` 与 `config generate-secrets` 仍可单独用于排障,但不再是发布前置人工步骤。`config init` 会补齐根/组件 `.env` 及当前声明的 `*_FILE` 文件,已有文件和值不会被覆盖,升级新增键前会保存 `.env` Preimage。
|
|
139
|
+
|
|
140
|
+
`deploy.sh` 默认为 `foundation`;它只转发到与 CLI/AI 共用的 Kernel。等价命令是:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
node bin/idpctl.mjs release deploy foundation
|
|
144
|
+
node bin/idpctl.mjs release deploy foundation --version 0.1.2
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
命令使用 `release/defaults.v1.json` 中固定的第三方 OCI digest 和 Tech Node 基础镜像,以确定性源码摘要生成统一 tag,例如 `0.1.2-r0123456789ab`。自研镜像映射固定为:
|
|
148
|
+
|
|
149
|
+
```text
|
|
150
|
+
<IDP_OCI_REPOSITORY_ROOT>:<统一releaseTag>
|
|
151
|
+
<IDP_OCI_REPOSITORY_ROOT>/flow:<统一releaseTag>
|
|
152
|
+
<IDP_OCI_REPOSITORY_ROOT>/portal:<统一releaseTag>
|
|
153
|
+
<IDP_OCI_REPOSITORY_ROOT>/smartgo:<统一releaseTag>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
全部当前 Profile 所需自研镜像必须先完成 `linux/amd64,linux/arm64` Buildx push,并核对 Buildx metadata 与远端 OCI Index digest,之后才会修改 `.env`。同一 Config Dir 重入会复用已验证 Artifact;换到 Mac M4 等新机器时,若远端 tag 的 amd64、arm64 镜像都携带与当前版本、组件、源码摘要和不可变 Plan 完全一致的发布标识,脚本会生成本机 Registry 收养证据并复用精确 OCI Index digest,不会重复构建。缺少标识、重复应用平台 Manifest 或任一平台不一致时仍会拒绝复用和覆盖。企业 OCI 仓库必须启用 tag 不可变策略并限制推送权限;这些标识用于跨机确定性核对,不替代 Registry 权限、不可变 tag 或后续可选的签名证明。Build、备份、镜像锁定、pull、switch、verify、`bindings sync` 与最终回执都持有同一个显式实例 Lease,其他 idpctl 写操作不能插入事务阶段。
|
|
157
|
+
|
|
158
|
+
Docker Desktop 的 BuildKit 若经宿主代理推送大 Layer 超时,可显式启用“宿主 OCI Layout + ORAS 单并发”回退。该能力默认关闭,且不会下载工具、创建 Builder、切换当前 Builder 或执行 bootstrap。先由企业软件供应链把已批准 ORAS 二进制放入 Config Dir,使用发布方签名或官方 checksum 在平台流程外完成来源核验,再固定本机文件摘要和精确版本:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
node bin/idpctl.mjs config init
|
|
162
|
+
|
|
163
|
+
# 由企业批准流程安装,不要由部署脚本联网下载。
|
|
164
|
+
install -m 0500 <approved-oras-binary> "$IDP_CONFIG_DIR/tools/oras/oras"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Builder 也由宿主供应流程预先建好,正式白名单是 `docker-container`、`kubernetes` 或 `remote` driver。当前 Docker Desktop 的 `desktop-linux`/`docker` driver 在 containerd image store 上虽有本机 OCI Layout 实测成功证据,但该行为不是可跨版本/context/image store 复现的官方可移植合同,因此发布器仍会拒绝 `docker` driver。若选择 `docker-container`,在部署脚本之外用企业批准的 BuildKit digest 创建并 bootstrap;不使用 `--use`,因为发布命令会显式选择 Builder:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
docker buildx create \
|
|
171
|
+
--name idp-oci \
|
|
172
|
+
--driver docker-container \
|
|
173
|
+
--driver-opt 'image=<approved-buildkit-image@sha256:digest>'
|
|
174
|
+
docker buildx inspect --bootstrap idp-oci
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
将官方核验后的值写入仓库外 `$IDP_CONFIG_DIR/.env`:
|
|
178
|
+
|
|
179
|
+
```dotenv
|
|
180
|
+
IDP_OCI_LAYOUT_ORAS_FALLBACK_ENABLED=true
|
|
181
|
+
IDP_OCI_LAYOUT_BUILDER=idp-oci
|
|
182
|
+
IDP_ORAS_BINARY_PATH=tools/oras/oras
|
|
183
|
+
IDP_ORAS_VERSION=1.3.0
|
|
184
|
+
IDP_ORAS_SHA256=sha256:<approved-64-hex-digest>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
开启后,发布器会在 Docker 构建前复验 Config Dir containment、普通单硬链接、owner-only 读/执行权限、文件大小、SHA-256 和 `oras version`,并仅用无 `--bootstrap` 的 `docker buildx inspect <builder>` 检查 driver、running nodes 与双平台联集。正常情况下仍优先走 Buildx 双平台 `--push`;开启回退时 direct push 和 Layout 导出都固定使用同一 `--builder`,只有前者进程失败且尚未进入配置 Apply 时,才以相同 Dockerfile/context、Build Args、Secret、四个发布标签、`--provenance=mode=max` 和固定 SBOM generator 导出仓库外临时 OCI Layout,再固定执行 `oras cp --from-oci-layout ... --concurrency 1`。发布器不向命令传入明文凭据,也不自行读取或复制 Docker 凭据配置,且不追加 `--plain-http`/`--insecure`;宿主必须已按企业流程登录目标 Registry,由 ORAS 的标准宿主凭据发现完成认证。ORAS 返回成功仍不被直接信任,远端 OCI Index digest、双平台数量和 labels 必须与 Layout metadata及不可变 Plan 完全一致,才会生成不含宿主路径和 Secret 的 Artifact。任一步失败都不修改镜像配置或切换运行态,metadata、临时 npmrc 和 Layout 必须清理完成。若当前 release 所有远端 tag 都可收养,则不执行 ORAS 版本探测、Builder inspect 或任何 Buildx 构建。
|
|
188
|
+
|
|
189
|
+
已有活动 Profile 更新时,脚本会在镜像 Apply 前自动创建备份、校验摘要并执行隔离恢复演练;`foundation` 分别生成 `tech-only` 与 `registry` 两个可独立恢复的 Generation。备份证据写入不可变 Deployment Plan 与最终回执。后续阶段失败时只在 `.env` 与 `images.lock.json` 仍等于本次 Apply 结果时恢复 Preimage,用户并发修改不会被覆盖;受管 Binding 使用独立 CAS 令牌恢复;已有活动 Profile 会恢复并重新验收,首次部署则停止本次启动的容器。若数据库迁移导致旧运行态无法重新通过验收,命令会返回 `IDP_RELEASE_RECOVERY_REQUIRED` 并给出已验证的备份 Generation ID,数据恢复仍需显式确认,不会因普通发布失败而静默覆盖生产数据。
|
|
190
|
+
|
|
191
|
+
`release deploy core` 会额外构建 Flow/Portal,并对 Portal 只执行受控的 `yarn install --immutable`、`yarn tsc`、`yarn build:backend` Prebuild。P0 不伪造 Dyyto/Bench 事实;Core 在发布前仍必须按本文正式流程导入两类 Snapshot,缺失时由 `doctor core --require-configured` fail-closed。
|
|
192
|
+
|
|
193
|
+
`IDP_RUNTIME_PLATFORM` 是当前宿主实际运行容器的平台:Intel 验证机使用 `linux/amd64`,Mac M4 使用 `linux/arm64`。它与镜像发布门禁是两类事实:为保证同一 Release 能先在 Intel 验证机预验收、再由 Mac M4 原生接收,镜像锁固定要求每个镜像同时包含 `linux/amd64`、`linux/arm64`,不能用单平台镜像、宿主仿真或修改运行平台绕过。
|
|
194
|
+
|
|
195
|
+
因此 Intel 验证机首次运行上面的一键命令会构建并推送双平台镜像;Mac M4 检出相同版本源码、使用相同 Profile 与 OCI 仓库根后,只需 `docker login` 并再次运行同一条 `./deploy.sh`。脚本会核对 Registry 发布身份、收养已存在的 OCI Index,并按 arm64 原生运行,不需要复制 Intel 机器的 Secret 或整个 Config Dir。
|
|
196
|
+
|
|
197
|
+
`images lock` 已包含在一键入口中。它先解析同一个 OCI 引用的 `linux/amd64`、`linux/arm64` Manifest,并要求两次解析得到同一个 Index digest;任一平台缺失、tag 在检查期间漂移或证据不一致时,会在任何配置写入前失败。成功后才在 `$IDP_CONFIG_DIR/snapshots` 保存 `.env`/旧锁 Preimage,并原子生成权限为 `0600` 的 `images.lock.json` v2,逐镜像记录原始引用、digest 固定引用和双平台证据。相同语义重复执行不会改写锁文件。单独运行 `node bin/idpctl.mjs images lock` 只用于高级诊断或旧锁迁移,不是正常发布步骤。
|
|
198
|
+
|
|
199
|
+
`images.lock.json` 是部署镜像事实,不是普通缓存。`doctor --require-configured` 会强制校验整体摘要、`.env` 一致性、双平台证据和当前 Profile 的必需镜像,不能只靠镜像字符串中存在 digest 就放行。旧版 v1 单平台锁会对普通 `doctor/pull/up` fail-closed;只有 `release deploy` 的升级事务可以在 Apply 前只读接受 v1,以便先完成旧运行态备份和隔离恢复演练。不要手工改锁或只替换 tag/digest,应以新 SemVer 执行 `./deploy.sh <profile> --version <new-semver>`,由标准发布链重建双平台镜像、保存 Preimage 并迁移到 v2。
|
|
200
|
+
|
|
201
|
+
默认 `./deploy.sh` 直接发布 `foundation`,同时启动 Registry 与 Tech。完整 `core` 需要 Dyyto Package Catalog 与 Bench Environment Snapshot 两类真实输入;这两类事实不由 idp-deploy 伪造。先完成默认发布,再按以下正式导出链准备 Snapshot,最后执行 `./deploy.sh core`。各 Profile 使用同一实例名和数据根,不能并行当作多套环境;多环境必须使用不同的 `IDP_CONFIG_DIR`、`IDP_INSTANCE_ID` 和五个互不重叠的数据根。
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# ./deploy.sh 已完成foundation部署、verify和bindings sync。
|
|
205
|
+
dyyto catalog export --output - | node bin/idpctl.mjs sources import dyyto --stdin
|
|
206
|
+
|
|
207
|
+
export BENCH_REPOSITORY_DIR=/absolute/path/to/bench
|
|
208
|
+
(
|
|
209
|
+
cd "$BENCH_REPOSITORY_DIR"
|
|
210
|
+
corepack pnpm --filter @bench/benchctl benchctl export environment \
|
|
211
|
+
--config-dir "$IDP_CONFIG_DIR" \
|
|
212
|
+
--environment private \
|
|
213
|
+
--output "$IDP_CONFIG_DIR/imports/bench-environment-snapshot.candidate.json"
|
|
214
|
+
)
|
|
215
|
+
node bin/idpctl.mjs sources import bench \
|
|
216
|
+
--file "$IDP_CONFIG_DIR/imports/bench-environment-snapshot.candidate.json"
|
|
217
|
+
# 两类Snapshot准备完毕后,统一构建并发布Tech、Flow、Portal,再切换完整Core:
|
|
218
|
+
./deploy.sh core
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
普通 `up` 只能重入当前活动 Profile;跨 Profile 必须使用 `switch`。部署控制面会把活动 Profile、精确服务集合和配置/渲染摘要写入仓库外 Runtime State,切换时调用 Compose `--remove-orphans`。`verify` 对服务集合做严格相等检查,多一个旧容器也会失败。`down` 同样只能针对当前活动 Profile,避免拿局部 Compose 文件误停另一套运行服务。
|
|
222
|
+
|
|
223
|
+
## SmartGo 受管业务 Profile
|
|
224
|
+
|
|
225
|
+
SmartGo 使用与 IDP 相同的 Config Dir、镜像锁、Plan/Preimage、备份恢复和验证内核,但保持独立 Compose 片段。已有 Core 需要继续运行时应选择组合 Profile,不能切换到只包含业务闭包的 `business-smartgo`:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
# 仅部署SmartGo业务闭包
|
|
229
|
+
./deploy.sh business-smartgo
|
|
230
|
+
|
|
231
|
+
# 已有Core时,保留Core并加入SmartGo
|
|
232
|
+
./deploy.sh core-smartgo
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
源码目录名严格为与本仓同级的 `smartGO`。构建只从 `$IDP_CONFIG_DIR/components/smartgo/.env` 声明的 `SMARTGO_NPMRC_FILE` 读取 npm 凭据;该文件不会进入运行时投影。该 npmrc 除空行和整行注释外,只允许各一条 `registry`、`@aipt:registry`、`@bench:registry` 和唯一回环 authority 的 `_authToken`;所有端点必须精确为 `http://127.0.0.1:4873/`、`http://localhost:4873/` 或 `http://[::1]:4873/`,任何 HTTPS、其他端口、额外凭据、Scope、代理或未知键都会在 Docker 构建前被拒绝。发布器只在仓库外 Runtime Root 创建权限为 `0600` 的临时副本:默认公共 Registry 固定使用不携带 Token 的 `http://host.docker.internal:4873/`,`@aipt`/`@bench` 私有 Scope 与 Token authority 固定使用 `smartgo-private-registry.internal:4873`,两个别名都由 BuildKit 映射到宿主。构建成功或失败后临时副本都会立即删除,不回写 Config Dir,也不输出 Token。当前本机非 Git 源码验证使用确定性的 `.dockerignore` 文件清单与前后 CAS 复验;企业生产发布必须使用可追溯 Git revision,并设置 `IDP_ALLOW_NON_GIT_SMARTGO_SOURCE=false`。
|
|
236
|
+
|
|
237
|
+
SmartGo Dockerfile 的冷缓存 `corepack prepare` 也受发布前门禁保护:它必须在同一条 `RUN` 指令中立即设置 `COREPACK_NPM_REGISTRY=http://host.docker.internal:4873`,不得声明 Corepack Token/用户名/密码,也不得通过 `COREPACK_INTEGRITY_KEYS=0` 或空值关闭完整性校验。
|
|
238
|
+
|
|
239
|
+
唯一 frozen install RUN 是 npmrc Secret 与认证依赖网络的最后允许点。frozen install 后先固定执行 `pnpm --filter @smartgo/persistence db:generate >/dev/null`,让 Prisma 6.19.3 通过自身 checksum 物化目标 Alpine 引擎;随后才执行一次 `@smartgo/persistence --prod deploy --legacy /tmp/smartgo-persistence-runtime-closure-preflight >/dev/null` 并在同一 RUN 立即删除临时目录。该窗口复用已有的严格 npmrc,不增加 Secret、Registry route、`pnpm view`、手工引擎下载、checksum-ignore 或 engine mirror/path override;临时产物不能跨层、被 COPY 或进入最终镜像。随后的 build/package 与 runtime assembly 会在断网条件下重复 Prisma generate,以证明只复用已校验的冻结引擎。
|
|
240
|
+
|
|
241
|
+
安装窗口之后的 build/package RUN 与 runtime assembly RUN 还必须分别显式使用 BuildKit `--network=none`;前者不允许 mount,后者只能在 `--network=none` 之后挂载固定 pnpm store cache。Dockerfile 必须且只能有这两个断网 RUN,`--network=host`、显式 default、错序或其他网络例外都会在 Buildx 前被拒绝。九条 `pnpm --offline` 仍然保留:包管理器离线解析和 BuildKit 网络隔离是两条独立证据。
|
|
242
|
+
|
|
243
|
+
所有用于生成 runtime 产物的 `pnpm deploy` 都必须显式使用 `--offline` 与 `--prod`,并按 `api-contracts`、`domain`、`application`、`object-store`、`platform-session`、`persistence`、API、Agent Runtime、Worker 的固定顺序集中在 build/package 之后的独立单一 RUN;该 RUN 只能挂载固定 pnpm store cache,不能混入 build/package 或其他 mount。发布前门禁会从 Persistence 与三个 Service 根沿 production Workspace 依赖递归推导同一组六 Package/三 Service 闭包,不能靠漏复制依赖来缩小镜像。九条 deploy 后必须清理 Service 源码和测试产物,用 runtime 闭包内的 Prisma CLI 对随包 Schema 执行 `generate`,再以受控 sanitizer 精确删除 Prisma peer snapshot 带入的 TypeScript 6.0.3,并递归证明九个入口、全部 symlink、开发工具拒绝集合。sanitizer 后还要真实执行 Prisma CLI、tsx 版本命令并导入 Persistence production 模块,证明局部 Prisma Client 已初始化且不建立数据库连接。最终 runtime 只允许整体复制受控 `/runtime/packages`、三个 Service production deploy、三个 Next standalone、Config Dir 解析器与三个最小运行入口;不得复制 sanitizer、Package/Workspace 源码根、Vitest、TypeScript、类型包或 `@aipt/testkit`。
|
|
244
|
+
|
|
245
|
+
strict-offline deploy 的 metadata authority 由三条无凭据参数固定:`--config.registry=http://host.docker.internal:4873/`、`--config.@aipt:registry=http://smartgo-private-registry.internal:4873/` 和 `--config.@bench:registry=http://smartgo-private-registry.internal:4873/`。它们必须在 `pnpm --offline` 后按该顺序各出现一次;额外 Registry、Proxy、userconfig 或任何凭据覆盖都会在 Buildx 前被拒绝。
|
|
246
|
+
|
|
247
|
+
受管模式固定 `SMARTGO_DEPLOYMENT_MODE=idp-managed`、`SMARTGO_AUTH_MODE=local-open`、`NODE_ENV=development`,只允许宿主回环 HTTP,不要求 OIDC,也不能以该模式扩大到内网或公网。三个 Next.js 站点使用独立 Origin,避免未配置 `basePath` 时发生 `/_next/*` 资产冲突:
|
|
248
|
+
|
|
249
|
+
```text
|
|
250
|
+
Gotology http://127.0.0.1:13000/
|
|
251
|
+
Operations http://127.0.0.1:13101/
|
|
252
|
+
Studio http://127.0.0.1:13002/
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
SmartGo 的 API、Agent Runtime、Worker、三组站点和迁移任务使用同一个 digest 固定镜像;长期服务不发布宿主端口,只经 Edge 到达。运行容器只读挂载当前 SmartGo 投影到 `/run/idp-config`,不挂载整个 Config Dir、业务源码或构建 npmrc。PostgreSQL 使用独立 `smartgo` 角色/数据库,MinIO 数据位于 `$IDP_DATA_DIR/smartgo/object-store`;对象下载的 `/smartgo-artifacts/*` 路由保持原始 Path 与 Host 转发,供 S3 签名校验使用。
|
|
256
|
+
|
|
257
|
+
当前 `local-open` 固定使用 `SMARTGO_STUDIO_STATE_MODE=ephemeral-fixture`,Studio 本地状态目录固定为容器内 `/run/smartgo/studio`。该目录是仅挂给 `smartgo-studio` 的 256 MiB 非 root tmpfs,容器重建后内容必然丢失;它不进入 `$IDP_DATA_DIR`,也不属于 Backup Generation 或 Restore Candidate。需要保留的业务事实必须进入受管 `smartgo` PostgreSQL 数据库或 `smartgo-artifacts` 对象存储,不能依赖此 fixture 目录。
|
|
258
|
+
|
|
259
|
+
## 外部配置安全模型
|
|
260
|
+
|
|
261
|
+
- `IDP_CONFIG_DIR` 必须以原始绝对路径提供并位于仓库外,目录权限最大 `0700`;相对路径不会根据当前工作目录自动展开。
|
|
262
|
+
- `.env` 权限最大 `0600`,Secret/私钥最大 `0600`,公开证书最大 `0644`。
|
|
263
|
+
- 部署工具必须由非 root 账号运行,`IDP_CONTAINER_UID/GID` 必须与该部署用户一致;这样宿主生成的 `0400` 只读投影在原生 Linux 与 Docker Desktop 中都由同一数值 UID/GID 读取,不会出现只在 Mac 上偶然可用的权限假象。
|
|
264
|
+
- 拒绝符号链接、硬链接、路径逃逸、glob、重复变量、`export`、命令展开和变量展开。
|
|
265
|
+
- 变量名疑似包含 `PASSWORD`、`SECRET`、`TOKEN`、`API_KEY`、`PRIVATE_KEY` 或 `ACCESS_KEY` 时,只允许 `*_FILE`。
|
|
266
|
+
- 配置、数据、备份、日志、运行时与恢复根必须两两分离,且不能位于 Git 仓库内。
|
|
267
|
+
- 上述运行根会先解析真实路径,并在每次写操作前重新拒绝符号链接、路径重叠、错误 Owner 和过宽权限;数据库子目录允许由容器内 PostgreSQL UID 持有。
|
|
268
|
+
- 容器只挂载按 Profile 和事实摘要生成的内容寻址不可变代次,不直接看到整个宿主机配置根。重复 `doctor/config/verify` 会复用同一代次,不会替换正在挂载的目录;Config Dir 变化后 `verify` 会要求先执行 `switch/up`,不会把新投影冒充已运行事实。
|
|
269
|
+
- 调用 Docker Compose 时会清除宿主进程中继承的其他 `IDP_*` 变量,只保留已校验的 `IDP_CONFIG_DIR` 与生成目录;镜像、端口和数据路径不能绕过外部 `.env` 注入。
|
|
270
|
+
- 自动生成的密码和 Token 使用加密安全随机数;外部 Provider 凭据、TLS 私钥和 Registry 用户密码永不代填。
|
|
271
|
+
- Dyyto、Bench 与 Forge 发布只读取各自 `components/<name>/.env` 声明的 `*_NPMRC_FILE`;Registry scope、地址和 Token 必须一起放在组件专属的外部 `0600` npmrc 中,禁止仓库内 npmrc、`HOME` 回退或明文 Token 参数。
|
|
272
|
+
|
|
273
|
+
`secrets/infrastructure/registry/htpasswd` 是首次启动种子;实时文件位于 `IDP_DATA_DIR/registry/auth/htpasswd`。之后通过标准 `npm adduser --registry http://127.0.0.1:4873` 管理用户,Registry 允许匿名安装,但发布和撤销必须认证。
|
|
274
|
+
|
|
275
|
+
`IDP_REGISTRY_PRIVATE_SCOPES` 默认并强制至少包含 `@aipt,@bench`。生成的 Verdaccio 配置会为每个私有 Scope 建立高优先级规则且不包含 `proxy`,缺失的内部包不会查询 npmjs;其他公开包仍可按普通代理规则访问上游。新增公司私有 Scope 时只修改外部 `.env`,再执行 `config init`、`doctor` 和 `switch/up`,不得在模板中手改 Verdaccio YAML。
|
|
276
|
+
|
|
277
|
+
## HTTP、TLS 与内网开放
|
|
278
|
+
|
|
279
|
+
初始 HTTP 配置:
|
|
280
|
+
|
|
281
|
+
```dotenv
|
|
282
|
+
IDP_HTTP_ENABLED=true
|
|
283
|
+
IDP_HTTP_BIND=127.0.0.1
|
|
284
|
+
IDP_TLS_ENABLED=false
|
|
285
|
+
IDP_NETWORK_POLICY_MODE=loopback-only
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
默认安全边界是宿主机回环端口,Caddy 不使用容器内 `remote_ip=127/8`,因为 Docker Desktop NAT 后它看到的是桥接地址,会错误拒绝本机请求。
|
|
289
|
+
|
|
290
|
+
Edge 同时连接仅供后端服务通信的 `internal` 网络和只包含 Edge 的独立 Ingress bridge。后者用于可靠发布宿主端口,避免 Docker Engine 29 对“只连接 internal bridge”的容器保留 `HostConfig.PortBindings` 却不建立实际端口转发;PostgreSQL 与 Tech 仍只存在于 internal 网络。Edge 继续以非 root、只读根文件系统、`cap_drop=ALL` 和 `no-new-privileges` 运行,只恢复官方 Caddy 二进制执行所需的 `NET_BIND_SERVICE`。健康检查请求容器回环上的真实 `/live`,不再用“Caddyfile 语法有效”冒充服务可用。
|
|
291
|
+
|
|
292
|
+
需要供公司内网访问时,必须同时完成:
|
|
293
|
+
|
|
294
|
+
```dotenv
|
|
295
|
+
IDP_NETWORK_POLICY_MODE=host-firewall
|
|
296
|
+
IDP_HTTP_BIND=0.0.0.0
|
|
297
|
+
IDP_TLS_ENABLED=true
|
|
298
|
+
IDP_HTTPS_BIND=0.0.0.0
|
|
299
|
+
IDP_ALLOWED_CIDR=10.20.0.0/16
|
|
300
|
+
IDP_HOST_FIREWALL_EVIDENCE_FILE=trust/host-firewall-policy.txt
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
将真实办公网/VPN 防火墙规则或变更单证据写入对应文件并配置证书后,`doctor --require-configured` 才会放行。`IDP_ALLOWED_CIDR` 是宿主机防火墙契约,不是假装由容器内 Caddy 执行的过滤器;全网 CIDR 会被拒绝。无 OIDC 的初始模式绝不能暴露到公网。Registry 当前是 HTTP 直连能力,强制保持回环监听;需要内网访问时必须另行放到 TLS 反向代理之后。
|
|
304
|
+
|
|
305
|
+
启用 TLS:
|
|
306
|
+
|
|
307
|
+
```dotenv
|
|
308
|
+
IDP_TLS_ENABLED=true
|
|
309
|
+
IDP_HTTPS_BIND=0.0.0.0
|
|
310
|
+
IDP_PUBLIC_HOST=idp.example.internal
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
证书和私钥默认位于:
|
|
314
|
+
|
|
315
|
+
```text
|
|
316
|
+
$IDP_CONFIG_DIR/certs/tls-certificate.pem
|
|
317
|
+
$IDP_CONFIG_DIR/secrets/infrastructure/edge/tls-private-key.pem
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
当前轻量模式固定 `IDP_IDENTITY_MODE=anonymous-private`、`IDP_OIDC_ENABLED=false`,不部署 Keycloak。OIDC issuer、client、group claim、group mapping 和 client-secret 文件引用已经作为通用升级边界保留,但当前版本若把 OIDC 标记为启用会 fail-closed,不能用几项环境变量伪装成已接入身份执行面。Tech 匿名读取开放,所有 Operator 写接口必须携带 `TECH_OPERATOR_TOKEN_FILE` 中的 Bearer Token。受管部署固定 `TECH_OPERATOR_LOOPBACK_ONLY=false`,因为请求经 Edge 转发后 Tech 看到的是容器网地址,不能把 `X-Forwarded-For` 或伪造“回环来源”当授权;真正写门禁是强随机 Token。默认 HTTP 仍只绑定宿主回环,扩大到内网时必须同时启用 TLS 与宿主防火墙。引入企业 SSO 时优先复用公司 IdP 的标准 OIDC/OAuth2,不在此仓库自研认证协议。
|
|
321
|
+
|
|
322
|
+
## Tech 运行合同
|
|
323
|
+
|
|
324
|
+
Tech 镜像必须消费组件投影入口 `IDP_COMPONENT_CONFIG_DIR=/run/idp-config`。该目录本身为 `0700`,包含 `.env`、`config/` 以及相对于 `.env` 解析的 `secrets/...`;敏感投影为 `0400`,普通配置为 `0444`。部署端不会把组件子树冒充全局 `IDP_CONFIG_DIR`,也不会把完整宿主配置根挂入容器。
|
|
325
|
+
|
|
326
|
+
PostgreSQL 每次部署都会先运行一次非 root Bootstrap,再允许 Tech/Portal 启动。Bootstrap 只挂载当前不可变代次内的三个 `0400` 数据库 Secret,幂等收敛应用角色密码、最小角色属性、危险成员关系、数据库 Owner、PUBLIC 连接权限和 public schema 权限;长期运行的 PostgreSQL 容器只持有其自身启动所需的 superuser Secret。数据库密码与组件连接 URL 必须同步,`doctor` 会在切换前拒绝不一致的轮换状态。
|
|
327
|
+
|
|
328
|
+
私有 Git Source 的动态凭据使用 `TECH_SOURCE_CREDENTIALS_MANIFEST_FILE=config/source-credentials.json`。`config init` 创建的确定性初值是 `{"schemaVersion":1,"files":[]}`,公共仓库无需伪造凭据即可启动。清单只能包含最多 200 个、唯一且字典序升序的安全 basename;实际源文件只允许位于 `$IDP_CONFIG_DIR/components/tech/secrets/source-credentials/<basename>`。渲染器只把清单列出的文件投影到容器同名专属子树,未列出的文件、固定 database/operator/OpenAI Secret、保留名、空文件、路径、软硬链接都会 fail-closed。源文件为 `0600`,运行投影为 `0400`。
|
|
329
|
+
|
|
330
|
+
正式运行端口固定为 `TECH_BIND_HOST=0.0.0.0`、`TECH_PORT=3698`。容器健康检查使用 `/ready`,外部验收同时检查 `/knowledge/live` 和 `/knowledge/ready`。P0 AI 只接入 Tech 已真实支持的 Embedding:显式设置 `TECH_EMBEDDING_ENABLED=true` 后,`OPENAI_BASE_URL_FILE`、`OPENAI_API_KEY_FILE` 和 `OPENAI_EMBED_MODEL` 才成为必填;关闭时不需要 Provider Secret。Chat、Image 和对象正文/附件存储目前没有执行面;唯一对象能力键固定为 `TECH_OBJECT_STORAGE_MODE=none`,不生成任何 S3/OSS endpoint 或 credential 伪配置。当前可恢复边界是 PostgreSQL 数据、Git Knowledge Release pins 与仓库外配置;真正引入对象存储时必须先补版本化 Binding、对象清单与恢复验收,再增加配置键并允许开启。
|
|
331
|
+
|
|
332
|
+
## Flow 与 Portal 的真实数据源
|
|
333
|
+
|
|
334
|
+
Flow 默认使用 `FLOW_MODE=snapshot`:Dyyto PackageRelease Catalog 和 Bench Agent Environment 通过外部配置目录内的只读快照输入;Tech 通过 HTTP 读取,必要时可将 `FLOW_TECH_REQUIRED=false` 并提供经过校验的 Tech Context Bundle。Flow 是无状态编排层,不拥有 PostgreSQL,也不在这里配置 OpenAI Provider。
|
|
335
|
+
|
|
336
|
+
Portal 同样只把 Tech、Flow 当 HTTP 上游,把 Dyyto、Bench 当文件投影。`PORTAL_FIXTURE_FALLBACK=false` 是 private/production 强制默认;fixture fallback 只允许 `IDP_ENVIRONMENT=development` 显式开启,不能拿演示数据冒充生产事实。
|
|
337
|
+
|
|
338
|
+
推荐的无共享源码同步方式如下。Dyyto 只向 stdout 生成严格投影,由部署控制面校验摘要、保存 Preimage、原子分发到 Flow/Portal。Bench Snapshot 的严格顺序是 `switch foundation` → `verify foundation` → `bindings sync foundation` → Bench `export environment` → `sources import bench`:
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
dyyto catalog export --output - | node bin/idpctl.mjs sources import dyyto --stdin
|
|
342
|
+
|
|
343
|
+
node bin/idpctl.mjs switch foundation
|
|
344
|
+
node bin/idpctl.mjs verify foundation
|
|
345
|
+
node bin/idpctl.mjs bindings sync foundation
|
|
346
|
+
|
|
347
|
+
# 在Bench仓库根目录执行;benchctl是@bench/benchctl包提供的仓库CLI,不要求全局安装。
|
|
348
|
+
corepack pnpm --filter @bench/benchctl benchctl export environment \
|
|
349
|
+
--config-dir "$IDP_CONFIG_DIR" \
|
|
350
|
+
--environment private \
|
|
351
|
+
--output "$IDP_CONFIG_DIR/imports/bench-environment-snapshot.candidate.json"
|
|
352
|
+
node bin/idpctl.mjs sources import bench \
|
|
353
|
+
--file "$IDP_CONFIG_DIR/imports/bench-environment-snapshot.candidate.json"
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
`bindings sync` 不从 `.env` 猜测“应该存在”的服务。它要求活动 Profile 为 `active`、Profile/服务集合与命令一致、活动状态的 `configDigest` 等于当前 `doctor` 摘要,并要求最近一次 `verify` 回执证明相同配置、渲染代次和全部服务健康。当前 `foundation` 会确定性生成:Tech 的 HTTP/TLS Edge Binding、Tech 容器内 PostgreSQL Binding,以及 Bench 消费的 npm Registry `anonymous-read` Binding。Registry 发布 Token 没有可靠事实来源,因此不会被伪造为 `authToken`。
|
|
357
|
+
|
|
358
|
+
受管文件只位于 `$IDP_CONFIG_DIR/bindings/idp-deploy`;`bindings/identity` 和用户自建 Binding 不会被改动。生成过程先在 `$IDP_CONFIG_DIR/imports` 暂存并写入摘要 Manifest,再做目录级发布;更新前将完整旧目录移动到 `snapshots/binding-preimages`。若受管 YAML、Manifest、文件集合、权限或 Owner 被人工修改,命令会 fail-closed,不覆盖修改。导入器随后再次检查严格字段、self-digest、排序/唯一性、Bench 的文件引用证据、宿主路径扫描以及 configuration 中不得内联 Secret;失败时不会留下部分更新。三个项目之间只交换版本化 JSON 合同,不读取彼此仓库。
|
|
359
|
+
|
|
360
|
+
## 把本地项目接入 Docker 开发工作台
|
|
361
|
+
|
|
362
|
+
项目源码只读挂载,应用配置分别位于仓库外。先初始化 candidate:
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
export IDP_CONFIG_DIR=/Users/Shared/company/idp/config
|
|
366
|
+
node bin/idpctl.mjs workbench projects init
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
编辑 `$IDP_CONFIG_DIR/workbench/projects.candidate.json`。monorepo 中每个应用必须声明自己的 Config Dir:
|
|
370
|
+
|
|
371
|
+
```json
|
|
372
|
+
{
|
|
373
|
+
"schemaVersion": 1,
|
|
374
|
+
"projects": [
|
|
375
|
+
{
|
|
376
|
+
"id": "apex",
|
|
377
|
+
"hostPath": "/Users/dyyto/Desktop/works/sources/spaces/apex",
|
|
378
|
+
"configBindings": [
|
|
379
|
+
{
|
|
380
|
+
"relativePath": "apps/client",
|
|
381
|
+
"applicationConfigDir": "/Users/Shared/company/apps/apex-client/local"
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
"relativePath": "apps/server",
|
|
385
|
+
"applicationConfigDir": "/Users/Shared/company/apps/apex-server/local"
|
|
386
|
+
}
|
|
387
|
+
]
|
|
388
|
+
}
|
|
389
|
+
]
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
各 Config Dir 应先由 `app config-init` 创建并由操作者填写 `.env`、`secrets/` 和 `certs/`。随后生成不可变 Plan、应用并验证:
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
node bin/idpctl.mjs workbench projects plan \
|
|
397
|
+
--dyyto-root /Users/dyyto/Desktop/works/sources/spaces/dyyto \
|
|
398
|
+
--file "$IDP_CONFIG_DIR/workbench/projects.candidate.json" \
|
|
399
|
+
--output "$IDP_CONFIG_DIR/plans/workbench-projects/local.json"
|
|
400
|
+
node bin/idpctl.mjs workbench projects apply \
|
|
401
|
+
--plan "$IDP_CONFIG_DIR/plans/workbench-projects/local.json"
|
|
402
|
+
node bin/idpctl.mjs workbench projects verify
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
工作台进入 `project-readonly` 模式:允许登记项目和执行 Inspect、Integrations、Maintenance、运行态读取;不写项目 `.env/.env.local`,不执行 Upgrade、Apply、Install 或任意命令。配置状态来自项目内 `config/application-config.json` 与 `.dyyto/application-config-binding.json`,值只显示脱敏文本,Secret/证书只显示配置状态、摘要和安全元数据。
|
|
406
|
+
|
|
407
|
+
## 更新与回滚
|
|
408
|
+
|
|
409
|
+
正常更新继续使用同一个入口;不再手工改镜像 tag,也不再逐项执行 `images lock/pull/up`:
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
docker login idp-registry.cn-shanghai.cr.aliyuncs.com
|
|
413
|
+
./deploy.sh --config-dir /Users/Shared/company/idp/config
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
一键入口会自动生成并验证更新前备份。以下命令保留给独立恢复演练、事故处置或变更审计:
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
node bin/idpctl.mjs backup create core
|
|
420
|
+
node bin/idpctl.mjs backup verify <generation-id>
|
|
421
|
+
node bin/idpctl.mjs restore test <generation-id>
|
|
422
|
+
node bin/idpctl.mjs restore apply <generation-id>
|
|
423
|
+
node bin/idpctl.mjs restore verify <candidate-id>
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
发布失败时脚本会自动恢复可证明未被并发修改的镜像配置、活动 Profile 和受管 Binding。数据库迁移必须由各应用使用 expand/contract 策略并维持向后兼容;不能只回滚镜像而假设 Schema 自动回退。若旧版本无法恢复健康,按错误中返回的 Generation ID 执行显式 `restore apply`、`restore verify` 和带完全相同候选 ID 的 `restore promote --confirm`。
|
|
427
|
+
|
|
428
|
+
## 备份与恢复
|
|
429
|
+
|
|
430
|
+
`backup create <profile>` 从 Profile 推导数据集:Portal 只备份 Backstage 数据库,Tech-only 只备份 Tech 数据库,Registry 只归档制品,`business-smartgo` 协调备份独立 SmartGo 数据库与 MinIO 对象数据,Core/Full 才包含全部 Core 数据库与 Registry;无状态 Flow 没有可备份数据集。`core-smartgo` 在完整 Core 数据之外同时包含 SmartGo 数据库与对象存储。`pg_dump` 与 `pg_restore` 都通过文件描述符流式传输,已覆盖 101 MiB 回归,不会把增长后的 Dump 整体放入 Node 输出缓冲区。Registry 和 SmartGo 对象存储仅在各自归档窗口短暂停顿。每个 generation 固化 Profile、预期数据集、大小、SHA-256 和 Manifest 摘要,并绑定到运行目录中的独立、不可覆盖、摘要串联回执。
|
|
431
|
+
|
|
432
|
+
包含 Tech 时,备份会先停止 Tech 写入入口,在同一停写窗口用镜像内正式命令 `knowledge-service backup-boundary` 导出严格的 `tech.backup-boundary.v1`,随后立即创建 Tech custom dump,再恢复服务。Generation 同时绑定边界文件摘要、边界事实摘要与 dump 摘要;边界覆盖 migration checksum、四类业务计数、active index revision、每个 Source 的 commit/treeDigest,并强制对象存储仍为 `none`。边界命令失败、输出字段漂移或摘要不一致都会使整个 staging 代次进入 failed,不会发布一个只有 dump 的“半安全”备份。
|
|
433
|
+
|
|
434
|
+
`restore test` 不覆盖生产数据库:它为每个 Dump 创建隔离临时数据库,验证非空表、对象 Owner 和应用角色连接权限,再检查 Registry 归档的路径与链接安全。Tech 数据库还会由同一 Tech 镜像连接隔离库重新导出边界,必须与备份事实摘要完全相等。生产 promote 会先只启动 PostgreSQL + Tech 完成同样核对,再恢复其他入口;不一致会自动切回 Preimage。该自动证据证明数据库内保存的 Git pin/treeDigest 一致,但不伪装成 Git 远端当前可达;远端重取、仓库权限和 treeDigest 重算仍须由企业 Git 网络内的独立验收证据完成,回执显式记录 `external-evidence-required`。
|
|
435
|
+
|
|
436
|
+
`restore apply` 使用锁定的 PostgreSQL 镜像把备份恢复到 `IDP_RESTORE_DIR/candidates` 下的全新目录,不接触生产数据;`restore verify` 再校验候选摘要和物理数据。候选创建后若当前 Profile 的配置摘要发生变化,切换会 fail-closed,必须基于新配置重新执行 `restore apply`。只有显式提供完全相同候选 ID 时才允许切换:
|
|
437
|
+
|
|
438
|
+
```bash
|
|
439
|
+
node bin/idpctl.mjs restore promote <candidate-id> --confirm <candidate-id>
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
在 Tech-first 的 `foundation` 运行期,建议分别创建可独立切换的代次:`backup create tech-only` 保护 Tech 数据库,`backup create registry` 保护 npm 制品。即使活动 Profile 是 `foundation`,Tech 单库逻辑恢复也会使用当前活动 Profile 停启 Tech/Edge,Registry保持运行;它不会要求先退回 `tech-only`。`backup create foundation` 可用于同一时间窗的联合校验和隔离恢复演练,但因为它同时混合“单数据库逻辑切换”和“Registry目录切换”,当前不会伪装成原子生产切换;完整物理切换仍使用同时包含两个数据库的 `core/full` 代次。
|
|
443
|
+
|
|
444
|
+
切换先保存不可变 Plan。`core/full` 仍在同盘暂存区保留非目标数据,只替换完整 PostgreSQL 与 Registry 候选,并保留物理 Preimage;启动失败自动放回。`portal`/`tech-only` 单数据库候选则使用安全的逻辑路径:先把 Dump 恢复到新数据库并完成表、Owner、连接权限验收,短暂停止对应应用后以数据库重命名切换,保留 `idp_preimage_*` 数据库;新服务不健康时把新库改名隔离并自动切回 Preimage。它不会物理替换共享 PostgreSQL,也不会影响另一个数据库。Registry 候选可以独立切换且不会擦除 PostgreSQL 或 Edge 数据。成功后旧数据仍保留,必须在独立备份确认后再按企业变更流程清理。完整步骤见 `docs/restore-runbook.md`。
|
|
445
|
+
|
|
446
|
+
## M4 原生首次验收
|
|
447
|
+
|
|
448
|
+
Intel 机器可使用 `IDP_RUNTIME_PLATFORM=linux/amd64` 完成功能、备份和恢复预验收,但不能代替 M4 原生证据。正式接收必须在目标 Mac M4 上设置 `IDP_RUNTIME_PLATFORM=linux/arm64` 并执行一次原生闭环。若 Profile 包含 Registry,先把仅用于验收的发布凭据写入 `IDP_REGISTRY_ACCEPTANCE_NPMRC_FILE` 指向的仓库外 `0600` 文件,然后运行:
|
|
449
|
+
|
|
450
|
+
```bash
|
|
451
|
+
node bin/idpctl.mjs accept mac-m4 core \
|
|
452
|
+
--confirm-restart company-idp-mac-01
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
该命令拒绝非 `darwin/arm64` 宿主和非 ARM64 Linux Docker Engine,逐个检查已拉取镜像的平台,执行初始健康验证、真实备份校验、隔离恢复演练、私有 `@aipt` 临时包发布、Compose 冷停机/重启、重启后安装与清理,并写入摘要串联的验收回执。失败发生在停机后时会先尝试恢复原 Profile;验收凭据、临时 npm cache 和包工程都位于仓库外 Runtime Root。
|
|
456
|
+
|
|
457
|
+
外部配置根包含恢复数据库所需的密码和 Token,必须进入企业 Secret Manager 或加密离线备份;`idp-deploy` 不把它复制到普通数据备份中。
|
|
458
|
+
|
|
459
|
+
## 开发验证
|
|
460
|
+
|
|
461
|
+
```bash
|
|
462
|
+
npm test
|
|
463
|
+
npm run check
|
|
464
|
+
npm run lifecycle:check
|
|
465
|
+
npm run smoke:local
|
|
466
|
+
npm run smoke:smartgo
|
|
467
|
+
npm run smoke:smartgo:object-store
|
|
468
|
+
npm run test:integration
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
测试覆盖配置幂等生成、Secret 防泄漏、路径逃逸/软硬链、网络暴露门禁、按 Profile 条件校验、AMD64/ARM64 双平台镜像锁定与旧锁迁移、运行时只读投影、全局互斥、摘要链回执、Compose 组合、SmartGo 三站点与真实 Next 静态资产、MinIO 初始化、101 MiB 流式数据、原子备份、篡改检测、隔离恢复、生产切换与回滚边界。
|
package/bin/idpctl.mjs
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { main } from '../src/cli.mjs';
|
|
3
|
+
|
|
4
|
+
main(process.argv.slice(2)).catch((error) => {
|
|
5
|
+
const code = typeof error?.code === 'string' ? error.code : 'IDP_UNEXPECTED_ERROR';
|
|
6
|
+
process.stderr.write(`${code}: ${error?.message ?? String(error)}\n`);
|
|
7
|
+
process.exitCode = 1;
|
|
8
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
services:
|
|
2
|
+
edge:
|
|
3
|
+
image: ${IDP_CADDY_IMAGE}
|
|
4
|
+
platform: ${IDP_RUNTIME_PLATFORM}
|
|
5
|
+
restart: unless-stopped
|
|
6
|
+
user: ${IDP_CONTAINER_UID}:${IDP_CONTAINER_GID}
|
|
7
|
+
ports:
|
|
8
|
+
- target: ${IDP_HTTP_PORT}
|
|
9
|
+
published: "${IDP_HTTP_PORT}"
|
|
10
|
+
host_ip: ${IDP_HTTP_BIND}
|
|
11
|
+
protocol: tcp
|
|
12
|
+
- target: ${IDP_HTTPS_PORT}
|
|
13
|
+
published: "${IDP_HTTPS_PORT}"
|
|
14
|
+
host_ip: ${IDP_HTTPS_BIND}
|
|
15
|
+
protocol: tcp
|
|
16
|
+
volumes:
|
|
17
|
+
- ${IDP_RENDER_DIR}/edge:/etc/caddy:ro
|
|
18
|
+
- ${IDP_DATA_DIR}/edge/data:/data
|
|
19
|
+
- ${IDP_DATA_DIR}/edge/config:/config
|
|
20
|
+
secrets:
|
|
21
|
+
- source: tls-certificate
|
|
22
|
+
target: tls-certificate.pem
|
|
23
|
+
- source: tls-private-key
|
|
24
|
+
target: tls-private-key.pem
|
|
25
|
+
networks:
|
|
26
|
+
- idp_backend
|
|
27
|
+
# Docker Engine 29不会为仅连接internal bridge的容器发布宿主端口。
|
|
28
|
+
# 独立Ingress网络只包含Edge,后端服务仍仅存在于internal网络。
|
|
29
|
+
- idp_ingress
|
|
30
|
+
healthcheck:
|
|
31
|
+
test: [CMD, wget, --quiet, --spider, http://127.0.0.1:2019/live]
|
|
32
|
+
interval: 15s
|
|
33
|
+
timeout: 5s
|
|
34
|
+
retries: 8
|
|
35
|
+
start_period: 10s
|
|
36
|
+
read_only: true
|
|
37
|
+
tmpfs:
|
|
38
|
+
- /tmp:size=64m,mode=1777
|
|
39
|
+
init: true
|
|
40
|
+
cap_drop:
|
|
41
|
+
- ALL
|
|
42
|
+
# 官方 Caddy 二进制带 cap_net_bind_service=ep;若从 capability
|
|
43
|
+
# bounding set 中移除该能力,Linux 会在 execve 阶段返回 EPERM。
|
|
44
|
+
cap_add:
|
|
45
|
+
- NET_BIND_SERVICE
|
|
46
|
+
security_opt:
|
|
47
|
+
- no-new-privileges:true
|
|
48
|
+
|
|
49
|
+
secrets:
|
|
50
|
+
tls-certificate:
|
|
51
|
+
file: ${IDP_CONFIG_DIR}/${IDP_TLS_CERT_FILE}
|
|
52
|
+
tls-private-key:
|
|
53
|
+
file: ${IDP_CONFIG_DIR}/${IDP_TLS_KEY_FILE}
|
|
54
|
+
|
|
55
|
+
networks:
|
|
56
|
+
idp_backend:
|
|
57
|
+
internal: true
|
|
58
|
+
idp_ingress:
|
|
59
|
+
driver: bridge
|
|
60
|
+
driver_opts:
|
|
61
|
+
com.docker.network.bridge.enable_icc: "false"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
services:
|
|
2
|
+
flow:
|
|
3
|
+
image: ${IDP_FLOW_IMAGE}
|
|
4
|
+
platform: ${IDP_RUNTIME_PLATFORM}
|
|
5
|
+
restart: unless-stopped
|
|
6
|
+
user: ${IDP_CONTAINER_UID}:${IDP_CONTAINER_GID}
|
|
7
|
+
environment:
|
|
8
|
+
IDP_CONFIG_DIR: /run/idp-config
|
|
9
|
+
volumes:
|
|
10
|
+
- ${IDP_RENDER_DIR}/flow:/run/idp-config:ro
|
|
11
|
+
expose:
|
|
12
|
+
- "3710"
|
|
13
|
+
networks:
|
|
14
|
+
- idp_backend
|
|
15
|
+
- idp_egress
|
|
16
|
+
healthcheck:
|
|
17
|
+
test: [CMD, node, -e, "fetch('http://127.0.0.1:3710/live').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"]
|
|
18
|
+
interval: 15s
|
|
19
|
+
timeout: 5s
|
|
20
|
+
retries: 12
|
|
21
|
+
start_period: 30s
|
|
22
|
+
read_only: true
|
|
23
|
+
tmpfs:
|
|
24
|
+
- /tmp:size=256m,mode=1777
|
|
25
|
+
init: true
|
|
26
|
+
security_opt:
|
|
27
|
+
- no-new-privileges:true
|
|
28
|
+
cap_drop:
|
|
29
|
+
- ALL
|
|
30
|
+
|
|
31
|
+
networks:
|
|
32
|
+
idp_backend:
|
|
33
|
+
internal: true
|
|
34
|
+
idp_egress: {}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
services:
|
|
2
|
+
portal:
|
|
3
|
+
image: ${IDP_PORTAL_IMAGE}
|
|
4
|
+
platform: ${IDP_RUNTIME_PLATFORM}
|
|
5
|
+
restart: unless-stopped
|
|
6
|
+
user: ${IDP_CONTAINER_UID}:${IDP_CONTAINER_GID}
|
|
7
|
+
environment:
|
|
8
|
+
IDP_CONFIG_DIR: /run/idp-config
|
|
9
|
+
NODE_ENV: production
|
|
10
|
+
volumes:
|
|
11
|
+
- ${IDP_RENDER_DIR}/portal:/run/idp-config:ro
|
|
12
|
+
expose:
|
|
13
|
+
- "7007"
|
|
14
|
+
networks:
|
|
15
|
+
- idp_backend
|
|
16
|
+
- idp_egress
|
|
17
|
+
depends_on:
|
|
18
|
+
postgresql-bootstrap:
|
|
19
|
+
condition: service_completed_successfully
|
|
20
|
+
healthcheck:
|
|
21
|
+
test: [CMD, node, -e, "fetch('http://127.0.0.1:7007/.backstage/health/v1/readiness').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"]
|
|
22
|
+
interval: 15s
|
|
23
|
+
timeout: 5s
|
|
24
|
+
retries: 12
|
|
25
|
+
start_period: 60s
|
|
26
|
+
read_only: true
|
|
27
|
+
tmpfs:
|
|
28
|
+
- /tmp:size=256m,mode=1777
|
|
29
|
+
init: true
|
|
30
|
+
security_opt:
|
|
31
|
+
- no-new-privileges:true
|
|
32
|
+
cap_drop:
|
|
33
|
+
- ALL
|
|
34
|
+
|
|
35
|
+
networks:
|
|
36
|
+
idp_backend:
|
|
37
|
+
internal: true
|
|
38
|
+
idp_egress: {}
|