
# 自托管

在一台 Linux 主机上，以有 sudo 权限的用户执行：

```bash
curl -fsSL https://acahti.dev/install.sh | bash -s -- --domain acahti.example.com
```

脚本会在缺少 Docker 时自动安装，从 `ghcr.io/acahti-ai/acahti` 拉取发布镜像，把 compose 文件和主机脚本复制到 `~/acahti`，生成密钥写入 `~/acahti/.env`，然后启动实例。主机上不进行任何构建。

也可以交给你的编程 Agent 来做：

```text
Install https://acahti.dev/install.md
Self-host Acahti on my infrastructure. Ask me for the domain, SSH access, and Runner host.
```

## 准备工作

- 一台有 sudo 权限、内存至少 2 GiB 的 Linux 主机。
- 一个公网 HTTPS 域名。TLS 在你自己的反向代理或隧道上终止，再转发到网关。
- 可选：再准备一台主机作为「执行节点」，第一台主机要能通过 SSH 访问它。CI 永远不在控制平面上运行。

## 选项

| 选项 | `.env` | 含义 |
|---|---|---|
| `--domain HOST` | `DOMAIN`, `ROOT_URL` | 公网域名；首次安装时必填 |
| `--org NAME` | `ACAHTI_ORG` | 仓库所属的组织（默认 `acme`） |
| `--runner SSH` | `ACAHTI_BUILD` | 执行节点主机，通过 SSH 配置，例如 `ci@10.0.0.5` |
| `--bind ADDR` | `GATEWAY_BIND` | 网关监听地址（默认 `127.0.0.1:8080`；设置了执行节点时为 `0.0.0.0:8080`） |
| `--version X.Y.Z` | `ACAHTI_VERSION` | 要安装的版本（默认最新版） |
| `--dir PATH` | | 安装目录（默认 `~/acahti`） |

选项会写入 `.env`，后续运行时沿用。数据存放在 `ACAHTI_DATA` 下（默认 `/var/lib/acahti`）。

## 安装之后

1. 把代理或隧道指向网关，然后打开实例 URL。
2. 在 Web 应用中邀请成员。每个人通过 `<instance>/join` 加入。
3. 把实例首页上的 Agent 提示词粘贴给编程 Agent，打开「看板」，看它跑完[演示流程](/demo.md)。

## 升级

先备份 PostgreSQL 和 `ACAHTI_DATA`，再重新运行安装脚本：

```bash
curl -fsSL https://acahti.dev/install.sh | bash
```

它会安装最新版本，保留 `.env` 和数据，并重新同步执行节点。用 `--version` 可以固定版本。早于 0.15.0 的实例先升级到 0.15.2，再升级到最新版本。不要在运行中的实例上执行 `docker compose down -v`。

## 发布交付

网关、MinIO、Caddy 边缘代理、Gotenberg、Tika 和 ONLYOFFICE Docs 是各自独立的镜像。MinIO 使用 `ghcr.io/acahti-ai/acahti:minio-<MINIO_IMAGE_VERSION>`，边缘代理使用 `caddy:<CADDY_VERSION>-alpine`，Gotenberg 使用 `gotenberg/gotenberg:<GOTENBERG_VERSION>` 生成 Office 预览，Tika 使用 `apache/tika:<TIKA_VERSION>` 从 PDF 和 Office 文件中提取可搜索文本，`onlyoffice/documentserver:<ONLYOFFICE_VERSION>` 用于在浏览器中编辑知识库文件。这些版本固定在 `versions.env` 中，应用版本发布时沿用。离线安装时，先加载全部六个镜像归档，再运行安装脚本。ONLYOFFICE 最多需要 3 GiB 内存，启动后还要一两分钟文档才能在编辑器中打开，在此之前文档显示 PDF 预览。边缘代理前面如果还有代理，必须允许至少 200 MiB 的请求体，才能完成上传。安装脚本会复用已缓存的镜像，只在缓存未命中时拉取。

升级会对有变化的服务进行协调，并等待健康检查通过。运行时配置和远程执行节点包只在其输入变化时才同步；单纯的网关版本发布不会重新初始化它们。

## 私有仓库源访问

使用私有镜像源时，先用能读取该仓库源的账号让 Docker 完成认证（`docker login <registry>`），再运行安装脚本。请使用只读的制品凭据或仓库源凭据助手；不要把凭据放进安装目录的 `.env`、Git 仓库、命令示例或共享的归档中。

### 网关更新

稳定的 `edge` 服务持有对外发布的 HTTP 端口。更新期间，唯一的网关先在就绪探针上返回 503，同时继续处理 edge 已经转发过来的请求。两秒后，它关闭事件流，并在最多 25 秒内处理完普通请求。Compose 会等待 30 秒再强制终止容器。新的进程只在旧进程退出后才启动，因此基于文件的凭据和后台任务不会同时有两个写入者。

Caddy 每 250 毫秒检查一次就绪状态。替换期间到达的请求最多会等待 120 秒，直到有可用的上游。只有 GET 和 HEAD 请求会在上游往返失败后重试。写请求在连接失败时可以等待，此时不会发送请求体；已经发往上游的写请求永远不会被重放。SSE 客户端在旧的事件流关闭后重新连接。这一机制不会持久化并重放客户端断开期间产生的事件。

这是一种有上限的等待策略：正常更新不会出现网关错误，但会增加延迟。客户端必须为切换留出足够的时间。超出排空期限的请求、超时设置更短的客户端、启动失败，以及超过 edge 重试预算的中断，仍然可能导致失败。它不是多副本高可用。对 edge 或有状态依赖的变更同样需要维护窗口；请求等待保证只覆盖仅更新网关的情况。

安装脚本会把网关固定到下载时的摘要（离线归档则固定到本地镜像 ID），保留一份受保护的 `.previous-release.*` 主机包，并在失败时恢复这份包。`scripts/upgrade.sh` 会在替换前下载镜像，就绪检查失败时恢复之前的网关镜像和环境。升级失败时仍会以失败状态退出，即使回滚已经成功。数据库迁移必须单独审查：镜像回滚不会撤销数据库变更。如果升级会改动数据库和持久化数据，请事先备份。

`CADDY_VERSION` 固定官方公开的 `caddy:<version>-alpine` 边缘代理镜像。正常安装会自动下载它。使用内部仓库源的运维人员可以镜像该版本，并把 `CADDY_IMAGE` 设为对应的仓库名；这是一项安装配置，不是产品分叉。离线安装必须加载全部六个镜像归档。仅更新网关时，不要改动边缘代理的镜像和配置。
