Skip to content

Commit faee6a6

Browse files
committed
feat(remote): ship signed cross-platform agents
1 parent 1c00f88 commit faee6a6

28 files changed

Lines changed: 1017 additions & 103 deletions

File tree

.github/workflows/ci.yml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ on:
99
- "tests/**"
1010
- "scripts/**"
1111
- "gui/**"
12+
- "platform/**"
13+
- "remote-agent/**"
1214
- ".gitattributes"
1315
- ".npmignore"
1416
- "package.json"
@@ -26,6 +28,8 @@ on:
2628
- "tests/**"
2729
- "scripts/**"
2830
- "gui/**"
31+
- "platform/**"
32+
- "remote-agent/**"
2933
- ".gitattributes"
3034
- ".npmignore"
3135
- "package.json"
@@ -45,6 +49,70 @@ concurrency:
4549
cancel-in-progress: true
4650

4751
jobs:
52+
remote-agent:
53+
name: Remote Agent ${{ matrix.package }}
54+
runs-on: ${{ matrix.os }}
55+
timeout-minutes: 25
56+
strategy:
57+
fail-fast: false
58+
matrix:
59+
include:
60+
- package: linux-x64
61+
os: ubuntu-24.04
62+
target: x86_64-unknown-linux-musl
63+
executable: opencodex-remote-agent
64+
- package: linux-arm64
65+
os: ubuntu-24.04-arm
66+
target: aarch64-unknown-linux-musl
67+
executable: opencodex-remote-agent
68+
- package: macos-x64
69+
os: macos-15-intel
70+
target: x86_64-apple-darwin
71+
executable: opencodex-remote-agent
72+
- package: macos-arm64
73+
os: macos-15
74+
target: aarch64-apple-darwin
75+
executable: opencodex-remote-agent
76+
- package: windows-x64
77+
os: windows-latest
78+
target: x86_64-pc-windows-msvc
79+
executable: opencodex-remote-agent.exe
80+
- package: windows-arm64
81+
os: windows-11-arm
82+
target: aarch64-pc-windows-msvc
83+
executable: opencodex-remote-agent.exe
84+
steps:
85+
- name: Checkout
86+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
87+
88+
- name: Install musl build tools
89+
if: ${{ startsWith(matrix.package, 'linux-') }}
90+
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends musl-tools
91+
92+
- name: Install pinned Rust target
93+
working-directory: remote-agent
94+
run: rustup target add "${{ matrix.target }}"
95+
96+
- name: Format
97+
working-directory: remote-agent
98+
run: cargo fmt --check
99+
100+
- name: Clippy
101+
working-directory: remote-agent
102+
run: cargo clippy --locked --target "${{ matrix.target }}" --all-targets -- -D warnings
103+
104+
- name: Test
105+
working-directory: remote-agent
106+
run: cargo test --locked --target "${{ matrix.target }}"
107+
108+
- name: Build release binary
109+
working-directory: remote-agent
110+
run: cargo build --locked --release --target "${{ matrix.target }}"
111+
112+
- name: Verify release binary exists
113+
shell: bash
114+
run: test -s "remote-agent/target/${{ matrix.target }}/release/${{ matrix.executable }}"
115+
48116
test:
49117
name: ${{ matrix.os }}
50118
runs-on: ${{ matrix.os }}

.github/workflows/release.yml

Lines changed: 121 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ on:
2626
default: true
2727
expected-sha:
2828
description: "Immutable release commit this dispatch must publish (fail if the branch moved)"
29-
required: false
29+
required: true
3030
type: string
3131

3232
permissions:
@@ -40,9 +40,78 @@ concurrency:
4040
cancel-in-progress: false
4141

4242
jobs:
43+
remote-agent-build:
44+
name: Remote Agent ${{ matrix.package }}
45+
runs-on: ${{ matrix.os }}
46+
timeout-minutes: 25
47+
permissions:
48+
contents: read
49+
strategy:
50+
fail-fast: false
51+
matrix:
52+
include:
53+
- package: linux-x64
54+
os: ubuntu-24.04
55+
target: x86_64-unknown-linux-musl
56+
executable: opencodex-remote-agent
57+
- package: linux-arm64
58+
os: ubuntu-24.04-arm
59+
target: aarch64-unknown-linux-musl
60+
executable: opencodex-remote-agent
61+
- package: macos-x64
62+
os: macos-15-intel
63+
target: x86_64-apple-darwin
64+
executable: opencodex-remote-agent
65+
- package: macos-arm64
66+
os: macos-15
67+
target: aarch64-apple-darwin
68+
executable: opencodex-remote-agent
69+
- package: windows-x64
70+
os: windows-latest
71+
target: x86_64-pc-windows-msvc
72+
executable: opencodex-remote-agent.exe
73+
- package: windows-arm64
74+
os: windows-11-arm
75+
target: aarch64-pc-windows-msvc
76+
executable: opencodex-remote-agent.exe
77+
steps:
78+
- name: Checkout
79+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
80+
81+
- name: Install musl build tools
82+
if: ${{ startsWith(matrix.package, 'linux-') }}
83+
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends musl-tools
84+
85+
- name: Install pinned Rust target
86+
working-directory: remote-agent
87+
run: rustup target add "${{ matrix.target }}"
88+
89+
- name: Build release binary
90+
working-directory: remote-agent
91+
run: cargo build --locked --release --target "${{ matrix.target }}"
92+
93+
- name: Stage release binary
94+
shell: bash
95+
run: |
96+
mkdir -p remote-agent-artifact
97+
cp "remote-agent/target/${{ matrix.target }}/release/${{ matrix.executable }}" \
98+
"remote-agent-artifact/${{ matrix.executable }}"
99+
test -s "remote-agent-artifact/${{ matrix.executable }}"
100+
101+
- name: Upload unsigned build artifact
102+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
103+
with:
104+
name: remote-agent-${{ matrix.package }}
105+
path: remote-agent-artifact/${{ matrix.executable }}
106+
if-no-files-found: error
107+
retention-days: 1
108+
43109
publish:
110+
needs: remote-agent-build
44111
runs-on: ubuntu-latest
45112
timeout-minutes: 15
113+
environment:
114+
name: ${{ inputs.dry-run && 'remote-agent-dry-run' || 'remote-agent-release' }}
46115
steps:
47116
- name: Checkout
48117
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
@@ -54,7 +123,8 @@ jobs:
54123
EXPECTED_SHA: ${{ inputs.expected-sha }}
55124
run: |
56125
if [ -z "$EXPECTED_SHA" ]; then
57-
echo "::warning::no expected-sha supplied; publishing whatever the branch currently points at"
126+
echo "::error::expected-sha is required; refusing to release an unaudited branch tip"
127+
exit 1
58128
elif [ "$GITHUB_SHA" != "$EXPECTED_SHA" ]; then
59129
echo "::error::branch moved after the release audit (expected ${EXPECTED_SHA}, got ${GITHUB_SHA}) — refusing to publish an unaudited commit"
60130
exit 1
@@ -92,6 +162,12 @@ jobs:
92162
- name: Install dependencies
93163
run: bun install --frozen-lockfile
94164

165+
- name: Download platform Agent artifacts
166+
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
167+
with:
168+
pattern: remote-agent-*
169+
path: remote-agent-artifacts
170+
95171
- name: Verify version matches package.json
96172
env:
97173
RELEASE_VERSION: ${{ inputs.version }}
@@ -243,10 +319,51 @@ jobs:
243319
fi
244320
fi
245321
322+
- name: Assemble and sign Remote Agent bundle
323+
shell: bash
324+
env:
325+
REMOTE_AGENT_SIGNING_KEY: ${{ secrets.REMOTE_AGENT_SIGNING_KEY }}
326+
DRY_RUN: ${{ inputs.dry-run }}
327+
run: |
328+
set -euo pipefail
329+
signing_key="$RUNNER_TEMP/opencodex-remote-agent-signing.pem"
330+
public_key="$RUNNER_TEMP/opencodex-remote-agent-signing.pub.pem"
331+
cleanup() {
332+
if [ -f "$signing_key" ] && command -v shred >/dev/null 2>&1; then
333+
shred -u "$signing_key"
334+
else
335+
rm -f "$signing_key"
336+
fi
337+
}
338+
trap cleanup EXIT
339+
umask 077
340+
expected_public_key=""
341+
if [ "$DRY_RUN" = "true" ]; then
342+
openssl genpkey -algorithm ED25519 -out "$signing_key"
343+
openssl pkey -in "$signing_key" -pubout -out "$public_key"
344+
expected_public_key="$public_key"
345+
echo "OPENCODEX_REMOTE_AGENT_DRY_RUN_PUBLIC_KEY_FILE=$public_key" >> "$GITHUB_ENV"
346+
else
347+
if [ -z "$REMOTE_AGENT_SIGNING_KEY" ]; then
348+
echo "::error::REMOTE_AGENT_SIGNING_KEY is not configured in the protected release environment"
349+
exit 1
350+
fi
351+
printf '%s' "$REMOTE_AGENT_SIGNING_KEY" > "$signing_key"
352+
fi
353+
REMOTE_AGENT_SIGNING_KEY_FILE="$signing_key" \
354+
REMOTE_AGENT_EXPECTED_PUBLIC_KEY_FILE="$expected_public_key" \
355+
GITHUB_SHA="$GITHUB_SHA" \
356+
bun scripts/remote-agent-bundle.ts assemble \
357+
remote-agent-artifacts \
358+
bin/remote-agent \
359+
.tmp/remote-agent-release
360+
246361
- name: Publish (or dry-run)
247362
env:
248363
DRY_RUN: ${{ inputs.dry-run }}
249364
NPM_DIST_TAG: ${{ inputs.tag }}
365+
OPENCODEX_REQUIRE_REMOTE_AGENT_BUNDLE: "1"
366+
OPENCODEX_RELEASE_DRY_RUN: ${{ inputs.dry-run }}
250367
run: |
251368
if [ "$DRY_RUN" = "true" ]; then
252369
echo "::notice::DRY RUN — building + packing, not publishing"
@@ -452,5 +569,6 @@ jobs:
452569
git push origin "refs/tags/${release_tag}"
453570
fi
454571
455-
gh release create "$release_tag" --target "$GITHUB_SHA" --title "$release_tag" \
572+
gh release create "$release_tag" .tmp/remote-agent-release/* \
573+
--target "$GITHUB_SHA" --title "$release_tag" \
456574
--notes-file "$notes_file" ${prerelease_flag:+$prerelease_flag}

docs-site/src/content/docs/guides/web-dashboard.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ The **Remote** page makes the local `ocx gui` the start of remote setup:
3535

3636
1. Continue with GitHub. `opencodexpages.me` handles OAuth and asks you to approve the device code.
3737
2. Set a separate Remote password. GitHub OAuth tokens are not stored or sent to the local PC.
38-
3. Choose a one-level `<name>.opencodexpages.me` address. The central worker provisions an isolated
39-
Cloudflare Tunnel and the page follows `pending`, `awaiting agent`, `connecting`, and `online`.
40-
4. Pair the signed local helper. Visiting the assigned hostname later requires both the owning
41-
GitHub account and the Remote password before the Gateway creates a host-only instance session.
38+
3. Choose a unique one-level `<name>.opencodexpages.me` address and a unique name for this computer.
39+
4. Start the signed, unprivileged Agent from the page. It opens one outbound encrypted WSS connection;
40+
no root access, port forwarding, or inbound listener is required. Visiting the assigned hostname
41+
later requires both the owning GitHub account and the Remote password.
4242

43-
This is currently an invite-only Linux MVP. The privileged signed helper installer and Windows/macOS
44-
helpers are not shipped in the npm package yet. The GUI reports that limitation instead of claiming
45-
the Tunnel is online. **Super Sync is not part of this flow.**
43+
This remains an invite-only beta. Release packages carry signature-verified Agent binaries for Linux,
44+
macOS, and Windows on x64 and arm64. Unsupported OS/architecture combinations fail honestly instead of
45+
compiling code on the user's computer. **Super Sync is not part of this flow.**
4646

4747
## What you can do
4848

@@ -58,7 +58,7 @@ the Tunnel is online. **Super Sync is not part of this flow.**
5858
| **Providers** | Add, edit, enable/disable, and remove providers; manage OAuth account pools and API-key pools where supported. Provider Settings can disable live model discovery for endpoints with missing, slow, or oversized `/models` catalogs. For Claude (Anthropic) OAuth pools, each logged-in account shows its own 5-hour and weekly rate-limit bars (usage is per credential); a failed probe keeps the last-known bars and marks them unavailable until the next successful refresh. |
5959
| **Add provider** | Search registry-backed presets for account login, API-key services, local servers, or a custom endpoint. |
6060
| **Codex Auth** | Add ChatGPT/Codex pool accounts, select the next-session account, refresh 5h / weekly / 30d quotas, enable or disable quota auto-switch, set its 1–100% threshold, and configure transient-failure failover. |
61-
| **Remote** | Connect this PC to the central GitHub identity service, set a separate access password, reserve its hostname, and follow Tunnel/Agent provisioning. |
61+
| **Remote** | Connect this PC to the central GitHub identity service, set a separate E2EE password, reserve its hostname, and start its signed outbound Agent. |
6262
| **Subagents** | Feature up to five bare native or namespaced routed models in the `spawn_agent` override list. |
6363
| **Models** | Toggle native GPT and routed models, set provider allowlists and context caps, choose v1/base/v2, and configure the v2 thread limit. Configured providers stay visible as zero-model groups when discovery is off or returns no rows. |
6464
| **Logs** | Auto-refresh recent requests with tokens, requested effort and (when available) effective outbound effort, resolved model, provider, status, request id, duration, and error details. The detail view includes the exact reasoning wire field when the adapter emits one. Filter by opaque conversation/session id (when the client sends one) to total tokens and estimated list-price cost for the currently loaded Logs ring. |
@@ -138,7 +138,7 @@ The GUI is a thin client over the proxy's JSON management API. Useful endpoints
138138
| --- | --- |
139139
| `GET` / `PUT /api/settings` | Read settings or toggle Codex autostart. |
140140
| `GET /api/remote/status` · `POST /api/remote/link` · `PUT /api/remote/password` | Read local Remote state, start device approval, and set the separate access password without exposing device or GitHub tokens to the browser. |
141-
| `POST /api/remote/activate` · `POST /api/remote/pairing-code` · `DELETE /api/remote/device` | Reserve the instance hostname, prepare one-time Agent pairing, or revoke this PC. |
141+
| `POST /api/remote/activate` · `POST /api/remote/agent/start` · `POST /api/remote/pairing-code` · `DELETE /api/remote/device` | Reserve the workspace/computer names, start the packaged Agent, use the legacy pairing path, or revoke this PC. |
142142
| `GET /api/startup-health` | Read secret-free routing, service, shim, and restart-safety diagnostics. |
143143
| `POST /api/startup-action` | Install the background service or Codex launcher shim through fixed, allowlisted actions. |
144144
| `GET` / `POST /api/windows-tray` | Read or change the Windows tray installation and visible-process state. POST accepts `install`, `start`, `stop`, or `uninstall`. |

docs-site/src/content/docs/ja/guides/web-dashboard.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ bun run dev:gui
2727

2828
1. GitHub で続行します。`opencodexpages.me` が OAuth を処理し、デバイスコードの承認を求めます。
2929
2. GitHub パスワードとは別の Remote パスワードを設定します。GitHub OAuth トークンは保存せず、ローカル PC にも送りません。
30-
3. 1 階層の `<名前>.opencodexpages.me` を選びます。中央 worker が分離された Cloudflare Tunnel を作成し、GUI が `pending` から `online` まで追跡します
31-
4. 署名済みローカルヘルパーをペアリングします。割り当て済みアドレスへアクセスする際は、所有者の GitHub アカウントと Remote パスワードを確認してから hostname 専用 session を作ります
30+
3. 重複しない 1 階層の `<名前>.opencodexpages.me` と、この PC 固有の名前を選びます
31+
4. ページから署名済み非特権 Agent を起動します。Agent は暗号化 outbound WSS 接続を 1 本だけ開くため、root、ポート転送、inbound listener は不要です。割り当て済みアドレスでは所有者の GitHub アカウントと Remote パスワードを確認します
3232

33-
現在は招待制 Linux MVP です。管理者権限が必要な署名済み helper installer と Windows/macOS helper は npm パッケージにまだ含まれません。GUI はこの制限を隠しません**Super Sync はこのフローに含まれません。**
33+
現在は招待制プライベートベータです。release package には Linux、macOS、Windows の x64・arm64 用署名検証済み Agent が含まれます。未対応 OS/architecture ではユーザー PC 上でコンパイルせず明確に停止します**Super Sync はこのフローに含まれません。**
3434

3535
## できること
3636

@@ -46,7 +46,7 @@ bun run dev:gui
4646
| **プロバイダー** | プロバイダーを追加、編集、有効化/無効化、削除し、対応する OAuth アカウントプールと API キープールを管理します。Claude(Anthropic)OAuth プールでは、ログイン済みの各アカウントに独自の 5 時間・週間レート制限バーが表示され(利用量は資格情報単位)、取得失敗時は直近の値を保持して一時利用不可と表示します。 |
4747
| **プロバイダー追加** | レジストリベースのプリセットからアカウントログイン、API キーサービス、ローカルサーバー、custom エンドポイントを検索します。 |
4848
| **Codex 認証** | ChatGPT/Codex プールアカウントを追加し、次回セッションアカウントを選び、5 時間 / 週間 / 30 日クォータを更新し、クォータ自動切り替えのオン/オフと 1~100% のしきい値、一時的失敗フェイルオーバーを設定します。 |
49-
| **Remote** | この PC を中央 GitHub ID サービスへ接続し、別のアクセスパスワードと hostname を設定して Tunnel/Agent 状態を追跡します|
49+
| **Remote** | この PC を中央 GitHub ID サービスへ接続し、別の E2EE パスワードと hostname を設定して署名済み outbound Agent を起動します|
5050
| **サブエージェント** | `spawn_agent` オーバーライド一覧にネイティブまたはルーティングモデルを最大 5 つまで優先公開します。 |
5151
| **モデル** | ネイティブ GPT とルーティングモデルをオン/オフし、プロバイダー許可リストとコンテキスト上限、v1/base/v2、v2 スレッド数を設定します。 |
5252
| **ログ** | トークン、要求された強度と(利用可能な場合は)実際に送信された強度、実際のモデル、プロバイダー、状態、リクエスト ID、所要時間、エラー詳細を含む最近のリクエストを自動更新します。アダプターが reasoning パラメーターを送信した場合、詳細表示に正確な wire field も表示されます。 |
@@ -111,7 +111,7 @@ GUI はプロキシの JSON 管理 API を使うシンクライアントです
111111
--- | --- |
112112
| `GET` / `PUT /api/settings` | 設定を読むか Codex 自動起動をオン/オフします。 |
113113
| `GET /api/remote/status` · `POST /api/remote/link` · `PUT /api/remote/password` | デバイス/GitHub トークンをブラウザへ公開せず、Remote 状態、デバイス承認、アクセスパスワードを管理します。 |
114-
| `POST /api/remote/activate` · `POST /api/remote/pairing-code` · `DELETE /api/remote/device` | hostname の予約、一回限りの Agent ペアリング、またはこの PC の失効を行います。 |
114+
| `POST /api/remote/activate` · `POST /api/remote/agent/start` · `POST /api/remote/pairing-code` · `DELETE /api/remote/device` | workspace/PC 名の予約、同梱 Agent の起動、従来のペアリング経路、またはこの PC の失効を行います。 |
115115
| `GET /api/startup-health` | 秘密情報を含まないルーティング、サービス、shim、再起動安全性診断を読み取ります。 |
116116
| `GET` / `POST /api/windows-tray` | Windows トレイの導入・表示状態を読み取り、`install``start``stop``uninstall` を実行します。 |
117117
| `POST /api/sync` | 共有モデルカタログを再構築し Codex モデルキャッシュを古い状態としてマークします。 |

0 commit comments

Comments
 (0)