Vibe Kanban Remote 云端服务本地开发与自托管部署完全指南
【免费下载链接】vibe-kanbanGet 10X more out of Claude Code, Codex or any coding agent项目地址: https://gitcode.com/GitHub_Trending/vi/vibe-kanban
本篇指南围绕开源仓库 Vibe Kanban 的remotecrate(托管云端 API 与 Web 应用)展开,完整讲解如何在本机拉起remote-db、remote-server、electric三件套,如何配置 JWT 密钥、OAuth(GitHub/Google)或自托管本地认证,如何启用 relay 隧道与本地附件存储,以及如何通过 Caddy 在本地终止 TLS 完成端到端联调。读完本文,你可以把整条云端链路(Postgres → ElectricSQL → REST API → React 前端 → 桌面客户端)跑通,并理解其底层的 ElectricSQL 同步、txid握手机制与类型生成流程。
Remote 服务是什么
remotecrate 是 Vibe Kanban 的托管云端服务端,位于 crates/remote(其主文档为 crates/remote/README.md)。它由三部分组成:
- Axum HTTP API:对外暴露
/v1/*REST 接口,负责认证、组织/项目/Issue/PR 等 CRUD、Webhook 与导出; - React SPA 前端:
packages/remote-web,由 Vite 构建后在容器内通过/srv/static兜底托管; - ElectricSQL 实时同步层:Postgres → ElectricSQL → 客户端按 HTTP Shape 订阅增量。
从 crates/remote/src/app.rs 的启动流程可以看到服务端启动顺序为:创建 Postgres 连接池 → 运行 SQLx 迁移 → 创建electric_sync角色并设置密码 → 同步 Electric 发布 → 初始化 JWT/OAuth 服务 → 组装路由 → 监听SERVER_LISTEN_ADDR。这个启动顺序(先跑迁移、后起 Electric)是整套系统能正常工作的关键前提。
本地环境准备:创建.env.remote
在启动前,需要在crates/remote/.env.remote中准备环境变量。README 给出的最小配置如下:
# Required VIBEKANBAN_REMOTE_JWT_SECRET=replace_with_openssl_rand_base64_48 ELECTRIC_ROLE_PASSWORD=replace_with_secure_password # Configure at least one auth option GITHUB_OAUTH_CLIENT_ID= GITHUB_OAUTH_CLIENT_SECRET= GOOGLE_OAUTH_CLIENT_ID= GOOGLE_OAUTH_CLIENT_SECRET= # Or use bootstrap local auth for self-hosting SELF_HOST_LOCAL_AUTH_EMAIL= SELF_HOST_LOCAL_AUTH_PASSWORD= # Optional PUBLIC_BASE_URL=http://localhost:3000 VITE_RELAY_API_BASE_URL=http://localhost:8082 VITE_PUBLIC_REACT_VIRTUOSO_LICENSE_KEY= LOOPS_EMAIL_API_KEY= # Loops transactional email template IDs (optional — defaults are the upstream templates). # Override these with your own Loops account template IDs if using a custom Loops account. LOOPS_INVITE_TEMPLATE_ID=cmhvy2wgs3s13z70i1pxakij9 LOOPS_REVIEW_READY_TEMPLATE_ID=cmj47k5ge16990iylued9by17 LOOPS_REVIEW_FAILED_TEMPLATE_ID=cmj49ougk1c8s0iznavijdqpoJWT 密钥必须一次性生成,README 给出的命令是:
openssl rand -base64 48环境变量的实际解析规则(源码级)
这些变量最终被 crates/remote/src/config.rs 中的RemoteServerConfig::from_env()消费,几个关键约束值得注意:
VIBEKANBAN_REMOTE_JWT_SECRET:必填。AuthConfig::from_env()会先对其做 Base64 解码校验,解码后字节长度小于 32 会直接报InvalidVar(见validate_jwt_secret)。因此不能用随意字符串,必须用openssl rand -base64 48这类命令生成。- 空字符串即未配置:配置解析统一遵循“空字符串当作未设置”的约定。例如
GITHUB_OAUTH_CLIENT_ID为空时 GitHub OAuth 被视为禁用。Docker Compose 的${VAR:-}展开会产生空串,而std::env::var()对空串返回Ok(""),所以源码里处处用!v.is_empty()做判断,这是本仓库的一条重要约定(crates/remote/AGENTS.md 中也有专门提醒)。 - 认证三选一:GitHub、Google、本地认证(
SELF_HOST_LOCAL_AUTH_EMAIL+SELF_HOST_LOCAL_AUTH_PASSWORD)三者都未配置时,启动会失败并返回ConfigError::NoOAuthProviders。注意本地认证必须邮箱、密码成对出现,只填其一同样报MissingVar。 ELECTRIC_ROLE_PASSWORD:会通过ensure_electric_role_password()写入 Postgres 的electric_sync角色,ElectricSQL 容器用同一密码连接数据库(docker-compose.yml 中DATABASE_URL引用了它)。- 可选扩展:除 README 列出的变量外,docker-compose.yml 还支持
DIGEST_ENABLED(通知摘要开关,默认false)、R2_*(R2 制品存储)、REVIEW_WORKER_BASE_URL、GITHUB_APP_*(GitHub App 集成)、STRIPE_*(计费,私有依赖)以及AZURE_STORAGE_*(附件存储)等,按需配置即可。
启动完整技术栈
从仓库根目录执行:
pnpm run remote:dev该命令默认只启动核心三件套(不含 relay 与附件存储)。如需完整栈(relay + 本地附件存储):
pnpm run remote:dev:full手动执行的等价命令(即 README 中给出的方式):
cd crates/remote docker compose --env-file .env.remote up --build这会启动三个容器:
remote-db:PostgreSQL 16,wal_level=logical,监听宿主机5433端口;remote-server:Axum 服务,Dockerfile 采用“Node(前端构建)→ Rust(服务编译)→ Debian slim(运行)”的多阶段构建,前端产物打进镜像的/srv/static;electric:ElectricSQL 1.4.13,通过逻辑复制订阅 Postgres 并把变更流以 Shape 形式经 HTTP 推给客户端。
启动后的默认端点:
- Remote Web UI/API:
http://localhost:3000 - Postgres:
postgres://remote:remote@localhost:5433/remote
可选 Profile:relay 与 attachments
Docker Compose 通过 profile 控制可选组件(定义见 docker-compose.yml):
启用 relay 支持(relayprofile),多出一个relay-server容器,即 Vibe Kanban 的隧道中继服务:
cd crates/remote docker compose --env-file .env.remote --profile relay up --build启用后新增端点:
- Relay API:
http://localhost:8082
启用本地附件存储(attachmentsprofile),用 Azurite 模拟 Azure Blob:
cd crates/remote docker compose --env-file .env.remote --profile attachments up --build该 profile 会启动azurite(Blob 服务,端口10000)与azurite-init(自动创建issue-attachments容器并配置 CORS)。
两者同时启用:
cd crates/remote docker compose --env-file .env.remote --profile relay --profile attachments up --build从 Compose 配置看,remote-server的depends_on对azurite-init使用了required: false,因此 attachments profile 未启用时不会阻塞启动。
本地 HTTPS:用 Caddy 终止 TLS
如需在本地以 HTTPS 联调 OAuth 回调或 relay 隧道,可以使用仓库根目录提供的 Caddyfile.example 作为反向代理。
安装 Caddy
# macOS brew install caddy # Debian/Ubuntu sudo apt install caddy启动 Caddy
在仓库根目录另开一个终端:
caddy run --config Caddyfile.example首次运行时 Caddy 会安装本地 CA 证书(可能提示输入密码)。通过 Caddyfile.example 的路由规则,你获得:
https://localhost:3001→ remote Web UI/API(反代127.0.0.1:3000)https://relay.localhost:3001→ relay API(反代127.0.0.1:8082,需要relayprofile)
相应地,OAuth 回调地址要更新为:
- GitHub:
https://localhost:3001/v1/oauth/github/callback - Google:
https://localhost:3001/v1/oauth/google/callback
端到端验证 relay 隧道
在另一个终端设置环境变量并启动桌面端:
export VK_SHARED_API_BASE=https://localhost:3001 export VK_SHARED_RELAY_API_BASE=https://relay.localhost:3001 pnpm run dev快速健康检查:
curl -sk https://localhost:3001/v1/health curl -sk https://relay.localhost:3001/healthREADME 特别提示:如果 relay 健康检查返回的是 HTML 而非{"status":"ok"},说明 Caddy 的主机路由配置有误(两个域名没有正确分流到 8082 端口)。
让桌面端指向本地 Remote 栈
桌面/本地应用对接这套 remote 栈只需导出 API 基地址再启动:
export VK_SHARED_API_BASE=http://localhost:3000 pnpm run dev这里用到的VK_SHARED_API_BASE会让桌面端把云端 API 指向本地 remote-server;若同时联调 relay 隧道,再叠加VK_SHARED_RELAY_API_BASE即可。
深入源码:ElectricSQL 同步与 txid 握手机制
Shape 订阅链路
Vibe Kanban 使用 ElectricSQL 作为读路径同步引擎:写走 REST API,读走“Postgres → ElectricSQL → 客户端 HTTP Shape”的订阅链路。整个链路由三个模块支撑:
- Shape 定义:单表订阅,可带
WHERE/columns过滤,定义为常量放在 crates/remote/src/shapes.rs,由 crates/remote/src/shape_definition.rs 的define_shape!宏构造。该宏会在编译期用sqlx::query!对SELECT 1 FROM <table> WHERE <where>做类型化校验,保证表名与 SQL 的合法性在编译期就被发现。 - Electric 代理:crates/remote/src/routes/electric_proxy.rs 中的
proxy_table先校验组织/项目成员身份,再把 shape 请求转发给内部 ElectricSQL 服务。关键安全点是:table与where子句由服务端常量决定并拼接进转发 URL,客户端无法覆盖;客户端的params[1]等仅作为$1占位参数传入;此外只透传offset、handle、live、cursor、columns这几个白名单参数。 - Mutation 响应:create/update/delete 全部走 REST,返回
MutationResponse<T>,内含 Postgres 事务 ID(txid,取自pg_current_xact_id())。前端拿到txid后,在 Electric 流上等到该txid出现,才丢弃乐观更新状态——省略 txid 会导致 UI 闪烁。
给新增同步表接入同步的四个步骤
- 写迁移:建表后执行
ALTER TABLE ... REPLICA IDENTITY FULL并CALL electric.electrify('table_name')(可参考迁移 crates/remote/migrations/20260114000000_electric_sync_tables.sql 中的示例); - 在 crates/remote/src/shapes.rs 用
define_shape!定义 shape,按 organization/project/issue 作用域参数化; - 若需要新的作用域模式,在 crates/remote/src/routes/electric_proxy.rs 增加代理路由;
- 该表的所有 mutation 路由都返回
txid。
安全边界
- ElectricSQL 仅限内部访问:所有 shape 请求必须经过 crates/remote/src/routes/electric_proxy.rs 的鉴权代理,绝不直接暴露给客户端;
- Shape 定义由服务端控制:表、WHERE、列集合都是服务端常量,客户端无法请求任意表,从机制上杜绝越权订阅。
深入源码:MutationBuilder 与统一 CRUD 模式
所有 CRUD 路由遵循统一的MutationBuilder模式(定义见 crates/remote/src/mutation_definition.rs):
MutationBuilder::<Entity, CreatePayload, UpdatePayload>::new("entities") .list(list_handler) .get(get_handler) .create(create_handler) .update(update_handler) .delete(delete_handler) .build()该构建器同时产出 Axum 路由与 TypeScript 类型元数据:通过HasJsonPayload<T>特征把C/U泛型参数与 handler 签名中的Json<T>载荷结构性地绑定,防止元数据与 handler 签名漂移。新增实体时遵循这一模式,然后运行pnpm run generate-types即可自动生成前端类型。
路由树的组织见 crates/remote/src/routes/mod.rs:/v1下公开路由(health、OAuth、组织成员、token、review、GitHub App、billing)与受保护路由(需require_session中间件)分开嵌套,SPA 由ServeDir从/srv/static兜底;billing模块整体用#[cfg(feature = "vk-billing")]门控,自托管构建时FEATURES为空即被裁剪,业务代码中不能直接 importbillingcrate。
类型生成与共享类型约定
api-typescrate(crates/api-types)保存 remote 服务端与本地桌面端共用的类型:行类型(Issue、Project、User、Workspace等)、请求类型(CreateIssueRequest、UpdateProjectRequest等)与共享枚举(IssuePriority、MemberRole、PullRequestStatus、NotificationType等)。所有类型derive(TS)(ts-rs),可自动导出为 TypeScript。
remote 前端使用的单一类型文件shared/remote-types.ts由 crates/remote/src/bin/generate_types.rs 生成,运行方式:
pnpm run remote:generate-types # 写入 shared/remote-types.ts pnpm run remote:generate-types --check # CI 模式,文件过期则非零退出生成内容包含:全部行/请求类型的 TypeScript 接口、每个 ElectricSQL shape 对应的ShapeDefinition<T>常量、每个 CRUD 实体对应的MutationDefinition<TRow, TCreate, TUpdate>常量,以及MutationRowType/MutationCreateType/MutationUpdateType类型工具。在api-types中新增前端所需类型时,需在generate_types.rs的type_decls中补充其::decl()调用并重新生成。桌面端另有独立生成器(crates/server/src/bin/generate_types.rs),输出shared/types.ts。
测试与常见坑位
运行 remote crate 的测试:
cargo test --manifest-path crates/remote/Cargo.toml由于 SQLx 在编译期做查询校验,需要运行中的 Postgres 或离线查询数据(.sqlx/目录),本地可用pnpm run remote:prepare-db生成离线数据,便于 CI 构建。
最后是文档与源码共同强调的几个高频坑位:
- 空字符串 ≠ 未设置:所有可选配置都要按
!v.is_empty()判断,不能只判断env::var是否Ok; - 启动顺序:remote-server 必须先启动并完成迁移、创建
electric_sync角色,ElectricSQL 后启动才能连上数据库,顺序颠倒会连接失败; - billing 特性门控:所有计费代码必须放在
#[cfg(feature = "vk-billing")]之后,自托管 Docker 构建会把 billing crate 从 Cargo.toml 中剥离; VITE_*是构建期变量:会烘焙进前端 JS 产物,修改后必须重新构建镜像;- SPA 路径硬编码:前端从
/srv/static兜底提供,该路径只在 Docker 容器内存在,本地直接跑二进制时需要保证该目录存在或另行调整。
【免费下载链接】vibe-kanbanGet 10X more out of Claude Code, Codex or any coding agent项目地址: https://gitcode.com/GitHub_Trending/vi/vibe-kanban
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考