Sourcehut 配置参数详解

Sourcehut /etc/sr.ht 配置体系详解:global.ini 公共节、meta/git 服务节逐参数说明,密钥体系(service/network/webhook)与联动关系,附 9 条配置类故障症状-根因-修法速查表与生效路径。

SourcehutSourcehut代码托管版本控制Python 编程2026/10/28

Sourcehut 配置参数详解

本篇是 Sourcehut 系列的配置篇:以单机最小可用集(meta.sr.ht 0.88.9 + git.sr.ht 0.102.8,Ubuntu 22.04.5)的 /etc/sr.ht 目录为主线,逐节讲清每个 ini 参数的含义、取值与联动关系,并给出配置类故障的症状→根因→修法速查表(全部来自真实排障)。安装过程见《Sourcehut 0.88.9 + git.sr.ht 0.102.8 Ubuntu 22.04 安装指南》。

一、配置体系总览

1.1 文件布局与加载顺序

Sourcehut 没有单一配置文件,而是 /etc/sr.ht/ 下的一组 ini:

/etc/sr.ht/
├── global.ini        # [sr.ht] 公共节 + 跨服务引用
├── meta.sr.ht.ini    # [meta.sr.ht] 及其子节
└── git.sr.ht.ini     # [git.sr.ht] 及其子节

加载规则(core.sr.ht 的 srht/config.py 实测确认):

  1. 优先读工作目录下的 config.ini;存在即停。
  2. 否则读 /etc/sr.ht/config.ini;存在即停。
  3. 否则按 glob 读 /etc/sr.ht/*.ini 全部文件并逐个合并——同节同键后者覆盖前者(glob 按文件名字母序展开)。

Go 层(各 *-api 二进制,core-go 的 config.LoadFiles)同样合并 /etc/sr.ht/*.ini。因此"公共放 global.ini、差异放各自文件"是官方样例约定的组织方式,两侧一致。

1.2 ini 语法与节命名约定

  • 标准 configparser 语法:[节] + key=value,# 开头为注释。
  • 节名即服务名:[meta.sr.ht]、[git.sr.ht];子节用 :: 分隔:[meta.sr.ht::settings]、[git.sr.ht::api]。
  • 每服务源码里的 config.example.ini 是该版本参数的权威清单(升级后对照一遍,新参数以它为准)。

二、[sr.ht] 公共节:全网络共享的身份与基础设施

[sr.ht]
site-name=My Sourcehut
site-info=http://meta.srht.local
site-blurb=the hacker's forge
environment=development
owner-name=admin
owner-email=admin@srht.local
source-url=http://meta.srht.local
service-key=<64 位十六进制,sr.ht-keygen service>
network-key=<base64,sr.ht-keygen network>
redis-host=redis://127.0.0.1:6379/0
global-domain=srht.local

逐项说明:

参数作用取值要点
site-name / site-blurb站点名称与一句话简介,页面标题栏取 site-name自由文本
site-info"关于"页地址一般指向 meta 站
environmentdevelopment 时页脚显示非生产横幅,且静态资源走未压缩路径;生产必须设 production二选一
owner-name / owner-email页脚展示的运营者信息自由
source-url页脚"源码"链接自由
service-key会话 cookie 与内部会话签名的对称密钥同一服务的多节点必须一致;全站一致也可(简化部署)
network-key服务间内部调用报文的加密密钥全部服务必须一致,不一致则跨服务 GraphQL 调用解密失败
redis-host缓存/webhook 队列的 Redis 地址必须是完整 URL(见六.1 排障表)
global-domain全站域名,决定 cookie 域与"每服务一个子域"的推导设了它,meta.<域>、git.<域> 的 origin 才能联动
security-address安全问题报告邮箱可选
assets静态资源根目录,默认 /usr/share/sourcehut一般不动

service-key / network-key 生成命令(实测输出格式):

$ sr.ht-keygen service
Secret key: 63382df4607b494e6aeadbecd31e891e8542c670da47fd9bb26c501f296673c3
$ sr.ht-keygen network
Secret key: Nv_VAcXJfwgKST_StkD1SLSDMp7wWH0B2u-s8g1vjwQ=

cookie 域与 global-domain 的联动是配置篇最重要的一个联动:会话 cookie 以 .srht.local 为域下发(实测 Set-Cookie 可见),所以任何绕过域名直接用 IP 的访问都拿不到登录态。改域名要同时改 global-domain 与各服务的 origin。

三、[meta.sr.ht]:认证中心的参数

3.1 基础节

[meta.sr.ht]
origin=http://meta.srht.local      # 对外访问 URL(协议://域名)
debug-host=0.0.0.0                 # debug 服务器监听地址(flask run 用)
debug-port=5000                    # debug 端口;gunicorn 场景下无作用
connection-string=postgresql://srht:q1w2e3r4@127.0.0.1/meta
migrate-on-upgrade=no              # yes 时包升级触发自动迁移
webhooks=redis://127.0.0.1:6379/1  # webhook worker 的队列(注意 DB 序号)
api-origin=http://127.0.0.1:5100   # 本服务 Go API 的地址

要点:

  • connection-string 建议显式走 127.0.0.1(TCP)。PostgreSQL 默认 local 认证是 peer 模式,应用进程用户(root/www-data)与库属主 srht 不同名,走 socket 会直接 Peer authentication failed。
  • migrate-on-upgrade=yes 只在用官方包管理器/迁移工具管升级时开启;源码手工部署建议 no,升级走手工迁移,避免启动期意外改库。
  • api-origin 不配时按"web 端口 + 100"推导,但显式写出来能避免歧义(尤其反代/多网卡场景)。

3.2 [meta.sr.ht::api]:GraphQL 服务端预算

[meta.sr.ht::api]
max-complexity=200    # 单次查询复杂度上限(约等于返回字段数)
max-duration=3s       # 单次查询处理时限
internal-ipnet=127.0.0.0/8,::1/128,10.0.0.0/8   # 允许内部认证(service-key 签名)的网段

internal-ipnet 是安全关键项:列出哪些来源网段可以用内部签名直接调 API。必须只含你的服务间网络,绝不能放公网网段。多机部署时写各服务所在内网 CIDR 的并集。

3.3 [meta.sr.ht::settings]:注册行为

[meta.sr.ht::settings]
registration=yes                 # no 则关闭公开注册(仅管理员可建号)
onboarding-redirect=http://git.srht.local   # 注册完成后的跳转
welcome-emails=no                # 欢迎邮件(需 SMTP 与 cron 配套)
key-expiration-emails=no         # PGP/SSH key 到期提醒

3.4 [meta.sr.ht::billing]:计费开关

[meta.sr.ht::billing]
enabled=no     # yes 时启用 Stripe 计费流(需 stripe-* 三项 + 注册页首步)

联动行为实测:enabled=no 时访问 /register 会 302 直接跳 /register/step2(两步注册的第一步只在计费开启时出现);enabled=yes 则需要完整 Stripe 配置,否则注册流不可用。自托管建议保持 no。

四、[git.sr.ht]:代码托管的参数

[git.sr.ht]
origin=http://git.srht.local
api-origin=http://127.0.0.1:5101
debug-host=0.0.0.0
debug-port=5001
connection-string=postgresql://srht:q1w2e3r4@127.0.0.1/git
migrate-on-upgrade=no
webhooks=redis://127.0.0.1:6379/1
post-update-script=/usr/local/bin/git.sr.ht-update-hook
repos=/var/lib/git/
repos-index=                     # 代码搜索索引目录,留空禁用
s3-bucket=                       # 大文件对象存储(可选)
s3-prefix=
enable-dispatch=no               # SSH 统一分发(需 sourcehut-ssh)
outgoing-domain=                 # 补件邮件出站域

[git.sr.ht::api]
max-complexity=200
max-duration=3s
internal-ipnet=127.0.0.0/8,::1/128,10.0.0.0/8

四个最容易踩的参数:

  1. repos:裸仓库根目录,实际仓库落在 repos/~<用户>/<仓库>。目录必须存在且对运行进程可写(实测建仓成功后生成 /var/lib/git/~srhtadmin/demo-repo)。
  2. post-update-script:仓库 hooks 符号链接的源(pre-receive/update/post-update/post-receive 四个链接都指向它)。留空 = 建仓必败,实测症状是页面报 symlink /var/lib/git/~user/repo/hooks/pre-receive: no such file or directory。它必须指向编译产物 git.sr.ht-update-hook。
  3. api-origin:git Web 层创建仓库等写操作全部转给本服务的 Go API 执行。不配时回退到 origin(Web 端口),而 Web 端口上并没有 /query 路由,表现为 ConnectError/500。
  4. enable-dispatch:SSH 推送链路的开关。最小集关掉(HTTP clone 照常可用);要开 push 需部署 sourcehut-ssh 分发器与授权链。

五、[mail]、[webhooks]、[objects]:辅助节

5.1 [mail](每服务都要有一份)

[mail]
smtp-host=            # 空 = 不发信(注册欢迎邮件、通知邮件静默失败但不阻塞注册)
smtp-port=587
smtp-from=no-reply@srht.local
smtp-encryption=starttls   # starttls | tls | insecure
smtp-auth=none             # plain | none
smtp-user=
smtp-password=
error-to=                  # 应用异常告警邮箱
error-from=
pgp-privkey=               # 出站邮件签名(可选)
pgp-pubkey=
pgp-key-id=

最小集用 smtp-host= 空值即可让站点工作;一旦要发信,smtp-auth=plain 时 user/password 必填。

5.2 [webhooks]

[webhooks]
private-key=<base64,sr.ht-keygen webhook>

Ed25519 签名密钥:服务端持私钥,所有 webhook 载荷用它签名;消费方拿对应的 Public key 验签。全站一致可简化下游验签。

5.3 [objects](可选,S3 兼容存储)

[objects]
s3-upstream=
s3-access-key=
s3-secret-key=

用于头像、构建工件等对象存储;全部留空时相关功能退化(头像不可上传等),最小集可忽略。

六、配置类故障速查表(实测沉淀)

#症状(原文回显)根因修法
1Exception: Unknown redis connection URL schemeredis-host 写成裸 127.0.0.1:6379改 redis://127.0.0.1:6379/0
2FATAL: Peer authentication failed for user "srht"connection-string 走 socket + peer 认证改走 TCP:@127.0.0.1
3symlink .../hooks/pre-receive: no such file or directorypost-update-script 留空指向 /usr/local/bin/git.sr.ht-update-hook
4httpx.ConnectError: [Errno 111] Connection refused(建仓 500)git.sr.ht 未配 api-origin,回退到 web 端口补 api-origin=http://127.0.0.1:5101
5在 meta 已登录,git 站仍是未登录用 IP 访问,cookie 域(global-domain)对不上用域名访问;核对 global-domain 与 origin 同域
6TypeWarning/TypeError: unsupported operand type(s) for 'in': 'str' and 'EnumMeta'(建仓 500,validation.py)Python 3.10 的枚举 in 语义限制(上游按 3.12 开发)按 3.12+ 部署,或给 srht/validation.py 打兼容补丁
7This username is not available(注册 admin)admin 在上游 username_blacklist 硬编码保留名换非保留用户名(如 srhtadmin)
8fatal: detected dubious ownership in repository(HTTP clone 500)仓库属主与 fcgiwrap 运行身份不一致git config --system --add safe.directory "*"
9ModuleNotFoundError: No module named 'metasrht.graphql'sdist 未带 codegen 产物ariadne-codegen 生成后重装(见安装篇 3.3)

七、改配置之后:生效路径

改了什么谁在读生效动作
[sr.ht] 公共节、connection-string、originPython web重启对应 *.service(web)
[xx::api]、internal-ipnet、api-originGo api重启对应 *-api.service
post-update-script、reposGo api(建仓时)重启 git.sr.ht-api
[mail]Python web + worker重启 web 与 webhook worker
redis-host两层都读web 与 api 都要重启
nginx 配置nginxnginx -t && systemctl reload nginx

原则:web 与 api 是两个进程、两个密钥消费者,别只重启一半。配置错误一般不会让进程退出(懒加载),首次触发对应功能才炸,所以改完配置建议把关键路径(注册、建仓、clone)各点一遍。

八、生产加固清单

  1. environment=production(关闭 debug 静态路径与非生产横幅)。
  2. registration 按需收紧;开启 [meta.sr.ht::api] 复杂度/时限预算的监控。
  3. internal-ipnet 精确到服务网段;service-key/network-key/webhook key 三把密钥轮换流程先演练。
  4. PostgreSQL 连接串强制 TLS(sslmode=require 起步),应用账号最小授权(只授业务库)。
  5. HTTPS 全站(origin 写 https,反代终结 TLS),cookie 才能加 Secure 语义。

系列其余篇目:《Sourcehut 介绍与选型指南》《Sourcehut 0.88.9 + git.sr.ht 0.102.8 Ubuntu 22.04 安装指南》《Sourcehut 常用命令与运维详解》。

参考资料

  1. 各服务 config.example.ini(随版本自带,权威参数清单):meta.sr.ht-0.88.9 / git.sr.ht-0.102.8 源码包
  2. 官方自托管文档:https://man.sr.ht/pages/install.md
  3. core.sr.ht 配置加载实现:srht/config.py(git.sr.ht/~sircmpwn/core.sr.ht,tag 0.84.10)