
tsgit:基于 Tailscale 身份认证的 Git 托管服务发布
来源标题:Tailscale Native Git Forge
tsgit 是一款面向 Tailscale 网络的 Git forge,直接复用 Tailscale 身份与 ACL 标签判断读写权限,无需自建账号系统或配置 TLS。它支持通过 smart HTTP 进行 clone、fetch、push,并可在首次推送时自动创建仓库,目前仅供 tailnet 内节点使用,SSH、代码浏览、Issue、PR 和 CI 尚未实现。项目由 AI 编码代理在人工指导下完成大部分代码、测试和文档。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
为你的 tailnet 打造的 git forge。无需账号,无公开写入,无需配置 TLS。每个能访问 tsgit 的 peer 都已通过 Tailscale 完成认证。tsgit 利用该身份来判断谁可以读、谁可以写,因此它从不运行自己的登录系统。
tsgit 由 AI 构建。编码 agent 在人类指导下编写大部分代码、测试和文档,并且每一处变更都有人类审核。
项目尚处早期,但已可用。tsgit 通过 smart HTTP 提供 git 服务,并展示一个只读的仓库索引。SSH、代码浏览、issue、pull request 和 CI 均未实现。
通过 smart HTTP 为 tailnet 内的 peer 提供 git clone、fetch 和 push 服务。 在获得授权的首次 push 时创建仓库。 在 https://<hostname>/ 列出你有权读取的仓库。 具有 admin 权限的查看者可在 Web UI 中删除仓库。 依据 Tailscale 身份和 ACL tag 进行授权。没有用户数据库。 可选地将策略选定的只读子集通过 Tailscale Funnel 发布到互联网。
它不会向 tailnet 之外的用户提供服务(tailnet 策略指定的只读 Funnel 仓库除外),不运行自己的认证,也不充当 CI 系统。
一个 tailnet。 一个 Tailscale auth key,或一个已注册的节点状态。 一条授予 peer tsgit 仓库能力的 tailnet 策略 grant。 该 grant 需要同时包含 ip 和 app:
"grants": [{ "src": ["group:devs", "tag:ci"], "dst": ["tag:tsgit"], "ip": ["tcp:443"], "app": { "github.com/kevindurb/tsgit/cap/repo": [ {"repos": ["acme/*"], "access": "write"}, {"repos": ["shared/docs"], "access": "read"}
如果没有匹配的 grant,每个请求都会返回 404。这是有意为之:服务器不会向无权读取的人透露某个仓库是否存在。完整的 payload 见 docs/access-control.md。
删除是一项独立的权限。只有 admin 级别才能删除仓库,而该级别同时也隐含了 write 和 read。授权时应尽量收窄:
"grants": [{ "src": ["group:admins"], "dst": ["tag:tsgit"], "ip": ["tcp:443"], "app": { "github.com/kevindurb/tsgit/cap/repo": [ {"repos": ["acme/*"], "access": "admin"}
不要让 admin 落到 CI 和其他仅有写权限的身份上。被授予 admin 的 peer 无法使用较旧的 tsgit 二进制:未知的 access 值会导致整个能力 payload 失效并关闭,因此在降级之前应先移除 admin 条目。
镜像发布在 GitHub Container Registry 上。
podman volume create tsgit-state
podman volume create tsgit-repos
export TS_AUTHKEY=tskey-auth-...
podman run -d --name tsgit
--restart=unless-stopped
-e TS_AUTHKEY
-e TSGIT_HOSTNAME=tsgit
-e TS_ADVERTISE_TAGS=tag:tsgit
-v tsgit-state:/var/lib/tsgit/state
-v tsgit-repos:/var/lib/tsgit/repos
ghcr.io/kevindurb/tsgit:main
示例中声明了 tag:tsgit,因此 auth key 必须被允许应用该 tag。请在上面的策略 grant 中使用同一个 tag。
:main 跟踪最新的 merge。对于不希望在你脚下发生变化的部署,请固定到某个发布 tag,例如 ghcr.io/kevindurb/tsgit:v1.2.3。latest 只在发布 tag 时才移动。
状态卷保存节点密钥,因此 auth key 只需用于首次注册。仓库卷保存裸 git 目录。请备份仓库卷;并将其与状态卷分开保存。关于 bind mount、auth-key 文件、升级和 arm64,见 docs/deployment.md。
在 https://tsgit/(MagicDNS)打开索引。它会列出你有权读取的仓库,并标明你是否可以 push。要克隆:
git clone https://tsgit/acme/widget.git
要创建仓库,就向它 push。具有写权限的用户或被标记的节点会在首次 push 时创建它:
git remote add origin https://tsgit/acme/widget.git git push -u origin main
删除仓库 对某仓库具有 admin 权限的查看者会在其摘要页看到 Delete 链接。确认页要求输入规范名称,随后会永久删除该目录及其全部历史。没有撤销,也没有回收站;如果可能还需要这些数据,请备份仓库卷。公开的 Funnel 监听器不提供删除界面。
重命名是客户端操作:将旧仓库镜像到新名称,然后在 Web UI 中删除旧仓库。
git clone --mirror https://tsgit/acme/widget.git widget.git cd widget.git git push --mirror https://tsgit/acme/gadget.git
客户端需要对新名称有写权限,对旧名称有 admin 权限。refs 和对象会随之迁移;裸仓库的 description 和本地配置可能不会。旧名称一旦删除,旧的 clone URL 就会失效。
如果 HTTPS 不可用,节点会在 80 端口提供纯 HTTP 服务,因此请使用 http://tsgit/。流量仍由 WireGuard 加密。
默认关闭。将精选子集发布到互联网需要同时具备以下三项配置:
"nodeAttrs": [{ "target": ["tag:tsgit"], "attr": ["funnel"], "app": { "github.com/kevindurb/tsgit/cap/funnel": [ {"repos": ["public/*"]}
Funnel 还需要 MagicDNS、HTTPS 证书以及受支持的端口(443、8443 或 10000)。仅凭这三项配置中的任何一项都不会暴露任何内容。
git clone https://tsgit.<tailnet>.ts.net/public/widget.git
公开的只有 clone/fetch 和经过过滤的索引。receive-pack、push-to-create 以及所有变更操作都返回 404;该 payload 没有 access 字段,因此公开写入不可能实现。如果缺少某个前置条件,tsgit 会记录错误并继续为 tailnet 提供服务。见 docs/access-control.md 和 docs/deployment.md。
开发模式不需要 tailnet,也不需要 auth key。它使用临时的仓库根目录和一个固定的、具有写权限的身份:
just run serve --dev-listen 127.0.0.1:8080
然后打开 http://127.0.0.1:8080/ 或克隆:
git clone http://127.0.0.1:8080/acme/widget.git
此模式不是生产环境的回退方案。
docs/project.md — 目的、用户、范围、非目标 docs/architecture.md — 各部分如何组合 docs/configuration.md — 参数和环境变量 docs/deployment.md — 容器细节与发布 docs/access-control.md — 能力契约 Layout Path Holds cmd/tsgit/ 二进制文件的入口 internal/ 实现,每个关注点一个包 docs/
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。