Anarlog Enterprise Control Plane 部署实战:企业级会议捕获 API 的自托管与配置指南
【免费下载链接】anarlogOpen source Granola AI Alternative项目地址: https://gitcode.com/GitHub_Trending/hy/anarlog
导读
Anarlog Enterprise control plane 是 anarlog 开源仓库中以商业许可提供的企业级服务,它将 workspace 维度的会议捕获 API 与自动 PostgreSQL 迁移、fail-closed 启动配置、优雅停机打包在一起:捕获事件被持久化追加到数据库,每个 revision 被投影为共享的 session-ingest 契约,再投递给已授权的客户端。本文以 enterprise/control-plane/README.md 为主体,结合仓库内的 Compose 编排、Dockerfile 与 Rust 源码实现,完整讲解评估部署、Infisical 密钥注入、全部环境变量、健康检查、REST API 路由与捕获作业租约协议,并给出可在仓库内直接验证的命令与配置。
服务定位:企业捕获 API 的控制面
control plane 是一个独立的二进制服务,入口位于 enterprise/control-plane/src/main.rs,其职责可以概括为四件事:
- 持久化捕获事件:以 provider-neutral(供应商无关)的形式追加 capture events,每个被接受的事件会原子性地推进 PostgreSQL checkpoint;
- 投影会话契约:每个 revision 通过 enterprise/control-plane/src/projector.rs 投影为
anlg-session-ingest定义的共享会话契约(SessionIngestEnvelope); - 投递给授权客户端:客户端按 consumer 分页拉取投递项并显式 ack;
- 自动运维:启动时自动应用 SQL 迁移、连接池管理、优雅停机。
从依赖来看,它复用anlg-meeting-capture(捕获线缆契约)与anlg-session-ingest(会话摄入契约)两个共享 crate,并以anarlog-enterprise-zoom-rtms-worker作为 Zoom RTMS 捕获 worker,见 enterprise/control-plane/Cargo.toml。控制面通过 trait 抽象存储与鉴权(ControlPlaneStore、WorkspaceAuthenticator),核心逻辑位于 enterprise/control-plane/src/api.rs、store.rs、auth.rs、capture.rs、schedule.rs、zoom.rs。
评估部署:Docker Compose v2 一键启动
前置条件与 .env 准备
评估部署需要 Docker 与 Compose v2。从仓库根目录执行:
cp enterprise/control-plane/.env.sample enterprise/control-plane/.enventerprise/control-plane/.env.sample 提供以下模板变量:
POSTGRES_DB=anarlog POSTGRES_USER=anarlog POSTGRES_PASSWORD=replace-with-random-hex ANARLOG_ENTERPRISE_DATABASE_URL=postgres://anarlog:replace-with-random-hex@postgres:5432/anarlog ANARLOG_ENTERPRISE_DATABASE_MAX_CONNECTIONS=10 ANARLOG_ENTERPRISE_WORKSPACE_TOKENS={"evaluation-workspace":"replace-with-at-least-32-random-characters"} ANARLOG_ENTERPRISE_PORT=8080 RUST_LOG=info,tower_http=info按 README 要求,替换两处数据库密码为相同的随机十六进制值(十六进制值可避免POSTGRES_PASSWORD与ANARLOG_ENTERPRISE_DATABASE_URL中 URL 编码差异导致的不一致),并将 workspace bearer token 替换为至少 32 个随机字符。
启动、验证与停止
docker compose \ --file enterprise/control-plane/compose.yaml \ --env-file enterprise/control-plane/.env \ up --build --wait --detachenterprise/control-plane/compose.yaml 中值得注意的编排细节:
postgres使用固定 digest 的postgres:17-alpine,自带pg_isready健康检查(5s 间隔、12 次重试);control-plane以read_only: true挂载只读根文件系统,/tmp使用 tmpfs(noexec,nosuid,size=16m),两者都带no-new-privileges: true;- 端口默认只绑定
127.0.0.1:${ANARLOG_ENTERPRISE_PORT:-8080}:8080,不对外发布 PostgreSQL; - 数据持久化仅通过
postgres-datavolume,docker compose down不会删除数据库数据。
启动后验证 API 与数据库就绪:
curl --fail http://127.0.0.1:8080/health/ready curl --fail \ --header 'Authorization: Bearer YOUR_WORKSPACE_TOKEN' \ 'http://127.0.0.1:8080/v1/workspaces/evaluation-workspace/session-envelopes?consumerId=evaluation-device&after=0'停止服务(保留数据):
docker compose \ --file enterprise/control-plane/compose.yaml \ --env-file enterprise/control-plane/.env \ down镜像与运行约束
enterprise/control-plane/Dockerfile 揭示了镜像的加固策略:
- 构建与运行镜像均使用固定的 Debian/Rust 基础镜像 digest(
rust:1.94.0-bookworm与debian:bookworm-slim); - 以 UID/GID
10001的非 root 用户anarlog运行,USER 10001:10001; - 内置
HEALTHCHECK(10s 间隔,curl/health/ready); - 默认
ENV ANARLOG_ENTERPRISE_BIND_ADDRESS=0.0.0.0:8080与RUST_LOG=info,tower_http=info。
README 强调:在将 API 暴露到 localhost 之外前,应在可信反向代理处终止 TLS。
配置总览:环境变量与 fail-closed 启动
完整变量表
| 变量 | 必填 | 用途 |
|---|---|---|
ANARLOG_ENTERPRISE_DATABASE_URL | 是 | PostgreSQL 连接 URL。数据库不可用或迁移失败时启动失败。 |
ANARLOG_ENTERPRISE_WORKSPACE_TOKENS | 是 | JSON 对象,将 workspace ID 映射到 bearer token。至少需要 1 个 32–512 字节的唯一 token。 |
ANARLOG_ENTERPRISE_BIND_ADDRESS | 否 | 监听地址。默认0.0.0.0:8080;镜像中设置相同值。 |
ANARLOG_ENTERPRISE_DATABASE_MAX_CONNECTIONS | 否 | PostgreSQL 连接池大小,1–100。默认10。 |
ANARLOG_ENTERPRISE_ZOOM_CLIENT_ID | 成组 | Zoom RTMS 应用 client ID。 |
ANARLOG_ENTERPRISE_ZOOM_CLIENT_SECRET | 成组 | Zoom RTMS 应用 client secret。 |
ANARLOG_ENTERPRISE_ZOOM_WEBHOOK_SECRET | 成组 | Zoom webhook 密钥,用于校验签名请求体。 |
ANARLOG_ENTERPRISE_ZOOM_ACCOUNT_WORKSPACES | 成组 | JSON 对象,将已签名的 Zoom account ID 映射到已配置的 workspace ID。 |
RUST_LOG | 否 | 标准 tracing 过滤规则。默认输出请求与服务信息,不导出遥测。 |
源码级的校验逻辑
这些约束并不是文档空谈,而是由 enterprise/control-plane/src/config.rs 的Config::from_values_with_zoom在进程启动时强制执行的:
- 数据库 URL 必须以
postgres://或postgresql://开头,否则报InvalidDatabaseUrl; bind_address必须可解析为SocketAddr,默认0.0.0.0:8080;DATABASE_MAX_CONNECTIONS解析为u32后必须在1..=100区间,默认10,数据库获取连接超时固定 10 秒;- workspace token 用
serde_json解析为 JSON 对象(WorkspaceTokens),空对象报EmptyWorkspaceTokens;workspace ID 长度 1–128 且只允许 ASCII 字母数字与-_.;token 长度必须在 32–512 字节之间,否则报InvalidWorkspaceToken; - 四个 Zoom 变量必须整体出现:全缺席 →
zoom=None,部分出现 →IncompleteZoomConfiguration;ANARLOG_ENTERPRISE_ZOOM_ACCOUNT_WORKSPACES只能引用ANARLOG_ENTERPRISE_WORKSPACE_TOKENS中已存在的 workspace,否则报UnknownZoomWorkspace; - 此外还支持可选的离线 license 变量(
LICENSE与LICENSE_KEY必须成对出现),逻辑见 enterprise/control-plane/src/license.rs。
对应测试位于 enterprise/control-plane/src/config.rs 的#[cfg(test)]模块,覆盖了最小配置、空凭据失败、短 token 拒绝(且错误消息不回显 token 本身)、Zoom 成组校验等场景——这也是"fail-closed"(启动即失败,绝不带病运行)的实践证据。
健康检查语义
GET /health/live:进程级存活探针,恒返回{"status":"ok"},不触碰数据库;GET /health/ready:就绪探针,执行store.readiness()检查 PostgreSQL,失败时返回 503{"status":"not_ready"}。
实现见 enterprise/control-plane/src/api.rs,镜像的HEALTHCHECK正是基于/health/ready。
使用 Infisical 注入密钥
该部署接受的所有密钥都来自环境变量,因此 Infisical 可以在不生成.env文件的情况下注入。将.env.sample中的变量存入一个 Infisical 环境,然后运行:
infisical run --env=prod -- \ docker compose \ --file enterprise/control-plane/compose.yaml \ up --build --wait --detach注意事项(README 明确要求):
- 机器身份(machine identity)与项目选项请参考
infisical run官方命令文档; - 生产环境 Compose 进程不要使用
--watch;当凭据轮换时应执行受控重启; - Infisical 应在进程启动时注入这些值;服务本身不会主动获取、持久化或记录供应商凭据(Zoom client secret、webhook secret 等在
Debug输出中也会被遮蔽,见 config.rs 的测试断言)。
捕获作业与租约协议:事件追加的可靠性机制
READ ME 中关于 capture job 的描述是理解系统可靠性的关键:
- 捕获 worker 通过
POST /v1/workspaces/{workspace_id}/capture-jobs/{job_id}创建持久化作业; - 通过
/claim认领作业,并持续通过/lease续租返回的60 秒 fencing lease; - 每次追加到
/events的新事件必须携带该租约身份(worker_id、lease_id、epoch); - 租约过期后可用更高 epoch 重新认领,从而阻止前一个 worker 继续推进作业;已持久化的完全相同事件重放仍然安全;
- 事件 ID 与零基序号(zero-based sequence)是幂等键:每个被接受的事件原子性推进 PostgreSQL checkpoint 并发布一个 delivery revision;冲突的 ID、序号、生命周期转换或过期租约都会 fail closed。
这些常量在代码中有据可查:租约时长CAPTURE_LEASE_DURATION = 60s(api.rs),claim 时要求epoch > 0(api.rs),事件请求体契约AppendCaptureEventRequest、CaptureJobLease等类型由anlg-meeting-capture::wire统一导出(capture.rs),其数据表由 enterprise/control-plane/migrations/0003_capture_job_leases.sql 承载。
REST API 路由一览
由 api.rs 的router()注册的路由如下:
| 方法与路径 | 说明 |
|---|---|
GET /health/live | 进程存活探针 |
GET /health/ready | PostgreSQL 就绪探针 |
POST|GET /v1/workspaces/{workspace_id}/capture-jobs/{job_id} | 创建捕获作业 / 读取捕获 checkpoint |
POST /v1/workspaces/{workspace_id}/capture-jobs/{job_id}/events | 追加捕获事件(携带租约身份) |
POST /v1/workspaces/{workspace_id}/capture-jobs/{job_id}/claim | 认领作业,返回 60s 租约 |
POST /v1/workspaces/{workspace_id}/capture-jobs/{job_id}/lease | 续租 |
GET /v1/workspaces/{workspace_id}/session-envelopes | 分页拉取会话投递(consumerId必填,after/limit可选,limit 默认 10、上限 100) |
POST /v1/workspaces/{workspace_id}/session-envelopes/{job_id}/ack | 按 revision + contentHash 显式确认投递 |
GET /v1/workspaces/{workspace_id}/sessions/{job_id} | 读取会话 |
GET|PUT /v1/workspaces/{workspace_id}/capture-policy | 读写捕获策略 |
PUT /v1/workspaces/{workspace_id}/calendar-events | 批量 upsert 日历事件(单批上限 500),生成定时捕获 |
GET|POST /v1/workspaces/{workspace_id}/scheduled-captures | 列出 / 立即触发到期的定时捕获 |
DELETE /v1/workspaces/{workspace_id}/scheduled-captures/{calendar_event_id} | 取消定时捕获 |
POST /webhooks/zoom(仅在配置 Zoom 后挂载) | Zoom webhook 入口,校验x-zm-request-timestamp与x-zm-signature |
鉴权与错误语义
所有捕获与投递路由都要求Authorization: Bearer <token>;token 被 SHA-256 哈希后与 workspace 映射比对(StaticTokenAuthenticator,见 auth.rs),并且拒绝用某 workspace 的 token 访问其他 workspace(返回 403workspace_forbidden)。若配置了离线 license,还会校验 license 是否授权该 workspace(api.rs)。
错误响应统一为{"error":{"code","message"}}结构:冲突类错误使用 409(如revision_conflict、capture_event_conflict、capture_lease_lost),非法事件返回 422invalid_capture_event,未授权返回 401 并附带WWW-Authenticate: Bearer。
定时捕获调度
控制面在启动时还会tokio::spawn一个每 30 秒触发的调度循环(lib.rs),调用dispatch_due_scheduled_captures(Utc::now())将到期的日历捕获作业批量投递;对应 SQL 迁移为 0005_scheduled_captures.sql。定时作业 ID 以cal-前缀标识(api.rs)。
优雅停机与启动流程
run()(lib.rs)先构建状态(连接池、自动迁移、可选的 Zoom 恢复与 license),再绑定监听地址,并通过axum::serve(...).with_graceful_shutdown(...)等待 SIGINT(Ctrl-C)或 SIGTERM,收到信号后记录日志并优雅退出(lib.rs)。Zoom 配置存在时,启动阶段还会执行dispatcher.recover_pending()恢复未完成的持久化投递并spawn_recovery()(lib.rs)。
测试与验收路径
仓库为控制面提供了可复现的验证手段:
- 单元测试:
Config、auth的鉴权与重复 token 拒绝、API 错误映射等(config.rs、auth.rs); - 集成测试:enterprise/control-plane/tests/postgres.rs、api.rs、convergence.rs、upgrade.rs;
- 冒烟脚本:enterprise/control-plane/tests/compose-smoke.sh 可直接用于验证上述
docker compose up后的健康检查与投递拉取链路。
已知边界
README 明确说明:静态 token 映射面向评估部署,位于WorkspaceAuthenticator接口之后;生产级 OIDC、SCIM、离线 license 强制、对象存储与会议浏览器 worker 均不在本服务范围内,属后续演进方向。部署时应将镜像与 Compose 中固定 digest 的依赖一并纳入供应链审查,并在反向代理层完成 TLS 终结。
【免费下载链接】anarlogOpen source Granola AI Alternative项目地址: https://gitcode.com/GitHub_Trending/hy/anarlog
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考