运行 orcad¶
orcad 是由普通 Node 提供的 Orca 运行时。这是它与任何监督者之间的契约:它绑定什么、在磁盘上拥有什么、谁重启什么,以及其就绪负载实际证明了什么。
两个长生命周期进程,而不是一个¶
一个部署由 orcad 加上 终端守护进程 组成。
| orcad | 终端守护进程 | |
|---|---|---|
| 启动方 | 监督者 | orcad,分离启动 |
| 拥有 | RPC、git、工作树、持久化 | 每个本地 PTY |
| 生命周期 | 一次受监督运行 | 与 orcad 分离,不是其服务 |
| 端点 | ws://<bind>:<port> |
<data-root>/daemon/daemon-v<N>.sock |
orcad 分离守护进程并调用 disconnectDaemon(),绝不调用 shutdownDaemon()。内置远程部署路径只停止记录的 orcad PID,因此守护进程及其 PTY 会存活。后继进程采用当前端点,并通过旧版适配器路由受支持的先前协议版本。这使得以 PID 为范围的更新、回滚或重启不会破坏正在运行的工作。
进程分离不等于服务隔离。由 orcad fork 的守护进程及其拥有的每个 PTY 都保留在同一个 systemd 服务 cgroup 中。KillMode=mixed 不会保留它们:它只向主进程发送优雅停止信号,然后在停止超时到期时向 cgroup 中剩余的每个进程发送 SIGKILL。KillMode=control-group 同样具有破坏性。KillMode=process 会留下服务拥有的进程不受管理,并且不是受支持的保留机制。服务重启存活需要单独受监督的 cgroup;当前部署未提供它们。
绑定策略¶
--bind <literal-ip>,默认 127.0.0.1。
只接受字面 IP;拒绝主机名,因为 DNS 会决定绑定哪个接口。localhost 映射到 127.0.0.1。0.0.0.0 / :: 是明确选择网络可达的选项,并且启动日志在每次启动时都会说明这一点。
绑定是固定的,而不是默认值。有两件事会自行扩大桌面的监听器——orca serve 的宽泛默认值,以及某些设备在之前已连接的启动——并且无人值守主机的暴露面必须恰好是操作员在每次启动时要求的内容。移动配对提议通常会重新绑定到所有接口,但在绑定固定到回环时会被拒绝,并报告 network_exposure_failed,而不是广播一个没有任何东西可以到达的端点。
在随附设计中,客户端通过 SSH 本地端口转发访问远程 orcad,因此回环是正确的默认值,配对凭据通过 SSH 传输。
数据根和实例锁¶
数据根是 $ORCA_USER_DATA,否则是 $XDG_DATA_HOME/Orca,否则是 ~/.orca。
在触及配置文件索引或存储之前,orcad 会获取 <data-root>/orcad.lock。在以下情况下,它拒绝启动:
| 代码 | 含义 |
|---|---|
orcad_data_root_wrong_owner |
根目录由另一个 uid 拥有(POSIX) |
orcad_data_root_shared |
根目录可被组/其他用户访问,且无法收紧 |
orcad_instance_lock_held |
另一个存活的 orcad 拥有此根目录 |
orcad_instance_lock_foreign_identity |
锁属于不同身份 |
orcad_data_root_unusable |
无法创建、stat 或写入根目录 |
一个仅仅过于宽松且由我们拥有的根目录会被收紧到 0700,而不是被拒绝——orcad 在那里以未密封方式存储凭据(此主机上没有操作系统密钥环),因此目标是私有根目录,而在我们可以直接修复时拒绝启动对谁都没有帮助。当权限不是我们可以修复的时,我们会拒绝。Windows 免于所有者和模式检查:ACL 无法表示为 POSIX 模式,并且那里的 statSync().mode 报告的是合成值。
已死亡持有者的记录会被回收(PID 加上进程启动时间,因此被回收的 PID 不会被读取为存活)。属于不同身份的记录永远不会被回收。
锁限定一个角色——谁是运行时。 它有意不对守护进程说任何话,守护进程位于 <data-root>/daemon 下,并用其自身的 PID 记录为其端点设置围栏。一个询问“是否有任何进程正在使用此根目录”的锁,会恰好拒绝那些让存活守护进程变得有价值的重启。
监督¶
进程范围与 cgroup 范围的停止¶
内置远程更新器执行以 PID 为范围的停止,并在其拥有会话期间保持守护进程的安装版本固定。组合单元的 systemd 停止或重启则不同:它会在优雅窗口之后回收守护进程和每个存活终端。
在执行整个 cgroup 的停止之前,使用与守护进程相同的操作系统账户和主目录,获取一个新的 orca-ide terminal list --json 结果。调用安装程序的绝对启动器路径,以便 sudo 的 secure_path 无法隐藏按用户注册(例如,sudo -Hu orca /home/orca/.local/bin/orca-ide terminal list --json)。将 orca 和 /home/orca 都替换为单元使用的服务账户和主目录;解包部署可以改用其绝对 resources/bin/orca-ide 启动器。安全的空清点结果未被截断,具有显式 hostScope,覆盖受停止影响的每个执行主机,并且未列出这些主机上的任何终端。每个 omittedHostIds 条目都必须明确说明位于目标服务的执行边界之外。单独配对的运行时位于该边界之外;通过此运行时到达的本地执行和 SSH 主机则位于边界之内。受影响的或未知的遗漏、缺失的范围、截断、请求失败或联系丢失都会使结果变为 unverifiable:推迟停止。清点之后不要接受新工作。Orca 尚未提供原子的清点与停止围栏。
谁监督 orcad¶
外部监督者(systemd、launchd、进程管理器)。orcad 遵循它:
- 就绪。 在 stdout 上输出一行 JSON(
--json),type: "orca_server_ready",在监听器绑定且 daemon 判定完成后发布。没有独立的就绪 socket;该行就是信号。请宽松设置监督者的启动超时——daemon 启动有自己的重试,在冷启动主机上可能需要数十秒。 - 关闭。
SIGTERM或SIGINT启动优雅停止。第二次信号会立即以代码 1 退出,而不是被吞掉——监督者的第二次信号意味着其第一次截止时间已过,而静默等待正是把停止变成SIGKILL的原因,这是唯一会跳过 daemon 交接的拆除方式。orcad 还施加自己的 15 秒截止时间并以 1 退出,因此故障仍可归因,而不是以未记录的 kill 形式出现。 - 退出码。
| 代码 | 含义 | 监督者应 |
|---|---|---|
| 0 | 干净关闭 | 按策略重启 |
| 1 | 启动或关闭失败 | 带退避重启 |
| 78 | 配置故障(绑定地址、数据根、实例锁) | 不重启 |
78 是 EX_CONFIG。将其放入 systemd 的 RestartPreventExitStatus:在由他人拥有的数据根上重启是重启循环,而不是恢复。
- 日志。 orcad 将人类可读的诊断信息写入 stderr,将其就绪契约写入 stdout;监督者负责捕获和轮转。daemon 由于是分离的,会将自己的 NDJSON 生命周期日志写入
<data-root>/logs/daemon.log(由ORCA_DIAGNOSTICS_DISABLED=1抑制)。该文件的轮转尚未实现——参见 未覆盖的内容。
orcad 监督 daemon¶
- 启动。 从
orcad.js旁边的daemon-entry.js分叉并分离,拥有自己的 PID 记录、token 和 socket,位于<data-root>/daemon下。 - 先收养后生成。 已经响应端点的 daemon 会被收养而不是替换,除非它不健康、外部来源,或构建自已被取代的 bundle 并且 不拥有任何活动会话。替换健康的 daemon 会杀死其 PTY,因此代码新鲜度始终让位于活动工作。
- 重启。 适配器在 daemon 死亡时重新生成它,对调用方透明。
- 崩溃循环遏制。 每次 orcad 运行最多 每 60 秒滚动窗口内 5 次启动;超过后,启动会以
daemon_crash_loop拒绝,终端会以该消息失败,而不是进程永远分叉。窗口会滑动,因此修复后的主机会恢复,而无需重启 orcad。操作员发起的 daemon 重启会清除它——那是有意的“再试一次”。 - 无 macOS 登录会话监视。 该监视会在生成它的 GUI 登录会话死亡时退役 daemon。orcad daemon 必须在其 SSH 会话结束后存活。
- 关闭。 orcad 从不停止 daemon。从未被收养的 daemon 会在其收养窗口后自行退役;被收养的 daemon 保持驻留(参见 退役)。
退役¶
在 PID 范围的停止之后,被收养的 daemon 保持驻留,以便下一个 orcad 可以重新连接。组合单元 systemd 停止则会杀死它。要退役进程范围的部署,请应用上述普查规则,停止 orcad,然后停止由 health.terminalDaemon.pid 命名的 daemon。仅在执行主机上验证后才报告其为 exited;失去联系为 unverifiable。
健康¶
就绪负载携带一个 health 对象:
buildHash sha256 (16 hex) of the running orcad bundle — build identity that a version
string cannot give, so a rollback that did not replace the file is visible
buildVersion ORCA_VERSION
nodeVersion / nodeAbi process.versions.node / .modules — the ABI native addons must match
platform / arch / pid
terminalDaemon:
state live | degraded | absent
ownsFreshSessions whether NEW terminals are daemon-owned; this supports PID-scoped
restart recovery, not supervisor or service-cgroup isolation
pid the live daemon's pid, from its own PID record
buildVersion the build the LIVE daemon was forked from (may legitimately predate
this orcad after an update — reporting orcad's version for both would
hide exactly that)
entryPath / protocolVersion
selfTest { ok, coverage, verdict, durationMs }
自检证明了什么¶
selfTest 针对 daemon 的 socket 运行 checkDaemonHealth。只有当 daemon 打开了其 socket、完成了协议握手,并运行了 ptySpawnHealth——在 daemon 自身进程内生成的一个真实短生命周期 PTY 时,它才是绿色。因此它跨越两个进程:orcad 驱动它,daemon 执行它,判定跨越 socket。
coverage: 'pty-spawn'—— 上述完整往返。coverage: 'handshake'—— 仅限 win32,其中checkPtySpawnHealth不生成任何东西就返回。那里的绿色判定只覆盖握手,别无其他。它单独报告而不是并入ok,以免有人将其读作 PTY 往返。
state 仅在自检通过 并且 ownsFreshSessions 为 true 时为 live。一个响应但已回退到本地生成新终端的 daemon 是 degraded,因为这些终端会随 orcad 死亡。一个曾响应但随后生成探测失败的 daemon 也是 degraded,而不是 absent:它仍持有活动会话,而将这些称为已退出,正是 ssh-execution-boundary.md 所禁止的猜测性判定。
未覆盖的内容¶
在此列出,以免任何未实现的东西被读作已实现:
- 持续健康端点。
health只发布一次,位于就绪负载中。监督者的周期性存活/就绪探测需要基于同一collectOrcadHealth()的 HTTP 或 RPC 接口;该接口尚不存在。 - systemd 隔离的 daemon 监督。 orcad 及其 daemon 当前共享一个服务 cgroup,因此组合单元停止无法保留活动终端。
- libc 槽位。 在原生 libc 检测拥有它之前,没有可诚实发布的健康值。
degradations[]。 就绪契约尚未发布此集合。- 凭据管理(列出 / 撤销 / 轮换设备、使待处理提议过期、结构化安全日志)。
- 固定端口失败关闭。 固定的
--port在冲突时仍会回退到操作系统分配的端口。 - 在回环默认下协调
webClientUrl与可达性。 - 状态模式回滚规则。
- daemon 日志轮转。
<data-root>/logs/daemon.log无限制增长。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 orca48/orca