diff --git a/.github/workflows/ssh-relay-runtime-artifacts.yml b/.github/workflows/ssh-relay-runtime-artifacts.yml
index 83d675d4ac0..3ee2724d17c 100644
--- a/.github/workflows/ssh-relay-runtime-artifacts.yml
+++ b/.github/workflows/ssh-relay-runtime-artifacts.yml
@@ -287,6 +287,7 @@ jobs:
src/main/ssh/ssh-relay-runtime-source-scan.test.ts \
src/main/ssh/ssh-relay-runtime-source-stream.test.ts \
src/main/ssh/ssh-relay-runtime-posix-file-destination.test.ts \
+ src/main/ssh/ssh-relay-runtime-system-ssh-file-channel.test.ts \
src/main/ssh/ssh-relay-runtime-sftp-file-destination.test.ts \
src/main/ssh/ssh-relay-runtime-sftp-connection-transfer.test.ts \
src/main/ssh/ssh-relay-runtime-sftp-session.test.ts \
@@ -933,6 +934,7 @@ jobs:
src/main/ssh/ssh-relay-runtime-source-scan.test.ts `
src/main/ssh/ssh-relay-runtime-source-stream.test.ts `
src/main/ssh/ssh-relay-runtime-posix-file-destination.test.ts `
+ src/main/ssh/ssh-relay-runtime-system-ssh-file-channel.test.ts `
src/main/ssh/ssh-relay-runtime-sftp-file-destination.test.ts `
src/main/ssh/ssh-relay-runtime-sftp-connection-transfer.test.ts `
src/main/ssh/ssh-relay-runtime-sftp-session.test.ts `
diff --git a/config/scripts/ssh-relay-runtime-workflow.test.mjs b/config/scripts/ssh-relay-runtime-workflow.test.mjs
index 673b8990b4d..c93fb9e2158 100644
--- a/config/scripts/ssh-relay-runtime-workflow.test.mjs
+++ b/config/scripts/ssh-relay-runtime-workflow.test.mjs
@@ -165,6 +165,7 @@ describe('SSH relay runtime artifact workflow', () => {
'ssh-relay-runtime-source-scan.test.ts',
'ssh-relay-runtime-source-stream.test.ts',
'ssh-relay-runtime-posix-file-destination.test.ts',
+ 'ssh-relay-runtime-system-ssh-file-channel.test.ts',
'ssh-relay-runtime-sftp-file-destination.test.ts',
'ssh-relay-runtime-sftp-connection-transfer.test.ts',
'ssh-relay-runtime-sftp-session.test.ts',
diff --git a/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist-summary.md b/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist-summary.md
index 68ee7a29113..5922a3a34ed 100644
--- a/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist-summary.md
+++ b/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist-summary.md
@@ -9,10 +9,21 @@ keeps commands, hashes, runner identities, timings, and failure details.
A checked box means the work has evidence in the detailed ledger. Design approval alone does not
complete a box.
-Active checkpoint: **Milestone 6 / Work Package 5 bounded runtime transfer — disconnected POSIX
-system-SSH/no-tar per-file boundary is exact-head green; command-channel adapter audit is next,
-2026-07-15, Codex implementation owner.** `E-M6-POSIX-SYSTEM-SSH-FILE-LOCAL-001` records 25/25 purpose,
-628 broad relay, 281/281 release, and all static/isolation gates.
+Active checkpoint: **Milestone 6 / Work Package 5 bounded runtime transfer — authenticated system-SSH
+command-channel adapter is locally green and exact-head CI is next, 2026-07-15, Codex implementation
+owner.** `E-M6-POSIX-SYSTEM-SSH-CHANNEL-LOCAL-001` records 13/13 purpose, 150/150 focused, 641 broad
+relay cases with six declared skips, 281/281 release contracts, typecheck, full lint, formatting,
+max-lines, diff, protected-resolver, and no-product-consumer gates. It retains a single cancellation
+owner, drains stdout, caps copied stderr at 16 KiB, and exposes idempotent SIGTERM/SIGKILL hooks. It
+has no real tree/live/product consumer; exact-head all-six and adjacent Actions evidence is next.
+`E-M6-POSIX-SYSTEM-SSH-CHANNEL-AUDIT-001` limits this slice to one already-authenticated system-SSH
+`SshConnection.exec()` channel, bounded copied stderr, drained stdout, exact stdin callback/EOF/
+settlement, and idempotent SIGTERM/SIGKILL hooks consumed by the proven single per-file cancellation
+owner. The adapter prechecks but does not forward that signal into `exec`, avoiding a duplicate
+immediate SIGTERM listener.
+No direct spawn, legacy uploader change, real tree/live claim, or product path is included.
+`E-M6-POSIX-SYSTEM-SSH-FILE-LOCAL-001` records 25/25 purpose, 628 broad relay, 281/281 release, and all
+static/isolation gates.
`E-M6-POSIX-SYSTEM-SSH-FILE-CI-001` proves the 25 cases on all six primary native clients; both Linux
supplements, Windows x64 floor, PR Checks, computer-use Windows/Ubuntu, and Golden E2E macOS/Linux
pass. Windows arm64 retains only hosted build 26200 versus required 26100 after complete runtime
diff --git a/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist.md b/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist.md
index 241907c6a85..44732f12823 100644
--- a/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist.md
+++ b/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist.md
@@ -9,7 +9,7 @@ work; keep exact commands, runner identities, hashes, metrics, and residual gaps
Date created: 2026-07-14
Last updated: 2026-07-15
Current phase: Milestone 6 / Work Package 5 bounded runtime transfer — **In progress — 2026-07-15, Codex implementation owner: disconnected POSIX system-SSH/no-tar per-file command and destination contract**. Keep every capability disconnected from every production/default SSH path. Real Apple/SignPath rehearsals remain an explicit late gate and are not on this package's critical path.
-Session checkpoint: **POSIX NO-TAR FILE BOUNDARY EXACT-HEAD GREEN / COMMAND-CHANNEL ADAPTER AUDIT NEXT — 2026-07-15, Codex implementation owner** — `E-M6-POSIX-SYSTEM-SSH-FILE-CI-001` proves the 25-case purpose suite on all six primary native clients; both Linux supplements, Windows x64 floor, PR Checks, computer-use, and Golden E2E pass. Windows arm64 retains only the declared hosted build-26200 versus required-26100 rejection after full runtime smoke. The completed module has no real SSH/tree/product caller. Audit the next smallest disconnected command-channel adapter slice before adding RED tests; do not connect a product/default path. Merging to `main` remains prohibited, SignPath is deferred, and legacy remains the production default.
+Session checkpoint: **SYSTEM-SSH COMMAND-CHANNEL ADAPTER LOCALLY GREEN / EXACT-HEAD CI NEXT — 2026-07-15, Codex implementation owner** — `E-M6-POSIX-SYSTEM-SSH-CHANNEL-LOCAL-001` proves the adapter and per-file composition across 13 purpose cases, 150 focused cases, 641 broad relay cases, 281 release contracts, typecheck, lint, formatting, max-lines, diff, protected-resolver, and no-product-consumer gates. It remains disconnected and has no tree or live system-SSH claim. Commit/push and require all-six native plus adjacent exact-head evidence before the next package. Merging to `main` remains prohibited, SignPath is deferred, and legacy remains the production default.
Primary design: [SSH relay GitHub Release plan](./2026-07-14-ssh-relay-github-release-plan.html)
Motivating issues: [#8450](https://github.com/stablyai/orca/issues/8450), [#1693](https://github.com/stablyai/orca/issues/1693)
@@ -153,10 +153,12 @@ same change as the work it records.
- Completed package: the disconnected POSIX no-tar per-file command/destination boundary is green
locally and on every primary native client under E-M6-POSIX-SYSTEM-SSH-FILE-LOCAL-001 and
E-M6-POSIX-SYSTEM-SSH-FILE-CI-001. It has no real SSH or product caller.
-- Active package: **Audit next — 2026-07-15, Codex implementation owner** — establish the smallest
- disconnected adapter between one authenticated system-SSH exec channel and the proven per-file
- destination. Do not import or modify the legacy tar/base64 directory uploader, create a remote
- tree, add a semantic probe, or add product/settings/fallback/tuple/publication/default behavior.
+- Active package: **Local implementation complete / exact-head CI pending — 2026-07-15, Codex
+ implementation owner** — the purpose-named adapter fixed by
+ E-M6-POSIX-SYSTEM-SSH-CHANNEL-AUDIT-001 is green under
+ E-M6-POSIX-SYSTEM-SSH-CHANNEL-LOCAL-001. Do not import or modify the legacy tar/base64 directory
+ uploader, create a remote tree, add a semantic probe, or add product/settings/fallback/tuple/
+ publication/default behavior.
- Completed Work Package 2 gate: target-native Windows source-signature reports from exact-head
artifact jobs 87267322867 and 87267322870 were independently downloaded and matched to their
identities and signing-stage reports under E-M3-WINDOWS-SOURCE-SIGNATURE-CI-001. PR Checks
@@ -17620,6 +17622,125 @@ config/scripts/ssh-relay-runtime-workflow.test.mjs`.
Beta mode, fallback, tuple, publication, default behavior, or SignPath work exists; legacy remains
the only production path.
+### E-M6-POSIX-SYSTEM-SSH-CHANNEL-AUDIT-001 — authenticated exec-channel adapter boundary
+
+- Date/owner/base: 2026-07-15, Codex implementation owner; clean exact base
+ `62ffaec98df827a5a502f55a2c4a7e1cb7d03d03` after recording the completed file-boundary evidence.
+- Existing-code audit: `SshConnection.exec(command)` already preserves the connected
+ target, resolved OpenSSH configuration, host-scoped ControlMaster disable/retry state, command
+ tracking, and disconnect/reconnect teardown. Guarding `usesSystemSshTransport()` before that call
+ prevents accidental ssh2/SFTP-family use. `spawnSystemSshCommand` then wraps the local OpenSSH
+ child as a `ClientChannel`, exposes stdin/stderr plus its owned child process, forwards close/error,
+ and keeps stdout backpressure bounded when a consumer drains it.
+- Rejected reuse: `system-ssh-file-binary-transfer.ts` is a proven legacy/default whole-buffer/local-
+ file uploader and must remain unchanged. `waitForChannelClose` accumulates arbitrary stderr, while
+ `awaitWithSystemSshAbort` deliberately returns immediately after SIGTERM instead of awaiting
+ process settlement. Those contracts are correct for legacy callers but do not meet the new
+ bootstrap's capped-memory and pre-execution cancellation boundary; do not import or modify them.
+- New boundary: add one purpose-named disconnected adapter module that accepts only a connection,
+ exact command, and exact signal. It rejects a non-system transport or pre-aborted signal before
+ exec, calls `connection.exec(command)` exactly once, drains unused stdout immediately,
+ and returns `SshRelayRuntimePosixFileChannel`. Writes forward the exact borrowed chunk and callback
+ to child stdin; EOF ends child stdin once; settlement listens to child error/close, requires exit
+ zero, and includes at most a small fixed copied stderr prefix plus a truncation marker without
+ retaining an attacker-sized chunk.
+- Single cancellation owner: do not pass the signal into `SshConnection.exec`. The proven per-file
+ destination already owns that exact signal and its bounded SIGTERM/SIGKILL sequence; passing it to
+ the connection would install a second immediate SIGTERM listener on the same child. Connection-
+ level disconnect/reconnect still closes every tracked command independently.
+- Cancellation ownership: the adapter exposes idempotent graceful close through the tracked
+ channel's SIGTERM `close()` and idempotent forced close through its owned child process's
+ `kill('SIGKILL')`. Missing child-process ownership or a thrown kill fails closed. The proven
+ `ssh-relay-runtime-posix-file-destination.ts` owner retains the 250 ms graceful / 2,000 ms total
+ settlement timers and decides when to escalate; the adapter must not add a second timer or return
+ success before the process emits close.
+- Layer-A RED/green proof: purpose cases must cover system-transport/pre-abort rejection with zero
+ exec; exact command, signal precheck, and one exec; stdout drain; exact non-empty chunk/callback and
+ stdin EOF;
+ zero/nonzero/signal/error settlement; bounded copied stderr/truncation; listener cleanup; duplicate
+ graceful/forced calls; SIGTERM before SIGKILL; exited-child force no-op; missing process and thrown
+ close/kill failures; and composition with the per-file owner for retained-write cancellation and
+ forced/timeout settlement. Pin the purpose suite exactly once in both native workflow families
+ before implementation.
+- Explicit residual boundary: no direct spawn, SshConnection modification, ssh2/SFTP path, directory
+ or tree/root staging, primitive semantic probe, tar optimization, `MaxSessions=1` concurrency
+ policy, full-size/live system-SSH transfer, Windows PowerShell path, remote verification/install/
+ publish/launch, product/Beta/settings/fallback/tuple/publication/default behavior, or SignPath is
+ included. Legacy remains the sole production/default path.
+
+### E-M6-POSIX-SYSTEM-SSH-CHANNEL-LOCAL-RED-001 — missing adapter fails as designed
+
+- Date/owner/base: 2026-07-15, Codex implementation owner; exact uncommitted test/workflow state on
+ base `62ffaec98df827a5a502f55a2c4a7e1cb7d03d03` after
+ E-M6-POSIX-SYSTEM-SSH-CHANNEL-AUDIT-001.
+- Command: `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts
+--maxWorkers=1 src/main/ssh/ssh-relay-runtime-system-ssh-file-channel.test.ts
+config/scripts/ssh-relay-runtime-workflow.test.mjs`.
+- Result: expected RED, exit 1. The new 311-line purpose suite collects zero cases and fails solely
+ with `Cannot find module './ssh-relay-runtime-system-ssh-file-channel'`; that production module does
+ not exist yet. The independent release-workflow oracle passes all 8/8 cases and proves the suite is
+ pinned exactly once in both native command families.
+- Resource evidence: Vitest duration 539 ms; `/usr/bin/time -l` reports 1.69 seconds real,
+ 133,103,616 bytes maximum resident set size, zero swaps, and zero block I/O.
+- Disposition: RED is causally narrow and accepted. Implement only the audited adapter, then require
+ purpose composition GREEN plus focused, broad, static, isolation, and exact-head native evidence.
+ No live system-SSH, tree, product, or default-path claim follows from this evidence.
+
+### E-M6-POSIX-SYSTEM-SSH-CHANNEL-LOCAL-TYPE-RED-001 — typed stream boundary corrected
+
+- Date/owner/state: 2026-07-15, Codex implementation owner; first static pass on the uncommitted
+ adapter atop `62ffaec98df827a5a502f55a2c4a7e1cb7d03d03` after runtime purpose GREEN.
+- Command/result: `/usr/bin/time -l pnpm typecheck` — gate RED, exit 1 in 1.93 seconds real with
+ 1,044,365,312-byte maximum RSS. TypeScript reports fake `ClientChannel`/stream intersections,
+ deliberate rejected-promise narrowing, a readonly fake exit field, and the overloaded Node stdin
+ callback signature; no runtime assertion fails.
+- Correction: keep the fake structural until the `ClientChannel` return boundary, narrow the already-
+ validated child stdin to the exact Buffer/callback/end shape, explicitly narrow the caught error,
+ and retain mutable fake process state. The repeated 13-case purpose suite and typecheck below pass.
+ No behavior, lifecycle timer, product path, or legacy module changed.
+
+### E-M6-POSIX-SYSTEM-SSH-CHANNEL-LOCAL-001 — authenticated child-channel adapter is locally green
+
+- Date/owner/environment/base: 2026-07-15, Codex implementation owner; local macOS arm64, Node
+ 26.0.0, pnpm 10.24.0, uncommitted package atop exact base
+ `62ffaec98df827a5a502f55a2c4a7e1cb7d03d03`.
+- Implementation: the 192-line adapter rejects non-system transport and pre-abort before exec, calls
+ the already-authenticated/tracked `connection.exec(command)` exactly once, and deliberately keeps
+ cancellation single-owned by the per-file destination. It drains unused stdout, forwards the exact
+ borrowed Buffer/callback and EOF to child stdin, requires exit zero, copies/caps stderr at 16 KiB,
+ cleans all diagnostic/settlement listeners, and exposes idempotent SIGTERM/SIGKILL hooks. Missing
+ process ownership, false/throwing force signals, and termination failures fail closed.
+- Purpose command: `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts
+--maxWorkers=1 src/main/ssh/ssh-relay-runtime-system-ssh-file-channel.test.ts` — PASS, 13/13 in
+ 346 ms Vitest / 1.33 seconds real, 131,727,360-byte maximum RSS, zero swaps, and zero block I/O.
+ Cases cover transport/pre-abort rejection, exact one exec, stdout drain, retained chunk callback,
+ EOF, successful/nonzero/signaled/error settlement, copied capped diagnostic, listener cleanup,
+ idempotent ordered termination, exited/missing/unsignaled/thrown process cases, graceful retained-
+ write composition, and forced composition using only the file owner's timer.
+- Focused command: adapter, per-file destination, existing system-SSH fallback, `SshConnection`, and
+ native workflow oracle — PASS, five files and 150/150 cases in 2.95 seconds Vitest / 4.31 seconds
+ real with 156,680,192-byte maximum RSS. The workflow oracle passes 8/8 and pins the purpose suite
+ exactly once in POSIX and PowerShell native command families.
+- Broad commands: `src/main/ssh/ssh-relay-*.test.ts` — PASS, 48 files plus four declared skipped,
+ 641 cases plus six declared skips in 32.50 seconds Vitest / 34.59 seconds real with
+ 248,201,216-byte maximum RSS; `config/scripts/ssh-relay-runtime-*.test.mjs` — PASS, 50 files and
+ 281/281 cases in 21.88 seconds Vitest / 23.87 seconds real with 190,169,088-byte maximum RSS. Only
+ the established stale-lock diagnostic and local Node 26 module deprecation appear.
+- Static/isolation commands: repeated `/usr/bin/time -l pnpm typecheck` passes in 3.11 seconds real
+ with 1,209,237,504-byte maximum RSS. Targeted `oxlint`, targeted `oxfmt --check`, standalone
+ max-lines ratchet, `git diff --check`, protected-resolver empty diff, and no non-test consumer pass.
+ Full `pnpm lint` passes in 19.61 seconds real with 2,067,415,040-byte maximum RSS: switch
+ exhaustiveness, 41 reliability gates, the 355-entry max-lines ratchet, bundled-skill verification,
+ 9,837 localization references, locale parity, and zero-candidate localization coverage pass; only
+ pre-existing unrelated warnings remain. Module/test sizes are 192/329 lines with no bypass.
+- Residual: this proves an authenticated connection/child callback contract and its file-owner
+ composition, not a real local OpenSSH process, remote shell, directory/tree/root staging,
+ primitive semantics, full-size system-SSH transfer, `MaxSessions=1`, or Windows PowerShell path.
+ It has no non-test consumer. Remote verification/install/publish/launch, product/Beta/settings/
+ fallback/tuple/publication/default behavior, and SignPath remain absent; legacy remains the only
+ production path. Exact-head all-six native and adjacent Actions evidence is required before the
+ next package.
+
## Accepted Gaps
No product gap is accepted merely because it appears in this list. Each entry requires explicit
@@ -17678,9 +17799,9 @@ The project is not complete until every applicable item below is checked with ev
## Next Required Action
-Audit the smallest disconnected authenticated system-SSH exec-channel adapter that can satisfy the
-proven POSIX per-file boundary, record its exact ownership/cancellation/residual contract, then add
-the purpose-named RED suite and native workflow oracle. Do not
+Commit and push the locally green authenticated system-SSH exec-channel adapter, then require exact-
+head all-six native artifact jobs, both Linux supplements, Windows x64 floor, PR Checks, computer-
+use, and Golden E2E before auditing the next tree/root composition package. Do not
check the broad Milestone 6 SFTP or POSIX system-SSH items yet. Do not add an Electron/startup/product
importer, per-target mode wiring, fallback, tuple enablement, release publication, or default behavior.
Keep Node upstream `.tar.xz` inputs, Windows ZIP, `ORCA_RELAY_PATH`, existing desktop required-assets
diff --git a/src/main/ssh/ssh-relay-runtime-system-ssh-file-channel.test.ts b/src/main/ssh/ssh-relay-runtime-system-ssh-file-channel.test.ts
new file mode 100644
index 00000000000..567c40cd488
--- /dev/null
+++ b/src/main/ssh/ssh-relay-runtime-system-ssh-file-channel.test.ts
@@ -0,0 +1,329 @@
+import { EventEmitter } from 'node:events'
+import type { ChildProcess } from 'node:child_process'
+import type { ClientChannel } from 'ssh2'
+import { afterEach, describe, expect, it, vi } from 'vitest'
+import {
+ openSshRelayRuntimePosixFileDestination,
+ SSH_RELAY_RUNTIME_POSIX_FILE_DESTINATION_LIMITS
+} from './ssh-relay-runtime-posix-file-destination'
+import {
+ openSshRelayRuntimeSystemSshFileChannel,
+ SSH_RELAY_RUNTIME_SYSTEM_SSH_FILE_CHANNEL_LIMITS,
+ type SshRelayRuntimeSystemSshConnection
+} from './ssh-relay-runtime-system-ssh-file-channel'
+
+type WriteCallback = (error?: Error) => void
+
+type FakeChannel = EventEmitter & {
+ stdin: EventEmitter & {
+ write: ReturnType boolean>>
+ end: ReturnType void>>
+ }
+ stderr: EventEmitter
+ resume: ReturnType FakeChannel>>
+ close: ReturnType void>>
+ _process?: ChildProcess
+}
+
+function createChannel(options: { process?: boolean } = {}): {
+ channel: FakeChannel
+ kill: ReturnType boolean>>
+ process: { exitCode: number | null; signalCode: NodeJS.Signals | null }
+} {
+ const emitter = new EventEmitter() as FakeChannel
+ const stdin = Object.assign(new EventEmitter(), {
+ write: vi.fn((_chunk: Buffer, callback: WriteCallback) => {
+ callback()
+ return true
+ }),
+ end: vi.fn()
+ })
+ const kill = vi.fn(() => true)
+ emitter.stdin = stdin
+ emitter.stderr = new EventEmitter()
+ emitter.resume = vi.fn(() => emitter)
+ emitter.close = vi.fn()
+ const process = { exitCode: null, signalCode: null, kill }
+ if (options.process !== false) {
+ emitter._process = process as unknown as ChildProcess
+ }
+ return { channel: emitter, kill, process }
+}
+
+function createConnection(
+ channel: FakeChannel,
+ systemTransport = true
+): SshRelayRuntimeSystemSshConnection & {
+ exec: ReturnType>
+} {
+ return {
+ usesSystemSshTransport: vi.fn(() => systemTransport),
+ exec: vi.fn(async () => channel as unknown as ClientChannel)
+ }
+}
+
+afterEach(() => {
+ vi.useRealTimers()
+})
+
+describe('SSH relay runtime system-SSH file channel', () => {
+ it('rejects non-system transport and pre-abort before exec', async () => {
+ const { channel } = createChannel()
+ const nonSystem = createConnection(channel, false)
+
+ await expect(
+ openSshRelayRuntimeSystemSshFileChannel(
+ nonSystem,
+ 'cat command',
+ new AbortController().signal
+ )
+ ).rejects.toThrow(/system ssh/i)
+ expect(nonSystem.exec).not.toHaveBeenCalled()
+
+ const cancelled = createConnection(channel)
+ const controller = new AbortController()
+ const reason = new Error('cancelled before exec')
+ controller.abort(reason)
+ await expect(
+ openSshRelayRuntimeSystemSshFileChannel(cancelled, 'cat command', controller.signal)
+ ).rejects.toBe(reason)
+ expect(cancelled.exec).not.toHaveBeenCalled()
+ })
+
+ it('opens once with the exact command and keeps cancellation single-owned', async () => {
+ const { channel } = createChannel()
+ const connection = createConnection(channel)
+ const controller = new AbortController()
+ const adapted = await openSshRelayRuntimeSystemSshFileChannel(
+ connection,
+ 'exact remote command',
+ controller.signal
+ )
+
+ expect(connection.exec).toHaveBeenCalledOnce()
+ expect(connection.exec).toHaveBeenCalledWith('exact remote command')
+ expect(channel.resume).toHaveBeenCalledOnce()
+ channel.emit('close', 0, null)
+ await expect(adapted.settled).resolves.toBeUndefined()
+ expect(channel.listenerCount('error')).toBe(0)
+ expect(channel.listenerCount('close')).toBe(0)
+ expect(channel.stderr.listenerCount('data')).toBe(0)
+ expect(channel.stderr.listenerCount('error')).toBe(0)
+ })
+
+ it('forwards the exact borrowed chunk callback and EOF to child stdin', async () => {
+ const { channel } = createChannel()
+ let retainedCallback: WriteCallback | undefined
+ channel.stdin.write.mockImplementationOnce((_chunk, callback) => {
+ retainedCallback = callback
+ return false
+ })
+ const adapted = await openSshRelayRuntimeSystemSshFileChannel(
+ createConnection(channel),
+ 'cat command',
+ new AbortController().signal
+ )
+ const chunk = Buffer.alloc(64 * 1024, 3)
+ const callback = vi.fn()
+
+ adapted.write(chunk, callback)
+ expect(channel.stdin.write).toHaveBeenCalledWith(chunk, callback)
+ expect(callback).not.toHaveBeenCalled()
+ retainedCallback?.()
+ expect(callback).toHaveBeenCalledOnce()
+ adapted.end()
+ expect(channel.stdin.end).toHaveBeenCalledOnce()
+ channel.emit('close', 0, null)
+ await adapted.settled
+ })
+
+ it.each([
+ [1, null, 'exit 1'],
+ [null, 'SIGTERM', 'signal SIGTERM']
+ ] as const)('rejects nonzero settlement %j/%j', async (code, signal, expected) => {
+ const { channel } = createChannel()
+ const adapted = await openSshRelayRuntimeSystemSshFileChannel(
+ createConnection(channel),
+ 'cat command',
+ new AbortController().signal
+ )
+
+ channel.stderr.emit('data', Buffer.from('remote cat failed'))
+ channel.emit('close', code, signal)
+ await expect(adapted.settled).rejects.toThrow(expected)
+ await expect(adapted.settled).rejects.toThrow('remote cat failed')
+ })
+
+ it.each(['channel', 'stderr'] as const)(
+ 'propagates %s errors and removes listeners',
+ async (from) => {
+ const { channel } = createChannel()
+ const adapted = await openSshRelayRuntimeSystemSshFileChannel(
+ createConnection(channel),
+ 'cat command',
+ new AbortController().signal
+ )
+ const error = new Error(`${from} failed`)
+
+ if (from === 'channel') {
+ channel.emit('error', error)
+ } else {
+ channel.stderr.emit('error', error)
+ }
+ await expect(adapted.settled).rejects.toBe(error)
+ expect(channel.listenerCount('error')).toBe(0)
+ expect(channel.listenerCount('close')).toBe(0)
+ expect(channel.stderr.listenerCount('data')).toBe(0)
+ expect(channel.stderr.listenerCount('error')).toBe(0)
+ }
+ )
+
+ it('copies and caps attacker-sized stderr with an explicit marker', async () => {
+ const { channel } = createChannel()
+ const adapted = await openSshRelayRuntimeSystemSshFileChannel(
+ createConnection(channel),
+ 'cat command',
+ new AbortController().signal
+ )
+ const diagnostic = Buffer.alloc(
+ SSH_RELAY_RUNTIME_SYSTEM_SSH_FILE_CHANNEL_LIMITS.diagnosticBytes * 4,
+ 's'
+ )
+
+ channel.stderr.emit('data', diagnostic)
+ diagnostic.fill('x')
+ channel.emit('close', 9, null)
+ const error = await adapted.settled.catch((reason: Error) => reason)
+ expect(error).toBeInstanceOf(Error)
+ if (!(error instanceof Error)) {
+ throw new Error('expected system SSH settlement failure')
+ }
+ expect(error.message).toContain('sss')
+ expect(error.message).not.toContain('xxx')
+ expect(error.message).toContain('truncated')
+ expect(Buffer.byteLength(error.message)).toBeLessThanOrEqual(
+ SSH_RELAY_RUNTIME_SYSTEM_SSH_FILE_CHANNEL_LIMITS.diagnosticBytes + 200
+ )
+ })
+
+ it('makes graceful and forced close idempotent and ordered', async () => {
+ const { channel, kill } = createChannel()
+ const adapted = await openSshRelayRuntimeSystemSshFileChannel(
+ createConnection(channel),
+ 'cat command',
+ new AbortController().signal
+ )
+ void adapted.settled.catch(() => {})
+
+ adapted.requestClose()
+ adapted.requestClose()
+ adapted.forceClose()
+ adapted.forceClose()
+ expect(channel.close).toHaveBeenCalledOnce()
+ expect(kill).toHaveBeenCalledOnce()
+ expect(kill).toHaveBeenCalledWith('SIGKILL')
+ expect(channel.close.mock.invocationCallOrder[0]).toBeLessThan(kill.mock.invocationCallOrder[0])
+ })
+
+ it('does not force an exited child and fails closed without process ownership or signaling', async () => {
+ const exited = createChannel()
+ exited.process.exitCode = 0
+ const exitedAdapter = await openSshRelayRuntimeSystemSshFileChannel(
+ createConnection(exited.channel),
+ 'cat command',
+ new AbortController().signal
+ )
+ void exitedAdapter.settled.catch(() => {})
+ exitedAdapter.forceClose()
+ expect(exited.kill).not.toHaveBeenCalled()
+
+ const missing = createChannel({ process: false })
+ const missingAdapter = await openSshRelayRuntimeSystemSshFileChannel(
+ createConnection(missing.channel),
+ 'cat command',
+ new AbortController().signal
+ )
+ void missingAdapter.settled.catch(() => {})
+ expect(() => missingAdapter.forceClose()).toThrow(/process ownership/i)
+
+ const unsignaled = createChannel()
+ unsignaled.kill.mockReturnValue(false)
+ const unsignaledAdapter = await openSshRelayRuntimeSystemSshFileChannel(
+ createConnection(unsignaled.channel),
+ 'cat command',
+ new AbortController().signal
+ )
+ void unsignaledAdapter.settled.catch(() => {})
+ expect(() => unsignaledAdapter.forceClose()).toThrow(/forced termination/i)
+ })
+
+ it('propagates thrown graceful and forced termination failures once', async () => {
+ const { channel, kill } = createChannel()
+ channel.close.mockImplementation(() => {
+ throw new Error('SIGTERM failed')
+ })
+ kill.mockImplementation(() => {
+ throw new Error('SIGKILL failed')
+ })
+ const adapted = await openSshRelayRuntimeSystemSshFileChannel(
+ createConnection(channel),
+ 'cat command',
+ new AbortController().signal
+ )
+ void adapted.settled.catch(() => {})
+
+ expect(() => adapted.requestClose()).toThrow('SIGTERM failed')
+ expect(() => adapted.requestClose()).not.toThrow()
+ expect(() => adapted.forceClose()).toThrow('SIGKILL failed')
+ expect(() => adapted.forceClose()).not.toThrow()
+ })
+
+ it('composes retained-write cancellation through graceful process settlement', async () => {
+ const { channel, kill } = createChannel()
+ channel.stdin.write.mockImplementation(() => true)
+ channel.close.mockImplementation(() => channel.emit('close', null, 'SIGTERM'))
+ const connection = createConnection(channel)
+ const controller = new AbortController()
+ const reason = new Error('cancelled retained system SSH write')
+ const destination = await openSshRelayRuntimePosixFileDestination({
+ remotePath: '/owned-stage/bin/node',
+ mode: 0o755,
+ signal: controller.signal,
+ openChannel: (command, signal) =>
+ openSshRelayRuntimeSystemSshFileChannel(connection, command, signal)
+ })
+ const writing = destination.write(Buffer.alloc(64 * 1024, 5))
+
+ controller.abort(reason)
+ await expect(writing).rejects.toBe(reason)
+ await expect(destination.abort(reason)).resolves.toBeUndefined()
+ expect(channel.close).toHaveBeenCalled()
+ expect(kill).not.toHaveBeenCalled()
+ })
+
+ it('composes forced settlement without adding a second timer', async () => {
+ vi.useFakeTimers()
+ const { channel, kill } = createChannel()
+ kill.mockImplementation(() => {
+ channel.emit('close', null, 'SIGKILL')
+ return true
+ })
+ const controller = new AbortController()
+ const destination = await openSshRelayRuntimePosixFileDestination({
+ remotePath: '/owned-stage/bin/node',
+ mode: 0o755,
+ signal: controller.signal,
+ openChannel: (command, signal) =>
+ openSshRelayRuntimeSystemSshFileChannel(createConnection(channel), command, signal)
+ })
+
+ controller.abort(new Error('cancelled'))
+ const aborting = destination.abort(controller.signal.reason)
+ await vi.advanceTimersByTimeAsync(
+ SSH_RELAY_RUNTIME_POSIX_FILE_DESTINATION_LIMITS.gracefulCloseMs
+ )
+ await expect(aborting).resolves.toBeUndefined()
+ expect(channel.close).toHaveBeenCalledOnce()
+ expect(kill).toHaveBeenCalledWith('SIGKILL')
+ })
+})
diff --git a/src/main/ssh/ssh-relay-runtime-system-ssh-file-channel.ts b/src/main/ssh/ssh-relay-runtime-system-ssh-file-channel.ts
new file mode 100644
index 00000000000..ace3381340d
--- /dev/null
+++ b/src/main/ssh/ssh-relay-runtime-system-ssh-file-channel.ts
@@ -0,0 +1,192 @@
+import type { ClientChannel } from 'ssh2'
+import type { SshRelayRuntimePosixFileChannel } from './ssh-relay-runtime-posix-file-destination'
+import type { SystemSshCommandChannel } from './system-ssh-command'
+
+export type SshRelayRuntimeSystemSshConnection = Readonly<{
+ usesSystemSshTransport: () => boolean
+ exec: (command: string) => Promise
+}>
+
+export const SSH_RELAY_RUNTIME_SYSTEM_SSH_FILE_CHANNEL_LIMITS = Object.freeze({
+ diagnosticBytes: 16 * 1024
+})
+
+type ChannelCloseCode = number | null
+type ChannelCloseSignal = NodeJS.Signals | null | undefined
+
+function validateInput(
+ connection: SshRelayRuntimeSystemSshConnection,
+ command: string,
+ signal: AbortSignal
+): void {
+ if (
+ !connection ||
+ typeof connection.usesSystemSshTransport !== 'function' ||
+ typeof connection.exec !== 'function' ||
+ typeof command !== 'string' ||
+ command === '' ||
+ !signal
+ ) {
+ throw new Error('SSH relay runtime system SSH file channel input is invalid')
+ }
+ if (!connection.usesSystemSshTransport()) {
+ throw new Error('SSH relay runtime system SSH file channel requires system SSH transport')
+ }
+ signal.throwIfAborted()
+}
+
+function validateChannel(channel: SystemSshCommandChannel): void {
+ if (
+ !channel ||
+ typeof channel.on !== 'function' ||
+ typeof channel.off !== 'function' ||
+ typeof channel.resume !== 'function' ||
+ typeof channel.close !== 'function' ||
+ !channel.stdin ||
+ typeof channel.stdin.write !== 'function' ||
+ typeof channel.stdin.end !== 'function' ||
+ !channel.stderr ||
+ typeof channel.stderr.on !== 'function' ||
+ typeof channel.stderr.off !== 'function'
+ ) {
+ throw new Error('SSH relay runtime system SSH command channel is invalid')
+ }
+}
+
+function appendDiagnostic(
+ chunks: Buffer[],
+ size: number,
+ data: Buffer | string
+): { size: number; truncated: boolean } {
+ const bytes = Buffer.isBuffer(data) ? data : Buffer.from(data)
+ const remaining = SSH_RELAY_RUNTIME_SYSTEM_SSH_FILE_CHANNEL_LIMITS.diagnosticBytes - size
+ if (remaining > 0) {
+ // Why: copying avoids retaining an attacker-sized parent buffer for a small diagnostic prefix.
+ const copied = Buffer.from(bytes.subarray(0, remaining))
+ chunks.push(copied)
+ size += copied.length
+ }
+ return { size, truncated: bytes.length > remaining }
+}
+
+function diagnosticText(chunks: readonly Buffer[], truncated: boolean): string {
+ const text = Buffer.concat(chunks).toString('utf8').trim()
+ if (!truncated) {
+ return text
+ }
+ return `${text}${text ? ' ' : ''}[truncated]`
+}
+
+function settlementError(
+ code: ChannelCloseCode,
+ signal: ChannelCloseSignal,
+ diagnostic: string
+): Error {
+ const detail = code === null ? `signal ${signal ?? 'unknown'}` : `exit ${code}`
+ return new Error(
+ `SSH relay runtime system SSH file command failed (${detail})${diagnostic ? `: ${diagnostic}` : ''}`
+ )
+}
+
+function waitForSettlement(channel: SystemSshCommandChannel): Promise {
+ return new Promise((resolve, reject) => {
+ const diagnosticChunks: Buffer[] = []
+ let diagnosticSize = 0
+ let diagnosticTruncated = false
+ let complete = false
+
+ const cleanup = (): void => {
+ channel.off('error', onError)
+ channel.off('close', onClose)
+ channel.stderr.off('data', onStderrData)
+ channel.stderr.off('error', onError)
+ }
+ const settle = (callback: () => void): void => {
+ if (complete) {
+ return
+ }
+ complete = true
+ cleanup()
+ callback()
+ }
+ const onError = (error: Error): void => {
+ settle(() => reject(error))
+ }
+ const onStderrData = (data: Buffer | string): void => {
+ const appended = appendDiagnostic(diagnosticChunks, diagnosticSize, data)
+ diagnosticSize = appended.size
+ diagnosticTruncated ||= appended.truncated
+ }
+ const onClose = (code: ChannelCloseCode, signal?: ChannelCloseSignal): void => {
+ settle(() => {
+ if (code === 0) {
+ resolve()
+ return
+ }
+ reject(settlementError(code, signal, diagnosticText(diagnosticChunks, diagnosticTruncated)))
+ })
+ }
+
+ channel.on('error', onError)
+ channel.on('close', onClose)
+ channel.stderr.on('data', onStderrData)
+ channel.stderr.on('error', onError)
+ })
+}
+
+function adaptChannel(channel: SystemSshCommandChannel): SshRelayRuntimePosixFileChannel {
+ validateChannel(channel)
+ const settled = waitForSettlement(channel)
+ const input = channel.stdin as unknown as {
+ write: (chunk: Buffer, callback: (error?: Error) => void) => void
+ end: () => void
+ }
+ let closeRequested = false
+ let forceRequested = false
+
+ // Why: the remote command has no stdout contract; draining prevents login noise from deadlocking.
+ channel.resume()
+
+ const write = (chunk: Buffer, callback: (error?: Error) => void): void => {
+ input.write(chunk, callback)
+ }
+ const end = (): void => {
+ input.end()
+ }
+ const requestClose = (): void => {
+ if (closeRequested) {
+ return
+ }
+ closeRequested = true
+ channel.close()
+ }
+ const forceClose = (): void => {
+ if (forceRequested) {
+ return
+ }
+ forceRequested = true
+ const process = channel._process
+ if (!process) {
+ throw new Error('SSH relay runtime system SSH file channel process ownership is missing')
+ }
+ if (process.exitCode !== null || process.signalCode !== null) {
+ return
+ }
+ if (!process.kill('SIGKILL')) {
+ throw new Error('SSH relay runtime system SSH file channel forced termination failed')
+ }
+ }
+
+ return Object.freeze({ write, end, settled, requestClose, forceClose })
+}
+
+export async function openSshRelayRuntimeSystemSshFileChannel(
+ connection: SshRelayRuntimeSystemSshConnection,
+ command: string,
+ signal: AbortSignal
+): Promise {
+ validateInput(connection, command, signal)
+ // Why: the file destination owns this signal's bounded teardown; forwarding it would double-kill.
+ const channel = (await connection.exec(command)) as SystemSshCommandChannel
+ return adaptChannel(channel)
+}