diff --git a/.github/workflows/ssh-relay-runtime-artifacts.yml b/.github/workflows/ssh-relay-runtime-artifacts.yml
index 2a522a034a2..09293f303d6 100644
--- a/.github/workflows/ssh-relay-runtime-artifacts.yml
+++ b/.github/workflows/ssh-relay-runtime-artifacts.yml
@@ -147,6 +147,10 @@ jobs:
node --check config/scripts/ssh-relay-runtime-release-stage-gate.test.mjs
node --check config/scripts/ssh-relay-runtime-draft-recovery.mjs
node --check config/scripts/ssh-relay-runtime-draft-recovery.test.mjs
+ node --check config/scripts/ssh-relay-runtime-aggregate-input.mjs
+ node --check config/scripts/ssh-relay-runtime-aggregate-input.test.mjs
+ node --check config/scripts/ssh-relay-runtime-draft-readback.mjs
+ node --check config/scripts/ssh-relay-runtime-draft-readback.test.mjs
pnpm exec vitest run --config config/vitest.config.ts \
config/scripts/ssh-relay-node-release-verification.test.mjs \
config/scripts/ssh-relay-node-tar-inspection.test.mjs \
@@ -170,6 +174,8 @@ jobs:
config/scripts/ssh-relay-runtime-windows-source-signature-verification.test.mjs \
config/scripts/ssh-relay-runtime-release-stage-gate.test.mjs \
config/scripts/ssh-relay-runtime-draft-recovery.test.mjs \
+ config/scripts/ssh-relay-runtime-aggregate-input.test.mjs \
+ config/scripts/ssh-relay-runtime-draft-readback.test.mjs \
config/scripts/ssh-relay-runtime-closure.test.mjs \
config/scripts/ssh-relay-runtime-pty-smoke.test.mjs \
config/scripts/ssh-relay-runtime-reproducibility.test.mjs \
@@ -493,6 +499,14 @@ jobs:
if ($LASTEXITCODE -ne 0) { throw 'runtime draft recovery syntax check failed' }
node --check config/scripts/ssh-relay-runtime-draft-recovery.test.mjs
if ($LASTEXITCODE -ne 0) { throw 'runtime draft recovery test syntax check failed' }
+ node --check config/scripts/ssh-relay-runtime-aggregate-input.mjs
+ if ($LASTEXITCODE -ne 0) { throw 'runtime aggregate input syntax check failed' }
+ node --check config/scripts/ssh-relay-runtime-aggregate-input.test.mjs
+ if ($LASTEXITCODE -ne 0) { throw 'runtime aggregate input test syntax check failed' }
+ node --check config/scripts/ssh-relay-runtime-draft-readback.mjs
+ if ($LASTEXITCODE -ne 0) { throw 'runtime draft read-back syntax check failed' }
+ node --check config/scripts/ssh-relay-runtime-draft-readback.test.mjs
+ if ($LASTEXITCODE -ne 0) { throw 'runtime draft read-back test syntax check failed' }
pnpm exec vitest run --config config/vitest.config.ts `
config/scripts/ssh-relay-node-release-verification.test.mjs `
config/scripts/ssh-relay-node-tar-inspection.test.mjs `
@@ -516,6 +530,8 @@ jobs:
config/scripts/ssh-relay-runtime-windows-source-signature-verification.test.mjs `
config/scripts/ssh-relay-runtime-release-stage-gate.test.mjs `
config/scripts/ssh-relay-runtime-draft-recovery.test.mjs `
+ config/scripts/ssh-relay-runtime-aggregate-input.test.mjs `
+ config/scripts/ssh-relay-runtime-draft-readback.test.mjs `
config/scripts/ssh-relay-runtime-closure.test.mjs `
config/scripts/ssh-relay-runtime-pty-smoke.test.mjs `
config/scripts/ssh-relay-runtime-reproducibility.test.mjs `
diff --git a/config/scripts/ssh-relay-runtime-aggregate-input.mjs b/config/scripts/ssh-relay-runtime-aggregate-input.mjs
new file mode 100644
index 00000000000..f1410553d15
--- /dev/null
+++ b/config/scripts/ssh-relay-runtime-aggregate-input.mjs
@@ -0,0 +1,150 @@
+import { createHash } from 'node:crypto'
+import { createReadStream } from 'node:fs'
+import { lstat, readdir } from 'node:fs/promises'
+import { basename, join, resolve } from 'node:path'
+
+import { sshRelayRuntimeCompatibility } from './ssh-relay-runtime-compatibility.mjs'
+
+const DIGEST_PATTERN = /^sha256:[0-9a-f]{64}$/u
+const ASSET_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,239}$/u
+const MAX_ARCHIVE_BYTES = 100 * 1024 * 1024
+const MAX_TUPLES = 8
+const AGGREGATE_TIMEOUT_MS = 15 * 60_000
+const ASSET_FIELDS = ['tupleId', 'name', 'contentId', 'sha256', 'size']
+
+function assertObject(value, label) {
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
+ throw new Error(`SSH relay runtime aggregate ${label} must be an object`)
+ }
+}
+
+function assertExactFields(value, fields, label) {
+ assertObject(value, label)
+ const actual = Object.keys(value).sort()
+ const expected = [...fields].sort()
+ if (JSON.stringify(actual) !== JSON.stringify(expected)) {
+ throw new Error(`SSH relay runtime aggregate ${label} has unexpected or missing fields`)
+ }
+}
+
+function expectedArchiveName(tupleId, contentId) {
+ const digest = contentId.slice('sha256:'.length)
+ const extension = tupleId.startsWith('win32-') ? 'zip' : 'tar.xz'
+ return `orca-ssh-relay-runtime-v1-${tupleId}-${digest}.${extension}`
+}
+
+function normalizeAssets(assets) {
+ if (!Array.isArray(assets) || assets.length === 0 || assets.length > MAX_TUPLES) {
+ throw new Error('SSH relay runtime aggregate assets must be a bounded non-empty array')
+ }
+ const tupleIds = new Set()
+ const names = new Set()
+ return assets.map((asset, index) => {
+ assertExactFields(asset, ASSET_FIELDS, `asset ${index}`)
+ if (!Object.hasOwn(sshRelayRuntimeCompatibility, asset.tupleId)) {
+ throw new Error(`SSH relay runtime aggregate has an unsupported tuple: ${asset.tupleId}`)
+ }
+ if (tupleIds.has(asset.tupleId)) {
+ throw new Error(`SSH relay runtime aggregate has a duplicate tuple: ${asset.tupleId}`)
+ }
+ tupleIds.add(asset.tupleId)
+ if (typeof asset.name !== 'string' || !ASSET_NAME_PATTERN.test(asset.name)) {
+ throw new Error('SSH relay runtime aggregate asset has an invalid name')
+ }
+ if (names.has(asset.name)) {
+ throw new Error(`SSH relay runtime aggregate has a duplicate asset: ${asset.name}`)
+ }
+ names.add(asset.name)
+ if (!DIGEST_PATTERN.test(asset.contentId)) {
+ throw new Error('SSH relay runtime aggregate asset has an invalid content identity')
+ }
+ if (!DIGEST_PATTERN.test(asset.sha256)) {
+ throw new Error('SSH relay runtime aggregate asset has an invalid SHA-256')
+ }
+ if (!Number.isSafeInteger(asset.size) || asset.size <= 0 || asset.size > MAX_ARCHIVE_BYTES) {
+ throw new Error('SSH relay runtime aggregate asset has an invalid size')
+ }
+ if (asset.name !== expectedArchiveName(asset.tupleId, asset.contentId)) {
+ throw new Error(`SSH relay runtime aggregate archive name is inconsistent: ${asset.name}`)
+ }
+ return { ...asset }
+ })
+}
+
+function sameFileState(before, after) {
+ return (
+ before.dev === after.dev &&
+ before.ino === after.ino &&
+ before.size === after.size &&
+ before.mtimeNs === after.mtimeNs &&
+ before.ctimeNs === after.ctimeNs
+ )
+}
+
+async function hashArchive(path, asset, signal) {
+ signal?.throwIfAborted()
+ const before = await lstat(path, { bigint: true })
+ if (!before.isFile() || before.isSymbolicLink()) {
+ throw new Error(`SSH relay runtime aggregate input is not a regular file: ${asset.name}`)
+ }
+ if (before.size !== BigInt(asset.size)) {
+ throw new Error(`SSH relay runtime aggregate input size mismatch: ${asset.name}`)
+ }
+ const hash = createHash('sha256')
+ let bytes = 0
+ for await (const chunk of createReadStream(path, { signal })) {
+ signal?.throwIfAborted()
+ bytes += chunk.length
+ if (bytes > asset.size) {
+ throw new Error(`SSH relay runtime aggregate input exceeded its size: ${asset.name}`)
+ }
+ hash.update(chunk)
+ }
+ const after = await lstat(path, { bigint: true })
+ // Why: aggregate identity must describe one stable file, not bytes swapped during hashing.
+ if (!sameFileState(before, after)) {
+ throw new Error(`SSH relay runtime aggregate input changed while hashing: ${asset.name}`)
+ }
+ if (bytes !== asset.size) {
+ throw new Error(`SSH relay runtime aggregate input size mismatch: ${asset.name}`)
+ }
+ const digest = `sha256:${hash.digest('hex')}`
+ if (digest !== asset.sha256) {
+ throw new Error(`SSH relay runtime aggregate input SHA-256 mismatch: ${asset.name}`)
+ }
+}
+
+export async function verifySshRelayRuntimeAggregateInputs({ inputDirectory, assets, signal }) {
+ const effectiveSignal = signal
+ ? AbortSignal.any([signal, AbortSignal.timeout(AGGREGATE_TIMEOUT_MS)])
+ : AbortSignal.timeout(AGGREGATE_TIMEOUT_MS)
+ effectiveSignal.throwIfAborted()
+ if (typeof inputDirectory !== 'string' || inputDirectory.length === 0) {
+ throw new Error('SSH relay runtime aggregate input directory is required')
+ }
+ const normalized = normalizeAssets(assets)
+ const root = resolve(inputDirectory)
+ const rootMetadata = await lstat(root)
+ if (!rootMetadata.isDirectory() || rootMetadata.isSymbolicLink()) {
+ throw new Error('SSH relay runtime aggregate input root must be a real directory')
+ }
+ const entries = await readdir(root, { withFileTypes: true })
+ const actualNames = entries.map((entry) => entry.name).sort()
+ const expectedNames = normalized.map((asset) => asset.name).sort()
+ if (JSON.stringify(actualNames) !== JSON.stringify(expectedNames)) {
+ throw new Error('SSH relay runtime aggregate input directory has missing or unexpected files')
+ }
+ for (const entry of entries) {
+ if (!entry.isFile() || entry.isSymbolicLink()) {
+ throw new Error(`SSH relay runtime aggregate input is not a regular file: ${entry.name}`)
+ }
+ }
+ for (const asset of normalized) {
+ const path = join(root, asset.name)
+ if (basename(path) !== asset.name) {
+ throw new Error(`SSH relay runtime aggregate asset path is unsafe: ${asset.name}`)
+ }
+ await hashArchive(path, asset, effectiveSignal)
+ }
+ return normalized
+}
diff --git a/config/scripts/ssh-relay-runtime-aggregate-input.test.mjs b/config/scripts/ssh-relay-runtime-aggregate-input.test.mjs
new file mode 100644
index 00000000000..7dded7d750d
--- /dev/null
+++ b/config/scripts/ssh-relay-runtime-aggregate-input.test.mjs
@@ -0,0 +1,114 @@
+import { mkdtemp, rm, symlink, writeFile } from 'node:fs/promises'
+import { tmpdir } from 'node:os'
+import { join } from 'node:path'
+
+import { afterEach, describe, expect, it } from 'vitest'
+
+import { verifySshRelayRuntimeAggregateInputs } from './ssh-relay-runtime-aggregate-input.mjs'
+
+const temporaryDirectories = []
+
+async function fixture() {
+ const inputDirectory = await mkdtemp(join(tmpdir(), 'orca-runtime-aggregate-'))
+ temporaryDirectories.push(inputDirectory)
+ const contentId = `sha256:${'a'.repeat(64)}`
+ const bytes = Buffer.from('verified runtime bytes')
+ const name = `orca-ssh-relay-runtime-v1-linux-x64-glibc-${contentId.slice(7)}.tar.xz`
+ await writeFile(join(inputDirectory, name), bytes)
+ return {
+ inputDirectory,
+ assets: [
+ {
+ tupleId: 'linux-x64-glibc',
+ name,
+ contentId,
+ sha256: 'sha256:d4c28c942ea1505c048b33251b3afb8b7f1ce9c54d629c9e4c8923afd93d9f45',
+ size: bytes.length
+ }
+ ]
+ }
+}
+
+afterEach(async () => {
+ await Promise.all(
+ temporaryDirectories
+ .splice(0)
+ .map((directory) => rm(directory, { recursive: true, force: true }))
+ )
+})
+
+describe('SSH relay runtime aggregate inputs', () => {
+ it('accepts only exact immutable runtime archive bytes', async () => {
+ const input = await fixture()
+
+ await expect(verifySshRelayRuntimeAggregateInputs(input)).resolves.toEqual(input.assets)
+ })
+
+ it('rejects a hash or size mismatch', async () => {
+ const hash = await fixture()
+ hash.assets[0].sha256 = `sha256:${'f'.repeat(64)}`
+ await expect(verifySshRelayRuntimeAggregateInputs(hash)).rejects.toThrow(/sha-?256/i)
+
+ const size = await fixture()
+ size.assets[0].size += 1
+ await expect(verifySshRelayRuntimeAggregateInputs(size)).rejects.toThrow(/size/i)
+ })
+
+ it('rejects missing or extra inputs', async () => {
+ const missing = await fixture()
+ await rm(join(missing.inputDirectory, missing.assets[0].name))
+ await expect(verifySshRelayRuntimeAggregateInputs(missing)).rejects.toThrow(/missing|exact/i)
+
+ const extra = await fixture()
+ await writeFile(join(extra.inputDirectory, 'unexpected.bin'), 'extra')
+ await expect(verifySshRelayRuntimeAggregateInputs(extra)).rejects.toThrow(/unexpected|exact/i)
+ })
+
+ it.skipIf(process.platform === 'win32')('rejects a linked archive input', async () => {
+ const input = await fixture()
+ const path = join(input.inputDirectory, input.assets[0].name)
+ const target = join(input.inputDirectory, 'target')
+ await writeFile(target, 'target')
+ await rm(path)
+ await symlink(target, path)
+
+ await expect(verifySshRelayRuntimeAggregateInputs(input)).rejects.toThrow(/unexpected|regular/i)
+ })
+
+ it('rejects duplicate tuples, names, and unsupported identities', async () => {
+ const duplicateTuple = await fixture()
+ duplicateTuple.assets.push({ ...duplicateTuple.assets[0], name: 'different.tar.xz' })
+ await expect(verifySshRelayRuntimeAggregateInputs(duplicateTuple)).rejects.toThrow(
+ /duplicate tuple/i
+ )
+
+ const duplicateName = await fixture()
+ duplicateName.assets.push({ ...duplicateName.assets[0], tupleId: 'linux-arm64-glibc' })
+ await expect(verifySshRelayRuntimeAggregateInputs(duplicateName)).rejects.toThrow(
+ /duplicate asset/i
+ )
+
+ const unsupported = await fixture()
+ unsupported.assets[0].tupleId = 'linux-riscv64-glibc'
+ await expect(verifySshRelayRuntimeAggregateInputs(unsupported)).rejects.toThrow(
+ /unsupported tuple/i
+ )
+ })
+
+ it('derives the archive name from tuple and content identity', async () => {
+ const input = await fixture()
+ input.assets[0].name = 'latest.tar.xz'
+
+ await expect(verifySshRelayRuntimeAggregateInputs(input)).rejects.toThrow(/archive name/i)
+ })
+
+ it('honors cancellation before reading an archive', async () => {
+ const input = await fixture()
+ const controller = new AbortController()
+ controller.abort(new Error('cancel aggregate'))
+
+ await expect(
+ verifySshRelayRuntimeAggregateInputs({ ...input, signal: controller.signal })
+ ).rejects.toThrow(/cancel aggregate/i)
+ })
+})
diff --git a/config/scripts/ssh-relay-runtime-draft-readback.mjs b/config/scripts/ssh-relay-runtime-draft-readback.mjs
new file mode 100644
index 00000000000..47b14ed2b9d
--- /dev/null
+++ b/config/scripts/ssh-relay-runtime-draft-readback.mjs
@@ -0,0 +1,256 @@
+import { createHash } from 'node:crypto'
+
+const API_VERSION = '2022-11-28'
+const DIGEST_PATTERN = /^sha256:[0-9a-f]{64}$/u
+const TAG_PATTERN = /^v\d+\.\d+\.\d+(?:-rc\.\d+(?:\.[0-9A-Za-z]+)?)?$/u
+const REPOSITORY_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u
+const ASSET_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,239}$/u
+const MANAGED_ASSET_PREFIX = 'orca-ssh-relay-runtime-'
+const RELEASE_ASSET_HOST = 'release-assets.githubusercontent.com'
+const MAX_ASSET_BYTES = 100 * 1024 * 1024
+const MAX_TOTAL_BYTES = 1024 * 1024 * 1024
+const MAX_ASSETS = 26
+const READBACK_TIMEOUT_MS = 15 * 60_000
+const ASSET_FIELDS = ['name', 'sha256', 'size']
+
+function assertObject(value, label) {
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
+ throw new Error(`SSH relay runtime draft read-back ${label} must be an object`)
+ }
+}
+
+function assertExactFields(value, fields, label) {
+ assertObject(value, label)
+ const actual = Object.keys(value).sort()
+ const expected = [...fields].sort()
+ if (JSON.stringify(actual) !== JSON.stringify(expected)) {
+ throw new Error(`SSH relay runtime draft read-back ${label} has unexpected or missing fields`)
+ }
+}
+
+function normalizeExpectedAssets(assets) {
+ if (!Array.isArray(assets) || assets.length === 0 || assets.length > MAX_ASSETS) {
+ throw new Error('SSH relay runtime draft read-back assets must be a bounded non-empty array')
+ }
+ const names = new Set()
+ let totalBytes = 0
+ const normalized = assets.map((asset, index) => {
+ assertExactFields(asset, ASSET_FIELDS, `expected asset ${index}`)
+ if (
+ typeof asset.name !== 'string' ||
+ !ASSET_NAME_PATTERN.test(asset.name) ||
+ !asset.name.startsWith(MANAGED_ASSET_PREFIX)
+ ) {
+ throw new Error('SSH relay runtime draft read-back expected an invalid managed asset')
+ }
+ if (names.has(asset.name)) {
+ throw new Error(`SSH relay runtime draft read-back has a duplicate asset: ${asset.name}`)
+ }
+ names.add(asset.name)
+ if (typeof asset.sha256 !== 'string' || !DIGEST_PATTERN.test(asset.sha256)) {
+ throw new Error('SSH relay runtime draft read-back expected an invalid SHA-256')
+ }
+ if (!Number.isSafeInteger(asset.size) || asset.size <= 0 || asset.size > MAX_ASSET_BYTES) {
+ throw new Error('SSH relay runtime draft read-back expected an invalid size')
+ }
+ totalBytes += asset.size
+ return { ...asset }
+ })
+ if (!Number.isSafeInteger(totalBytes) || totalBytes > MAX_TOTAL_BYTES) {
+ throw new Error('SSH relay runtime draft read-back expected assets exceed the total size limit')
+ }
+ return normalized
+}
+
+function authenticatedHeaders(token, accept = 'application/vnd.github+json') {
+ return {
+ Accept: accept,
+ Authorization: `Bearer ${token}`,
+ 'X-GitHub-Api-Version': API_VERSION
+ }
+}
+
+async function responseError(response) {
+ const body = await response.text().catch(() => '')
+ return new Error(
+ `SSH relay runtime GitHub request failed ${response.status} ${response.statusText}: ${body.slice(0, 300)}`
+ )
+}
+
+async function fetchRelease({ repo, releaseId, token, fetchImpl, signal }) {
+ const response = await fetchImpl(`https://api.github.com/repos/${repo}/releases/${releaseId}`, {
+ headers: authenticatedHeaders(token),
+ redirect: 'error',
+ signal
+ })
+ if (!response.ok) {
+ throw await responseError(response)
+ }
+ return response.json()
+}
+
+function validateRelease(release, releaseId, tag, expectedAssets) {
+ assertObject(release, 'release')
+ if (release.id !== releaseId || release.draft !== true) {
+ throw new Error('SSH relay runtime release must remain the requested draft during read-back')
+ }
+ if (release.tag_name !== tag) {
+ throw new Error('SSH relay runtime draft read-back tag does not match')
+ }
+ if (!Array.isArray(release.assets)) {
+ throw new Error('SSH relay runtime draft read-back release assets must be an array')
+ }
+ const expectedByName = new Map(expectedAssets.map((asset) => [asset.name, asset]))
+ const releaseByName = new Map()
+ for (const asset of release.assets) {
+ assertObject(asset, 'release asset')
+ if (typeof asset.name !== 'string' || releaseByName.has(asset.name)) {
+ throw new Error('SSH relay runtime draft read-back has a duplicate or invalid release asset')
+ }
+ releaseByName.set(asset.name, asset)
+ if (asset.name.startsWith(MANAGED_ASSET_PREFIX) && !expectedByName.has(asset.name)) {
+ throw new Error(`SSH relay runtime draft has an unexpected managed asset: ${asset.name}`)
+ }
+ }
+ for (const expected of expectedAssets) {
+ const actual = releaseByName.get(expected.name)
+ if (!actual) {
+ throw new Error(`SSH relay runtime draft is missing managed asset: ${expected.name}`)
+ }
+ if (!Number.isSafeInteger(actual.id) || actual.id <= 0) {
+ throw new Error(`SSH relay runtime draft asset has an invalid ID: ${expected.name}`)
+ }
+ if (actual.state !== 'uploaded') {
+ throw new Error(`SSH relay runtime draft asset is not uploaded: ${expected.name}`)
+ }
+ if (actual.size !== expected.size) {
+ throw new Error(`SSH relay runtime draft asset size disagrees: ${expected.name}`)
+ }
+ }
+ return releaseByName
+}
+
+function redirectedAssetUrl(response) {
+ const location = response.headers.get('location')
+ if (!location) {
+ throw new Error('SSH relay runtime draft read-back redirect is missing Location')
+ }
+ const url = new URL(location)
+ if (url.protocol !== 'https:') {
+ throw new Error('SSH relay runtime draft read-back redirect must use HTTPS')
+ }
+ if (url.hostname !== RELEASE_ASSET_HOST) {
+ throw new Error('SSH relay runtime draft read-back redirect has an unapproved origin')
+ }
+ return url.href
+}
+
+async function fetchAsset({ repo, assetId, token, fetchImpl, signal }) {
+ const response = await fetchImpl(
+ `https://api.github.com/repos/${repo}/releases/assets/${assetId}`,
+ {
+ headers: authenticatedHeaders(token, 'application/octet-stream'),
+ redirect: 'manual',
+ signal
+ }
+ )
+ if (response.status !== 302) {
+ // Why: partial or otherwise unusual successful responses cannot prove the exact release asset.
+ if (response.status !== 200) {
+ throw await responseError(response)
+ }
+ return response
+ }
+ const location = redirectedAssetUrl(response)
+ // Why: the API request is authenticated, but its signed CDN redirect must never receive the token.
+ const redirected = await fetchImpl(location, {
+ headers: { Accept: 'application/octet-stream' },
+ redirect: 'error',
+ signal
+ })
+ if (redirected.status !== 200) {
+ throw await responseError(redirected)
+ }
+ return redirected
+}
+
+async function hashResponse(response, expected, signal) {
+ const contentLength = response.headers.get('content-length')
+ if (contentLength !== null) {
+ const parsed = Number(contentLength)
+ if (!Number.isSafeInteger(parsed) || parsed !== expected.size) {
+ throw new Error(
+ `SSH relay runtime draft read-back Content-Length disagrees: ${expected.name}`
+ )
+ }
+ }
+ if (!response.body) {
+ throw new Error(`SSH relay runtime draft read-back body is missing: ${expected.name}`)
+ }
+ const hash = createHash('sha256')
+ let bytes = 0
+ for await (const chunk of response.body) {
+ signal?.throwIfAborted()
+ const buffer = Buffer.from(chunk)
+ bytes += buffer.length
+ if (bytes > expected.size || bytes > MAX_ASSET_BYTES) {
+ throw new Error(`SSH relay runtime draft read-back size exceeded: ${expected.name}`)
+ }
+ hash.update(buffer)
+ }
+ if (bytes !== expected.size) {
+ throw new Error(`SSH relay runtime draft read-back size mismatch: ${expected.name}`)
+ }
+ const digest = `sha256:${hash.digest('hex')}`
+ if (digest !== expected.sha256) {
+ throw new Error(`SSH relay runtime draft read-back SHA-256 mismatch: ${expected.name}`)
+ }
+}
+
+export async function verifySshRelayRuntimeDraftReadback({
+ repo,
+ releaseId,
+ tag,
+ token,
+ expectedAssets,
+ fetchImpl = fetch,
+ signal
+}) {
+ const effectiveSignal = signal
+ ? AbortSignal.any([signal, AbortSignal.timeout(READBACK_TIMEOUT_MS)])
+ : AbortSignal.timeout(READBACK_TIMEOUT_MS)
+ effectiveSignal.throwIfAborted()
+ if (typeof repo !== 'string' || !REPOSITORY_PATTERN.test(repo)) {
+ throw new Error('SSH relay runtime draft read-back repository is invalid')
+ }
+ if (!Number.isSafeInteger(releaseId) || releaseId <= 0) {
+ throw new Error('SSH relay runtime draft read-back release ID is invalid')
+ }
+ if (typeof tag !== 'string' || !TAG_PATTERN.test(tag)) {
+ throw new Error('SSH relay runtime draft read-back tag is invalid')
+ }
+ if (typeof token !== 'string' || token.length === 0) {
+ throw new Error('SSH relay runtime draft read-back token is required')
+ }
+ const expected = normalizeExpectedAssets(expectedAssets)
+ const release = await fetchRelease({
+ repo,
+ releaseId,
+ token,
+ fetchImpl,
+ signal: effectiveSignal
+ })
+ const releaseAssets = validateRelease(release, releaseId, tag, expected)
+ for (const asset of expected) {
+ effectiveSignal.throwIfAborted()
+ const response = await fetchAsset({
+ repo,
+ assetId: releaseAssets.get(asset.name).id,
+ token,
+ fetchImpl,
+ signal: effectiveSignal
+ })
+ await hashResponse(response, asset, effectiveSignal)
+ }
+ return { releaseId, tag, downloadedAssets: expected }
+}
diff --git a/config/scripts/ssh-relay-runtime-draft-readback.test.mjs b/config/scripts/ssh-relay-runtime-draft-readback.test.mjs
new file mode 100644
index 00000000000..5903acd5ef2
--- /dev/null
+++ b/config/scripts/ssh-relay-runtime-draft-readback.test.mjs
@@ -0,0 +1,212 @@
+import { afterEach, describe, expect, it, vi } from 'vitest'
+
+import { verifySshRelayRuntimeDraftReadback } from './ssh-relay-runtime-draft-readback.mjs'
+
+const TAG = 'v1.4.140-rc.1'
+const REPO = 'stablyai/orca'
+const TOKEN = 'secret-token'
+const bytes = Buffer.from('immutable release bytes')
+const expected = {
+ name: 'orca-ssh-relay-runtime-v1-linux-x64-glibc-a.tar.xz',
+ sha256: 'sha256:5396515749878bd28c5dae110040b4fbae1c33f59318b9c23f446319d68e236a',
+ size: bytes.length
+}
+
+function release(overrides = {}) {
+ return {
+ id: 42,
+ tag_name: TAG,
+ draft: true,
+ assets: [{ id: 101, name: expected.name, state: 'uploaded', size: expected.size }],
+ ...overrides
+ }
+}
+
+function fetchFixture(assetResponse = new Response(bytes)) {
+ return vi
+ .fn()
+ .mockResolvedValueOnce(Response.json(release()))
+ .mockResolvedValueOnce(assetResponse)
+}
+
+afterEach(() => vi.restoreAllMocks())
+
+describe('SSH relay runtime draft read-back', () => {
+ it('downloads and hashes every exact managed asset from the same draft', async () => {
+ const fetchImpl = fetchFixture()
+
+ await expect(
+ verifySshRelayRuntimeDraftReadback({
+ repo: REPO,
+ releaseId: 42,
+ tag: TAG,
+ token: TOKEN,
+ expectedAssets: [expected],
+ fetchImpl
+ })
+ ).resolves.toEqual({ releaseId: 42, tag: TAG, downloadedAssets: [expected] })
+ })
+
+ it('follows only the approved asset redirect without forwarding authorization', async () => {
+ const location = 'https://release-assets.githubusercontent.com/example/runtime?sig=signed'
+ const fetchImpl = vi
+ .fn()
+ .mockResolvedValueOnce(Response.json(release()))
+ .mockResolvedValueOnce(new Response(null, { status: 302, headers: { location } }))
+ .mockResolvedValueOnce(new Response(bytes))
+
+ await verifySshRelayRuntimeDraftReadback({
+ repo: REPO,
+ releaseId: 42,
+ tag: TAG,
+ token: TOKEN,
+ expectedAssets: [expected],
+ fetchImpl
+ })
+
+ expect(fetchImpl.mock.calls[1][1].headers.Authorization).toBe(`Bearer ${TOKEN}`)
+ expect(fetchImpl.mock.calls[2][0]).toBe(location)
+ expect(fetchImpl.mock.calls[2][1].headers).not.toHaveProperty('Authorization')
+ })
+
+ it.each([
+ ['http://release-assets.githubusercontent.com/runtime', 'HTTPS'],
+ ['https://example.com/runtime', 'origin']
+ ])('rejects an unsafe asset redirect %s', async (location, message) => {
+ const fetchImpl = vi
+ .fn()
+ .mockResolvedValueOnce(Response.json(release()))
+ .mockResolvedValueOnce(new Response(null, { status: 302, headers: { location } }))
+
+ await expect(
+ verifySshRelayRuntimeDraftReadback({
+ repo: REPO,
+ releaseId: 42,
+ tag: TAG,
+ token: TOKEN,
+ expectedAssets: [expected],
+ fetchImpl
+ })
+ ).rejects.toThrow(new RegExp(message, 'i'))
+ })
+
+ it.each(['direct API response', 'redirected CDN response'])(
+ 'rejects an unexpected successful status from the %s',
+ async (responseKind) => {
+ const partialResponse = new Response(bytes, {
+ status: 206,
+ headers: { 'content-range': `bytes 0-${bytes.length - 1}/${bytes.length}` }
+ })
+ const fetchImpl =
+ responseKind === 'direct API response'
+ ? fetchFixture(partialResponse)
+ : vi
+ .fn()
+ .mockResolvedValueOnce(Response.json(release()))
+ .mockResolvedValueOnce(
+ new Response(null, {
+ status: 302,
+ headers: {
+ location: 'https://release-assets.githubusercontent.com/example/runtime'
+ }
+ })
+ )
+ .mockResolvedValueOnce(partialResponse)
+
+ await expect(
+ verifySshRelayRuntimeDraftReadback({
+ repo: REPO,
+ releaseId: 42,
+ tag: TAG,
+ token: TOKEN,
+ expectedAssets: [expected],
+ fetchImpl
+ })
+ ).rejects.toThrow(/failed 206/i)
+ }
+ )
+
+ it('rejects changed, truncated, or oversized returned bytes', async () => {
+ for (const body of [
+ Buffer.from('changed release bytes'),
+ bytes.subarray(1),
+ Buffer.concat([bytes, bytes])
+ ]) {
+ await expect(
+ verifySshRelayRuntimeDraftReadback({
+ repo: REPO,
+ releaseId: 42,
+ tag: TAG,
+ token: TOKEN,
+ expectedAssets: [expected],
+ fetchImpl: fetchFixture(new Response(body))
+ })
+ ).rejects.toThrow(/size|sha-?256/i)
+ }
+ })
+
+ it('rejects a published, cross-tag, incomplete, or unexpected managed draft', async () => {
+ for (const changedRelease of [
+ release({ draft: false }),
+ release({ tag_name: 'v1.4.140-rc.2' }),
+ release({ assets: [] }),
+ release({
+ assets: [
+ ...release().assets,
+ { id: 102, name: 'orca-ssh-relay-runtime-extra.zip', state: 'uploaded', size: 1 }
+ ]
+ })
+ ]) {
+ const fetchImpl = vi.fn().mockResolvedValueOnce(Response.json(changedRelease))
+ await expect(
+ verifySshRelayRuntimeDraftReadback({
+ repo: REPO,
+ releaseId: 42,
+ tag: TAG,
+ token: TOKEN,
+ expectedAssets: [expected],
+ fetchImpl
+ })
+ ).rejects.toThrow(/draft|tag|missing|unexpected/i)
+ }
+ })
+
+ it('rejects non-uploaded metadata or a mismatched declared size before download', async () => {
+ for (const asset of [
+ { ...release().assets[0], state: 'new' },
+ { ...release().assets[0], size: expected.size + 1 }
+ ]) {
+ const fetchImpl = vi.fn().mockResolvedValueOnce(Response.json(release({ assets: [asset] })))
+ await expect(
+ verifySshRelayRuntimeDraftReadback({
+ repo: REPO,
+ releaseId: 42,
+ tag: TAG,
+ token: TOKEN,
+ expectedAssets: [expected],
+ fetchImpl
+ })
+ ).rejects.toThrow(/uploaded|size/i)
+ expect(fetchImpl).toHaveBeenCalledTimes(1)
+ }
+ })
+
+ it('honors cancellation before any GitHub request', async () => {
+ const controller = new AbortController()
+ controller.abort(new Error('cancel read-back'))
+ const fetchImpl = vi.fn()
+
+ await expect(
+ verifySshRelayRuntimeDraftReadback({
+ repo: REPO,
+ releaseId: 42,
+ tag: TAG,
+ token: TOKEN,
+ expectedAssets: [expected],
+ fetchImpl,
+ signal: controller.signal
+ })
+ ).rejects.toThrow(/cancel read-back/i)
+ expect(fetchImpl).not.toHaveBeenCalled()
+ })
+})
diff --git a/config/scripts/ssh-relay-runtime-workflow.test.mjs b/config/scripts/ssh-relay-runtime-workflow.test.mjs
index 93a773995d5..3eecea74467 100644
--- a/config/scripts/ssh-relay-runtime-workflow.test.mjs
+++ b/config/scripts/ssh-relay-runtime-workflow.test.mjs
@@ -98,7 +98,9 @@ describe('SSH relay runtime artifact workflow', () => {
for (const moduleName of [
'windows-source-signature-verification',
'release-stage-gate',
- 'draft-recovery'
+ 'draft-recovery',
+ 'aggregate-input',
+ 'draft-readback'
]) {
const script = `ssh-relay-runtime-${moduleName}`
expect(source.split(`${script}.test.mjs`)).toHaveLength(5)
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 d1f5aa262f8..1cfa41ea32e 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
@@ -60,9 +60,12 @@ complete a box.
remain separately gated.
- Next external proof: kernel 4.18, macOS 13.5, Windows arm64 build 26100, and native signing/trust.
- No tuple is enabled; every SSH transfer/runtime and rollout cell remains open.
-- [ ] **WP3 active implementation — Prove disconnected release/signing DAG behavior in exact-head
- CI.** Local contracts pass at `6f13e94f8`, which is one commit ahead of PR #8741; no production
- workflow, publication, desktop consumer, or tuple is connected.
+- [ ] **WP3 active implementation — Add disconnected aggregate-input and authenticated draft
+ read-back verification.** Release-DAG contracts pass locally and on all six native build jobs in
+ exact-head run [29390079639](https://github.com/stablyai/orca/actions/runs/29390079639) under
+ `E-M4-RELEASE-DAG-CI-001`. Aggregate/read-back RED/GREEN, workflow, full SSH-relay, and static
+ gates pass locally under `E-M4-AGGREGATE-READBACK-LOCAL-001`; exact-head CI is next. No
+ production workflow, publication, desktop consumer, signing credential, or tuple is connected.
## Work packages, in required order
@@ -97,11 +100,12 @@ complete a box.
### WP3 — Release build and signing
-- [ ] **In progress — 2026-07-15, Codex implementation owner:** add disconnected credential-free
- fail-closed release/signing DAG contracts before production workflow wiring. Local red/green,
- full SSH-relay, type, lint, formatting, and native-job wiring gates pass under
- `E-M4-RELEASE-DAG-LOCAL-001` at local commit `6f13e94f8`; the commit is one ahead of the PR and
- exact-head CI is next.
+- [x] Release/signing DAG contracts pass locally and on all six native build jobs under
+ `E-M4-RELEASE-DAG-LOCAL-001` and `E-M4-RELEASE-DAG-CI-001`.
+- [ ] **In progress — 2026-07-15, Codex implementation owner:** implement disconnected
+ aggregate-input and authenticated draft read-back verification before any production workflow,
+ publication, desktop consumer, or signing credential is connected. Local implementation and
+ verification pass under `E-M4-AGGREGATE-READBACK-LOCAL-001`; exact-head CI is next.
- [ ] Add target-native runtime jobs as desktop release prerequisites.
- [ ] Add native signing jobs; hash only the returned signed bytes.
- [ ] Add a fail-closed aggregate and immutable manifest-signing job.
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 1cf08da951c..72bce889119 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
@@ -8,8 +8,8 @@ work; keep exact commands, runner identities, hashes, metrics, and residual gaps
Date created: 2026-07-14
Last updated: 2026-07-15
-Current phase: Milestone 4 / Work Package 3 credential-free release/signing DAG failure contracts — **In progress — 2026-07-15, Codex implementation owner**. Work Package 2's target-native Windows official-Node and preserved-upstream source-signature gate is closed under E-M3-WINDOWS-SOURCE-SIGNATURE-CI-001. Exact-floor gaps and real native signing/trust remain open external gates, so the active package is limited to disconnected fail-closed release-DAG contracts and tests; it may not publish, connect desktop consumers, or enable a tuple. Production/default behavior is unchanged, no bundled-runtime path is enabled, and no artifact is published.
-Session checkpoint: **In progress — 2026-07-15, Codex implementation owner** — local implementation commit `6f13e94f8a7a315ac3da6bb2067b99da8e02a803` is one commit ahead of the PR head and closes the disconnected release-stage and draft-recovery contracts locally under E-M4-RELEASE-DAG-LOCAL-001. Exact-head CI for that commit is the active gate; push and GitHub-runner evidence collection are now authorized. The current PR head remains `be32653a7`: artifact jobs 87267322867 and 87267322870 in run [29388734922](https://github.com/stablyai/orca/actions/runs/29388734922) passed target-native Windows source-signature verification under E-M3-WINDOWS-SOURCE-SIGNATURE-CI-001, while PR Checks [29388734935](https://github.com/stablyai/orca/actions/runs/29388734935) and Golden E2E [29388734914](https://github.com/stablyai/orca/actions/runs/29388734914) are green. The artifact run's only failure is the retained Windows arm64 build-26200-versus-26100 floor gate. Real Apple/SignPath signing, returned production signatures, Gatekeeper/notarization, Defender/WDAC, missing exact-floor snapshots, and native trust remain separately gated. Nothing is published or enabled, and legacy remains the production default.
+Current phase: Milestone 4 / Work Package 3 credential-free aggregate-input and draft read-back verification — **In progress — 2026-07-15, Codex implementation owner**. The disconnected release/signing DAG failure contracts are closed locally and on all six native build jobs under E-M4-RELEASE-DAG-LOCAL-001 and E-M4-RELEASE-DAG-CI-001. Exact-floor gaps and real native signing/trust remain open external gates, so the active package may validate only immutable aggregate inputs and authenticated draft bytes; it may not publish, connect desktop consumers, use signing credentials, or enable a tuple. Production/default behavior is unchanged, no bundled-runtime path is enabled, and no artifact is published.
+Session checkpoint: **In progress — 2026-07-15, Codex implementation owner** — exact-head artifact run [29390079639](https://github.com/stablyai/orca/actions/runs/29390079639) executes the two release-DAG suites under Node 24 on all six target-native build jobs at `97164470c`; every build and supplemental userland job passes. The run remains red only because Windows arm64 runner build 26200 is not the required build 26100, after archive/tree/runtime smoke passes in 7,721.7682 ms with 48,504,832-byte RSS. Golden E2E [29390079632](https://github.com/stablyai/orca/actions/runs/29390079632) is green. PR Checks [29390079665](https://github.com/stablyai/orca/actions/runs/29390079665) attempt 1 passed lint, typecheck, Git compatibility, and tests, then hit a TCP reset downloading Electron; authorized attempt 2 job 87272898091 passes the complete verify/package/CLI-smoke job. The disconnected aggregate-input and authenticated draft read-back implementation now passes local RED/GREEN, workflow-wiring, full SSH-relay, type, lint, line-budget, formatting, and diff gates under E-M4-AGGREGATE-READBACK-LOCAL-RED-001 and E-M4-AGGREGATE-READBACK-LOCAL-001; exact-head native CI is next. Real Apple/SignPath signing, returned production signatures, Gatekeeper/notarization, Defender/WDAC, missing exact-floor snapshots, and native trust remain separately gated. Nothing is published or enabled, 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)
@@ -65,10 +65,11 @@ same change as the work it records.
[#8728](https://github.com/stablyai/orca/pull/8728), which is CI-green at current PR head
`0c299fe18`; implementation evidence remains anchored at `b9d80a4cb`. No deploy/resolver call site
is connected and no tuple is enabled.
-- Active package: Work Package 3 credential-free release/signing DAG failure contracts. The package
- must model fail-closed build, approval, signing, aggregate, upload, read-back, and recovered-draft
- outcomes without credentials, production workflow wiring, publication, desktop consumers, or
- tuple enablement. No Milestone 4 box is checked until an implemented contract and exact tests exist.
+- Active package: Work Package 3 credential-free aggregate-input and authenticated draft read-back
+ verification. It must bind every input and returned byte to exact name, size, SHA-256, tuple,
+ content identity, and source release without credentials, publication, production workflow wiring,
+ desktop consumers, or tuple enablement. No Milestone 4 box is checked until the corresponding
+ implementation and exact tests exist.
- 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
@@ -1459,7 +1460,7 @@ Update status and evidence as work begins. Do not combine these into one large b
| 0. #8450 legacy fix | Coherent Node/npm selection and live repro | Fixes legacy selection only | Complete and CI-green in draft PR #8724 | E-M0-UNIT-002, E-M0-LIVE-002, E-M0-STATIC-002, E-M0-PR-001, E-M0-CI-001 |
| 1. Contract and selectors | Manifest schema, identity, platform/libc selection, hostile inputs | None | Complete and CI-green in draft PR #8728 | `b9d80a4cb`; E-M2-RED-001, E-M2-CONTRACT-001, E-M2-CI-001 |
| 2. Runtime builds | Per-tuple assembly, native smoke, SBOM/provenance/signing | None | Draft PR #8741; native build/source trust green; real signing/trust pending | `be32653a7`; E-M3-METADATA-CI-001, E-M3-WINDOWS-SOURCE-SIGNATURE-CI-001 |
-| 3. Release publication | Prerequisite DAG, embedded manifest, draft upload/read-back gates | Asset-only | **In progress — 2026-07-15, Codex implementation owner** | E-M4-RELEASE-DAG-LOCAL-001; exact-head CI pending |
+| 3. Release publication | Prerequisite DAG, embedded manifest, draft upload/read-back gates | Asset-only | **In progress — 2026-07-15, Codex implementation owner** | E-M4-RELEASE-DAG-LOCAL-001; E-M4-RELEASE-DAG-CI-001 |
| 4. Desktop resolver/cache | Verified download, extraction, cache, offline behavior | None/forced mode only | Not started | — |
| 5. Transfer/install | Bounded transports, structured sentinel, bundled launch behind per-target Beta/forced mode | Per-target opt-in only | Not started | — |
| 6. Fallback/diagnostics | Abort-and-join state machine, mode isolation, reason codes, target-mode configuration/UI | Per-target Beta only | Not started | — |
@@ -1527,6 +1528,8 @@ focused commands as their scripts/tests are introduced.
- [x] `pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1 config/scripts/ssh-relay-runtime-release-stage-gate.test.mjs config/scripts/ssh-relay-runtime-draft-recovery.test.mjs` (E-M4-RELEASE-DAG-LOCAL-RED-001, E-M4-RELEASE-DAG-LOCAL-001; disconnected credential-free stage/recovery contract only)
- [x] `pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1 config/scripts/ssh-relay-runtime-release-stage-gate.test.mjs config/scripts/ssh-relay-runtime-draft-recovery.test.mjs config/scripts/ssh-relay-runtime-workflow.test.mjs` (E-M4-RELEASE-DAG-LOCAL-001; static native-job wiring only, no real release/signing execution)
+- [x] `pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1 config/scripts/ssh-relay-runtime-aggregate-input.test.mjs config/scripts/ssh-relay-runtime-draft-readback.test.mjs` (E-M4-AGGREGATE-READBACK-LOCAL-RED-001, E-M4-AGGREGATE-READBACK-LOCAL-001; local filesystem plus mocked GitHub responses only)
+- [x] `pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1 config/scripts/ssh-relay-runtime-aggregate-input.test.mjs config/scripts/ssh-relay-runtime-draft-readback.test.mjs config/scripts/ssh-relay-runtime-workflow.test.mjs` (E-M4-AGGREGATE-READBACK-LOCAL-001; static native-job wiring only)
### Commands/scripts that must be added or formally identified
@@ -8846,6 +8849,149 @@ diff --check`.
credential-free aggregate-input/read-back implementation without connecting publication or a
desktop consumer.
+### E-M4-RELEASE-DAG-CI-001 — Release-DAG contracts pass all six native build jobs
+
+- Date: 2026-07-15
+- Owner: Codex implementation owner
+- Source: exact PR head `97164470cb9abad670634b9d4f04424bcc8662b5`; implementation commit
+ `6f13e94f8a7a315ac3da6bb2067b99da8e02a803`; draft PR
+ [#8741](https://github.com/stablyai/orca/pull/8741).
+- Runner/network: GitHub-hosted native runners with normal artifact-build egress and no SSH remote,
+ signing service, release credential, approval environment, draft upload, publication, or desktop
+ consumer. Exact jobs and resolved images:
+ - linux-x64-glibc job 87271365779: `ubuntu-24.04`, `ubuntu24` image `20260705.232.1`, X64,
+ 04:53:59Z–04:58:13Z.
+ - linux-arm64-glibc job 87271365815: `ubuntu-24.04-arm`, `ubuntu24-arm64` image
+ `20260714.61.1`, ARM64, 04:54:03Z–04:59:11Z.
+ - darwin-x64 job 87271365782: `macos-15-intel`, `macos15` image `20260629.0276.1`, X64,
+ 04:53:59Z–04:59:21Z.
+ - darwin-arm64 job 87271365773: `macos-15`, `macos15` image `20260706.0213.1`, ARM64,
+ 04:53:59Z–04:57:05Z.
+ - win32-x64 job 87271365831: `windows-2022`, `win22` image `20260706.237.1`, X64,
+ 04:53:59Z–04:59:34Z.
+ - win32-arm64 job 87271365776: `windows-11-arm`, `win11-arm64` image `20260706.102.1`, ARM64,
+ 04:53:59Z–05:03:25Z.
+- Command: the workflow's `Run runtime artifact contract tests` step syntax-checks both new modules
+ and tests, then includes
+ `ssh-relay-runtime-release-stage-gate.test.mjs` and
+ `ssh-relay-runtime-draft-recovery.test.mjs` in the purpose-named native Vitest command. Inspection
+ used `gh run view 29390079639 --repo stablyai/orca --job --log` for every job.
+- Result: PASS for all six target-native build jobs. Each runner passes the release-stage suite's 15
+ tests and draft-recovery suite's 6 tests under Node v24.18.0. The four POSIX jobs report 32 passing
+ contract files; the two Windows jobs report 33, including the Windows-only PE diagnostic suite.
+ Both Linux oldest-userland jobs and Windows x64 floor job also pass. Overall workflow conclusion is
+ the expected `failure` only because downstream Windows arm64 floor job 87272581856 observes build
+ 26200 instead of required 26100 after the exact runtime passes archive/tree verification, bundled
+ Node, PTY, watcher, and resource settlement in 7,721.7682 ms with 48,504,832-byte RSS.
+- Concurrent regression: Golden E2E run
+ [29390079632](https://github.com/stablyai/orca/actions/runs/29390079632) passes both macOS job
+ 87271365717 and Linux job 87271365749. PR Checks run 29390079665 attempt 1 job 87271365846 passes
+ lint, typecheck, Git compatibility, and tests, then flakes during `Build unpacked app` when a
+ 125-MB Electron GitHub asset download receives `read: connection reset by peer`. Authorized
+ attempt 2 job 87272898091 runs from 05:05:54Z to 05:21:00Z and passes the complete verify job,
+ including unpacked packaging and packaged CLI smoke.
+- Oracle proved: the disconnected build → signing → aggregate → upload → read-back failure contract
+ and same-draft immutable recovery contract parse and execute identically on all six native runner
+ families, including Windows PowerShell workflow wiring. No platform can omit its required signing
+ stage, invent a Linux signing stage, exceed the bounded retry/time policy, or accept missing,
+ duplicate, drifted, failed, cancelled, approval-rejected, or recovered cross-release state.
+- Does not prove: real Apple/SignPath signing or approval, target-native trust of returned Orca bytes,
+ aggregate/manifest generation, manifest signing, GitHub draft upload/read-back, packaged manifest
+ embedding, SSH transfer/install, fallback, performance, or an enabled tuple.
+- Follow-up: implement disconnected aggregate-input and authenticated draft read-back verification,
+ then execute it on all six native job families. Retain every production/default and tuple state.
+
+### E-M4-AGGREGATE-READBACK-LOCAL-RED-001 — Aggregate and draft byte verifiers are absent
+
+- Date: 2026-07-15
+- Owner: Codex implementation owner
+- Source: exact implementation base `97164470cb9abad670634b9d4f04424bcc8662b5` plus the two new
+ uncommitted purpose-named test files.
+- Command:
+
+ ```sh
+ pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1 \
+ config/scripts/ssh-relay-runtime-aggregate-input.test.mjs \
+ config/scripts/ssh-relay-runtime-draft-readback.test.mjs
+ ```
+
+- Result: expected RED in 284 ms. Both suites fail before collecting tests with
+ `ERR_MODULE_NOT_FOUND` for `ssh-relay-runtime-aggregate-input.mjs` and
+ `ssh-relay-runtime-draft-readback.mjs`.
+- Oracle proved: the exact head had only declarative stage/recovery contracts; it had no filesystem
+ implementation binding aggregate input declarations to stable archive bytes and no authenticated,
+ bounded draft asset read-back implementation.
+- Does not prove: implementation, GitHub API behavior, actual release assets, native runners,
+ manifest generation/signing, publication, desktop embedding, SSH behavior, or an enabled tuple.
+- Correction: implement both byte boundaries as disconnected modules with bounded streaming,
+ cancellation, exact identity/asset checks, and credential-safe redirect handling.
+
+### E-M4-AGGREGATE-READBACK-LOCAL-001 — Immutable aggregate and draft read-back bytes pass locally
+
+- Date: 2026-07-15
+- Owner: Codex implementation owner
+- Source: uncommitted implementation based on exact head
+ `97164470cb9abad670634b9d4f04424bcc8662b5`.
+- Runner/network: local macOS 26.2 build 25C56 arm64, Node v26.0.0 and pnpm 10.24.0. Aggregate tests
+ use exclusive local temporary directories. Draft tests use injected WHATWG `Response` objects and
+ make no network, GitHub API, release, credential, signing-service, SSH, publication, or production
+ call. The repository emits its expected Node-24 engine warning.
+- Commands:
+
+ ```sh
+ node --check config/scripts/ssh-relay-runtime-aggregate-input.mjs
+ node --check config/scripts/ssh-relay-runtime-aggregate-input.test.mjs
+ node --check config/scripts/ssh-relay-runtime-draft-readback.mjs
+ node --check config/scripts/ssh-relay-runtime-draft-readback.test.mjs
+ /usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1 \
+ config/scripts/ssh-relay-runtime-aggregate-input.test.mjs \
+ config/scripts/ssh-relay-runtime-draft-readback.test.mjs
+ pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1 \
+ config/scripts/ssh-relay-runtime-aggregate-input.test.mjs \
+ config/scripts/ssh-relay-runtime-draft-readback.test.mjs \
+ config/scripts/ssh-relay-runtime-workflow.test.mjs
+ pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1 \
+ config/scripts/ssh-relay-*.test.mjs
+ pnpm run typecheck
+ pnpm run lint
+ pnpm run check:max-lines-ratchet
+ pnpm exec oxfmt --check
+ git diff --check
+ ```
+
+- Result: PASS. Four syntax checks pass. After exact-HTTP-200 hardening, the two purpose-named suites
+ pass 2 files/17 tests in 295 ms; `/usr/bin/time -l` records 0.89 seconds wall time,
+ 132,104,192-byte maximum RSS, zero swaps, and 96,277,880-byte peak memory footprint.
+ Workflow-inclusive focus passes 3 files/23 tests in 410 ms. The complete SSH-relay suite passes 35
+ files/201 tests in 6.63 seconds. Typecheck, full
+ lint/reliability/localization/bundled-skill gates, max-lines (355 grandfathered suppressions and no
+ new bypass), focused formatting, and `git diff --check` all pass; lint emits only existing unrelated
+ warnings.
+- Aggregate oracle: accepts at most eight declared supported tuples and derives each immutable
+ archive name from its tuple/content identity. The exclusive input directory must contain exactly
+ those regular files, with no extras or links; each file is streamed under a 100-MiB/file and
+ 15-minute bound, and exact size, SHA-256, and stable pre/post file metadata must match before the
+ declared asset chain is returned. Duplicate tuple/name, unsupported tuple, malformed identity,
+ cancellation, missing/extra input, or byte drift fails closed.
+- Draft read-back oracle: requires one exact draft ID and stable/RC/perf tag, exact managed asset
+ names, uploaded state, nonzero declared size, at most 100 MiB per asset/1 GiB total, and a 15-minute
+ bound. It manually handles the authenticated GitHub asset API redirect, accepts only HTTPS
+ `release-assets.githubusercontent.com`, never forwards authorization to the CDN, requires exact
+ HTTP 200 from either direct or redirected downloads, streams every body, and requires exact size
+ and SHA-256. Published/cross-tag/incomplete/unexpected drafts, unsafe redirects, unexpected 2xx
+ statuses, changed/truncated/oversized bytes, and cancellation fail closed.
+- Workflow oracle: both POSIX and Windows artifact jobs syntax-check and execute the two new suites
+ under the same Node 24 native job families. The workflow retains read-only contents permission and
+ has no release, signing credential, or publication authority.
+- Does not prove: a real GitHub API/CDN redirect, a real draft or full-size asset, aggregate manifest
+ generation, Ed25519 signing, native signing/trust, upload/recovery execution, desktop
+ prerequisites/embedding, SSH transfer/install, fallback, performance, or an enabled tuple. The
+ local symlink fixture is skipped on Windows, although the shared implementation rejects all
+ non-regular directory entries and exact-head Windows execution remains required.
+- Follow-up: execute both suites on all six native jobs at the exact implementation head, then add
+ credential-free canonical manifest assembly/signature handoff without production credentials,
+ publication, or a desktop consumer.
+
## Accepted Gaps
No product gap is accepted merely because it appears in this list. Each entry requires explicit
@@ -8903,12 +9049,13 @@ The project is not complete until every applicable item below is checked with ev
## Next Required Action
-Run the disconnected credential-free release/signing DAG and draft-recovery contracts on all six
-native jobs at the exact implementation head. If green, implement the next artifact-only
-aggregate-input/read-back capability without release publication or a desktop consumer. In parallel,
-provision qualifying exact-floor execution for Linux kernel 4.18, macOS 13.5, and Windows arm64 build
-26100 and real macOS/Windows signing and trust. Keep every tuple disabled until the applicable
-baseline and trust cells pass.
+Commit and run the disconnected aggregate-input and authenticated draft read-back verifiers on all
+six native jobs at the exact implementation head. If green, record exact job evidence and implement
+the next credential-free canonical-manifest assembly/signature-handoff slice without release
+publication, production signing credentials, or a desktop consumer. In parallel, provision
+qualifying exact-floor execution for Linux kernel 4.18, macOS 13.5, and Windows arm64 build 26100 and
+real macOS/Windows signing and trust. Keep every tuple disabled until the applicable baseline and
+trust cells pass.
Cross-family Layer B targets, the protected manifest-signing environment, oldest-baseline/native-
trust cells, and the paired legacy performance baseline remain release/default-path blockers. No