mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 08:03:22 +00:00
test(ssh): prove full-size POSIX system transfer
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -632,6 +632,96 @@ jobs:
|
||||
pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1 --reporter=verbose \
|
||||
src/main/ssh/ssh-relay-runtime-sftp-openssh-full-size.test.ts
|
||||
|
||||
- name: Start restricted loopback OpenSSH POSIX system-SSH fixture
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
fixture="$RUNNER_TEMP/orca-live-sftp"
|
||||
test -x /usr/sbin/sshd
|
||||
test -s "$fixture/client-key"
|
||||
test -s "$fixture/host-key"
|
||||
system_fixture="$fixture/system-ssh"
|
||||
primitives="$system_fixture/primitives"
|
||||
mkdir -p "$primitives"
|
||||
ln -s "$(command -v mkdir)" "$primitives/mkdir"
|
||||
ln -s "$(command -v chmod)" "$primitives/chmod"
|
||||
ln -s "$(command -v cat)" "$primitives/cat"
|
||||
ln -s "$(command -v rm)" "$primitives/rm"
|
||||
wrapper="$system_fixture/restricted-command"
|
||||
printf '%s\n' \
|
||||
'#!/bin/sh' \
|
||||
'PATH="${0%/*}/primitives"' \
|
||||
'export PATH' \
|
||||
'exec /bin/sh -c "$SSH_ORIGINAL_COMMAND"' > "$wrapper"
|
||||
chmod 755 "$wrapper"
|
||||
port=22223
|
||||
known_hosts="$HOME/.ssh/known_hosts"
|
||||
printf '[127.0.0.1]:%s %s\n' "$port" "$(cat "$fixture/host-key.pub")" >> "$known_hosts"
|
||||
chmod 600 "$known_hosts"
|
||||
config="$system_fixture/sshd_config"
|
||||
printf '%s\n' \
|
||||
"Port $port" \
|
||||
'ListenAddress 127.0.0.1' \
|
||||
"HostKey $fixture/host-key" \
|
||||
"PidFile $system_fixture/sshd.pid" \
|
||||
"AuthorizedKeysFile $HOME/.ssh/authorized_keys" \
|
||||
"AllowUsers $USER" \
|
||||
'PasswordAuthentication no' \
|
||||
'KbdInteractiveAuthentication no' \
|
||||
'ChallengeResponseAuthentication no' \
|
||||
'UsePAM no' \
|
||||
'StrictModes yes' \
|
||||
'LogLevel VERBOSE' \
|
||||
"ForceCommand $wrapper" > "$config"
|
||||
sudo /usr/sbin/sshd -f "$config" -E "$system_fixture/sshd.log"
|
||||
for attempt in {1..20}; do
|
||||
if ssh -F /dev/null -i "$fixture/client-key" -p "$port" \
|
||||
-o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes \
|
||||
-o UserKnownHostsFile="$known_hosts" "$USER@127.0.0.1" true; then
|
||||
break
|
||||
fi
|
||||
if test "$attempt" = 20; then
|
||||
sudo tail -200 "$system_fixture/sshd.log"
|
||||
exit 1
|
||||
fi
|
||||
sleep 0.25
|
||||
done
|
||||
server_version=$(/usr/sbin/sshd -V 2>&1 | head -1)
|
||||
{
|
||||
printf 'ORCA_SSH_RELAY_LIVE_SYSTEM_SSH_HOST=127.0.0.1\n'
|
||||
printf 'ORCA_SSH_RELAY_LIVE_SYSTEM_SSH_PORT=%s\n' "$port"
|
||||
printf 'ORCA_SSH_RELAY_LIVE_SYSTEM_SSH_USER=%s\n' "$USER"
|
||||
printf 'ORCA_SSH_RELAY_LIVE_SYSTEM_SSH_IDENTITY=%s\n' "$fixture/client-key"
|
||||
printf 'ORCA_SSH_RELAY_LIVE_SYSTEM_SSH_REMOTE_ROOT=%s\n' "$fixture/remote"
|
||||
printf 'ORCA_SSH_RELAY_LIVE_SYSTEM_SSH_SERVER_VERSION=%s\n' "$server_version"
|
||||
} >> "$GITHUB_ENV"
|
||||
|
||||
- name: Measure exact full-size runtime over POSIX system SSH
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
first_output="$RUNNER_TEMP/ssh-relay-runtime/${{ matrix.tuple }}/first"
|
||||
ORCA_SSH_FORCE_SYSTEM_TRANSPORT=1 \
|
||||
ORCA_SSH_RELAY_FULL_SIZE_RUNTIME_ROOT="$first_output/runtime" \
|
||||
ORCA_SSH_RELAY_FULL_SIZE_IDENTITY="$first_output/orca-ssh-relay-runtime-${{ matrix.tuple }}.identity.json" \
|
||||
pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1 --reporter=verbose \
|
||||
src/main/ssh/ssh-relay-runtime-posix-system-ssh-openssh-full-size.test.ts
|
||||
|
||||
- name: Stop restricted loopback OpenSSH POSIX system-SSH fixture
|
||||
if: always() && runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
system_fixture="$RUNNER_TEMP/orca-live-sftp/system-ssh"
|
||||
if test -s "$system_fixture/sshd.pid"; then
|
||||
sudo kill "$(cat "$system_fixture/sshd.pid")" || true
|
||||
fi
|
||||
if test -f "$system_fixture/sshd.log"; then
|
||||
sudo tail -200 "$system_fixture/sshd.log"
|
||||
fi
|
||||
|
||||
- name: Stop loopback OpenSSH SFTP fixture
|
||||
if: always() && runner.os == 'Linux'
|
||||
shell: bash
|
||||
|
||||
@@ -104,6 +104,42 @@ describe('SSH relay runtime artifact workflow', () => {
|
||||
expect(stop.if).toBe("always() && runner.os == 'Linux'")
|
||||
})
|
||||
|
||||
it('runs full-size POSIX system SSH against loopback OpenSSH with restricted primitives', async () => {
|
||||
const workflow = parse(await readFile(workflowUrl, 'utf8'))
|
||||
const steps = workflow.jobs['build-posix-runtime'].steps
|
||||
const start = steps.find(
|
||||
(step) => step.name === 'Start restricted loopback OpenSSH POSIX system-SSH fixture'
|
||||
)
|
||||
const measure = steps.find(
|
||||
(step) => step.name === 'Measure exact full-size runtime over POSIX system SSH'
|
||||
)
|
||||
const stop = steps.find(
|
||||
(step) => step.name === 'Stop restricted loopback OpenSSH POSIX system-SSH fixture'
|
||||
)
|
||||
|
||||
expect(start).toBeDefined()
|
||||
expect(measure).toBeDefined()
|
||||
expect(stop).toBeDefined()
|
||||
expect(start.if).toBe("runner.os == 'Linux'")
|
||||
expect(start.run).toContain('ListenAddress 127.0.0.1')
|
||||
expect(start.run).toContain('ForceCommand $wrapper')
|
||||
expect(start.run).toContain('PasswordAuthentication no')
|
||||
expect(start.run).toContain('StrictModes yes')
|
||||
expect(start.run).toContain('known_hosts')
|
||||
expect(start.run).toContain('StrictHostKeyChecking=yes')
|
||||
for (const primitive of ['mkdir', 'chmod', 'cat', 'rm']) {
|
||||
expect(start.run).toContain(`command -v ${primitive}`)
|
||||
}
|
||||
for (const forbidden of ['node', 'python', 'perl', 'tar', 'base64', 'sha256sum', 'shasum']) {
|
||||
expect(start.run).not.toContain(`command -v ${forbidden}`)
|
||||
}
|
||||
expect(measure.if).toBe("runner.os == 'Linux'")
|
||||
expect(measure.run).toContain('ORCA_SSH_FORCE_SYSTEM_TRANSPORT=1')
|
||||
expect(measure.run).toContain('ssh-relay-runtime-posix-system-ssh-openssh-full-size.test.ts')
|
||||
expect(measure.run).toContain('first_output/runtime')
|
||||
expect(stop.if).toBe("always() && runner.os == 'Linux'")
|
||||
})
|
||||
|
||||
it('uploads only the first output after two clean builds verify and compare', async () => {
|
||||
const source = await readFile(workflowUrl, 'utf8')
|
||||
const workflow = parse(source)
|
||||
|
||||
+13
@@ -43,6 +43,19 @@ immediate SIGTERM listener.
|
||||
No direct spawn, legacy uploader change, live claim, or product path is included. Audit only a
|
||||
disconnected live/full-size POSIX system-SSH proof next; legacy remains the only production/default
|
||||
path and SignPath remains deferred.
|
||||
`E-M6-POSIX-SYSTEM-SSH-TREE-LIVE-AUDIT-001` limits that proof to a second loopback-only stock
|
||||
`sshd` on Linux x64/arm64 artifact runners, exact generated host-key trust, public-key auth, and a
|
||||
forced remote `PATH` containing only `mkdir`, `chmod`, `cat`, and `rm` plus absolute `/bin/sh`.
|
||||
It adds only a purpose-named full-size test and workflow/oracle wiring; no production module changes.
|
||||
`E-M6-POSIX-SYSTEM-SSH-TREE-LIVE-LOCAL-RED-001` proves the live suite skips without its seven CI
|
||||
inputs while the independent workflow oracle fails only because the audited start/measure/stop
|
||||
steps are absent. That bounded fixture wiring now passes the local 9/9 workflow oracle while the
|
||||
purpose suite remains honestly skipped without CI inputs; broad regression gates and exact-head
|
||||
Linux x64/arm64 live evidence remain required.
|
||||
`E-M6-POSIX-SYSTEM-SSH-TREE-LIVE-LOCAL-001` records that bounded package locally green: 9/9 workflow
|
||||
oracle, 220 focused, 656 broad relay, 282 release-contract, typecheck, lint, 355-entry max-lines,
|
||||
formatting, diff, protected-resolver, and no-product-consumer gates pass. The live case is explicitly
|
||||
skipped locally and earns no proof; exact-head Linux x64/arm64 live evidence is still required.
|
||||
`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
|
||||
|
||||
+116
-7
@@ -9,7 +9,7 @@ work; keep exact commands, runner identities, hashes, metrics, and residual gaps
|
||||
Date created: 2026-07-14<br>
|
||||
Last updated: 2026-07-15<br>
|
||||
Current phase: Milestone 6 / Work Package 5 bounded runtime transfer — **In progress — 2026-07-15, Codex implementation owner: disconnected POSIX system-SSH/no-tar transfer composition**. 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.<br>
|
||||
Session checkpoint: **POSIX SYSTEM-SSH TREE/ROOT EXACT-HEAD GREEN / LIVE FULL-SIZE AUDIT NEXT — 2026-07-15, Codex implementation owner** — `E-M6-POSIX-SYSTEM-SSH-TREE-CI-001` proves the 15-case disconnected composition on all six primary Node 24 native clients at exact head `ec51e36b7`; Linux supplements, Windows x64 floor, PR Checks, computer-use, and Golden E2E pass. The artifact run remains red only for the declared Windows arm64 hosted build 26200 versus required 26100 floor mismatch after complete runtime verification. Audit only a purpose-named live/full-size loopback OpenSSH proof next; do not connect production behavior. Merging to `main` remains prohibited, SignPath is deferred, and legacy remains the production default.<br>
|
||||
Session checkpoint: **POSIX SYSTEM-SSH LIVE FULL-SIZE LOCAL PACKAGE GREEN / COMMIT AND EXACT-HEAD CI NEXT — 2026-07-15, Codex implementation owner** — `E-M6-POSIX-SYSTEM-SSH-TREE-LIVE-LOCAL-001` records the bounded workflow oracle, focused/broad/release suites, typecheck, lint, max-lines, formatting, diff, protected-resolver, and no-product-consumer gates green. The purpose suite remains honestly skipped without its seven CI inputs and earns no live proof locally. Commit and push only this test/workflow/docs package, then require exact-head Linux x64/arm64 live metrics and all-six adjacent regression cells. Merging to `main` remains prohibited, SignPath is deferred, and legacy remains the production default.<br>
|
||||
Primary design: [SSH relay GitHub Release plan](./2026-07-14-ssh-relay-github-release-plan.html)<br>
|
||||
Motivating issues: [#8450](https://github.com/stablyai/orca/issues/8450), [#1693](https://github.com/stablyai/orca/issues/1693)
|
||||
|
||||
@@ -17879,8 +17879,8 @@ config/scripts/ssh-relay-runtime-workflow.test.mjs`.
|
||||
separate five-second abort controller. Runtime-invalid staging-root input was corrected before
|
||||
final GREEN so validation cannot throw an incidental `.split` type error.
|
||||
- Purpose command: `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts
|
||||
--maxWorkers=1 src/main/ssh/ssh-relay-runtime-posix-control-command.test.ts
|
||||
src/main/ssh/ssh-relay-runtime-posix-tree-transfer.test.ts`. Result: 2 files and 15/15 cases pass;
|
||||
--maxWorkers=1 src/main/ssh/ssh-relay-runtime-posix-control-command.test.ts
|
||||
src/main/ssh/ssh-relay-runtime-posix-tree-transfer.test.ts`. Result: 2 files and 15/15 cases pass;
|
||||
Vitest 1.95 seconds, 2.96 seconds real, 139,640,832-byte maximum RSS, 95,851,848-byte peak memory
|
||||
footprint, zero swaps and block I/O. Cases cover exact/no-input success, nonzero/error, pre/mid
|
||||
abort, timeout, graceful/forced settlement and joined failures; invalid OS/path/concurrency before
|
||||
@@ -17888,7 +17888,7 @@ config/scripts/ssh-relay-runtime-workflow.test.mjs`.
|
||||
progress, default-one/four-channel bounds, collision ownership, cancellation settlement, and
|
||||
cleanup failure/timeout joining.
|
||||
- Focused composition command: `/usr/bin/time -l pnpm exec vitest run --config
|
||||
config/vitest.config.ts --maxWorkers=1` with the two new purpose suites plus
|
||||
config/vitest.config.ts --maxWorkers=1` with the two new purpose suites plus
|
||||
`ssh-relay-runtime-posix-file-destination.test.ts`,
|
||||
`ssh-relay-runtime-system-ssh-file-channel.test.ts`, the source-tree/scan/stream suites,
|
||||
`ssh-system-fallback.test.ts`, `ssh-connection.test.ts`, and
|
||||
@@ -17898,13 +17898,13 @@ config/scripts/ssh-relay-runtime-workflow.test.mjs`.
|
||||
lease, retained-buffer/no-later-write, missing process/false kill, diagnostic, child settlement,
|
||||
and exact native-workflow-family contracts inherited from the composed boundaries.
|
||||
- Broad relay command: `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts
|
||||
--maxWorkers=1 src/main/ssh/ssh-relay-*.test.ts`. Result: 50 files passed, four files skipped;
|
||||
--maxWorkers=1 src/main/ssh/ssh-relay-*.test.ts`. Result: 50 files passed, four files skipped;
|
||||
656 passed and six declared skips out of 662. Vitest 55.58 seconds, 58.05 seconds real,
|
||||
254,115,840-byte maximum RSS, 96,458,248-byte peak memory footprint, zero swaps and block I/O.
|
||||
Skips remain the declared case-fold/full-size/live SSH cells and are not treated as this package's
|
||||
evidence.
|
||||
- Release-contract command: `/usr/bin/time -l pnpm exec vitest run --config
|
||||
config/vitest.config.ts --maxWorkers=1 config/scripts/ssh-relay-runtime-*.test.mjs`. Result: 50
|
||||
config/vitest.config.ts --maxWorkers=1 config/scripts/ssh-relay-runtime-*.test.mjs`. Result: 50
|
||||
files and 281/281 cases pass; Vitest 37.74 seconds, 41.85 seconds real, 190,676,992-byte maximum
|
||||
RSS, 97,195,384-byte peak memory footprint, zero swaps and block I/O.
|
||||
- Static commands/results: `/usr/bin/time -l pnpm typecheck` passes in 8.38 seconds real with
|
||||
@@ -17917,7 +17917,7 @@ config/scripts/ssh-relay-runtime-workflow.test.mjs`.
|
||||
`pnpm check:max-lines-ratchet` command; no checklist box was credited to the failed spelling.
|
||||
- Isolation evidence: `git diff --check` and no-index whitespace checks for all four new files are
|
||||
clean. `git diff --exit-code HEAD -- src/main/ssh/ssh-remote-node-resolution.ts
|
||||
src/main/ssh/ssh-remote-node-resolution.test.ts` is empty, preserving the protected Milestone 0
|
||||
src/main/ssh/ssh-remote-node-resolution.test.ts` is empty, preserving the protected Milestone 0
|
||||
resolver. The only implementation references to the exported tree owner are its definition and
|
||||
purpose test; the control owner is consumed only by that disconnected tree owner and its test.
|
||||
No product, settings, legacy uploader, fallback, tuple, publication, or default-path importer
|
||||
@@ -17971,6 +17971,115 @@ config/scripts/ssh-relay-runtime-workflow.test.mjs`.
|
||||
publish/launch, product/Beta/fallback/default wiring, tuple enablement, release publication, and
|
||||
SignPath remain absent.
|
||||
|
||||
### E-M6-POSIX-SYSTEM-SSH-TREE-LIVE-AUDIT-001 — restricted-primitive full-size OpenSSH proof
|
||||
|
||||
- Date/owner/base: 2026-07-15, Codex implementation owner; exact clean evidence base
|
||||
`3fbf2339badb7bab0308a6daadb80e7d5444e280` after recording the disconnected tree package's
|
||||
exact-head evidence.
|
||||
- Existing-boundary audit: the exact tree owner already composes production `SshConnection.exec`,
|
||||
the system-SSH child/channel adapter, exclusive POSIX destinations, the scanned lease-backed
|
||||
source stream, and bounded owned-root cleanup. No production change is necessary for live proof.
|
||||
The existing live SFTP fixture cannot force a restricted command environment because its
|
||||
`internal-sftp` subsystem shares the daemon, so do not alter or weaken it.
|
||||
- Live fixture: on only the Linux x64/arm64 target-native artifact jobs, start a second stock
|
||||
loopback-only `sshd` on its own port/PID/log with the already-generated ephemeral Ed25519 client
|
||||
and host keys. Pin the generated public host key into the runner user's `known_hosts`; retain
|
||||
`StrictModes yes`, disable password/keyboard-interactive authentication, and never expose a
|
||||
non-loopback listener. `ForceCommand` invokes a fixed fixture-owned POSIX wrapper whose remote
|
||||
`PATH` contains symlinks only for `mkdir`, `chmod`, `cat`, and `rm`; `/bin/sh` is absolute. This
|
||||
proves transfer without remote Node, Python, Perl, tar, base64, `sha256sum`, or `shasum`, while
|
||||
preserving normal production system-SSH argument/channel behavior and authenticated transport.
|
||||
- Purpose suite: build a scanned source tree from the exact full-size runtime identity produced in
|
||||
the same native job, force the existing `SshConnection` into system transport only for the test,
|
||||
and call `transferSshRelayRuntimeTreeViaPosixSystemSsh` directly. Measure serial/default-one and
|
||||
explicit-four transfers; validate every remote path, byte, SHA-256, mode, file count, and total;
|
||||
prove a root collision cannot alter the complete tree; cancel after the first transferred bytes,
|
||||
require no later progress and owned-root removal after settlement; and prove the connection remains
|
||||
usable. Record tuple/content/server/runner, bytes/files, elapsed time, peak progress concurrency,
|
||||
incremental RSS, cancellation delay, and residual gaps. Enforce the existing 80 MiB incremental
|
||||
RSS and 10-second cancellation ceilings under the workflow's 20-minute job budget.
|
||||
- Evidence sequence: add only the skipped-without-input purpose suite plus workflow oracle first and
|
||||
accept RED only when the oracle reports the three missing start/measure/stop steps. Then add the
|
||||
bounded workflow fixture/wiring, require local skip/oracle GREEN plus broad/static/isolation gates,
|
||||
commit, and require exact-head Linux x64/arm64 live metrics and all-six adjacent regression cells.
|
||||
- Explicit residual boundary: no high-RTT host, `MaxSessions=1`, cross-client/remote family matrix,
|
||||
network interruption/disk/quota/inode/noexec/AV fault injection, semantic probe/cache, optional tar
|
||||
fast path, Windows transfer, remote bundled-Node verification/install/publish/launch, product/Beta/
|
||||
settings/fallback/default behavior, tuple enablement, publication, or SignPath is included. Legacy
|
||||
remains the only production/default path.
|
||||
|
||||
### E-M6-POSIX-SYSTEM-SSH-TREE-LIVE-LOCAL-RED-001 — audited workflow fixture is absent
|
||||
|
||||
- Date/owner/base: 2026-07-15, Codex implementation owner; audit, purpose suite, and workflow-oracle-
|
||||
only diff atop exact clean evidence base `3fbf2339badb7bab0308a6daadb80e7d5444e280`.
|
||||
- Command: `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1
|
||||
src/main/ssh/ssh-relay-runtime-posix-system-ssh-openssh-full-size.test.ts
|
||||
config/scripts/ssh-relay-runtime-workflow.test.mjs`.
|
||||
- Result: expected RED, exit 1. The purpose-named live suite declares its single case skipped because
|
||||
the seven full-size/OpenSSH inputs are absent locally and earns no transfer evidence. The workflow
|
||||
oracle passes 8/9 existing cases and its only failure is `expected undefined to be defined` for
|
||||
the audited restricted POSIX system-SSH start step; measure and stop are consequently absent too.
|
||||
- Resource evidence: Vitest 1.50 seconds, 3.73 seconds real, 143,818,752-byte maximum RSS,
|
||||
96,146,808-byte peak memory footprint, zero swaps and block I/O.
|
||||
- Disposition: RED is causal and accepted. Add only the second loopback daemon, restricted primitive
|
||||
wrapper, exact known-host pin, purpose measurement, and always-run cleanup steps. No production,
|
||||
product/default, fallback, tuple, publication, tar optimization, Windows transfer, or SignPath
|
||||
change is authorized by this evidence.
|
||||
|
||||
### E-M6-POSIX-SYSTEM-SSH-TREE-LIVE-LOCAL-001 — bounded live fixture package is locally green
|
||||
|
||||
- Date/owner/base/runner: 2026-07-15, Codex implementation owner; uncommitted test/workflow/docs
|
||||
package atop exact base `3fbf2339badb7bab0308a6daadb80e7d5444e280` on Apple arm64, macOS 26.2
|
||||
build 25C56, Node 26.0.0, pnpm 10.24.0. The local Node version is newer than the repository's Node
|
||||
24 contract, so exact-head native workflow evidence remains mandatory.
|
||||
- Package: add one purpose-named live/full-size test and three audited Linux-only artifact-workflow
|
||||
steps. The fixture starts a second stock loopback `sshd`, pins the generated host key, permits only
|
||||
public-key authentication, and forces a wrapper whose `PATH` contains only `mkdir`, `chmod`, `cat`,
|
||||
and `rm` while invoking absolute `/bin/sh`. The test uses the exact native runtime tree and existing
|
||||
production system-SSH composition to measure default-one and explicit-four transfer, exact tree
|
||||
bytes/hashes/modes/paths/counts, exclusive-root collision preservation, cancellation settlement,
|
||||
no later progress, owned-root cleanup, post-cancellation connection usability, latency, RSS, and
|
||||
active-file count. No production module changes.
|
||||
- Targeted command: `pnpm exec oxfmt --check` for the new test, workflow oracle, and artifact workflow;
|
||||
`pnpm exec oxlint` for the new test and oracle; then `/usr/bin/time -l pnpm exec vitest run --config
|
||||
config/vitest.config.ts --maxWorkers=1 src/main/ssh/ssh-relay-runtime-posix-system-ssh-openssh-full-size.test.ts
|
||||
config/scripts/ssh-relay-runtime-workflow.test.mjs`. Result: formatting/lint pass; the workflow
|
||||
oracle passes 9/9 while the one live case is explicitly skipped because its seven CI inputs are
|
||||
absent. Vitest 12.53 seconds; 25.90 seconds real; 131,907,584-byte maximum RSS; 96,146,904-byte
|
||||
peak footprint; zero swaps and block I/O. The skip earns no live/full-size transfer evidence.
|
||||
- Focused composition command: `/usr/bin/time -l pnpm exec vitest run --config
|
||||
config/vitest.config.ts --maxWorkers=1` with the control/tree/file-destination/system-channel,
|
||||
source-tree/scan/stream, system-fallback, connection, and workflow-oracle suites. Result: 10 files,
|
||||
220 passed and one declared platform skip; Vitest 13.07 seconds; 14.67 seconds real;
|
||||
178,061,312-byte maximum RSS; 96,277,904-byte peak footprint; zero swaps and block I/O.
|
||||
- Broad relay command: `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts
|
||||
--maxWorkers=1 src/main/ssh/ssh-relay-*.test.ts`. Result: 50 files passed, five files skipped; 656
|
||||
passed and seven declared skips out of 663. Vitest 59.16 seconds; 64.28 seconds real;
|
||||
269,565,952-byte maximum RSS; 96,114,160-byte peak footprint; zero swaps and block I/O. The new
|
||||
live case is one of the declared skips and is not counted as proof.
|
||||
- Release-contract command: `/usr/bin/time -l pnpm exec vitest run --config
|
||||
config/vitest.config.ts --maxWorkers=1 config/scripts/ssh-relay-runtime-*.test.mjs`. Result: 50 files
|
||||
and 282/282 cases pass; Vitest 35.69 seconds; 39.36 seconds real; 190,201,856-byte maximum RSS;
|
||||
96,638,376-byte peak footprint; zero swaps and block I/O.
|
||||
- Static evidence: `/usr/bin/time -l pnpm typecheck` passes in 6.13 seconds real with
|
||||
1,241,317,376-byte maximum RSS and 96,163,192-byte peak footprint. `/usr/bin/time -l pnpm lint`
|
||||
passes in 36.12 seconds real with 2,111,471,616-byte maximum RSS and 95,868,232-byte peak footprint;
|
||||
its 26 warnings are pre-existing outside this diff. Full lint proves switch exhaustiveness, 41
|
||||
reliability gates, the 355-entry max-lines ratchet with no new bypass, bundled guides, and all
|
||||
localization checks. Standalone max-lines and targeted formatting pass after formatting this
|
||||
ledger entry; the new test is 377 lines with no suppression.
|
||||
- Isolation evidence: `git diff --check` passes; `git diff --exit-code HEAD --
|
||||
src/main/ssh/ssh-remote-node-resolution.ts src/main/ssh/ssh-remote-node-resolution.test.ts` is empty.
|
||||
An asserted non-test reference search finds the tree-transfer export only in its owning production
|
||||
module; this package adds no product importer. The intended diff is limited to the live test,
|
||||
artifact workflow, workflow oracle, and both checklist documents. No settings, Beta, legacy
|
||||
uploader, fallback, tuple, publication, default-path, or signing behavior changes.
|
||||
- Residual boundary: local execution does not supply a Linux OpenSSH daemon or the exact full-size
|
||||
native runtime, so exact-head Linux x64/arm64 live metrics and all-six adjacent native regression
|
||||
cells remain required. High RTT, `MaxSessions=1`, cross-family remotes, Windows transfer, primitive
|
||||
probe/cache, remote bundled-Node verification/install/publish/launch, product/Beta/fallback/default
|
||||
wiring, tuple enablement, publication, Apple signing, and SignPath remain absent.
|
||||
|
||||
## Accepted Gaps
|
||||
|
||||
No product gap is accepted merely because it appears in this list. Each entry requires explicit
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
import { createHash } from 'node:crypto'
|
||||
import { lstat, open, opendir, readFile, rm } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
import { performance } from 'node:perf_hooks'
|
||||
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
vi.mock('electron', () => ({ app: { getAppPath: () => process.cwd() } }))
|
||||
|
||||
import type { SshTarget } from '../../shared/ssh-types'
|
||||
import { SshConnection } from './ssh-connection'
|
||||
import { runSshRelayRuntimePosixControlCommand } from './ssh-relay-runtime-posix-control-command'
|
||||
import { transferSshRelayRuntimeTreeViaPosixSystemSsh } from './ssh-relay-runtime-posix-tree-transfer'
|
||||
import {
|
||||
scanSshRelayRuntimeSourceTree,
|
||||
type SshRelayRuntimeScannedSourceTree
|
||||
} from './ssh-relay-runtime-source-scan'
|
||||
import type { SshRelayRuntimeSourceTree } from './ssh-relay-runtime-source-tree'
|
||||
import {
|
||||
SSH_RELAY_RUNTIME_SOURCE_STREAM_LIMITS,
|
||||
type SshRelayRuntimeSourceStreamProgress
|
||||
} from './ssh-relay-runtime-source-stream'
|
||||
import { openSshRelayRuntimeSystemSshFileChannel } from './ssh-relay-runtime-system-ssh-file-channel'
|
||||
|
||||
type MeasurementIdentity = Pick<
|
||||
SshRelayRuntimeSourceTree,
|
||||
'tupleId' | 'contentId' | 'os' | 'architecture'
|
||||
> & {
|
||||
entries: (
|
||||
| { path: string; type: 'directory'; mode: 0o755 }
|
||||
| {
|
||||
path: string
|
||||
type: 'file'
|
||||
role: SshRelayRuntimeSourceTree['files'][number]['role']
|
||||
size: number
|
||||
mode: 0o644 | 0o755
|
||||
sha256: SshRelayRuntimeSourceTree['files'][number]['sha256']
|
||||
}
|
||||
)[]
|
||||
archive: { expandedSize: number; fileCount: number }
|
||||
}
|
||||
|
||||
type Measurement<T> = {
|
||||
result: T
|
||||
elapsedMs: number
|
||||
baselineRss: number
|
||||
peakRss: number
|
||||
incrementalRssBytes: number
|
||||
}
|
||||
|
||||
const host = process.env.ORCA_SSH_RELAY_LIVE_SYSTEM_SSH_HOST
|
||||
const user = process.env.ORCA_SSH_RELAY_LIVE_SYSTEM_SSH_USER
|
||||
const identityFile = process.env.ORCA_SSH_RELAY_LIVE_SYSTEM_SSH_IDENTITY
|
||||
const remoteRoot = process.env.ORCA_SSH_RELAY_LIVE_SYSTEM_SSH_REMOTE_ROOT
|
||||
const runtimeRoot = process.env.ORCA_SSH_RELAY_FULL_SIZE_RUNTIME_ROOT
|
||||
const identityPath = process.env.ORCA_SSH_RELAY_FULL_SIZE_IDENTITY
|
||||
const port = Number.parseInt(process.env.ORCA_SSH_RELAY_LIVE_SYSTEM_SSH_PORT ?? '', 10)
|
||||
const hasLiveInput = Boolean(
|
||||
host &&
|
||||
user &&
|
||||
identityFile &&
|
||||
remoteRoot &&
|
||||
runtimeRoot &&
|
||||
identityPath &&
|
||||
Number.isInteger(port)
|
||||
)
|
||||
|
||||
function parseIdentity(value: unknown): MeasurementIdentity {
|
||||
if (
|
||||
typeof value !== 'object' ||
|
||||
value === null ||
|
||||
!('entries' in value) ||
|
||||
!Array.isArray(value.entries) ||
|
||||
!('archive' in value) ||
|
||||
typeof value.archive !== 'object' ||
|
||||
value.archive === null
|
||||
) {
|
||||
throw new Error('Live POSIX system-SSH measurement identity is incomplete')
|
||||
}
|
||||
return value as MeasurementIdentity
|
||||
}
|
||||
|
||||
function sourceTree(identity: MeasurementIdentity): SshRelayRuntimeSourceTree {
|
||||
const directories = identity.entries
|
||||
.filter((entry) => entry.type === 'directory')
|
||||
.map((entry) => ({
|
||||
...entry,
|
||||
localPath: join(runtimeRoot as string, ...entry.path.split('/'))
|
||||
}))
|
||||
const files = identity.entries
|
||||
.filter((entry) => entry.type === 'file')
|
||||
.map((entry) => ({
|
||||
...entry,
|
||||
localPath: join(runtimeRoot as string, ...entry.path.split('/'))
|
||||
}))
|
||||
return Object.freeze({
|
||||
tupleId: identity.tupleId,
|
||||
contentId: identity.contentId,
|
||||
releaseTag: 'measurement-only',
|
||||
os: identity.os,
|
||||
architecture: identity.architecture,
|
||||
runtimeRoot: runtimeRoot as string,
|
||||
directories,
|
||||
files,
|
||||
fileCount: identity.archive.fileCount,
|
||||
expandedBytes: identity.archive.expandedSize,
|
||||
assertLeaseOwned: async () => {}
|
||||
})
|
||||
}
|
||||
|
||||
async function measure<T>(operation: () => Promise<T>): Promise<Measurement<T>> {
|
||||
const baselineRss = process.memoryUsage().rss
|
||||
let peakRss = baselineRss
|
||||
const sample = (): void => {
|
||||
peakRss = Math.max(peakRss, process.memoryUsage().rss)
|
||||
}
|
||||
const sampler = setInterval(sample, 1)
|
||||
const startedAt = performance.now()
|
||||
try {
|
||||
const result = await operation()
|
||||
sample()
|
||||
return {
|
||||
result,
|
||||
elapsedMs: performance.now() - startedAt,
|
||||
baselineRss,
|
||||
peakRss,
|
||||
incrementalRssBytes: Math.max(0, peakRss - baselineRss)
|
||||
}
|
||||
} finally {
|
||||
clearInterval(sampler)
|
||||
}
|
||||
}
|
||||
|
||||
async function hashFile(path: string): Promise<string> {
|
||||
const handle = await open(path, 'r')
|
||||
const digest = createHash('sha256')
|
||||
const buffer = Buffer.allocUnsafe(64 * 1024)
|
||||
let position = 0
|
||||
try {
|
||||
while (true) {
|
||||
const { bytesRead } = await handle.read(buffer, 0, buffer.length, position)
|
||||
if (bytesRead === 0) {
|
||||
break
|
||||
}
|
||||
digest.update(buffer.subarray(0, bytesRead))
|
||||
position += bytesRead
|
||||
}
|
||||
} finally {
|
||||
await handle.close()
|
||||
}
|
||||
return `sha256:${digest.digest('hex')}`
|
||||
}
|
||||
|
||||
async function collectRemotePaths(root: string): Promise<string[]> {
|
||||
const paths: string[] = []
|
||||
const pending = [{ localPath: root, relativePath: '' }]
|
||||
while (pending.length > 0) {
|
||||
const current = pending.pop()!
|
||||
const directory = await opendir(current.localPath)
|
||||
for await (const entry of directory) {
|
||||
const relativePath = current.relativePath
|
||||
? `${current.relativePath}/${entry.name}`
|
||||
: entry.name
|
||||
paths.push(relativePath)
|
||||
if (entry.isDirectory()) {
|
||||
pending.push({ localPath: join(current.localPath, entry.name), relativePath })
|
||||
}
|
||||
}
|
||||
}
|
||||
return paths.sort()
|
||||
}
|
||||
|
||||
async function validateTransferredTree(
|
||||
tree: SshRelayRuntimeScannedSourceTree,
|
||||
stage: string
|
||||
): Promise<void> {
|
||||
const expectedPaths = [...tree.directories, ...tree.files].map((entry) => entry.path).sort()
|
||||
// Why: the loopback runner can inspect sshd's filesystem without adding another proof channel.
|
||||
expect(await collectRemotePaths(stage)).toEqual(expectedPaths)
|
||||
for (const directory of tree.directories) {
|
||||
const metadata = await lstat(join(stage, ...directory.path.split('/')))
|
||||
expect(metadata.isDirectory()).toBe(true)
|
||||
expect(metadata.mode & 0o777).toBe(directory.mode)
|
||||
}
|
||||
for (const file of tree.files) {
|
||||
const path = join(stage, ...file.path.split('/'))
|
||||
const metadata = await lstat(path)
|
||||
expect(metadata.isFile()).toBe(true)
|
||||
expect(metadata.size).toBe(file.size)
|
||||
expect(metadata.mode & 0o777).toBe(file.mode)
|
||||
expect(await hashFile(path)).toBe(file.sha256)
|
||||
}
|
||||
}
|
||||
|
||||
function stagePath(name: string, identity: MeasurementIdentity): string {
|
||||
return join(remoteRoot as string, `${name}-${identity.contentId.slice('sha256:'.length, 23)}`)
|
||||
}
|
||||
|
||||
function createProgressRecorder(): {
|
||||
record: (progress: SshRelayRuntimeSourceStreamProgress) => void
|
||||
snapshot: () => { bytes: number; updates: number; peakActiveFiles: number }
|
||||
} {
|
||||
let bytes = 0
|
||||
let updates = 0
|
||||
let peakActiveFiles = 0
|
||||
return {
|
||||
record: (progress) => {
|
||||
bytes = progress.bytesTransferred
|
||||
updates += 1
|
||||
peakActiveFiles = Math.max(peakActiveFiles, progress.activeFiles)
|
||||
},
|
||||
snapshot: () => ({ bytes, updates, peakActiveFiles })
|
||||
}
|
||||
}
|
||||
|
||||
async function transfer(
|
||||
connection: SshConnection,
|
||||
tree: SshRelayRuntimeScannedSourceTree,
|
||||
stage: string,
|
||||
signal: AbortSignal,
|
||||
onProgress: (progress: SshRelayRuntimeSourceStreamProgress) => void,
|
||||
maximumConcurrency?: number
|
||||
) {
|
||||
return transferSshRelayRuntimeTreeViaPosixSystemSsh({
|
||||
connection,
|
||||
tree,
|
||||
remoteStagingRoot: stage,
|
||||
signal,
|
||||
onProgress,
|
||||
...(maximumConcurrency === undefined ? {} : { maximumConcurrency })
|
||||
})
|
||||
}
|
||||
|
||||
describe.skipIf(!hasLiveInput)(
|
||||
'SSH relay full-size POSIX system-SSH transfer through restricted OpenSSH',
|
||||
() => {
|
||||
it(
|
||||
'preserves exact bytes and modes with bounded serial, concurrent, and cancellation behavior',
|
||||
{ timeout: 20 * 60_000 },
|
||||
async () => {
|
||||
expect(process.platform).toBe('linux')
|
||||
expect(process.env.ORCA_SSH_FORCE_SYSTEM_TRANSPORT).toBe('1')
|
||||
const identity = parseIdentity(JSON.parse(await readFile(identityPath as string, 'utf8')))
|
||||
expect(identity.os).toBe('linux')
|
||||
const tree = await scanSshRelayRuntimeSourceTree(
|
||||
sourceTree(identity),
|
||||
new AbortController().signal
|
||||
)
|
||||
const target: SshTarget = {
|
||||
id: 'live-posix-system-ssh-measurement',
|
||||
label: 'live-posix-system-ssh-measurement',
|
||||
host: host as string,
|
||||
port,
|
||||
username: user as string,
|
||||
identityFile: identityFile as string,
|
||||
identitiesOnly: true,
|
||||
systemSshConnectionReuse: true,
|
||||
source: 'manual'
|
||||
}
|
||||
const connection = new SshConnection(target, { onStateChange: () => {} })
|
||||
const serialStage = stagePath('system-serial', identity)
|
||||
const concurrentStage = stagePath('system-concurrent', identity)
|
||||
const cancelledStage = stagePath('system-cancelled', identity)
|
||||
const stages = [serialStage, concurrentStage, cancelledStage]
|
||||
await Promise.all(stages.map((stage) => rm(stage, { recursive: true, force: true })))
|
||||
|
||||
try {
|
||||
await connection.connect()
|
||||
expect(connection.usesSystemSshTransport()).toBe(true)
|
||||
|
||||
const serialProgress = createProgressRecorder()
|
||||
const serial = await measure(() =>
|
||||
transfer(
|
||||
connection,
|
||||
tree,
|
||||
serialStage,
|
||||
new AbortController().signal,
|
||||
serialProgress.record
|
||||
)
|
||||
)
|
||||
await validateTransferredTree(tree, serialStage)
|
||||
expect(serialProgress.snapshot().peakActiveFiles).toBe(1)
|
||||
|
||||
const concurrentProgress = createProgressRecorder()
|
||||
const concurrent = await measure(() =>
|
||||
transfer(
|
||||
connection,
|
||||
tree,
|
||||
concurrentStage,
|
||||
new AbortController().signal,
|
||||
concurrentProgress.record,
|
||||
4
|
||||
)
|
||||
)
|
||||
await validateTransferredTree(tree, concurrentStage)
|
||||
expect(concurrentProgress.snapshot().peakActiveFiles).toBeGreaterThan(1)
|
||||
expect(concurrentProgress.snapshot().peakActiveFiles).toBeLessThanOrEqual(4)
|
||||
await expect(
|
||||
transfer(connection, tree, concurrentStage, new AbortController().signal, () => {})
|
||||
).rejects.toBeTruthy()
|
||||
await validateTransferredTree(tree, concurrentStage)
|
||||
|
||||
const controller = new AbortController()
|
||||
const cancelledProgress = createProgressRecorder()
|
||||
let abortRequestedAt = 0
|
||||
const cancelled = await measure(async () => {
|
||||
const outcome = transfer(
|
||||
connection,
|
||||
tree,
|
||||
cancelledStage,
|
||||
controller.signal,
|
||||
(progress) => {
|
||||
cancelledProgress.record(progress)
|
||||
if (progress.bytesTransferred > 0 && !controller.signal.aborted) {
|
||||
abortRequestedAt = performance.now()
|
||||
controller.abort(new Error('live POSIX system-SSH cancellation'))
|
||||
}
|
||||
},
|
||||
4
|
||||
)
|
||||
await expect(outcome).rejects.toThrow('live POSIX system-SSH cancellation')
|
||||
return performance.now() - abortRequestedAt
|
||||
})
|
||||
const updatesAfterSettlement = cancelledProgress.snapshot().updates
|
||||
await expect(lstat(cancelledStage)).rejects.toMatchObject({ code: 'ENOENT' })
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
await expect(lstat(cancelledStage)).rejects.toMatchObject({ code: 'ENOENT' })
|
||||
expect(cancelledProgress.snapshot().updates).toBe(updatesAfterSettlement)
|
||||
|
||||
await runSshRelayRuntimePosixControlCommand({
|
||||
command: 'true',
|
||||
signal: new AbortController().signal,
|
||||
openChannel: (command, signal) =>
|
||||
openSshRelayRuntimeSystemSshFileChannel(connection, command, signal)
|
||||
})
|
||||
expect(connection.getState().status).toBe('connected')
|
||||
|
||||
const metrics = {
|
||||
tupleId: identity.tupleId,
|
||||
contentId: identity.contentId,
|
||||
files: tree.fileCount,
|
||||
bytes: tree.expandedBytes,
|
||||
serverVersion: process.env.ORCA_SSH_RELAY_LIVE_SYSTEM_SSH_SERVER_VERSION,
|
||||
runnerImage: process.env.ImageOS,
|
||||
runnerVersion: process.env.ImageVersion,
|
||||
runnerArchitecture: process.env.RUNNER_ARCH,
|
||||
remotePrimitives: ['/bin/sh', 'mkdir', 'chmod', 'cat', 'rm'],
|
||||
serialElapsedMs: serial.elapsedMs,
|
||||
serialIncrementalRssBytes: serial.incrementalRssBytes,
|
||||
serialPeakActiveFiles: serialProgress.snapshot().peakActiveFiles,
|
||||
concurrentElapsedMs: concurrent.elapsedMs,
|
||||
concurrentIncrementalRssBytes: concurrent.incrementalRssBytes,
|
||||
concurrentPeakActiveFiles: concurrentProgress.snapshot().peakActiveFiles,
|
||||
cancellationSettlementMs: cancelled.result,
|
||||
cancellationIncrementalRssBytes: cancelled.incrementalRssBytes,
|
||||
cancellationProgressUpdates: cancelledProgress.snapshot().updates
|
||||
}
|
||||
console.log(`ssh_relay_live_posix_system_ssh=${JSON.stringify(metrics)}`)
|
||||
expect(serial.result.bytesTransferred).toBe(tree.expandedBytes)
|
||||
expect(concurrent.result.bytesTransferred).toBe(tree.expandedBytes)
|
||||
expect(serialProgress.snapshot().bytes).toBe(tree.expandedBytes)
|
||||
expect(concurrentProgress.snapshot().bytes).toBe(tree.expandedBytes)
|
||||
expect(serial.incrementalRssBytes).toBeLessThanOrEqual(
|
||||
SSH_RELAY_RUNTIME_SOURCE_STREAM_LIMITS.maximumIncrementalMemoryBytes
|
||||
)
|
||||
expect(concurrent.incrementalRssBytes).toBeLessThanOrEqual(
|
||||
SSH_RELAY_RUNTIME_SOURCE_STREAM_LIMITS.maximumIncrementalMemoryBytes
|
||||
)
|
||||
expect(cancelled.result).toBeLessThan(10_000)
|
||||
} finally {
|
||||
await connection.disconnect()
|
||||
await Promise.all(stages.map((stage) => rm(stage, { recursive: true, force: true })))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user