本地运行平台
本地平台把提交、审核、试赛、对局和排名放在一起运行。要在自己的机器上让参赛模型打一场,用 tinybrains 就行,它不需要 Compose、数据库和赛季;提交前的测试介绍了它。当你想要的是完整闭环时,再把平台启动起来:一次提交被审核通过、进入试赛、晋级、配对并评分。
你需要准备什么
安装 Docker Engine,带 Compose v2.21 或更新版本,以及一个 POSIX shell。你需要两个检出目录,各自运行一个 compose 文件:
web/ the platform: Postgres, Redis, MinIO, Soma, the Orion console and the site
kalam/ a runner: the one container that plays matches for a Soma
每个服务都是由它自己的仓库发布的镜像。Soma 是 ghcr.io/tiny-brains/soma:内含 Soma 包的 orion-server,它应用数据库模式并加载自己的包。Kalam 是 ghcr.io/tiny-brains/kalam,一个加载自己的包的运行器。卡带没有镜像:这些镜像和网站的查看器都从 Ants 发布版本获取它。要试验 Soma 的改动,构建那个检出目录并设置 SOMA_IMAGE;要试验 Kalam 或 web 的改动,带上 --build 运行。
下面的每条命令都在 tinybrains/devops/ 中运行,这个目录存放 compose 文件、安装脚本和每个环境自己的设置。./tb <env> <web|runner> ... 把其余参数传给 docker compose,并已选好该环境的文件、项目和设置。
平台使用锁定的 Orion 1.12.0 运行时、Postgres 16、Redis、MinIO 和浏览器应用。没有推理边车服务:每个节点自己运行模型。宿主机上不需要 Rust。
配置平台
一条命令就能设置好所有凭据:
scripts/setup/init.sh
它创建 dev/web/.env,生成 POSTGRES_PASSWORD、SOMA_SESSION_SECRET、RUNNER_TOKEN_SECRET、模型读取密钥和 ORION_ADMIN_KEY,把 SOMA_IMAGE 锁定到最新的 Soma 发布版本,为这台机器生成 Ed25519 插件信任根,并为 Soma 镜像(以及 Kalam 镜像,如果本机有的话)里的插件签名。它是幂等的,所以换了新镜像之后它也是修复命令:未签名的组件会让调用它的频道被隔离,节点在启动应用包时就会停止。examples/web.env.example 是它据以填写的契约,它写出的是 dev/web/.env。
注册一个 GitHub OAuth 应用,主页填 http://localhost:5173,回调填 http://localhost:5173/v1/auth/github/callback,然后在 .env 中设置 GITHUB_CLIENT_ID 和 GITHUB_CLIENT_SECRET。纯 HTTP 开发用的是不带 Secure 标志的 cookie;HTTPS 部署需要与之匹配的 cookie 策略。
启动
./tb dev web up -d --build
./tb dev web ps -a
./tb dev web logs soma-bootstrap soma
Soma 之前先运行两个一次性任务。buckets 创建回放和模型存储桶、模型读取密钥,以及 models/* 的公开读取权限。soma-bootstrap 创建 Orion 状态数据库,在平台数据库为空时应用 Soma 的迁移,并注册游戏、卡带和它的镜像所携带的引擎摘要。它记录下所应用迁移的摘要,并拒绝它无法应用的数据库模式改写。随后 soma 加载自己的包,若有插件验证失败就停止,所以任何处于运行状态的 soma 节点都在正常服务;它的日志会用 every [packages] artifact is applied and serving 这一行来说明。
打开 http://localhost:5173,或者验证代理:
curl --fail --silent --show-error http://localhost:5173/v1/games
本地端口:Web 在 5173,Soma 在 8080,Orion 控制台在 8081,MinIO 在 9000/9001,全部只绑定到本机回环地址。应用请求和登录要使用同一个浏览器源(origin)。
启动运行器
平台自己不进行任何对局。对局由 Kalam 检出目录启动的运行器来打,它只通过 Soma 接触平台。登录之前,先把自己设为管理员:
scripts/setup/admin-user.sh <your-github-login> # then ./tb dev web up -d soma
它把 github:<your numeric id> 写进 .env 的 SOMA_ADMIN_IDS(绝不用登录名,因为改名后 GitHub 会把登录名转给别人),登录后该账户就成为管理员。然后在管理台的运行器页为运行器生成一个密钥,密钥只显示一次。
把 examples/runner.env.example 复制为 dev/kalam/.env,取消其本地块的注释:每个地址都是 host.docker.internal,并且已经写好了 RUNNER_SIG_DIR=../web/keys/signatures。填入密钥、TB_TRUST_PUBLIC_KEY 和 web 的 .env 中的那对 MODELS_READ_*,设置 KALAM_IMAGE(已发布的镜像,或配合 --build 使用 tinybrains/kalam:dev),再为运行器单独生成一个 ORION_ADMIN_KEY。然后:
./tb dev runner --profile admit up -d --build
./tb dev runner logs -f runner # 直到出现 "every [packages] artifact is applied and serving"
./tb dev runner logs -f admit # 同样的一行
--profile admit 在运行器旁边启动审核运行器。Soma 不运行任何模型,所以每一次提交和每个基线都由审核运行器审核;没有审核运行器在线时,它们一直停留在 testing。
运行器和 Soma 要用同一个 Ants 发布版本构建。如果运行器的引擎摘要与 Soma 声明的不同,它就永远什么也认领不到,而且看上去一切正常。
登录并促成一场对局
在 http://localhost:5173 用 GitHub 登录。要用 localhost,绝不要用 127.0.0.1:登录 cookie 只对该主机名有效。在你的个人主页用“新建模型”表单创建一个模型,然后通过网站自己的 /submit 表单提交,它会自己计算两个文件的哈希并上传,只有在传输失败时才显示上传命令。一个游戏需要一个进行中且有启用中地图的本地赛季、一个有参赛资格的账户、一个运行器,以及至少一个可以上场的对手来打试赛和常规对局。
全新的平台没有赛季。和生产环境一样,由管理员在管理台上创建;平台不会预置任何数据。
它也没有地图。没有任何发布版本附带赛季的地图:由管理员在赛季页面上传,再逐一启用。赛季的地图名为 <size>-<terrain>-<N>p-<H>h(地图),所以基础地图也要以这种形式的名字放进去。tinybrains maps export ants <dir> 写出发布版本附带的五张基础地图,tinybrains maps check 告诉你 Soma 会不会接受你自己的地图。在有地图启用之前,pair 时钟什么也不配对,候选版本一直停留在 verified。
它也没有基线。平台不附带任何模型:由管理员在赛季页面上传赛季的基线,每个基线是一个名字加它的两个文件,ants-starter/models 里有三个。admit 时钟像审核提交一样审核基线,基线上传后处于停用状态,直到管理员在那里启用它。候选版本卡在 verified,是因为没有基线能坐进它的试赛:到赛季页面看看有没有启用中的基线。同样的文件换一个名字就是第二个对手,多席位地图上的试赛就是这样凑够对手的。
只有管理员能创建赛季,管理员在管理台的用户页提升或降级其他任何人。跟踪已提交模型的阶段、它的试赛 ID,然后是它已结束的对局历史和排行榜条目。这比一次成功的健康检查更能证明平台在正常工作。
重新加载与停止
改动包就意味着新镜像:重新构建它,节点启动时会加载它。
docker build -t tinybrains/soma:dev ../soma && SOMA_IMAGE=tinybrains/soma:dev ./tb dev web up -d
scripts/setup/sign-plugins.sh # 换了新的 Soma 或 Kalam 镜像之后
加载包不会应用任何数据库模式迁移。soma-bootstrap 只在平台数据库为空时才应用迁移。数据库模式还处于发布前阶段,改动是直接改写 0001_init.sql 或 0002_sessions.sql,所以引导程序会立刻拒绝被改写的模式;你不会在之后才以“缺少某个表”的形式撞上它。./scripts/dev/resync-dev-schema.sh 按新模式重建本地数据库,并保留账户和会话。
新引擎意味着新的 Soma 镜像和新的运行器镜像,两者从同一个发布版本构建;引导程序只有在 ENGINE_RELEASE=1 时才声明新的摘要,而赛季进行中它会拒绝这样做:先结束赛季。
用 ./tb dev web stop 停止服务。运行器会先打完手头的对局再停止,但强制关闭仍可能留下需要平台收回的认领。只有当你确实要丢弃本地历史时才删除数据卷。
没有对局在进行时
| 症状 | 检查 |
|---|---|
| 登录反复跳转或返回未认证 | 浏览器源、OAuth 回调、cookie 策略和会话密钥 |
| 候选版本一直处于 testing | 两个对象是否都在模型存储桶里、已注册的参考观测、admit 时钟,以及是否有审核运行器在线 |
被拒绝,原因 ARTIFACT_MISSING | 上传这一步。没有任何东西会从发布版本抓取文件:由参赛者 PUT 到预签名 URL |
| 候选版本一直处于 verified | 赛季是否有启用中的地图且它的基线能坐进去,然后是最新的试赛状态和 pair 时钟 |
| 待进行的对局始终不开始 | 有运行器在线且其密钥有效;它的引擎摘要与 soma-bootstrap 声明的一致 |
运行器日志出现 invalid_key | 密钥来自另一个数据库:在这个数据库上重新生成一个 |
| 运行器不打就释放每一场对局 | 运行器的 kalam-roster 时钟,以及它的节点上席位的模型是否为 active |
| 模型无法加载 | 存储桶的地址(Soma 按公开地址为上传签名、通过内部地址访问,运行器用的是 host.docker.internal),以及读取密钥 |
| 对局能进行但无法完成 | 运行器的 RUNNER_BLOB_ENDPOINT 必须与 Soma 的逐字符一致 |
| 有结果但评分不变 | count 时钟;看看这场对局是否是不计评分的试赛 |
| 节点因频道被隔离而停止 | 组件的 Ed25519 签名。换了新镜像之后重新运行 devops/scripts/setup/sign-plugins.sh |
用 ./tb dev web logs 读取相关服务的日志,报告问题时带上模型和对局 ID。云端部署、TLS 入口、真实 R2 验证和自动扩缩容都不在这套本地环境的范围内。