architecture.md 回答「两边怎么分工、控制 API 有哪些」。 lifecycle.md 回答「实例怎么启停、闲置怎么回收」。 pairing.md 回答「密钥存在哪、一对一怎么约束」。 stability.md §6 回答「每户内存限额以后怎么进同一份文件」。
本文放大其中一层:运营旋钮只认一份磁盘文件,文件必须带齐全部字段, 打开就能看见生效值。 不再为这些旋钮保留环境变量,也不再允许「字段缺席 再回退 env」。镜像自带出厂文件;Helm / compose 删掉对应 env。
host-agent 启动时 loadConfig() 把全部 COMPUTER_* 读进内存,之后不再读。
闲置回收用的是冻住的 idleTimeoutSec。磁盘只有 /data/host.json(配对身份)。
PUT /v1/runtime 只能改 main_url。
要改 scale-to-zero,只能改 Deployment env 并重启宿主,所有 running 的 dsh 会被打断。
两套来源(env + 可选文件、字段可缺)看起来像两份真源,打开文件还不敢确定缺的键 到底是默认还是忘了写。本方案去掉这套叠加。
- 运营旋钮只有一份权威:
/data/settings.json。 七个字段必须同时在,数值就是生效值。看文件等于看配置,和看一份 values YAML 一样。 - 镜像带出厂完整文件。 数据卷上还没有这份文件时,启动从镜像拷一份过去,不读 env 拼默认。
- 删掉这五个旋钮的环境变量。 Helm / compose 同步删;装机定制走文件或配对后的控制 API,不走 env。
- 控制 API 改完写盘并替换内存,不轮询、不 SIGHUP、不热读
.env。 - 配对密钥仍在
host.json,不和旋钮混写。 - ApeMind 不是回收判定的真源。 管理页 GET/PUT runtime;
computer.host最多记上次下发副本。 - 管理页带齐全部旋钮。 每户内存/进程数顶可以填正整数或
max。
| 层 | 载体 | 谁改 | 何时生效 |
|---|---|---|---|
| 部署事实 | 启动 env | 装机 / Helm / compose | 必须重启进程 |
| 运营旋钮 | /data/settings.json(完整) |
控制 API;首次由镜像出厂文件生成 | 写盘即换内存,见 §6.3 |
部署事实仍走 env:监听端口、COMPUTER_DATA_DIR、COMPUTER_PUBLIC_ORIGIN、
COMPUTER_UID_BASE / 回环隔离、COMPUTER_PAIR_CODE、COMPUTER_DSH_COMMAND、
COMPUTER_PORT_BASE、预共享密钥兼容垫。它们绑着 socket、PVC、Ingress 和 capability。
删除、落地后代码与模板都不再认识的 env:
COMPUTER_IDLE_TIMEOUT_SECCOMPUTER_SESSION_TTL_SECCOMPUTER_READY_TIMEOUT_SECCOMPUTER_STOP_GRACE_SECCOMPUTER_MAX_INSTANCES
main_url 继续走 host.json(已配对)或 COMPUTER_MAIN_URL(兼容模式),不进 settings.json。
镜像内固定路径(只读):
/usr/local/share/apemind-computer/settings.json
内容永远完整,与代码默认一致:
{
"idle_timeout_sec": 1800,
"session_ttl_sec": 7200,
"ready_timeout_sec": 90,
"stop_grace_sec": 10,
"max_instances": 200,
"instance_memory_max_mb": 2048,
"instance_pids_max": 512
}数据根 COMPUTER_DATA_DIR(默认 /data)是 PVC,会盖住镜像里的 /data,所以出厂文件不能只放在 /data 里。
/data/host.json 0600 配对身份(pairing.md §4)
/data/settings.json 0600 运营旋钮,七个字段必须齐
/data/users/<key>/ … 租户 HOME
启动时保证数据卷文件完整:
/data/settings.json不存在 → 把镜像出厂文件原样拷过去(0600)。- 存在但是合法 JSON、缺了当前版本认识的键 → 缺的键用出厂值补上,写回完整文件(镜像升级加字段时用)。
- 存在但不是 JSON、根不是对象、或某个已有字段类型/范围非法 → 打 error 日志,用出厂文件整份覆盖。不带着残缺文件跑,也不静默回退到已删除的 env。
- 多出来的未知键:启动时忽略,写回时丢掉,避免旧实验字段一直待着。
写盘与 host.json 相同:同目录临时文件 + rename。禁止往这份文件写令牌、签票密钥、main_url。
进程跑起来之后,内存里的旋钮与文件字节级对应。五个时间/容量键是整数;每户限额两个键是正整数或字面量 "max"。运维 cat /data/settings.json 看到的就是正在用的值。
装机时若 Helm 需要非出厂值:挂一份完整的 settings 到数据卷(或配对后 PUT)。不要再提供「只改某一个 env」。compose 示例同样删掉那五个环境变量。
没有「文件 → env → 代码」三条线。启动只做 §4 的「保证文件完整,然后读文件」。之后只被 PUT /v1/runtime 改。
曾用 COMPUTER_IDLE_TIMEOUT_SEC=60 的现网:升级后该 env 被忽略,第一次启动会写出厂 1800。若仍要 60 秒,升级后在管理页再存一次,或预先放好完整的 /data/settings.json。
不新开路径。扩 :9090 的 GET/PUT /v1/runtime。未配对 GET 仍无 Bearer,只回 state / public_origin / version。运营旋钮只在已配对的 GET 里出现。
实现以 host-agent/src/control.ts 为准。本文是契约。
{
"state": "paired",
"public_origin": "https://computer.example.com",
"version": "v0.2.6",
"main_url": "https://app.example.com",
"paired_at": "2026-08-31T00:01:00.000Z",
"settings": {
"idle_timeout_sec": 1800,
"session_ttl_sec": 7200,
"ready_timeout_sec": 90,
"stop_grace_sec": 10,
"max_instances": 200,
"instance_memory_max_mb": 2048,
"instance_pids_max": 512
}
}settings 七个键必须都在,没有 *_source。GET 不回令牌或签票密钥。
补丁:省略的字段保持文件里的值;null 表示该字段回到镜像出厂值(不是删键)。写回后文件仍然七个键齐全。
{
"main_url": "https://app.example.com",
"settings": {
"idle_timeout_sec": 900,
"session_ttl_sec": null
}
}上例:闲置改成 900;会话 TTL 回到出厂 7200;其余三个不动。
settings省略:只按今天规则处理main_url(旧客户端兼容)。settings为空对象:旋钮全不动。- 未知键:整次 400,磁盘与内存不动。
- 非
null且类型/范围不合法:整次 400。每户限额只收正整数或"max",0也是 400。
| 字段 | 最小 | 最大 | 0 的含义 |
出厂 |
|---|---|---|---|---|
idle_timeout_sec |
0 | 86400 | 关闭闲置回收 | 1800 |
session_ttl_sec |
60 | 604800 | 不允许 | 7200 |
ready_timeout_sec |
5 | 300 | 不允许 | 90 |
stop_grace_sec |
1 | 120 | 不允许 | 10 |
max_instances |
1 | 10000 | 不允许 | 200 |
instance_memory_max_mb |
1 | 1048576 | 不允许;不限制填 "max" |
2048 |
instance_pids_max |
1 | 1000000 | 不允许;不限制填 "max" |
512 |
越界 → 400,{error: "<field> out of range"}。
成功:写出完整 settings.json → 换内存 → 200,body 与 GET 相同。
预共享兼容模式:可以 PUT settings;main_url 仍 409。
| 字段 | 生效点 |
|---|---|
idle_timeout_sec |
下一次 idleSweep(≤60s)。已在停的不回滚。0 之后不再因闲置新停;desired=running 的休眠实例仍靠流量唤醒。 |
session_ttl_sec |
之后新签的 cookie。已发出的仍按其签发时的 e。 |
ready_timeout_sec |
下一次冷启动 / 唤醒。 |
stop_grace_sec |
下一次 SIGTERM 宽限。 |
max_instances |
下一次要新占槽位的 ensure。下调不杀已有实例,只拦超员新开(507)。 |
instance_memory_max_mb |
立刻写到已有实例的 cgroup;用超了内核马上杀。检测不到 cgroup 则只记文件。 |
instance_pids_max |
同上。 |
不通知 dsh。dsh 无配置协议。
- 宿主内存 =
/data/settings.json(GET /v1/runtime.settings)。 computer.host可选记下上次下发,仅宿主暂时不可达时回显,不参与回收。
/admin/computer 第一期一个「闲置多久停进程」:打开拉 runtime;保存时 PUT。宿主 4xx / 不可达则保存失败,不把失败值写成 ApeMind 真源。
工作区页、侧栏、开通名单不读这些旋钮。GET /api/v2/computer 的轮询不续命。
- 旧数据卷没有
settings.json:第一次用新镜像启动,拷出厂文件,闲置 1800。与今天没改过 env 的行为相同。 - 旧数据卷只改过 env、没有文件:升级后 env 失效,回到出厂 1800,须在管理页再存,或事先放入完整
settings.json。 - 旧 ApeMind 只 PUT
main_url:新宿主旋钮不动,文件保持完整。 - 新 ApeMind 打旧宿主:GET 没有
settings则管理页该字段只读,提示升级宿主镜像。 - Helm chart /
compose.example.yml/ README 配置表:落地时删掉 §3 那五个 env,改指向本文和出厂文件。
- 按用户 / 组织不同闲置超时。
- 热改 Origin、端口、数据目录、uid、隔离、dsh 命令、端口起点。
- 给 dsh 加心跳。
- 保留运营旋钮的 env 作为第二套缺省。
- 允许
settings.json缺字段或空对象当正常状态。 - 多 host 调度(每台自己一份完整文件,1∶N 时逐台 PUT)。
- 配额、计费。
- 生产发布与现网改值。
- 本仓:镜像加入出厂文件;启动保证
/data/settings.json完整;删五个 env;扩 runtime;supervisor 读文件值;Helm / compose / README 同步删 env。单测:缺文件则拷出厂、缺键则补齐、坏文件覆盖、PUT 补丁与null回出厂、越界 400、文件始终五键、idleSweep 用新值、启动不再读已删 env。 - aperag-enterprise:host client 认识
settings;管理页一个闲置字段;GET 无settings时降级。 - 先发 computer-host 镜像,再发 ApeMind。
- 空数据卷启动后
/data/settings.json与镜像出厂文件七个键、值都相同。 - 已配对 PUT
idle_timeout_sec=60,GET 与文件都是 60,其余四键仍在;60~120 秒无流量则停进程,desired仍 running,cookie 能唤醒。 - PUT
idle_timeout_sec=0后不再因闲置新停。 - PUT
idle_timeout_sec=null后文件该键为 1800,七个键仍齐。 - 未知键 / 越界 → 400,文件不变。
- 多次 PUT 后
host.json令牌字节级不变。 - 设了已删除的
COMPUTER_IDLE_TIMEOUT_SEC=60再启动,生效值仍是文件里的数,不是 60。 - 人为删掉数据卷上该文件再重启,重新出现完整出厂文件。
- 运营旋钮为什么不再用 env?缺字段为什么不允许?
- 镜像出厂文件在哪,为什么不能只放在
/data? - 数据卷上没有文件、缺键、文件损坏时,启动各做什么?
GET/PUT /v1/runtime的settings长什么样,null回到哪里?- 改闲置超时最晚多久按新值回收?改会话 TTL 影不影响已发出的 cookie?
- 以前只改过 Helm 里的
COMPUTER_IDLE_TIMEOUT_SEC,升级后会怎样? - ApeMind 数据库是不是回收真源?第一期页面暴露什么?