MoonCode 管理代码仓库的不可变 commit 快照,使用 scc 生成基础质量指标,并通过飞书或钉钉接收团队消息、发送分析完成通知。当前代码仍处于首个正式版本之前,不包含旧 API、旧类型或迁移兼容层。
- 认证:由 Tinyauth v5 完成 GitHub OAuth 登录和会话管理;MoonCode 不保存密码,也不实现密码登录。
- 多租户:Workspace 是业务隔离与 RBAC 边界,支持
owner、admin、member。 - 代码托管凭证:GitHub/GitLab PAT 是当前用户的个人 Provider Connection,不属于 Workspace;管理员不能查看或代用他人的 PAT。
- 仓库:用户只需提交仓库地址和
ref;公共 GitHub/GitLab 仓库通过匿名 HTTPS 同步,私有仓库可选用当前用户自己的 Provider Connection。 - 快照:每个 Repository 对应一个受控 bare Git 对象库。同步只允许受控的 heads/tags refspec,解析后的 commit 固定在
refs/mooncode/snapshots/<snapshot-id>,历史快照不会随远端分支移动。 - 分析:分析任务关联确定的 Commit Snapshot,由 Hatchet Worker checkout 固定 commit 后运行可替换的 Analyzer;首版默认使用
scc。 - Channel:飞书使用 WebSocket,钉钉使用 Stream Mode。runtime 统一执行发送者 allowlist、群聊 mention/prefix 策略、消息归一化和幂等入库;分析成功后通过 App API 发送通知。
- 异步可靠性:Repository、Analysis、Notification 和数据保留清理都由 Hatchet Workflow/Task 执行;事务性 PostgreSQL outbox 负责弥合业务提交与 Hatchet 派发之间的崩溃窗口,没有本地任务 Worker fallback。
- 数据保留:Workspace 可配置 1–3650 天的分析保留期;Hatchet 定期删除到期终态 Run/Report/Notification、幂等释放 Snapshot pin,并在引用归零后重新派发 Repository purge。
本地查看仓库、分析已有快照、删除 Repository,以及同步公共仓库都不需要 PAT。私有仓库的创建、刷新或来源修改使用发起用户显式选择的个人 PAT;任务会固化连接 ID 和凭据版本,PAT 更换后旧排队任务会拒绝执行。
- 后端:Go 1.23、Gin、Cobra、Viper、pgx/v5、sqlc
- 工作流:Hatchet v0.98.9,V1 engine
- 数据库:PostgreSQL 17;MoonCode 与 Hatchet 使用独立 database 和 role
- 认证:Tinyauth v5.1.2 + GitHub OAuth
- 前端:React 19、TypeScript、Vite、Tailwind CSS
- IM SDK:Lark/Feishu OpenAPI SDK、DingTalk Stream SDK
同一个 mooncode 二进制提供两个明确分离的角色:
mooncode serve:HTTP API、Tinyauth ForwardAuth 身份消费、飞书/钉钉长连接。mooncode worker:workflow outbox dispatcher 与 Hatchet Worker;只有该角色执行远程 Git、代码分析和保留期清理。
API 与 Worker 共享 PostgreSQL 和 Repository 持久卷。首版要求只有一个 Git 存储节点。
Compose 将 PostgreSQL/Hatchet 放在内部 backend 网络;Worker 额外连接不发布任何端口的 outbound 网络,用于访问 GitHub/GitLab。数据库和 Worker 指标端口不会因此暴露到宿主机。
两个进程分别暴露 Prometheus 指标:API 在自身 HTTP 地址提供 /metrics,Worker 在独立的私有监听地址(默认 :9090)提供 /metrics 和 /healthz。Compose 中可从后端网络访问 http://mooncode-api:8080/metrics 与 http://mooncode-worker:9090/metrics;这些端点不会通过公共 Traefik 路由暴露。
- Docker 与 Docker Compose
- 一个 GitHub OAuth App
- 本机端口
3100可用
GitHub OAuth App Callback URL:
http://mooncode.localhost:3100/api/oauth/callback/github
Tinyauth 的 OAuth API 通过 MoonCode 同域暴露:/api/oauth/* 与 /api/user/logout 由 Traefik 转发给 Tinyauth,/api/v1/* 转发给 MoonCode API。Tinyauth 使用仅限 mooncode.localhost 的 host-only session Cookie,不启用跨子域 Cookie。
准备配置:
cp .env.example deploy/.env
mkdir -p deploy/secrets
openssl rand -base64 32 # 分别填入两个 MoonCode key,必须使用不同值
printf '%s' '<github-oauth-client-secret>' > deploy/secrets/github_oauth_client_secret在 deploy/.env 中填写:
MOONCODE_SECRETS_KEYMOONCODE_AUTH_CSRF_KEYGITHUB_OAUTH_CLIENT_ID
首次使用自托管 Hatchet 时,先启动基础服务:
docker compose --env-file deploy/.env -f deploy/docker-compose.yml up -d postgres traefik hatchet访问 http://hatchet.mooncode.localhost:3100 完成 Hatchet 初始化,创建 MoonCode client token,并把它写入 deploy/.env 的 HATCHET_CLIENT_TOKEN。随后启动完整开发环境:
make compose-dev-up访问:
- MoonCode:http://mooncode.localhost:3100
- Hatchet:http://hatchet.mooncode.localhost:3100
停止环境:
make compose-dev-down项目源码必须位于工作区根目录的 worktree/ 下。可跨 worktree 共享的数据和缓存分别放在根目录 data/、cache/,不得提交。
npm ci --prefix frontend
make generate
make test
make vet
make frontend-i18n frontend-lint frontend-build运行 PostgreSQL 与 Git 集成测试:
MOONCODE_TEST_DATABASE_URL='postgres://mooncode:mooncode@127.0.0.1:5432/mooncode?sslmode=disable' \
make integration-test对已经完成 GitHub OAuth 登录的真实部署运行只读 smoke test:
MOONCODE_E2E_BASE_URL='https://mooncode.example.com' \
MOONCODE_E2E_SESSION_COOKIE='tinyauth=<session-cookie>' \
make e2e-live该检查只发送 GET 请求,依次验证 /healthz、Tinyauth 认证后的 /api/v1/session 和 /api/openapi.yaml,不会伪造 Remote-* 身份头。没有真实 OAuth 会话时可以跳过此项。
直接运行单个角色:
go run ./cmd/mooncode serve --config mooncode.example.yaml
go run ./cmd/mooncode worker --config mooncode.example.yaml
go run ./cmd/mooncode versioncmd/mooncode/ Cobra 命令入口
internal/app/ 进程生命周期与 Wire 顶层装配
internal/conf/ 配置加载与校验
internal/server/ HTTP 协议适配与认证中间件
internal/service/ 面向传输层的应用服务门面
internal/biz/ 业务模型、用例与工作流规则
internal/data/ PostgreSQL/sqlc 与外部数据适配
internal/worker/ Hatchet、调度器与长连接运行时
pkg/ 可被第三方独立使用的接口、Options 与默认实现
migrations/ 唯一初始 PostgreSQL migration
sql/queries/ sqlc 查询定义
frontend/ React 前端
deploy/ Dockerfile 与 Compose
- Traefik 在 ForwardAuth 前删除客户端伪造的
Remote-*头,只转发 Tinyauth 返回的身份头。 - 改写请求使用 HMAC CSRF token。
- PAT 与 Channel secret 使用 AES-GCM 加密,API 永不返回明文。
- Provider Connection 查询始终带当前
user_id;Workspace owner 没有读取或使用成员 PAT 的路径。 - 首版只允许
https://github.com与https://gitlab.com,避免任意 Provider base URL 造成 SSRF。 - Git 凭据通过单次命令的临时 HTTP authorization header 提供,不写入 Repository remote URL 或 Git 配置。
- Git 与 Analyzer 子进程使用最小环境,只按需保留网络代理,不继承数据库 URL、Hatchet Token 或主密钥。
- MoonCode API/Worker 容器使用非 root、只读根文件系统、
no-new-privileges和最小 capability。
GitHub Actions 会校验:
go mod tidy和 sqlc 生成结果无差异- golangci-lint、Go 单元测试与
go vet - PostgreSQL、事务性 outbox 和受控 bare Git 集成测试
- 前端 i18n、ESLint 与生产构建
- gitleaks 敏感信息扫描
- PR 依赖变更审查、Go
govulncheck与 npm 高危漏洞审计 - 后端/前端镜像构建及 Trivy
HIGH、CRITICAL漏洞扫描 - 使用 Syft 生成并上传两个镜像的 SPDX JSON SBOM
推送严格 SemVer 格式的 v* Tag 后,Release 工作流会重新执行生成校验、单元/集成测试和前端构建,随后向 GHCR 发布后端与前端的 linux/amd64、linux/arm64 镜像。预发布版本不会覆盖 latest。镜像携带版本、commit 和构建时间,附带 OCI SBOM/构建来源证明,并使用 GitHub OIDC + Cosign 无密钥签名;全部镜像成功后自动生成 GitHub Release。
飞书官方 Go SDK v3.9.4 的 Start 在连接 context 取消后仍不会返回。MoonCode 会关闭实际连接并停止接收入站消息,但 SDK 的等待 goroutine 只能随 API 进程退出回收。频繁停用、启用或热更新飞书 Channel 后,建议重启 API 容器。
