核心概念
Town OS 建立在一组核心思想之上。理解这些概念,有助于你看清各部分是如何组合在一起的—— 从定义一个软件包,到管理一个正在运行的服务。
软件包(Packages)
软件包是对某个服务的自包含 YAML 定义,从 git 仓库拉取。每个软件包都会声明自己的
网络端口映射、存储卷、环境变量和交互式提问——运行该服务所需的一切。软件包可以以
容器方式运行(默认),也可以是基于 QEMU 的虚拟机,或通过 Valve 的 Proton 兼容层
运行的 Windows 应用。文件模板(Go 的 text/template 文件)可以在安装时
渲染进卷中,实现动态配置。软件包通过 supplies 标签(例如
http、database、cache)描述自身能力。
默认仓库自带一批精选软件包,涵盖 git 托管、视频会议、媒体流、团队沟通
和数据库——并且还在持续增加。
提问(Questions)
安装软件包时,Town OS 可能会呈现一组称为「提问」的交互式提示。你的回答会填入软件包
定义中各处的 @variable@ 模板占位符——用于设置主机名、端口、凭据和其他选项。
每个提问都带有类型化的校验器(hostname、port、bytes、
volume、archive、duration、secret、
boolean、oauth 或自由文本)以及可选的默认值,
因此配置错误在服务启动前就会被拦下。类型还决定了你看到的控件——boolean
是复选框,secret 是掩码输入框,而 oauth 提问是一个「连接」按钮,
会带你登录相应服务商并保存它返回的令牌。有几种类型在留空时会自动生成取值:端口在
10000–60000 范围内随机分配,主机名按 package-name-4hex 的形式生成,
密钥则生成 64 位十六进制字符串。标记为 optional 的提问可以直接留空——
你没有的 SMTP 中继会保持未设置,而不是被随便填上一个值。
服务(Services)
软件包安装完成后就成为一个服务——一个由 systemd 单元管理的运行中容器。Town OS 提供 启动、停止和重启控制,通过 Server-Sent Events 实时推送日志,支持基于游标的日志分页, 也可以启用或禁用服务。各服务之间以及与宿主系统之间都是相互隔离的。
网络(Networking)
Town OS 内置了
rolodex,
这是一个 DNS 服务器,为你的软件包管理权威区域并转发上游查询。每个软件包声明自己的
端口映射——external 端口暴露给宿主机,internal 端口仅在容器
之间可用。端口取值支持 @variable@ 模板,因此用户可以在安装时自行选择端口。
声明了依赖关系的软件包会共享同一网络,使同一依赖树中的容器可以直接通信。父软件包可以在
环境变量中使用 @dep_KEY_host@ 和 @dep_KEY_port_N@ 模板变量引用
依赖,也可以通过运行时环境变量
(TOWNOS_DEP_{KEY}_HOST 和
TOWNOS_DEP_{KEY}_PORT_{port})访问。UPnP 转发由系统自动处理。
网络
网络是一个具名的 WireGuard 覆盖网,并与一个 DNS TLD 配对。软件包安装到某个网络中,
对等设备加入这个网络,而 TLD 决定谁能解析什么。home 网络始终存在、无法删除,
并且只提供 DNS——它没有隧道,而且在它上面的对等设备注册会被拒绝,因为每个账户本就属于它,
仅凭成员身份不应成为进入隧道的途径。其他网络则会获得 WireGuard 接口,以及一个由本机自身身份推导出的子网,
因此同时加入两台 Town OS 主机的设备永远不会遇到冲突。禁用网络只会停掉传输层:
远程访问中断,而本地 DNS 与容器照常运行。
Ingress
一扇共享的前门。单个 Caddy 实例为整台主机持有 :443 与 :80,
并且完全依据 SNI 和 Host 来决定你访问到哪个服务——没有按服务划分的监听器,也没有 bind 指令,
所以局域网客户端和覆盖网对等设备命中同一个套接字、拿到同一张证书、被代理到同一个容器。
软件包的名称是一个字符串,同时以四种方式被使用:A 记录、证书的 SAN、TLSA 的所有者,以及 ingress 的虚拟主机。
TLS 与本地 CA
Town OS 运行自己的证书颁发机构,为软件包所声明的名称签发证书,
因此无需公共证书、也不会有浏览器警告,服务就能通过 HTTPS 访问。
CA 证书可从 /tls/ca.crt 下载——只要信任它一次,所有本地名称就都能正常工作。
证书在后台续期,而不是在启动时续期;更改 TLD 会重新签发依赖于它的名称。
对象存储
Town OS 通过 gfeh 提供对象存储。一个分区是一个 btrfs 子卷, 拥有自己的守护进程和自己的一套用户,并且每个网络恰好对应一个分区—— 因此某个网络中的用户、权限或已发布链接,在另一个网络中毫无意义。每个分区提供四种视图: S3、普通 HTTP、类似云盘的浏览界面,以及 IPFS。用户是按树状结构组织的主体; 访问权限是一项授权,并且始终被收窄到所授予主体的权限上限; 而已发布链接是一次可以事后撤回的公开暴露。任何分区都不会发布宿主机端口—— ingress 通过容器名称访问它们。
存储(Storage)
所有数据都存放在 btrfs 子卷上,按软件包相互隔离。每个卷都有一个容器内的挂载点、
一个可配置的配额(例如 512mb、2gb),以及可选的
uid/gid 归属。卷可以通过 archives 功能从容器
镜像预填充内容,也可以在安装时把一个 git 仓库克隆进空卷来完成初始化。用户还可以创建
自己的独立卷。虚拟机磁盘镜像会缓存在专用子卷中,后续安装可以直接复用。系统支持 btrfs
快照,可用于时间点备份。数据在软件包重装和升级之间始终保留——更新服务时你的文件不会丢失。
仓库(Repositories)
软件包按仓库组织——即包含 packages/ 目录、其中存放带版本 YAML 定义的 git 仓库。
每套 Town OS 都包含默认仓库,但任何人都可以创建和分享自定义仓库。仓库是有顺序的——
当软件包重名时,靠后的条目会覆盖靠前的。仓库中的 featured.json 文件可以
在界面上突出展示选定的软件包。
模板与说明(Templates & Notes)
@variable@ 模板系统把用户对提问的回答连接到环境变量、端口映射、卷配额等各处。
像 @LOCAL_EXTERNAL_HOST@ 和 @LOCAL_INTERNAL_HOST@ 这样的内置变量
始终可用。安装完成后,notes 会展示关键信息,例如连接 URL、凭据或联系方式——
并可对 url、phone 和 email 进行可选的类型校验。
页面(Pages)
Town OS 可以托管静态网站。每个页面有三种可选的来源:上传 tar 归档、从容器镜像中提取文件, 或克隆一个 git 仓库。每个页面都有自己的域名,并通过 Caddy 对外提供服务。归档类型的页面可以 重新上传来更新;git 和容器镜像类型的页面可以按需重新构建。
监控(Monitoring)
内置的可观测性套件开箱即用,无需任何配置。Prometheus 负责采集指标,Node Exporter 报告 主机统计数据(CPU、内存、磁盘、网络)。可选两种监控后端:轻量的内置界面(默认) 或带自动配置仪表盘的 Grafana。整套组件都作为系统服务运行,失败时会自动重启。
国际化(Internationalization)
所有面向用户的字符串都经由一个以 BCP 47 语言代码为键的消息目录管理。界面以母语字形呈现
21 种常见语言,另有 87 种以上的国家/地区代码可选。目前只有英语(en-US)
完成了全部翻译——相关基础设施已经就绪,只等社区参与翻译。
账户与会话(Accounts & Sessions)
Town OS 支持管理员账户和普通用户账户。密码至少需要 8 个字符。身份验证使用 JWT 令牌, 闲置 7 天后过期。会话是临时性的——签名密钥会在每次重启时重新生成,从而自动清除所有活动会话。
审计日志(Audit Logging)
每一项管理操作都会被记录,包含账户名、动作、请求路径、详情、成功标志和时间戳。 只读端点不计入其中。审计日志支持分页、按账户过滤和全文搜索。
设置(Settings)
全系统配置通过界面中的键值设置进行管理。可配置项包括默认卷配额(50 GB)、 归档上传大小上限(1 GB)、归档解包超时(600 秒)、语言区域、Proton 运行器镜像 以及 DNS 顶级域名。
软件包升级(Package Upgrades)
当你配置的仓库中出现更新版本时,Town OS 会自动检测。仪表盘上的角标会显示待处理升级的数量。 你可以忽略不想升级的版本——忽略记录以 SHA256 哈希跟踪,只有当可升级集合发生变化时才会再次出现。
系统服务(System Services)
基础设施容器——监控、DNS 和 Web 界面——与用户安装的软件包分开管理。它们使用不同的
systemd 前缀(town-os-system--),失败时总会重启,且用户无法将其禁用。