# 安装

你可以在一台干净的 Linux 主机上使用 `setup-host.sh` 脚本（或在已有 Docker 的主机上使用 `install.sh` 脚本）完成 Raise 的系统安装与初始化。部署脚本会自动生成系统密钥、启动 PostgreSQL 与 Zitadel 身份服务、执行数据库迁移与身份引导，并按照所选的环境包与组件配置拉起其余服务。安装完成后，你需要使用平台管理员账号登录身份中心并修改初始密码，随后在运营台中开通首个租户。

## 谁能用

执行安装与后续系统初始化涉及以下角色与权限要求：

| 角色 / 身份 | 账号或权限标识 | 权限说明 |
|---|---|---|
| 主机运维人员 | 主机 `root` 或 `sudo` 用户 | 具备主机系统权限，负责运行主机预检、依赖安装与部署脚本。 |
| 平台管理员 | `platform.ops`（`PLATFORM_ADMIN_EMAIL`，默认 `admin@{域名}`） | 拥有平台与运营台的完整管理权限。负责开通首个租户与运维操作。初始密码记录在 `deploy/compose/.env` 的 `PLATFORM_ADMIN_PASSWORD` 变量中，首次登录强制修改密码。 |
| 只读运营人员 | `platform.support` | 拥有运营后台的只读权限。不能开通租户（界面中不显示开通按钮，调用开通接口返回 403）。 |
| 租户管理员 | `tenant.admin`（开通租户时填写的管理员邮箱） | 拥有对应租户的组织管理权限。首次使用验证码设置密码后，可登录工作台并进入「我的组织」进行日常管理。 |

## 怎么打开

安装成功后，Raise 提供的各项服务访问地址如下：

| 模块名称 | 访问地址 | 说明 |
|---|---|---|
| 工作台 | `https://app.{域名}` | 租户成员与管理员日常办公入口。 |
| 组织应用 | 在[工作台](/guide/workspace)里打开「我的组织」 | 租户管理员配置组织成员、域名、模型与存储的入口。 |
| 身份中心 | `https://id.{域名}` | 统一身份认证中心。平台管理员在此修改初始密码。 |
| 开放 API | `https://api.{域名}` | 平台开放 API 接口（健康状态可访问 `/api/capabilities`）。 |
| 开放平台 | `https://dev.{域名}` | 开发者控制台与生态对接入口。 |
| 运营台 | `https://ops.{域名}:8443` | 平台运营管理后台。默认仅绑定 `127.0.0.1`，需通过 SSH 隧道访问。 |

> **提示**
>
> 运营台默认仅绑定本机的 `127.0.0.1` 地址。你可以在本地终端建立 SSH 隧道进行访问：
> ```bash
> ssh -L 8443:127.0.0.1:8443 user@主机IP
> ```
> 并在本地电脑的 hosts 文件中添加解析记录：`127.0.0.1 ops.{域名}`。

## 界面一览

### 终端安装完成摘要

安装过程在 Linux 终端中运行，本身不包含网页界面。当 `setup-host.sh` 脚本执行结束时，终端会打印以下摘要信息与后续指引：

| 摘要项目 | 终端输出内容与说明 |
|---|---|
| 运行状态 | 显示 `kwork is running`。 |
| 管理员凭据 | 输出平台管理员邮箱，提示通过 `grep PLATFORM_ADMIN_PASSWORD deploy/compose/.env` 查看初始密码，并标注「首次登录强制修改」。 |
| 密钥与状态文件 | 指出 `deploy/compose/.env` 与 `state/` 目录，提示「加密备份后离线保存」。 |
| 内置根证书 | 若使用内置 CA（internal TLS），提示把 `state/gateway-root-ca.crt` 导入系统的受信任根证书。 |
| 解封密钥（onprem） | 若使用 `onprem` 环境包，提示 OpenBao 解封密钥保存在 `state/openbao-init.json`，并注明「立即分发给 3–5 位保管人并删除该文件」。 |
| 后续操作指引 | 提示开通第一个租户，之后使用该租户管理员登录并在工作台里打开「我的组织」。 |

### 运营台开通租户对话框

在运营台（`https://ops.{域名}:8443`）中点击「开通租户」按钮后，界面弹出开通对话框：

| 区域 / 字段 | 类型 | 说明 |
|---|---|---|
| 对话框标题 | 文本 | 显示「开通租户」。 |
| 「租户名称」 | 输入框 | 必填。输入租户的组织名称。 |
| 「租户 ID」 | 输入框 | 必填。长度为 2–63 位，由小写字母、数字或连字符 `-` 组成，且必须以字母开头。创建后不可更改。 |
| 「管理员邮箱」 | 输入框 | 必填。租户首位管理员的登录邮箱，将被授予 `tenant.admin` 角色。 |
| 「直接返回邮箱验证码」 | 复选框 | 默认处于勾选状态。主机未配置 SMTP 邮件服务时必须勾选，提交后直接在界面上展示验证码。 |
| 操作按钮 | 按钮 | 包含「取消」按钮，以及提交按钮「开通」；未勾选验证码时按钮再拼「并发送邀请」。 |

## 常用操作

### 全新主机一键安装

在干净的 Linux 主机上，推荐使用 `setup-host.sh` 脚本完成全自动部署：

1. 连接主机终端，克隆代码仓库至 `/opt/kwork` 并进入目录：
   ```bash
   git clone <仓库> /opt/kwork && cd /opt/kwork
   ```
2. 执行主机一键安装脚本（指定基础域名与 ACME 邮箱）：
   ```bash
   sudo ./deploy/setup-host.sh --domain work.example.com --email ops@example.com
   ```
   你也可以按需附加 `--admin-email`、`--pack`、`--with`、`--tls` 等参数。
3. 若未指定 `--yes` 参数，终端会弹出交互确认：
   ```text
   continue? [y/N]
   ```
   输入 `y` 确认继续。
4. 脚本按顺序执行 8 个部署步骤（各步骤支持重入，已完成的步骤自动跳过，随机密钥仅在首次生成）：
   - 步骤 1/8 预检：检查操作系统、CPU 架构、CPU 核心数、内存、磁盘空间，以及 80、443、8443 端口是否被占用。
   - 步骤 2/8 基础软件：安装 curl、ca-certificates、git、jq、openssl 与 chrony。
   - 步骤 3/8 Docker：安装 Docker Engine 与 Compose v2，配置容器日志轮转，按需配置镜像加速。
   - 步骤 4/8 防火墙：在系统防火墙中放行 22、80、443（含 443/udp）端口。
   - 步骤 5/8 配置生成：首次调用 `install.sh` 生成 `.env` 文件（权限为 600），写入域名、TLS 模式、`OPS_BIND` 与 `SETUP_*` 变量。
   - 步骤 6/8 镜像构建：在本地构建 `kwork/kwork:<git describe>` 镜像并打上 `local` 标签（若传入 `--no-build` 则使用 BOM 清单镜像）。
   - 步骤 7/8 DNS 检查：检查 `app`、`id`、`api`、`dev`、`files`、`organization.apps`、`ops` 子域名是否解析到本机。`organization.apps` 代表全部 `*.apps` 应用（「我的组织」就在这里，没有单独的 `admin.` 站点）。若指定 `--hosts`，脚本把网关按名字提供的全部站点（含各应用与运维子站）写入本机 `/etc/hosts`。
   - 步骤 8/8 服务安装：调用 `deploy/install.sh` 脚本，附带所选的环境包与组件 profile 启动服务。
5. 等待服务就绪：脚本等待服务健康检查变为 healthy（最多约 60 秒）；若使用 internal TLS 模式，网关会导出根证书文件。
6. 查看终端打印的平台管理员初始密码，登录身份中心修改密码。
7. 建立 SSH 隧道打开运营台，开通第一个租户。

### 常用一键安装命令

针对不同部署场景，常用的安装命令如下：

```bash
# 公网最小安装
sudo ./deploy/setup-host.sh --domain work.example.com --email ops@example.com --admin-email it@example.com

# 私有化标准部署（使用 internal TLS、onprem 环境包与附加组件）
sudo ./deploy/setup-host.sh --domain work.corp --tls internal --pack onprem --with shell,obs --ops-bind 10.0.0.5

# 中国大陆网络环境安装（使用阿里云安装脚本与镜像加速）
sudo ./deploy/setup-host.sh --domain work.example.cn --mirror cn \
     --registry-mirror https://<你的ID>.mirror.aliyuncs.com

# 仅执行环境预检与配置生成（不启动容器）
sudo ./deploy/setup-host.sh --domain work.example.com --dry-run

# 已安装主机的就地更新（在 deploy/compose 目录执行，参数自动沿用 .env）
git pull && sudo ../setup-host.sh --yes
```

### 在已有 Docker 主机上安装

若主机已安装 Docker 与 Docker Compose v2，可以直接使用 `install.sh` 执行安装：

1. 进入 `deploy` 目录：
   ```bash
   cd deploy
   ```
2. 首次运行 `install.sh` 生成基础配置：
   ```bash
   ./install.sh --pack onprem --infra --shell
   ```
   若当前目录下不存在 `.env` 文件，脚本会从 `.env.example` 复制创建，将其权限设置为 600，自动生成所有随机密钥，在终端输出 `edit BASE_DOMAIN… then run ./install.sh again`，随后退出（退出码为 0）。
3. 编辑 `deploy/compose/.env` 文件，将 `BASE_DOMAIN` 修改为实际域名。
> **注意**
>
>    若 `BASE_DOMAIN` 仍保留为默认的 `work.example.com` 或为空值，脚本在下次运行时会直接拒绝执行。
4. 再次运行相同的安装命令启动服务：
   ```bash
   ./install.sh --pack onprem --infra --shell
   ```
5. 脚本按顺序执行服务初始化：
   - 启动 postgres 与 zitadel，最多等待 180 秒直至生成 `state/provisioner.pat` 凭据。
   - 执行数据库初始化脚本，创建核心数据库角色（含分析角色与可选备份角色）。
   - 执行数据库迁移，初始化 `app` 与 `audit` 数据库结构。
   - 启动基础设施服务（若指定 `--infra` 或 `--core-infra`，启动 OpenBao 与 SeaweedFS，初始化 OpenBao 并引导 S3 存储桶）。
   - 根据组件 profile 将配置回写至 `.env` 文件。
   - 执行 Zitadel 引导：创建项目、角色与 OIDC 客户端，为 `PLATFORM_ADMIN_EMAIL` 与 `OPS_ADMINS` 授予 `platform.ops` 角色，为 `OPS_SUPPORT` 授予支持角色（client secret 仅在首次创建时返回）。
   - 若指定 `--backup` 且本地无镜像，构建 `pg-backup` 镜像。
   - 执行 `up -d` 启动全部容器，重载网关配置并输出服务访问入口。

### 离线环境安装

在无外网连接的主机上，使用离线安装包执行部署：

1. 确保主机已提前通过操作系统介质安装好 Docker（含 Compose v2）、bash、coreutils、openssl、curl、jq 与 git，且 chrony/NTP 已配置指向内网时间源。
2. 如果下载到的是分片（`….tar.part-aa`、`….tar.part-ab` 等），先拼回完整的安装包，再校验哈希值：
   ```bash
   cat kwork-offline-<版本>-<架构>.tar.part-* > kwork-offline-<版本>-<架构>.tar
   sha256sum -c kwork-offline-<版本>-<架构>.tar.sha256
   ```
3. 解压离线安装包并进入目录：
   ```bash
   tar -xf kwork-offline-….tar && cd kwork-offline-…
   ```
4. 使用由外部独立安全渠道交付的公钥，校验离线包的完整性与数字签名：
   ```bash
   sudo deploy/offline/install-offline.sh --pubkey /path/bundle-signing.pub --verify-only
   ```
   脚本会验证 `SHA256SUMS` 的数字签名，逐个文件校验校验和，并严禁安装包内包含清单以外的未授权文件。
5. 执行离线安装：
   ```bash
   sudo deploy/offline/install-offline.sh --pubkey /path/bundle-signing.pub \
        --domain work.corp --tls internal --pack onprem --with backup
   ```
   你也可以附加 `--hosts` 自动将域名写入本机 `/etc/hosts`，或使用 `--ops-bind 10.0.0.5` 指定运营台监听 IP。脚本会自动核对 `images.lock` 并导入 Docker 镜像，随后以 `--offline` 模式调用 `setup-host.sh` 完成安装。
6. 其他离线选项：
   - 仅加载镜像：执行时附加 `--load-only` 参数，仅导入 Docker 镜像而不执行后续安装步骤。
   - 推送至私有仓库：使用 `--registry reg.corp:5000/kwork` 将镜像推送至内网镜像仓库，后续配合 Helm 进行部署。

### 平台管理员首次登录与密码修改

1. 在主机上查看平台管理员初始密码：
   ```bash
   grep PLATFORM_ADMIN_PASSWORD deploy/compose/.env
   ```
   初始密码格式为 `Kw1-<16位十六进制字符>!`。
2. 在浏览器中打开身份中心：
   ```text
   https://id.{域名}
   ```
3. 输入平台管理员邮箱（默认为 `admin@{域名}`）与初始密码登录。
4. 界面提示「首次登录强制修改」，按照提示输入新密码并确认。
5. （可选）创建其他运营人员账号：
   - 创建完整运营人员：在主机上执行 `deploy/scripts/create-operator.sh alice@example.com`，脚本赋予其 `platform.ops` 角色并打印一次性密码。
   - 创建只读支持人员：在主机上执行 `deploy/scripts/create-operator.sh bob@example.com --support`，脚本赋予其 `platform.support` 角色并打印一次性密码。
   - 或者在 `deploy/compose/.env` 中将已存在的平台用户登录名配置到 `OPS_ADMINS` 或 `OPS_SUPPORT` 后重新执行 `install.sh`。

> **注意**
>
> 请勿在 Zitadel 控制台中手动直接创建用户。手动创建的用户会停留在待初始化状态，无法正常使用。

### 在运营台开通首个租户

平台部署完成后，必须在运营台中开通首个租户：

1. 在本地电脑终端建立前往主机的 SSH 隧道：
   ```bash
   ssh -L 8443:127.0.0.1:8443 user@主机IP
   ```
   并在本地 hosts 文件中添加解析记录：`127.0.0.1 ops.{域名}`。
2. 打开浏览器访问运营后台：
   ```text
   https://ops.{域名}:8443
   ```
3. 使用平台管理员账号登录。
4. 在页面右上角点击「开通租户」按钮，打开「开通租户」对话框。
5. 填写开通信息：
   | 字段 | 必填 | 说明 |
   |---|---|---|
   | 「租户名称」 | 是 | 租户的组织全称，例如公司或企业名称。 |
   | 「租户 ID」 | 是 | 租户的唯一标识符。长度为 2–63 位，由小写字母、数字或连字符 `-` 组成，必须以字母开头。创建后不可更改。 |
   | 「管理员邮箱」 | 是 | 首位租户管理员的登录邮箱。系统将为其赋予 `tenant.admin` 角色。 |
   | 「直接返回邮箱验证码」 | 否 | 主机未配置 SMTP 邮件服务时请勾选（对话框默认已勾选）。系统将直接在界面展示设置密码的验证码。 |
6. 点击「开通」（未勾选验证码时按钮再拼「并发送邀请」）。
7. 开通结果处理：
   - 若未勾选验证码且配置了 SMTP：界面弹出提示「租户……已开通」，邀请邮件会自动发送给租户管理员。
   - 若勾选了验证码：界面弹出提示「租户已开通」与「请把验证码交给管理员」，并显示该租户管理员的一次性验证码，同时附带说明「未配置 SMTP，邮件不会发出。验证码只显示这一次。」。请复制该验证码并线下交付给租户管理员。
> **提示**
>
>    若输入的「租户 ID」已被使用，界面会提示「该租户 ID 已存在」，请更换租户 ID 后重试。若开通出现 502 错误，通常为身份服务未就绪，系统会自动回滚操作，可排查后重试。

### 租户管理员首次登录与激活

1. 租户管理员在浏览器中打开工作台：
   ```text
   https://app.{域名}
   ```
2. 在登录页面输入管理员邮箱账号。
3. 点击「用验证码设置密码」。
4. 输入线下获取的一次性验证码，并设置新的登录密码。
5. 登录成功后进入[工作台](/guide/workspace)。
6. 在工作台里打开「我的组织」，进入[组织管理](/admin/)进行成员邀请、自定义域名、模型额度与存储等组织初始化配置。

## 状态说明

### 租户开通与管理员激活状态流转

```
运营台点击「开通租户」 → Zitadel 创建组织与项目 → 首位管理员（tenant.admin）处于待激活 → 管理员使用验证码设置密码 → 账号激活并正常登录
```

| 状态 / 阶段 | 说明 |
|---|---|
| 运营开通 | 平台管理员在运营台中提交开通信息，系统在底层创建租户专属组织与项目。 |
| 待激活 | 租户组织创建成功，首位租户管理员账号已生成，等待管理员通过验证码设置密码。 |
| 正常可用 | 管理员完成密码设置，可正常登录工作台并使用「我的组织」。 |
| 待初始化 | 异常状态。在 Zitadel 控制台中手动创建用户时会出现此状态，用户无法正常登录。 |

### 容器与服务状态

| 状态 | 说明 |
|---|---|
| 正常运行（healthy） | 终端输出 `kwork is running`，所有容器通过健康检查。 |
| OpenBao 密封（Sealed） | 采用 `onprem` 环境包时，OpenBao 容器在每次重启后默认处于密封状态。未解封前健康检查 `/readyz` 返回 503，容器显示为 unhealthy。必须使用解封密钥完成解封。 |

## 让助手帮你做

安装系统与开通租户属于底层主机运维与平台初始化操作，不属于租户内部助手的对话操作范畴，运营后台也没有安装相关的助手指令。你无法通过助手执行主机部署或开通租户。

你可以在日常操作中向助手咨询安装参数、环境包定义与首个租户的开通规则：

> 怎样在全新 Linux 主机上一键安装 Raise？
> setup-host 脚本支持哪些环境包和可选 profile 组件？
> 离线安装时如何校验数字签名与镜像文件？
> 安装完成后如何通过运营台开通首个租户？
> 租户管理员首次登录时如何用验证码设置密码？

## 设置

### setup-host.sh 全部参数

| 参数 | 必填 | 默认值 | 说明 |
|---|---|---|---|
| `--domain <域名>` | 是 | 无（重跑时沿用已有 `BASE_DOMAIN`） | 系统基础域名。若填 `auto`，则自动获取 `<公网IP>.sslip.io` 且 TLS 默认设为 internal。 |
| `--email <邮箱>` | 否 | `ops@<域名>` | ACME 证书申请通知邮箱。 |
| `--admin-email <邮箱>` | 否 | `admin@<域名>` | 平台管理员初始登录账号邮箱。 |
| `--tls <模式>` | 否 | `acme`（`--domain auto` 时默认为 `internal`） | TLS 证书模式：`acme`（Let's Encrypt 自动申请）或 `internal`（网关内置 CA 自签证书）。 |
| `--pack <环境包>` | 否 | `single`（重跑时沿用 `SETUP_PACK`） | 目标环境包：`single`、`onprem`、`aws`、`aliyun`、`tencent`。拒绝传入 `dev`。 |
| `--with <组件>` | 否 | 空（重跑时沿用 `SETUP_WITH`） | 附加 profile 组件，多个组件用英文逗号分隔。允许值见下方 profile 说明。 |
| `--ops-bind <IP>` | 否 | `127.0.0.1` | 运营后台 8443 端口的监听绑定 IP 地址。 |
| `--mirror cn` | 否 | 关 | 使用中国大陆镜像源（Docker 走阿里云安装脚本，设置 `GOPROXY=goproxy.cn`）。 |
| `--registry-mirror <URL>` | 否 | 无 | 写入 Docker `daemon.json` 的镜像加速源地址。 |
| `--no-build` | 否 | 默认本机构建 | 不在本地构建镜像，直接使用 `versions.env` 中定义的 `KWORK_IMAGE` 镜像清单。 |
| `--skip-docker` | 否 | 关 | 跳过 Docker 与 Compose 的安装检测。 |
| `--offline` | 否 | 关 | 离线模式：不安装系统包、不装 Docker、不检测公网 IP，仅使用已加载的本地 BOM 镜像。 |
| `--hosts` | 否 | 关 | 自动将网关按名字提供的各子域名（含 `*.apps` 应用）写入本机 `/etc/hosts` 文件。组织的地址 `{租户}.{域名}` 不在其中，需要时自行添加。 |
| `--dry-run` | 否 | 关 | 仅执行环境预检、生成配置文件并校验 compose 文件，不启动服务。 |
| `--yes` / `-y` | 否 | 关 | 自动确认所有交互式询问，静默运行。 |
| `--force` | 否 | 关 | 预检检测失败时强制继续执行。 |
| `-h` / `--help` | 否 | 无 | 打印命令帮助信息。 |

### 环境包（Pack）对比

| 环境包名称 | 配置文件 | 说明与适用场景 |
|---|---|---|
| `single` | `deploy/packs/single.env` | 单机极简模式（`setup-host.sh` 默认）。`KWORK_ENV=prod`，对象存储使用本地文件卷（`KWORK_BLOB=fs`），密钥由 `KWORK_MASTER_KEY` 包裹保护（`KWORK_SECRETS=local`）。 |
| `onprem` | `deploy/packs/onprem.env` | 私有化标准部署。对象存储对接 SeaweedFS（`http://seaweedfs:8333`，存储桶 `kwork`，采用路径风格），密钥存储使用 OpenBao（`SECRETS=openbao`），PDF 渲染使用 Gotenberg（`http://gotenberg:3000`）。自动配合 `--infra` 使用。 |
| `aliyun` | `deploy/packs/aliyun.env` | 阿里云环境。对象存储对接阿里云 OSS（采用虚拟主机风格，`KWORK_S3_ENDPOINT=https://oss-${ALIYUN_REGION}.aliyuncs.com`），存储桶与访问密钥记录在 `.env` 中。 |
| `tencent` | `deploy/packs/tencent.env` | 腾讯云环境。对象存储对接腾讯云 COS（`https://cos.${COS_REGION}.myqcloud.com`，存储桶名称须包含 APPID），自动配合 `--core-infra` 使用。审计归档桶须自行在 `.env` 中填写 `KWORK_AUDIT_ARCHIVE_BUCKET`。 |
| `aws` | `deploy/packs/aws.env` | AWS 环境。对象存储对接 AWS S3（`https://s3.${AWS_REGION}.amazonaws.com`），支持可选接入 Temporal Cloud。 |
| `dev` | `deploy/packs/dev.env` | 本地开发环境（`KWORK_ENV=dev`，使用临时开发密钥）。仅供本地开发调试，`setup-host.sh` 会直接拒绝使用此环境包。 |

> **注意**
>
> 使用云环境包（`aliyun`、`tencent`、`aws`）时，首次执行后系统会提示警告：请在 `.env` 文件中补充填入具体的云存储与 KMS 凭据。

### 可选功能组件（Profile / `--with`）

| Profile 名称 | 包含服务与系统影响 |
|---|---|
| `infra` | 启动 OpenBao、SeaweedFS 与 Gotenberg，自动创建审计归档存储桶。选择 `onprem` 环境包时会自动添加。 |
| `core-infra` | 仅启动 OpenBao 密钥管理服务（对象存储使用外部云存储）。选择 `tencent` 环境包时会自动添加。 |
| `shell` | 启动 Agent 闸门服务，配置 `KWORK_AGENT_ENABLED=true` 并写入 Bifrost 地址。启用后工作台界面才会显示助手与定时任务。 |
| `llm` | 配置模型网关地址：`KWORK_LLM_BASE_URL=http://bifrost:8080/v1` 与 `KWORK_LLM_ADMIN_URL=http://bifrost:8080`。 |
| `obs` | 启动 Grafana 可观测性套件，配置 `OTEL_EXPORTER_OTLP_ENDPOINT` 与 `KWORK_LOKI_URL`。 |
| `obs-llm` | 启动 Langfuse 与 ClickHouse，生成 Langfuse 访问密钥，用于大语言模型调用追踪。 |
| `backup` | 生成备份加密密钥，构建并启动 `pg-backup` 数据库自动备份服务。 |
| `coord` | 启动 Redis 协调实例，配置 `KWORK_REDIS_URL=redis://redis:6379/3`。 |
| `apps` | 配置 `KWORK_RUNTIME=docker` 并启动 app-runner 容器运行时。启用后运营台出现「托管运行」菜单。 |
| `byo-test` | 启动外部依赖模拟器（用于客户存储、IdP、微信、支付与短信模拟测试，仅供功能演示）。 |

> **提示**
>
> `setup-host.sh` 的 `--with` 参数支持传入：`infra`、`core-infra`、`search`、`shell`、`obs`、`obs-llm`、`backup`、`coord`。脚本不会自动传递 `install.sh` 的 `--llm`、`--apps` 与 `--byo-test` 参数；若需启用这些组件，请直接使用 `install.sh` 执行安装。

### install.sh 参数设置

`install.sh` 脚本命令格式如下：

```bash
./install.sh [--pack PACK] [--infra|--core-infra|--search|--shell|--llm|--analytics|--collab|--obs|--obs-llm|--byo-test|--apps|--coord|--backup] [--dry-run]
```

若传入未知的选项，脚本会输出 `unknown flag` 并以退出码 2 退出；若传入未知的环境包名称，脚本会输出 `unknown pack` 并退出。

### 运行时持久化设置项

部署脚本首次执行后会将配置写入 `deploy/compose/.env`。后续重新执行脚本时，若未显式指定参数，将自动沿用以下持久化配置：

| 配置变量 | 作用与来源 |
|---|---|
| `SETUP_PACK` | 安装所选的环境包名称。 |
| `SETUP_WITH` | 安装所选的 profile 组件列表。 |
| `SETUP_TLS` | 使用的 TLS 模式（`acme` 或 `internal`）。 |
| `BASE_DOMAIN` | 系统基础域名。 |
| `ACME_EMAIL` | ACME 证书申请通知邮箱。 |
| `OPS_BIND` | 运营后台 8443 端口绑定的 IP 地址。 |
| `OPS_TLS` | 运营后台证书配置（`internal` 或 ACME 邮箱）。 |
| `CADDY_GLOBAL_OPTIONS=local_certs` | 当指定 `--tls internal` 时写入，启用网关内置 CA 证书。 |
| `KWORK_IMAGE` | 自定义容器镜像标签，覆盖 BOM 清单默认值（本机构建时为 `kwork/kwork:local`）。 |

## 注意事项

- 配置文件安全：`deploy/compose/.env` 文件权限为 600，包含系统所有主密钥与密码。该文件与 `state/` 状态目录必须在安装完成后立即加密备份并离线保存。
- 解封密钥保管：数据库备份密钥 `BACKUP_ENCRYPTION_KEY` 以及 OpenBao 的 5 份解封密钥必须离开主机离线保管。对于 `onprem` 环境包，初次生成的解封密钥记录在 `state/openbao-init.json`，请立即分发给 3–5 位保管人并删除该本地文件。
- OpenBao 重启密封：使用 `onprem` 环境包时，OpenBao 容器在每次重启后均会恢复为密封（Sealed）状态。未解封前健康检查接口 `/readyz` 返回 503，容器显示为 unhealthy。必须使用分发的解封密钥执行解封操作，服务才能恢复正常响应。
- 引导凭据一次性生成：Zitadel OIDC 客户端的 client secret 仅在首次执行引导时返回。重新运行脚本时不会重新返回已有的 client secret，系统将沿用初次生成的值。
- 平台管理员邮箱更变限制：平台管理员初次引导完成后，无法通过修改 `--admin-email` 参数重新执行脚本来更改登录邮箱（参数会被忽略并给出警告提示），必须直接登录身份中心后台修改。
- 备份配置依赖：若启用 `--backup` 组件，但未启用 `--infra` 且未在 `.env` 中配置 `BACKUP_S3_*` 对象存储变量，安装脚本会直接拒绝执行。
- 容器重启策略：所有服务容器均配置为 `restart: unless-stopped`，主机重启后容器会自动拉起（若启用了 OpenBao 仍需手动解封）。
- Zitadel 未就绪排查：若 Zitadel 启动超时未就绪，终端会提示 `zitadel did not become ready; see: docker compose logs zitadel`。
- 应用依赖关联：
  - 开通租户后，「我的组织」自动可用且不可卸载。
  - 工作台默认不显示助手与定时任务，仅在配置启用 `--shell`（`KWORK_AGENT_ENABLED=true`）后才会显示。
  - 组织管理员在「助手设置」中开启联网搜索的前提是安装时启用了 `--search` 组件。
  - 运营台的「托管运行」菜单仅在安装时启用了 `--apps` 组件后才会出现。
  - 系统的功能模块、席位配额与 ISV 应用由许可文件控制，未导入许可文件时缺省为 Community 社区版。

## 目前不支持

- 自助注册：平台当前采用受邀内测机制，尚未提供租户成员或外部用户的公开自主注册功能。
- 分析与协同组件：`analytics` 与 `collab` 镜像目前尚未公开发布（未传入 `--force` 时安装脚本会提示镜像未发布并跳过）。
- 离线原地升级验证：使用新版本离线安装包对已有环境进行原地升级、自动化发布工作流以及 cosign 签名校验目前尚未完成验证。

## 常见问题

### 首次登录平台管理员时密码是什么？忘记或需要重置怎么办？
安装脚本执行完成后，初始管理员密码保存在 `deploy/compose/.env` 文件的 `PLATFORM_ADMIN_PASSWORD` 变量中，格式为 `Kw1-<16位十六进制字符>!`。平台管理员登录身份中心（`https://id.{域名}`）时强制修改密码。若需要新增运营人员，可在主机运行 `deploy/scripts/create-operator.sh <邮箱>` 脚本生成一次性密码，请勿直接在 Zitadel 原生控制台手动新建用户。

### 开通租户时提示「该租户 ID 已存在」或接口返回 502 错误，该如何处理？
提示「该租户 ID 已存在」说明当前输入的「租户 ID」已被其他组织占用。租户 ID 全局唯一且创建后不可修改，请更换一个新的租户 ID。如果开通时出现 502 错误，通常是由于 Zitadel 身份服务响应异常或 `PROVISIONER_PAT_EXPIRY` 凭据过期，系统会自动回滚本次开通操作，你可以检查身份服务日志确认原因后直接重试。

### 为什么开通租户后管理员没有收到邮件通知？
如果主机尚未配置外部 SMTP 邮件服务器，开通租户对话框默认勾选了「直接返回邮箱验证码」。系统不会发送任何外发邮件，而是直接在运营台界面弹出一次性验证码，并提示「未配置 SMTP，邮件不会发出。验证码只显示这一次。」。运营人员需要复制该验证码并通过线下安全渠道交付给租户管理员。管理员访问工作台登录页，点击「用验证码设置密码」即可完成账号激活。

### 为什么在运营台中看不到「开通租户」按钮？
只有具备 `platform.ops` 角色的完整运营人员才能开通租户。如果你的账号被赋予了 `platform.support` 角色（只读运营人员），界面上的「开通租户」按钮不会出现，直接调用接口也会返回 403 权限拒绝。

### 重启服务器后为什么服务显示健康检查失败（unhealthy）？
如果你的环境使用了 `onprem` 环境包（启用 OpenBao 密钥管理服务），OpenBao 容器在每次重启后默认处于密封（Sealed）状态，其健康检查接口 `/readyz` 会返回 503。你需要使用初始化时备份的解封密钥完成解封，容器才会恢复正常运行。
