Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2a1891b
feat(git): add short-lived Gitoxide admission helper
zhiiw Aug 23, 2026
0ceb353
fix(git): classify unsupported repository formats
zhiiw Aug 23, 2026
80ba64d
feat(git): bind helper artifacts to opaque capabilities
zhiiw Aug 23, 2026
30812e3
feat(git): own short-lived helper invocations
zhiiw Aug 23, 2026
035adf6
test(gitoxide): isolate parallel repository fixtures
zhiiw Aug 24, 2026
18068ba
feat(git): issue repository admission capabilities
zhiiw Aug 23, 2026
40eccd8
feat(git): import admitted source heads
zhiiw Aug 23, 2026
7b3bf06
build(git): lock source import dependency
zhiiw Aug 23, 2026
c09f0e2
fix(git): bound managed tree traversal
zhiiw Aug 24, 2026
cb9628c
fix(runtime-host): preserve tree policy failures
zhiiw Aug 24, 2026
d73e1f1
style(gitoxide): match pinned Rust formatting
zhiiw Aug 24, 2026
0417edc
fix(gitoxide): make source import restartable
zhiiw Aug 24, 2026
e90611b
fix(gitoxide): close helper admission gaps
zhiiw Aug 24, 2026
4dede78
fix(gitoxide): harden source import ownership
zhiiw Aug 24, 2026
8fdd865
style(gitoxide): apply pinned Rust formatting
zhiiw Aug 24, 2026
9d0e579
test(gitoxide): prove import timeout behavior
zhiiw Aug 24, 2026
967f000
fix(gitoxide): verify imported object identity
zhiiw Aug 24, 2026
d67e03c
style(gitoxide): apply pinned Rust formatting
zhiiw Aug 24, 2026
37e47d2
test(gitoxide): replace read-only object fixtures
zhiiw Aug 24, 2026
a98d231
fix(gitoxide): close source import identity gaps
zhiiw Aug 24, 2026
3fa865c
fix(gitoxide): implement Git tree ordering
zhiiw Aug 24, 2026
1a76928
fix(gitoxide): bound repository metadata admission
zhiiw Aug 24, 2026
472d0a0
style(gitoxide): apply pinned Rust formatting
zhiiw Aug 24, 2026
ff3eb77
fix(gitoxide): close helper admission resource bounds
zhiiw Aug 25, 2026
3326e68
feat(gitoxide): replace managed tree policy with v2
zhiiw Aug 25, 2026
0a1926f
fix(gitoxide): close attributes and admission bounds
zhiiw Aug 25, 2026
37c4060
fix(gitoxide): canonicalize attributes grammar
zhiiw Aug 25, 2026
1562ef6
fix(gitoxide): bound object database discovery
zhiiw Aug 26, 2026
295e447
fix(ci): refresh Windows test inventory
zhiiw Aug 26, 2026
f0b38b4
test(runtime-host): await POSIX descendant reaping
zhiiw Aug 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions .github/workflows/gitoxide-helper-admission.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

name: Gitoxide helper admission

on:
pull_request:
paths:
- '.github/workflows/gitoxide-helper-admission.yml'
- 'native/gitoxide-helper/**'
- 'packages/runtime-host/src/server/gitoxide-helper-*.ts'
- 'packages/runtime-host/src/__tests__/gitoxide-helper-*.test.ts'
- 'packages/runtime-host/src/server/gitoxide-repository-admission-authority-internal.ts'
- 'packages/runtime-host/src/__tests__/gitoxide-repository-admission-authority-internal.test.ts'
- 'packages/runtime/package.json'
- 'docs/architecture/gitoxide-*.md'
push:
branches:
- main
paths:
- '.github/workflows/gitoxide-helper-admission.yml'
- 'native/gitoxide-helper/**'
- 'packages/runtime-host/src/server/gitoxide-helper-*.ts'
- 'packages/runtime-host/src/__tests__/gitoxide-helper-*.test.ts'
- 'packages/runtime-host/src/server/gitoxide-repository-admission-authority-internal.ts'
- 'packages/runtime-host/src/__tests__/gitoxide-repository-admission-authority-internal.test.ts'
- 'packages/runtime/package.json'
- 'docs/architecture/gitoxide-*.md'

permissions:
contents: read

concurrency:
group: gitoxide-helper-admission-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
name: ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24.18.1
cache: npm
- name: Check Rust formatting
working-directory: native/gitoxide-helper
run: cargo fmt --check
- name: Test the short-lived Gitoxide helper
working-directory: native/gitoxide-helper
run: cargo test --locked
- name: Install JavaScript dependencies without packaging hooks
run: npm ci --ignore-scripts
- name: Build the helper invocation owner
run: >-
npm --workspace @maka/core run build &&
npm --workspace @maka/storage run build &&
npm --workspace @maka/runtime run build &&
npm --workspace @maka/runtime-host run build
- name: Test the real helper invocation contract
env:
MAKA_GITOXIDE_HELPER_PATH: ${{ github.workspace }}/native/gitoxide-helper/target/debug/maka-gitoxide-helper${{ runner.os == 'Windows' && '.exe' || '' }}
run: >-
node --test
packages/runtime-host/dist/__tests__/gitoxide-helper-artifact-authority-internal.test.js
packages/runtime-host/dist/__tests__/gitoxide-helper-invocation-internal.test.js
packages/runtime-host/dist/__tests__/gitoxide-repository-admission-authority-internal.test.js
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ docs/assets/
apps/desktop/tests/real-window-smoke/
deepseek.key

# Built only by the dedicated Gitoxide helper lane; normal workspace tests do not use Cargo.
/native/gitoxide-helper/target/

# Generated Computer Use executor binary; provenance metadata stays tracked.
apps/desktop/resources/bin/
# Rebuilt from experiments/windows-sandbox by scripts/package-windows-x64.mjs.
Expand Down
119 changes: 119 additions & 0 deletions docs/architecture/gitoxide-helper-artifact-authority-v1.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

# Gitoxide helper artifact authority v1

状态:可独立合并的 enabling infrastructure;尚未形成 product-ready 的 release trust root,也未接入
Desktop/CLI managed-workspace 产品路径。

## 1. 主要不变量

本切片只证明:

> 普通 caller 不能用自报的 executable path 或 SHA-256 获得 Gitoxide helper 调用资格;只有内部
> release owner 签发、与 owner token 绑定的 artifact claim,在 exact platform、architecture、
> protocol、size 与 SHA-256 校验通过后,才能转换为另一个指定 owner 可消费的 opaque invocation
> capability。artifact 在 admission 后变化时,调用前重验必须 fail closed。

它不证明平台签名、安装目录保护、helper spawn、repository observation、T1 admission、managed
workspace 或 crash recovery。

## 2. Owner 与 API 权限

```text
未来的 packaged-release owner
└─ issueGitoxideHelperReleaseArtifactClaimInternal(ownerToken, exact artifact identity)
↓ opaque release claim
artifact authority
└─ exact file/platform/protocol verification
↓ opaque invocation capability
未来的 invocation owner
└─ verifyGitoxideHelperArtifactForInvocationInternal(ownerToken, capability)
```

- claim 与 capability 的状态存放在模块私有 `WeakMap` 中;对象表面不包含 path、digest 或 size。
- claim 必须由相同的 release owner token 消费;capability 必须由签发时指定的 invocation owner token
消费。
- 相关 internal API 不从 `@maka/runtime-host/server` 导出。
- 旧的 caller-provided `{ executablePath, expectedSha256 }` 不能成为这条链的 authority。

当前没有 production release owner。`issueGitoxideHelperReleaseArtifactClaimInternal()` 只是未来受信
packaging owner 的接缝,不是签名信任根;这限制产品启用条件,但不阻止该窄 authority 作为后续切片的
可审查基础设施合并。

## 3. 校验边界

一次 artifact 校验包含:

1. 输入 claim 的 protocol/platform/architecture/size/digest 形状检查;
2. 拒绝 claimed path 任意组件中的 symlink 或 Windows junction;
3. 打开 canonical regular file,并限制 helper artifact 最大为 256 MiB;
4. 在同一 handle 上进行 64 KiB 有界缓冲的 SHA-256 流式读取;
5. 比较读取前后 handle identity/size/timestamps;
6. 比较读取后 path identity 与已打开 handle;
7. 比较 exact byte count 与 digest。

admission 与每次 invocation resolve 都执行这套校验。它可以识别校验之前或校验期间的替换,不会把
相邻 manifest 当作自证信任根;但校验完成后必须关闭 handle,而 Node 只能按 path spawn,所以这里不把
“刚验证的 bytes”表述成“实际执行的 bytes”。

## 4. 原子性、失败状态与回滚

| 项目 | v1 合同 |
| --- | --- |
| owner | Runtime Host 内部 artifact authority |
| 原子性边界 | 单个打开 file handle 的一次 identity + streaming digest observation |
| durable state | 无;claim/capability 仅存在于进程内 |
| 非法/伪造 claim | `gitoxide_helper_release_claim_invalid` |
| 平台或架构不匹配 | `gitoxide_helper_release_claim_unsupported` |
| path/symlink/读取失败 | `gitoxide_helper_artifact_invalid` |
| size/digest/identity 漂移 | `gitoxide_helper_artifact_identity_mismatch` |
| 错误 owner/伪造 capability | `gitoxide_helper_invocation_capability_invalid` |
| rollback | 只读校验,无副作用,无需回滚 |

## 5. 明确不承诺的威胁模型

本切片没有声称抵抗拥有同一 OS 用户文件写权限的主动攻击者。特别是:

- 它尚未验证 macOS code signature、Windows Authenticode 或 Linux 发布清单的受信签名;
- 它尚未把 helper 放进由正式安装器保护的只读目录;
- invocation 已接入 path-based spawn,但不能消除“最后一次 handle 校验完成后、exec 开始前”的替换
窗口;重复 rehash 只能缩小窗口,不能形成 executable CAS,因此 v1 明确保留该限制。

正式生产接入前,必须由 packaged-release owner 提供信任根,并明确三平台安装目录与签名能力。不能
通过给本 API 再传一个裸 expected digest 来绕过这一门槛。

## 6. 平台能力矩阵

| 平台 | 当前持续验证 | 尚未承诺 |
| --- | --- | --- |
| Linux | regular-file identity、digest、symlink path rejection | package signature、protected install root、spawn identity |
| macOS | 同 Linux | code-sign verification、notarized artifact binding、spawn identity |
| Windows | regular-file identity、digest、junction path rejection | Authenticode binding、ACL-protected install root、spawn identity |

## 7. 后续切片

后续只能按下面顺序推进:

1. 发布/安装 owner 把受信 helper identity 绑定到 signed product artifact;
2. 短生命周期 invocation owner 消费 opaque capability 并运行 strict helper protocol;合同见
`gitoxide-helper-invocation-owner-v1.zh-CN.md`;
3. repository observation 再转换为 T1 前的 opaque admission capability。

在第 1 项完成以前,不接 Desktop/CLI,也不恢复旧 Git CLI adapter。
110 changes: 110 additions & 0 deletions docs/architecture/gitoxide-helper-invocation-owner-v1.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

# Gitoxide short-lived invocation owner v1

状态:可独立合并的 enabling infrastructure;真实 Rust helper 的三平台 contract 进入 CI,但正式
release issuer 与 Desktop/CLI managed-workspace 产品接线仍未完成。

## 1. 主要不变量

本切片只证明:

> Runtime Host 只能通过 owner-bound opaque artifact capability 启动一个刚完成 artifact observation 的
> Gitoxide helper path;
> invocation 使用固定 strict JSON request、最小环境、有界 stdin/stdout/stderr、固定超时与取消边界;
> exit 0/1/2 必须分别匹配 inspected/operational failure/policy rejection 的 exact response shape,任意
> 不一致均 fail closed。

它不签发 repository admission capability,不写 SQLite/T1,不创建 Git artifact,也不接 Desktop/CLI。

## 2. Owner 与调用链

```text
opaque GitoxideHelperInvocationCapability
↓ invocation owner token 验证 + artifact bytes 重验
fixed argv [] / minimal env / no shell
↓ 64 KiB strict JSON request
one short-lived Rust helper
↓ bounded stdout/stderr + exact exit/response decoder
typed observation | typed policy rejection | stable error
```

caller 不能提供 executable path、argv、environment、protocol version、timeout 或 output limit。可变
业务输入只包括 operation 所需的 absolute repository path、显式 policy 与 AbortSignal;repository path
在 spawn 前 canonicalize。

## 3. 原子性、失败状态与回滚

| 项目 | v1 合同 |
| --- | --- |
| owner | 单次 Runtime Host invocation owner |
| 原子性边界 | 从 public operation 入口开始的一次绝对 deadline、artifact/path preflight、一个 helper process 及其 exact response |
| 成功 | exit 0 + exact SHA-1 `repository_inspected` |
| policy rejection | exit 2 + exact `unsupported_object_format` |
| repository/helper failure | exit 1 + allowlisted stable helper reason |
| timeout | inspect 为 5 秒、source import 为 10 分钟;同一个绝对 deadline 从 public 入口覆盖 preflight 与执行。若 helper 已启动,共享 lifecycle force-kill process tree,并以有界 exit acknowledgement/output drain 收口;若仍在 Node 文件系统 preflight,调用方按 deadline fail closed,迟到结果被丢弃且不得签发 capability |
| cancellation | preflight 或运行中 fail closed,`gitoxide_helper_invocation_aborted` |
| resource failure | repository open 前的本地 metadata 总量 1 MiB、16,384 entries、primary `objects/pack` 1,024 entries;source alternates 一律拒绝;Gitoxide object allocation 64 MiB、object-store slots 固定 1,024、stdout 64 KiB、stderr 16 KiB;超限 fail closed 或 force-kill |
| malformed protocol | exit code、JSON shape、OID 或字段不一致均拒绝 |
| rollback | inspect 无 durable side effect;import 只允许 fresh destination,但 partial artifact cleanup 留给未来 storage owner |

Rust helper v1 不启动 descendants;Runtime 仍使用共享 process-tree terminator 处理 timeout、abort 和
output overflow,不允许常驻或 detached helper。

Node 的 `realpath`/artifact 文件读取没有可移植的 syscall cancellation。v1 因此只承诺 public operation
在绝对 deadline 内返回或 fail closed,不声称 timeout 能终止已经交给操作系统的 preflight I/O;该 I/O
的迟到完成不能启动 helper、签发 repository capability 或发布 artifact。

## 4. 配置与数据边界

- argv 固定为空,禁止 caller 注入 helper option;
- `shell: false`,不会经过 shell parsing;
- child `PATH` 为空,只保留 Windows loader 与临时目录所需的最少环境变量;
- Rust 侧在 `gix::open()` 前按 pinned Gitoxide discovery 顺序检查 worktree 与 bare candidate,并有界
解析 `.git`/`commondir` 路径文件、`HEAD`、config、packed refs、shallow、refs tree 与 primary
`objects/pack`;`objects/info/alternates` 或 `http-alternates` 只要存在就以
`repository_alternates_unsupported` 拒绝,不递归打开外部 object database;metadata 总量超过 1 MiB、
条目超过 16,384、pack 目录超过 1,024 entries 或深度超过 64 时返回
`repository_metadata_limit_exceeded`;随后使用 `gix::open::Options::isolated()`、
`lossy_config(true)`、`strict_config(true)` 与固定 1,024 个 object-store slots,禁止 Gitoxide 再按
未验证磁盘状态决定初始 slot allocation;
- request 最大 64 KiB;stdout 最大 64 KiB;stderr 最大 16 KiB;
- SHA-1 OID 必须是 40 位小写十六进制;SHA-256/未知格式只返回 rejection,禁止 fallback。

## 5. 平台证据

同一个 workflow 使用 release toolchain 的 Node 24.18.1,在 Linux、macOS、Windows 上:

1. 编译并测试 Rust helper;
2. 构建 Runtime Host;
3. 通过真实 helper executable 验证 SHA-1 success、SHA-256/未知格式 rejection、unborn SHA-1
failure、checksum mismatch 和 portable managed-tree policy。

该证据只覆盖 helper 协议、进程终止与 fresh-only import,不包含平台安装签名或恶意同用户替换。
当前实现会在 spawn 前完成 bytes/identity observation,但 Node 的 path-based spawn 不能把已打开并验证的
handle 直接作为 executable,因此 observation 与 exec 之间仍有 TOCTOU。正式 packaged-release owner
必须依赖平台签名和受保护安装目录;v1 不声称抵抗拥有同用户写权限的攻击者。

## 6. 下一切片

同一 consolidated PR 的后续 authority layer 已把 exact repository observation 转换成 owner-bound
opaque admission capability,并用它驱动 fresh-only source import;合同见
`gitoxide-repository-admission-capability-v1.zh-CN.md`。仍不在本 PR 实现 durable T1、worktree
projection、candidate、recovery owner 或 ref CAS。
Loading